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