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