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