Fix typo: environnement->environment.
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / Xcos.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Bruno JOFRET
4  * Copyright (C) 2010 - DIGITEO - Clement DAVID
5  *
6  * This file must be used under the terms of the CeCILL.
7  * This source file is licensed as described in the file COPYING, which
8  * you should have received as part of this distribution.  The terms
9  * are also available at
10  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
11  *
12  */
13
14 package org.scilab.modules.xcos;
15
16 import java.awt.Component;
17 import java.awt.GraphicsEnvironment;
18 import java.io.File;
19 import java.io.IOException;
20 import java.lang.reflect.InvocationTargetException;
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.logging.LogManager;
31 import java.util.logging.Logger;
32
33 import javax.swing.Action;
34 import javax.swing.ImageIcon;
35 import javax.swing.SwingUtilities;
36
37 import org.scilab.modules.action_binding.InterpreterManagement;
38 import org.scilab.modules.commons.xml.XConfiguration;
39 import org.scilab.modules.core.Scilab;
40 import org.scilab.modules.graph.utils.ScilabExported;
41 import org.scilab.modules.gui.bridge.menu.SwingScilabMenu;
42 import org.scilab.modules.gui.bridge.menubar.SwingScilabMenuBar;
43 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
44 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
45 import org.scilab.modules.gui.messagebox.ScilabModalDialog.AnswerOption;
46 import org.scilab.modules.gui.messagebox.ScilabModalDialog.ButtonType;
47 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
48 import org.scilab.modules.gui.tabfactory.AbstractScilabTabFactory;
49 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
50 import org.scilab.modules.gui.utils.BarUpdater;
51 import org.scilab.modules.gui.utils.ClosingOperationsManager;
52 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
53 import org.scilab.modules.gui.utils.WindowsConfigurationManager;
54 import org.scilab.modules.localization.Messages;
55 import org.scilab.modules.xcos.actions.ExternalAction;
56 import org.scilab.modules.xcos.block.BasicBlock;
57 import org.scilab.modules.xcos.block.SuperBlock;
58 import org.scilab.modules.xcos.configuration.ConfigurationManager;
59 import org.scilab.modules.xcos.configuration.model.DocumentType;
60 import org.scilab.modules.xcos.graph.DiagramComparator;
61 import org.scilab.modules.xcos.graph.SuperBlockDiagram;
62 import org.scilab.modules.xcos.graph.XcosDiagram;
63 import org.scilab.modules.xcos.io.XcosFileType;
64 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
65 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
66 import org.scilab.modules.xcos.palette.PaletteManager;
67 import org.scilab.modules.xcos.palette.view.PaletteManagerView;
68 import org.scilab.modules.xcos.preferences.XcosConfiguration;
69 import org.scilab.modules.xcos.utils.BlockPositioning;
70 import org.scilab.modules.xcos.utils.FileUtils;
71 import org.scilab.modules.xcos.utils.XcosMessages;
72
73 import com.mxgraph.model.mxCell;
74 import com.mxgraph.model.mxGraphModel;
75 import com.mxgraph.model.mxICell;
76 import com.mxgraph.util.mxEvent;
77 import com.mxgraph.util.mxEventObject;
78 import com.mxgraph.view.mxStylesheet;
79
80 /**
81  * Xcos entry point class
82  */
83 // CSOFF: ClassFanOutComplexity
84 // CSOFF: ClassDataAbstractionCoupling
85 public final class Xcos {
86     /**
87      * The current Xcos version
88      */
89     public static final String VERSION = "1.0";
90     /**
91      * The current Xcos tradename
92      */
93     public static final String TRADENAME = "Xcos";
94     public static final ImageIcon ICON = new ImageIcon(ScilabSwingUtilities.findIcon("utilities-system-monitor", "256x256"));
95
96     private static final String LOAD_XCOS_LIBS_LOAD_SCICOS = "loadXcosLibs(); loadScicos();";
97
98     /*
99      * Dependencies version
100      */
101     private static final List<String> MXGRAPH_VERSIONS = null;
102     private static final List<String> BATIK_VERSIONS = Arrays.asList("1.7", "1.8pre", "1.8");
103
104     private static final String IS_HEADLESS = Messages.gettext("a graphical environment is needed.");
105     private static final String UNABLE_TO_LOAD_JGRAPHX = Messages.gettext("Unable to load the jgraphx library.\nExpecting version %s ; Getting version %s .");
106     private static final String UNABLE_TO_LOAD_BATIK = Messages.gettext("Unable to load the Batik library. \nExpecting version %s ; Getting version %s .");
107
108     private static final String CALLED_OUTSIDE_THE_EDT_THREAD = "Called outside the EDT thread.";
109     private static final Logger LOG = Logger.getLogger(Xcos.class.getSimpleName());
110
111     /** common shared instance */
112     private static volatile Xcos sharedInstance;
113
114     static {
115         Scilab.registerInitialHook(new Runnable() {
116             @Override
117             public void run() {
118                 /* load scicos libraries (macros) */
119                 InterpreterManagement.requestScilabExec(LOAD_XCOS_LIBS_LOAD_SCICOS);
120             }
121         });
122
123         XConfiguration.addXConfigurationListener(new XcosConfiguration());
124     }
125
126     /*
127      * Instance data
128      */
129     private final Map<File, Collection<XcosDiagram>> diagrams;
130     private boolean onDiagramIteration = false;
131     private String lastError = null;
132
133     /*
134      * Instance handlers
135      */
136     private final PaletteManager palette;
137     private final ConfigurationManager configuration;
138     private final mxStylesheet styleSheet;
139     private final List<ExternalAction> externalActions;
140
141     private final XcosTabFactory factory;
142
143     /**
144      * Construct an Xcos instance.
145      *
146      * There must be only one Xcos instance per Scilab application
147      */
148     private Xcos(final XcosTabFactory factory) {
149         /*
150          * Read the configuration to support dynamic (before Xcos launch)
151          * settings.
152          */
153         try {
154             LogManager.getLogManager().readConfiguration();
155         } catch (final SecurityException e) {
156             LOG.severe(e.toString());
157         } catch (final IOException e) {
158             LOG.severe(e.toString());
159         }
160
161         /* Check the dependencies at startup time */
162         checkDependencies();
163
164         /*
165          * Allocate data
166          */
167         diagrams = new HashMap<File, Collection<XcosDiagram>>();
168         // null is used for not saved diagrams
169         addDiagram(null, null);
170
171         /*
172          * get the handlers instance
173          */
174         palette = PaletteManager.getInstance();
175         configuration = ConfigurationManager.getInstance();
176         styleSheet = new mxStylesheet();
177         externalActions = new ArrayList<ExternalAction>();
178
179         try {
180             FileUtils.decodeStyle(styleSheet);
181         } catch (final IOException e) {
182             LOG.severe(e.toString());
183         }
184
185         /*
186          * Register as an AbstractScilabTabFactory
187          */
188         if (factory == null) {
189             this.factory = new XcosTabFactory(false);
190         } else {
191             this.factory = factory;
192         }
193         ScilabTabFactory.getInstance().addTabFactory(this.factory);
194     }
195
196     /**
197      * Check the dependencies and the version dependencies.
198      *
199      * This method use runtime class loading to handle ClassNotFoundException.
200      *
201      * This method catch any exception and rethrow it with a well defined
202      * message. Thus it doesn't pass the IllegalCatch metrics.
203      */
204     // CSOFF: IllegalCatch
205     // CSOFF: MagicNumber
206     private void checkDependencies() {
207         final ClassLoader loader = ClassLoader.getSystemClassLoader();
208
209         /* Check not headless */
210         if (GraphicsEnvironment.isHeadless()) {
211             throw new RuntimeException(IS_HEADLESS);
212         }
213
214         /* JGraphx */
215         String mxGraphVersion = "";
216         try {
217             final Class<?> klass = loader.loadClass("com.mxgraph.view.mxGraph");
218             mxGraphVersion = (String) klass.getDeclaredField("VERSION").get(null);
219
220             if (MXGRAPH_VERSIONS != null && !MXGRAPH_VERSIONS.contains(mxGraphVersion)) {
221                 throw new Exception();
222             }
223         } catch (final Throwable e) {
224             throw new RuntimeException(String.format(UNABLE_TO_LOAD_JGRAPHX, MXGRAPH_VERSIONS.get(0), mxGraphVersion), e);
225         }
226
227         /* Batik */
228         String batikVersion = null;
229         try {
230             final Class<?> klass = loader.loadClass("org.apache.batik.Version");
231             batikVersion = klass.getPackage().getImplementationVersion().split("\\+")[0];
232
233             if (!BATIK_VERSIONS.contains(batikVersion)) {
234                 throw new Exception();
235             }
236
237         } catch (final Throwable e) {
238             throw new RuntimeException(String.format(UNABLE_TO_LOAD_BATIK, BATIK_VERSIONS.get(0), batikVersion), e);
239         }
240     }
241
242     // CSON: MagicNumber
243     // CSON: IllegalCatch
244
245     /**
246      * @return the per Scilab application, Xcos instance
247      */
248     public static synchronized Xcos getInstance() {
249         return getInstance(null);
250     }
251
252     /**
253      * @param factory
254      *            the tab factory instance or null on creation
255      * @return the per Scilab application, Xcos instance
256      */
257     private static synchronized Xcos getInstance(final XcosTabFactory factory) {
258         if (sharedInstance == null) {
259             sharedInstance = new Xcos(factory);
260
261             LOG.finest("Session started");
262         }
263
264         return sharedInstance;
265     }
266
267     /**
268      * Try to quit xcos
269      */
270     public void quit(boolean force) {
271         if (sharedInstance == null) {
272             return;
273         }
274
275     }
276
277     /**
278      * Clear the shared instance.
279      */
280     private static synchronized void clearInstance() {
281         sharedInstance = null;
282         LOG.finest("Session ended");
283     }
284
285     /**
286      * All Opened diagrams
287      *
288      * @return the opened diagrams list
289      */
290     public List<XcosDiagram> openedDiagrams() {
291         final List<XcosDiagram> opened = new ArrayList<XcosDiagram>();
292         for (File f : diagrams.keySet()) {
293             opened.addAll(openedDiagrams(f));
294         }
295
296         return opened;
297     }
298
299     /**
300      * Opened diagrams
301      *
302      * @param f
303      *            the file
304      * @return the opened diagrams list
305      */
306     public List<XcosDiagram> openedDiagrams(File f) {
307         final List<XcosDiagram> opened = new ArrayList<XcosDiagram>();
308         for (XcosDiagram d : diagrams.get(f)) {
309             if (d.isOpened()) {
310                 opened.add(d);
311             }
312         }
313
314         return opened;
315     }
316
317     /**
318      * Check if the in memory file representation is modified
319      *
320      * @param f
321      *            the file
322      * @return is modified
323      */
324     public boolean isModified(File f) {
325         for (XcosDiagram d : diagrams.get(f)) {
326             if (d.isModified()) {
327                 return true;
328             }
329         }
330
331         return false;
332     }
333
334     /**
335      * @return the global shared styleSheet
336      */
337     public mxStylesheet getStyleSheet() {
338         return styleSheet;
339     }
340
341     /**
342      * Open a file from it's filename.
343      *
344      * This method must be called on the EDT thread. For other use, please use
345      * the {@link #xcos(String, String)} method.
346      *
347      * @param file
348      *            the file to open. If null an empty diagram is created.
349      * @param variable
350      *            the variable to decode. If null no decode is performed.
351      */
352     public void open(final String file, final String variable) {
353         if (!SwingUtilities.isEventDispatchThread()) {
354             LOG.severe(CALLED_OUTSIDE_THE_EDT_THREAD);
355         }
356
357         /*
358          * If it is the first window opened, then open the palette first.
359          */
360         if (file == null && variable == null && openedDiagrams().isEmpty()) {
361             PaletteManager.setVisible(true);
362         }
363
364         XcosDiagram diag = null;
365         final File f;
366         if (file != null) {
367             f = new File(file);
368         } else {
369             f = null;
370         }
371
372         if (f != null && f.exists()) {
373             configuration.addToRecentFiles(f);
374         }
375
376         /*
377          * looking for an already opened diagram
378          */
379         final Collection<XcosDiagram> diags = diagrams.get(f);
380         if (diags != null && !diags.isEmpty()) {
381             diag = diags.iterator().next();
382         }
383         // if unsaved and empty, reuse it. Allocate otherwise.
384         if (f == null && diag != null && diag.getModel().getChildCount(diag.getDefaultParent()) > 0) {
385             diag = null;
386         }
387         // if reuse then request focus
388         if (diag != null) {
389             XcosTab tab = XcosTab.get(diag);
390             if (tab != null) {
391                 tab.setCurrent();
392                 tab.requestFocus();
393             }
394         }
395
396         if (diag != null) {
397             // loading disabled, unlock
398             synchronized (this) {
399                 setLastError("");
400                 notify();
401             }
402         } else {
403             // loading enable, unlock will be performed later, on another thread
404
405             /*
406              * Allocate and setup a new diagram
407              */
408             diag = new XcosDiagram();
409             diag.installListeners();
410
411             /*
412              * Ask for file creation
413              */
414             if (f != null && !f.exists()) {
415                 if (!diag.askForFileCreation(f)) {
416                     // loading disabled, unlock
417                     synchronized (this) {
418                         setLastError("");
419                         notify();
420                     }
421
422                     // return now, to avoid tab creation
423                     return;
424                 }
425             }
426
427             /*
428              * Create a visible window before loading
429              */
430             if (XcosTab.get(diag) == null) {
431                 XcosTab.restore(diag);
432             }
433
434             /*
435              * Load the file
436              */
437             diag.transformAndLoadFile(file, variable);
438
439             if (diag != null) {
440                 addDiagram(diag.getSavedFile(), diag);
441             }
442         }
443
444         if (diag != null) {
445             diag.updateTabTitle();
446         }
447     }
448
449     /**
450      * Log a loading error
451      *
452      * @param lastError
453      *            the error description
454      */
455     public void setLastError(String error) {
456         this.lastError = error;
457     }
458
459     /**
460      * Get an unmodifiable view of the diagrams for a specific file
461      *
462      * @param f
463      *            the file
464      * @return the diagram collection
465      */
466     public Collection<XcosDiagram> getDiagrams(final File f) {
467         final Collection<XcosDiagram> diags = diagrams.get(f);
468         if (diags == null) {
469             return null;
470         }
471         return Collections.unmodifiableCollection(diags);
472     }
473
474     /**
475      * Add a diagram to the diagram list for a file. Be sure to set the right
476      * opened status on the diagram before calling this method.
477      *
478      * @param f
479      *            the file
480      * @param diag
481      *            the diag
482      */
483     public void addDiagram(final File f, final XcosDiagram diag) {
484         if (onDiagramIteration) {
485             throw new RuntimeException();
486         }
487
488         /*
489          * Create the collection if it does not exist
490          */
491         Collection<XcosDiagram> diags = diagrams.get(f);
492         if (diags == null) {
493             diags = createDiagramCollection();
494             diagrams.put(f, diags);
495         }
496
497         if (diag != null) {
498             /*
499              * Remove the diagram (and any child)
500              */
501             final Collection<XcosDiagram> toBeMoved = removeChildren(diag);
502
503             /*
504              * Add the diagram to the collection
505              */
506             diags.addAll(toBeMoved);
507         }
508     }
509
510     private Collection<XcosDiagram> removeChildren(XcosDiagram diag) {
511         final Collection<XcosDiagram> removed = new HashSet<XcosDiagram>();
512         removed.add(diag);
513
514         for (Collection<XcosDiagram> it : diagrams.values()) {
515             if (!it.contains(diag)) {
516                 continue;
517             }
518
519             /*
520              * Add all children to the removed collection.
521              */
522             for (XcosDiagram graph : it) {
523                 if (graph instanceof SuperBlockDiagram) {
524                     final XcosDiagram parent = ((SuperBlockDiagram) graph).getContainer().getParentDiagram();
525
526                     // As "it" is sorted according to the hierarchy, "removed"
527                     // is also ordered.
528                     if (removed.contains(parent)) {
529                         removed.add(graph);
530                     }
531                 }
532
533             }
534
535             /*
536              * really remove them all
537              */
538             it.removeAll(removed);
539
540         }
541
542         return removed;
543     }
544
545     /**
546      * Create a diagram collections (sorted List)
547      *
548      * @return the diagram collection
549      */
550     @SuppressWarnings("serial")
551     public Collection<XcosDiagram> createDiagramCollection() {
552         return new ArrayList<XcosDiagram>() {
553             @Override
554             public boolean add(XcosDiagram element) {
555                 final boolean status = super.add(element);
556                 DiagramComparator.sort(this);
557                 return status;
558             }
559
560             @Override
561             public boolean addAll(Collection <? extends XcosDiagram > c) {
562                 final boolean status = super.addAll(c);
563                 DiagramComparator.sort(this);
564                 return status;
565             }
566         };
567     }
568
569     /**
570      * Try to close the graph (popup save dialog)
571      *
572      * @param graph
573      *            the graph to close
574      * @return if we can (or not) close the graph
575      */
576     public boolean canClose(final XcosDiagram graph) {
577         boolean canClose = false;
578         final File f = graph.getSavedFile();
579
580         final boolean wasLastOpened = openedDiagrams(f).size() <= 1;
581         final boolean isModified = isModified(f);
582         if (!(wasLastOpened && isModified)) {
583             canClose = true;
584         }
585
586         if (!canClose) {
587             final AnswerOption ans = ScilabModalDialog.show(XcosTab.get(graph), XcosMessages.DIAGRAM_MODIFIED, XcosMessages.XCOS, IconType.QUESTION_ICON,
588                                      ButtonType.YES_NO_CANCEL);
589
590             switch (ans) {
591                 case YES_OPTION:
592                     canClose = diagrams.get(f).iterator().next().saveDiagram();
593                     break;
594                 case NO_OPTION:
595                     canClose = true; // can close
596                     break;
597                 default:
598                     canClose = false; // operation canceled
599                     break;
600             }
601         }
602
603         /*
604          * Update configuration before the destroy call to validate the uuid
605          */
606         if (canClose) {
607             configuration.addToRecentTabs(graph);
608             configuration.saveConfig();
609         }
610         return canClose;
611     }
612
613     /**
614      * Close a diagram.
615      *
616      * This method must be called on the EDT thread.
617      *
618      * @param graph
619      *            the diagram to close
620      */
621     public void destroy(XcosDiagram graph) {
622         final File f = graph.getSavedFile();
623         final boolean wasLastOpenedForFile = openedDiagrams(f).size() <= 1;
624
625         if (!onDiagramIteration && wasLastOpenedForFile) {
626             diagrams.remove(f);
627         }
628
629         if (openedDiagrams().size() <= 1) {
630             /* halt scicos (stop the simulation) */
631             InterpreterManagement.requestScilabExec("if isdef('haltscicos'), haltscicos(), end;");
632         }
633     }
634
635     /**
636      * @param graph
637      *            the graph to handle
638      * @param list
639      *            the diagram to check
640      * @return diagram name for the "Are your sure ?" dialog
641      */
642     public String askForClosing(final XcosDiagram graph, final List<SwingScilabTab> list) {
643         final String msg;
644
645         if (wasLastOpened(list)) {
646             msg = TRADENAME;
647         } else {
648             msg = null;
649         }
650
651         return msg;
652     }
653
654     /**
655      * Does Xcos will close or not ?
656      *
657      * @param list
658      *            the list to be closed
659      * @return true if all files will be close on tabs close.
660      */
661     public boolean wasLastOpened(final List<SwingScilabTab> list) {
662         final HashSet<String> opened = new HashSet<String>();
663         for (XcosDiagram diag : openedDiagrams()) {
664             opened.add(diag.getGraphTab());
665         }
666
667         final HashSet<String> tabs = new HashSet<String>();
668         for (SwingScilabTab tab : list) {
669             if (tab != null) {
670                 tabs.add(tab.getPersistentId());
671             }
672         }
673
674         opened.removeAll(tabs);
675
676         return opened.isEmpty();
677     }
678
679     /**
680      * @return the external action list
681      */
682     public List<ExternalAction> getExternalActions() {
683         return externalActions;
684     }
685
686     /**
687      * Close the current xcos session.
688      *
689      * This method must be called on the EDT thread. For other use, please use
690      * the {@link #closeXcosFromScilab()} method.
691      */
692     public static synchronized void closeSession(final boolean ask) {
693         if (!SwingUtilities.isEventDispatchThread()) {
694             LOG.severe(CALLED_OUTSIDE_THE_EDT_THREAD);
695         }
696
697         /* Doesn't instantiate xcos on close operation */
698         if (sharedInstance == null) {
699             return;
700         }
701
702         /*
703          * Try to close all opened files
704          */
705         final Xcos instance = sharedInstance;
706
707         // get all tabs
708         final List<SwingScilabTab> tabs = new ArrayList<SwingScilabTab>();
709         for (final Collection<XcosDiagram> diags : instance.diagrams.values()) {
710             for (final XcosDiagram diag : diags) {
711                 final SwingScilabTab tab = XcosTab.get(diag);
712                 if (tab != null) {
713                     tabs.add(tab);
714                 }
715             }
716         }
717
718         // ask to close
719         final boolean status = ClosingOperationsManager.startClosingOperation(tabs, ask, ask);
720
721         // clear states
722         if (status) {
723             /* reset the shared instance state */
724             instance.diagrams.keySet().clear();
725             instance.addDiagram(null, null);
726
727             /* terminate any remaining simulation */
728             InterpreterManagement.putCommandInScilabQueue("if isdef('haltscicos'), haltscicos(), end;");
729
730             /* Saving modified data */
731             instance.palette.saveConfig();
732             instance.configuration.saveConfig();
733         }
734     }
735
736     /*
737      * Scilab exported methods.
738      *
739      * All the following methods must use SwingUtilities method to assert that
740      * the operations will be called on the EDT thread.
741      *
742      * @see modules/xcos/src/jni/Xcos.giws.xml
743      *
744      * @see sci_gateway/xcos_gateway.xml
745      *
746      * @see modules/xcos/sci_gateway/cpp/sci_*.cpp
747      */
748
749     /**
750      * Main entry point
751      *
752      * This method invoke Xcos operation on the EDT thread.
753      *
754      * @param file
755      *            The filename (can be null)
756      * @param variable
757      *            The Scilab variable to load (can be null)
758      */
759     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
760     public static void xcos(final String file, final String variable) {
761         final Xcos instance = getInstance();
762         instance.lastError = null;
763
764         /* load scicos libraries (macros) */
765         InterpreterManagement.requestScilabExec(LOAD_XCOS_LIBS_LOAD_SCICOS);
766
767         synchronized (instance) {
768             /*
769              * Open the file
770              */
771             SwingUtilities.invokeLater(new Runnable() {
772                 @Override
773                 public void run() {
774                     // open on EDT
775                     instance.open(file, variable);
776                 }
777             });
778
779             /*
780              * Wait loading and fail on error only if the variable is readeable
781              */
782             try {
783                 while (variable != null && instance.lastError == null) {
784                     instance.wait();
785                 }
786             } catch (InterruptedException e) {
787                 e.printStackTrace();
788             }
789         }
790         if (instance.lastError != null && !instance.lastError.isEmpty()) {
791             throw new RuntimeException(instance.lastError);
792         }
793     }
794
795     /**
796      * Close the current xcos session from any thread.
797      *
798      * This method invoke Xcos operation on the EDT thread. Please prefer using
799      * {@link #closeSession()} when the caller is on the EDT thread.
800      */
801     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
802     public static void closeXcosFromScilab() {
803         try {
804             SwingUtilities.invokeAndWait(new Runnable() {
805                 @Override
806                 public void run() {
807                     closeSession(false);
808                     clearInstance();
809                 }
810             });
811         } catch (final InterruptedException e) {
812             e.printStackTrace();
813         } catch (final InvocationTargetException e) {
814             e.printStackTrace();
815
816             Throwable throwable = e;
817             String firstMessage = null;
818             while (throwable != null) {
819                 firstMessage = throwable.getLocalizedMessage();
820                 throwable = throwable.getCause();
821             }
822
823             throw new RuntimeException(firstMessage, e);
824         }
825     }
826
827     /**
828      * Look in each diagram to find the block corresponding to the given uid and
829      * display a warning message.
830      *
831      * This method invoke Xcos operation on the EDT thread.
832      *
833      * @param uid
834      *            A String as UID.
835      * @param message
836      *            The message to display.
837      */
838     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
839     public static void warnCellByUID(final String[] uid, final String message) {
840         try {
841             SwingUtilities.invokeAndWait(new Runnable() {
842                 @Override
843                 public void run() {
844                     getInstance().warnCell(uid, message);
845                 }
846             });
847         } catch (final InterruptedException e) {
848             LOG.severe(e.toString());
849         } catch (final InvocationTargetException e) {
850             Throwable throwable = e;
851             String firstMessage = null;
852             while (throwable != null) {
853                 firstMessage = throwable.getLocalizedMessage();
854                 throwable = throwable.getCause();
855             }
856
857             throw new RuntimeException(firstMessage, e);
858         }
859     }
860
861     private void warnCell(final String[] uid, final String message) {
862         final mxCell cell = (mxCell) lookupForCell(uid);
863
864         // We are unable to find the block with the right id
865         if (cell == null) {
866             return;
867         }
868
869         // finally perform the action on the last block
870         final XcosDiagram parent = findParent(cell);
871         parent.warnCellByUID(cell.getId(), message);
872
873         SwingUtilities.invokeLater(new Runnable() {
874             @Override
875             public void run() {
876                 /*
877                  * Focus on an existing diagram
878                  */
879                 XcosTab.get(parent).setCurrent();
880             }
881         });
882     }
883
884     private Object lookupForCell(final String[] uid) {
885         final ArrayDeque<String> deque = new ArrayDeque<String>(Arrays.asList(uid));
886
887         // specific case with an empty array
888         if (deque.isEmpty()) {
889             return null;
890         }
891
892         // first element
893         Object cell = null;
894         Collection<XcosDiagram> diags = null;
895         String id = deque.pop();
896         try {
897             onDiagramIteration = true;
898
899             for (Collection<XcosDiagram> ds : diagrams.values()) {
900                 if (ds.isEmpty()) {
901                     continue;
902                 }
903
904                 final XcosDiagram root = ds.iterator().next();
905
906                 cell = ((mxGraphModel) root.getModel()).getCell(id);
907                 if (cell != null) {
908                     diags = ds;
909                     break;
910                 }
911             }
912         } finally {
913             onDiagramIteration = false;
914         }
915
916         // loop to get only the last diagram
917         while (cell instanceof SuperBlock && !deque.isEmpty()) {
918             final SuperBlock block = (SuperBlock) cell;
919
920             block.getParentDiagram().warnCellByUID(block.getId(), XcosMessages.ERROR_UNABLE_TO_COMPILE_THIS_SUPER_BLOCK);
921
922             id = deque.pop();
923
924             if (!diags.contains(block.getChild()) || !block.getChild().isOpened()) {
925                 block.openBlockSettings(null);
926             }
927
928             final mxGraphModel model = ((mxGraphModel) block.getChild().getModel());
929             cell = model.getCell(id);
930         }
931
932         return cell;
933     }
934
935     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
936     public static void updateBlock(final String h5File) {
937         try {
938             SwingUtilities.invokeAndWait(new Runnable() {
939                 @Override
940                 public void run() {
941                     getInstance().updateBlockInstance();
942                 }
943             });
944         } catch (final InterruptedException e) {
945             LOG.severe(e.toString());
946         } catch (final InvocationTargetException e) {
947             Throwable throwable = e;
948             String firstMessage = null;
949             while (throwable != null) {
950                 firstMessage = throwable.getLocalizedMessage();
951                 throwable = throwable.getCause();
952             }
953
954             throw new RuntimeException(firstMessage, e);
955         }
956     }
957
958     private void updateBlockInstance() {
959         // get the cell
960         BasicBlock modifiedBlock;
961
962         final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
963         if (handler == null) {
964             return;
965         }
966
967         try {
968             modifiedBlock = handler.readBlock();
969         } catch (ScicosFormatException e) {
970             throw new RuntimeException(e);
971         } finally {
972             handler.release();
973         }
974         if (modifiedBlock == null) {
975             return;
976         }
977
978         // diagram lookup
979         final XcosDiagram diag = findParent(modifiedBlock);
980         if (diag == null) {
981             throw new RuntimeException(Messages.gettext("parent diagram not found."));
982         }
983
984         // finally update the instance
985         final mxGraphModel model = (mxGraphModel) diag.getModel();
986         final BasicBlock block = (BasicBlock) model.getCell(modifiedBlock.getId());
987         assert block != null;
988
989         block.updateBlockSettings(modifiedBlock);
990         block.setInterfaceFunctionName(modifiedBlock.getInterfaceFunctionName());
991         block.setSimulationFunctionName(modifiedBlock.getSimulationFunctionName());
992         block.setSimulationFunctionType(modifiedBlock.getSimulationFunctionType());
993         if (block instanceof SuperBlock) {
994             ((SuperBlock) block).setChild(null);
995         }
996
997         block.setStyle(block.getStyle() + ";blockWithLabel");
998         block.setValue(block.getSimulationFunctionName());
999         BlockPositioning.updateBlockView(block);
1000     }
1001
1002     /**
1003      * This function convert a Xcos diagram to Scilab variable.
1004      *
1005      * This method invoke Xcos operation on the EDT thread.
1006      *
1007      * @param xcosFile
1008      *            The xcos diagram file
1009      * @return Not used (compatibility)
1010      */
1011     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
1012     public static int xcosDiagramToScilab(final String xcosFile) {
1013         final File file = new File(xcosFile);
1014
1015         if (!file.exists()) {
1016             return 1;
1017         }
1018
1019         try {
1020             SwingUtilities.invokeAndWait(new Runnable() {
1021                 @Override
1022                 public void run() {
1023                     LOG.finest("xcosDiagramToScilab: entering");
1024                     final XcosDiagram diagram = new XcosDiagram();
1025
1026                     final XcosFileType filetype = XcosFileType.findFileType(file);
1027                     if (filetype != null) {
1028                         try {
1029                             LOG.finest("xcosDiagramToScilab: initialized");
1030                             filetype.load(xcosFile, diagram);
1031                             LOG.finest("xcosDiagramToScilab: loaded");
1032
1033                             final ScilabDirectHandler handler = ScilabDirectHandler.acquire();
1034                             if (handler == null) {
1035                                 return;
1036                             }
1037
1038                             try {
1039                                 handler.writeDiagram(diagram);
1040                             } finally {
1041                                 handler.release();
1042                             }
1043                         } catch (Exception e) {
1044                             throw new RuntimeException(e);
1045                         }
1046                     }
1047                     LOG.finest("xcosDiagramToScilab: exiting");
1048                 }
1049             });
1050         } catch (final InterruptedException e) {
1051             throw new RuntimeException(e);
1052         } catch (final InvocationTargetException e) {
1053             Throwable throwable = e;
1054             String firstMessage = null;
1055             while (throwable != null) {
1056                 firstMessage = throwable.getLocalizedMessage();
1057                 throwable = throwable.getCause();
1058             }
1059
1060             throw new RuntimeException(firstMessage, e);
1061         }
1062
1063         return 0;
1064     }
1065
1066     /**
1067      * Add a menu into xcos
1068      *
1069      * @param label
1070      *            the label to use
1071      * @param command
1072      *            the callback (as a Scilab executable String)
1073      */
1074     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
1075     public static void addToolsMenu(final String label, final String command) {
1076         final ExternalAction action = new ExternalAction(null, command);
1077         action.putValue(Action.NAME, label);
1078         final Xcos instance = Xcos.getInstance();
1079
1080         /*
1081          * Store for future tabs
1082          */
1083         instance.externalActions.add(action);
1084
1085         /*
1086          * Update opened tabs
1087          */
1088         for (final XcosDiagram d : instance.openedDiagrams()) {
1089             final String uuid = d.getGraphTab();
1090             final SwingScilabTab tab = ScilabTabFactory.getInstance().getFromCache(uuid);
1091
1092             if (tab != null) {
1093                 final SwingScilabMenuBar bar = ((SwingScilabMenuBar) tab.getMenuBar().getAsSimpleMenuBar());
1094
1095                 final Component[] comps = bar.getComponents();
1096                 for (Component component : comps) {
1097                     if (component instanceof SwingScilabMenu) {
1098                         final SwingScilabMenu menu = (SwingScilabMenu) component;
1099
1100                         if (menu.getText() == XcosMessages.TOOLS) {
1101                             menu.add(new ExternalAction(action, d));
1102                         }
1103                     }
1104                 }
1105
1106                 // Also update the parent window toolbar
1107                 BarUpdater.updateBars(tab.getParentWindowId(), tab.getMenuBar(), tab.getToolBar(), tab.getInfoBar(), tab.getName(), tab.getWindowIcon());
1108             }
1109         }
1110     }
1111
1112     /**
1113      * Open a diagram by uid.
1114      *
1115      * This method invoke Xcos operation on the EDT thread.
1116      *
1117      * @param uid
1118      *            UID path to a block.
1119      */
1120     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
1121     @Deprecated
1122     public static void xcosDiagramOpen(final String[] uid) {
1123         throw new UnsupportedOperationException();
1124     }
1125
1126     /**
1127      * Close a diagram by uid.
1128      *
1129      * This method invoke Xcos operation on the EDT thread.
1130      *
1131      * @param uid
1132      *            The diagram id path
1133      */
1134     @ScilabExported(module = "xcos", filename = "Xcos.giws.xml")
1135     @Deprecated
1136     public static void xcosDiagramClose(final String[] uid) {
1137         throw new UnsupportedOperationException();
1138     }
1139
1140     /**
1141      * Look for the parent diagram of the cell in the diagram hierarchy.
1142      *
1143      * @param cell
1144      *            the cell to search for
1145      * @return the associated diagram
1146      */
1147     public static XcosDiagram findParent(Object cell) {
1148         final Xcos instance = getInstance();
1149         try {
1150             instance.onDiagramIteration = true;
1151
1152             for (Collection<XcosDiagram> diags : instance.diagrams.values()) {
1153                 for (XcosDiagram diag : diags) {
1154                     final mxGraphModel model = (mxGraphModel) diag.getModel();
1155
1156                     // use the O(1) lookup
1157                     if (cell instanceof mxICell && model.getCell(((mxICell) cell).getId()) != null) {
1158                         if (cell instanceof BasicBlock) {
1159                             ((BasicBlock) cell).setParentDiagram(diag);
1160                         }
1161                         return diag;
1162                     }
1163                 }
1164             }
1165         } finally {
1166             instance.onDiagramIteration = false;
1167         }
1168
1169         return null;
1170     }
1171
1172     /*
1173      * @see org.scilab.modules.gui.tabfactory.AbstractScilabTabFactory
1174      */
1175     public static class XcosTabFactory extends AbstractScilabTabFactory {
1176
1177         /*
1178          * Cache
1179          */
1180         private DocumentType cachedDocumentType;
1181
1182         /**
1183          * Default constructor
1184          */
1185         public XcosTabFactory() {
1186             this(true);
1187         }
1188
1189         private XcosTabFactory(boolean instanciateXcos) {
1190             if (instanciateXcos) {
1191                 getInstance(this);
1192             }
1193         }
1194
1195         /**
1196          * Create/restore a tab for a given uuid
1197          *
1198          * @param uuid
1199          *            the specific uuid
1200          * @return the tab instance
1201          */
1202         @Override
1203         public synchronized SwingScilabTab getTab(final String uuid) {
1204             if (uuid == null) {
1205                 return null;
1206             }
1207
1208             SwingScilabTab tab = ScilabTabFactory.getInstance().getFromCache(uuid);
1209
1210             // Palette manager restore
1211             if (tab == null) {
1212                 if (PaletteManagerView.DEFAULT_TAB_UUID.equals(uuid)) {
1213                     PaletteManagerView.restore(null, false);
1214                     tab = PaletteManagerView.get();
1215                 }
1216             }
1217
1218             // diagram (tab or viewport) restore
1219             if (tab == null) {
1220                 cache(uuid);
1221                 if (cachedDocumentType == null) {
1222                     return null;
1223                 }
1224
1225                 final boolean isTab = uuid.equals(cachedDocumentType.getUuid());
1226                 final boolean isViewport = uuid.equals(cachedDocumentType.getViewport());
1227
1228                 final XcosDiagram graph = getDiagram(isTab, isViewport);
1229                 if (graph != null && isTab) {
1230                     XcosTab.restore(graph, false);
1231                     graph.fireEvent(new mxEventObject(mxEvent.ROOT));
1232                     tab = XcosTab.get(graph);
1233                 } else if (graph != null && isViewport) {
1234                     ViewPortTab.restore(graph, false);
1235                     tab = ViewPortTab.get(graph);
1236
1237                     ClosingOperationsManager.addDependency(XcosTab.get(graph), tab);
1238                     WindowsConfigurationManager.makeDependency(graph.getGraphTab(), tab.getPersistentId());
1239                 } else {
1240                     return null;
1241                 }
1242             }
1243
1244             WindowsConfigurationManager.restorationFinished(tab);
1245             ScilabTabFactory.getInstance().addToCache(tab);
1246
1247             return tab;
1248         }
1249
1250         private XcosDiagram getDiagram(boolean isTab, boolean isViewport) {
1251             XcosDiagram graph = null;
1252             if (isTab) {
1253                 // load a new diagram
1254                 graph = getInstance().configuration.loadDiagram(cachedDocumentType);
1255             } else if (isViewport) {
1256                 // get the cached diagram
1257                 final File f = getInstance().configuration.getFile(cachedDocumentType);
1258                 final Collection<XcosDiagram> diags = getInstance().diagrams.get(f);
1259
1260                 for (XcosDiagram d : diags) {
1261                     final String id = d.getGraphTab();
1262                     if (id != null && id.equals(cachedDocumentType.getUuid())) {
1263                         graph = d;
1264                         break;
1265                     }
1266                 }
1267             }
1268
1269             return graph;
1270         }
1271
1272         @Override
1273         public synchronized boolean isAValidUUID(String uuid) {
1274             // check the Palette manager view (static uuid)
1275             if (PaletteManagerView.DEFAULT_TAB_UUID.equals(uuid)) {
1276                 return true;
1277             }
1278
1279             /*
1280              * Cache and check against cache to ease next getTab(uuid) call
1281              */
1282             cache(uuid);
1283             return cachedDocumentType != null;
1284         }
1285
1286         /**
1287          * Cache the {@link DocumentType} for the specific uuid
1288          *
1289          * @param uuid
1290          *            the uuid
1291          */
1292         private void cache(String uuid) {
1293             /*
1294              * Handle a non null cache
1295              */
1296             if (cachedDocumentType != null) {
1297                 final boolean isTab = uuid.equals(cachedDocumentType.getUuid());
1298                 final boolean isViewport = uuid.equals(cachedDocumentType.getViewport());
1299
1300                 if (isTab || isViewport) {
1301                     return;
1302                 } else {
1303                     cachedDocumentType = null;
1304                 }
1305             }
1306
1307             /*
1308              * Invalid cache, look for the right one
1309              */
1310             final ConfigurationManager config = getInstance().configuration;
1311             final List<DocumentType> docs = config.getSettings().getTab();
1312             for (DocumentType d : docs) {
1313                 final boolean isTab = uuid.equals(d.getUuid());
1314                 final boolean isViewport = uuid.equals(d.getViewport());
1315
1316                 if (isTab || isViewport) {
1317                     cachedDocumentType = d;
1318                     break;
1319                 }
1320             }
1321         }
1322
1323         @Override
1324         public String getPackage() {
1325             return TRADENAME;
1326         }
1327
1328         @Override
1329         public String getClassName() {
1330             return XcosTabFactory.class.getName();
1331         }
1332
1333         @Override
1334         public String getApplication() {
1335             return TRADENAME;
1336         }
1337     }
1338 }
1339 // CSON: ClassDataAbstractionCoupling
1340 // CSON: ClassFanOutComplexity