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