60664ac6739c736fc5c6c11fa06d4b4a2ce5ac47
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / utils / WindowsConfigurationManager.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011 - Calixte DENIZET
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10  *
11  */
12
13 package org.scilab.modules.gui.utils;
14
15 import java.awt.Component;
16 import java.awt.Dimension;
17 import java.io.File;
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.HashMap;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import java.util.UUID;
26
27 import javax.swing.SwingUtilities;
28 import javax.xml.xpath.XPath;
29 import javax.xml.xpath.XPathConstants;
30 import javax.xml.xpath.XPathFactory;
31
32 import org.flexdock.docking.Dockable;
33 import org.flexdock.docking.DockingManager;
34 import org.flexdock.docking.activation.ActiveDockableTracker;
35 import org.flexdock.docking.state.LayoutNode;
36 import org.flexdock.perspective.persist.xml.LayoutNodeSerializer;
37 import org.flexdock.perspective.persist.xml.PersistenceConstants;
38 import org.scilab.modules.commons.ScilabCommons;
39 import org.scilab.modules.commons.ScilabCommonsUtils;
40 import org.scilab.modules.commons.xml.ScilabXMLUtilities;
41 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
42 import org.scilab.modules.gui.bridge.window.SwingScilabWindow;
43 import org.scilab.modules.gui.console.ScilabConsole;
44 import org.scilab.modules.gui.tab.Tab;
45 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
46 import org.scilab.modules.gui.window.ScilabWindow;
47 import org.scilab.modules.gui.window.Window;
48 import org.w3c.dom.Document;
49 import org.w3c.dom.Element;
50 import org.w3c.dom.NodeList;
51
52 /**
53  *
54  * Save the windows properties.
55  *
56  * @author Calixte DENIZET
57  */
58 public class WindowsConfigurationManager {
59
60     private static final int DEFAULTX = 0;
61     private static final int DEFAULTY = 0;
62     private static final int DEFAULTHEIGHT = 500;
63     private static final int DEFAULTWIDTH = 500;
64
65     private static final String SCI = "SCI";
66     private static final String WINDOWS_CONFIG_FILE = System.getenv(SCI) + "/modules/gui/etc/windowsConfiguration.xml";
67     private static final String USER_WINDOWS_CONFIG_FILE = ScilabCommons.getSCIHOME() + "/windowsConfiguration.xml";
68     private static final String NULLUUID = new UUID(0L, 0L).toString();
69     private static final Map<SwingScilabTab, EndedRestoration> endedRestoration = new HashMap<SwingScilabTab, EndedRestoration>();
70     private static final List<String> alreadyRestoredWindows = new ArrayList<String>();
71     private static final Map<String, Object> defaultWinAttributes = new HashMap<String, Object>();
72     private static final List<String> currentlyRestored = new ArrayList<String>();
73
74     private static boolean oneTry;
75     private static Document doc;
76
77     static {
78         defaultWinAttributes.put("x", new Integer(DEFAULTX));
79         defaultWinAttributes.put("y", new Integer(DEFAULTY));
80         defaultWinAttributes.put("height", new Integer(DEFAULTHEIGHT));
81         defaultWinAttributes.put("width", new Integer(DEFAULTWIDTH));
82         /*java.awt.Toolkit.getDefaultToolkit().addAWTEventListener(new java.awt.event.AWTEventListener() {
83           public void eventDispatched(java.awt.AWTEvent e) {
84           System.out.println(e);
85           }
86           }, java.awt.AWTEvent.FOCUS_EVENT_MASK);*/
87     }
88
89     /**
90      * Create a copy of windows configuration file in the user directory
91      */
92     public static void createUserCopy() {
93         if (isCopyNeeded()) {
94             ScilabCommonsUtils.copyFile(new File(WINDOWS_CONFIG_FILE), new File(USER_WINDOWS_CONFIG_FILE));
95             doc = null;
96         }
97     }
98
99     /**
100      * Read the file to modify
101      */
102     private static void readDocument() {
103         if (doc == null) {
104             createUserCopy();
105             doc = ScilabXMLUtilities.readDocument(USER_WINDOWS_CONFIG_FILE);
106         }
107
108         if (doc == null && !oneTry) {
109             System.err.println("Try to reload the default configuration file: " + WINDOWS_CONFIG_FILE);
110             File f = new File(USER_WINDOWS_CONFIG_FILE);
111             if (f.exists() && f.isFile()) {
112                 f.delete();
113             }
114             oneTry = true;
115             readDocument();
116         } else if (doc == null && oneTry) {
117             System.err.println("Serious problem to copy and parse the configuration file.");
118             System.err.println("Please check if you have the rights to write the file: " + USER_WINDOWS_CONFIG_FILE);
119             System.err.println("If the previous file exists, please check if it is a valid XML");
120             System.err.println("and if yes, please report a bug: http://bugzilla.scilab.org");
121         }
122     }
123
124     /**
125      * Write the document
126      */
127     private static void writeDocument() {
128         ScilabXMLUtilities.writeDocument(doc, USER_WINDOWS_CONFIG_FILE);
129     }
130
131     /**
132      * @return true if a copy is needed
133      */
134     private static final boolean isCopyNeeded() {
135         return !new File(USER_WINDOWS_CONFIG_FILE).exists();
136     }
137
138     /**
139      * Register an EndedRestoration, op.finish() will be executed when the tab restoration will be finished.
140      * @param tab the associated tab
141      * @param ended the closing operation
142      */
143     public static void registerEndedRestoration(SwingScilabTab tab, EndedRestoration ended) {
144         endedRestoration.put(tab, ended);
145     }
146
147     /**
148      * Register an EndedRestoration, op.finish() will be executed when the tab restoration will be finished.
149      * @param tab the associated tab
150      * @param ended the closing operation
151      */
152     public static void registerEndedRestoration(Tab tab, EndedRestoration ended) {
153         registerEndedRestoration((SwingScilabTab) tab.getAsSimpleTab(), ended);
154     }
155
156     /**
157      * Create a new node with parent element
158      * @param parent the parent element
159      * @param nodeName the node name
160      * @param attr an array containing attribute name followed by its value: "attr1", 1, "attr2", true, ...
161      * @return the created element
162      */
163     public static Element createNode(Element parent, String nodeName, Object[] attr) {
164         readDocument();
165         for (int i = 0; i < attr.length; i += 2) {
166             if (attr[i].equals("uuid")) {
167                 removeNode(parent, nodeName, (String) attr[i + 1]);
168             }
169         }
170
171         return ScilabXMLUtilities.createNode(doc, parent, nodeName, attr);
172     }
173
174     /**
175      * Save the window properties
176      * @param window the window
177      */
178     public static void saveWindowProperties(SwingScilabWindow window) {
179         readDocument();
180
181         Element root = doc.getDocumentElement();
182         Element win = createNode(root, "Window", new Object[] {"uuid", window.getUUID(),
183                                  "x", (int) window.getLocation().getX(),
184                                  "y", (int) window.getLocation().getY(),
185                                  "width", (int) window.getSize().getWidth(),
186                                  "height", (int) window.getSize().getHeight()
187                                                               });
188         LayoutNode layoutNode = window.getDockingPort().exportLayout();
189         LayoutNodeSerializer serializer = new LayoutNodeSerializer();
190         win.appendChild(serializer.serialize(doc, layoutNode));
191
192         for (Dockable dockable : (Set<Dockable>) window.getDockingPort().getDockables()) {
193             saveTabProperties((SwingScilabTab) dockable, false);
194         }
195
196         writeDocument();
197     }
198
199     /**
200      * Create a window according to the uuid.
201      *
202      * This method can be used to create a reference windows.
203      *
204      * @param uuid
205      *            the reference uuid
206      * @param preserveUUID
207      *            if true the uuid will be used on the new windows, generate a
208      *            new uuid otherwise
209      * @return the window
210      */
211     public static SwingScilabWindow createWindow(final String uuid, final boolean preserveUUID) {
212         readDocument();
213
214         final Element root = doc.getDocumentElement();
215         final boolean nullUUID = uuid.equals(NULLUUID);
216         final Map<String, Object> attrs = new HashMap<String, Object>();
217         Element win = null;
218         boolean containsX = true;
219
220         if (!nullUUID) {
221             win = getElementWithUUID(root, "Window", uuid);
222             if (win == null) {
223                 return null;
224             }
225
226             containsX = !win.getAttribute("x").equals("");
227
228             if (containsX) {
229                 attrs.put("x", int.class);
230                 attrs.put("y", int.class);
231             }
232
233             attrs.put("height", int.class);
234             attrs.put("width", int.class);
235             ScilabXMLUtilities.readNodeAttributes(win, attrs);
236         } else {
237             attrs.putAll(defaultWinAttributes);
238         }
239
240         SwingScilabWindow window = new SwingScilabWindow();
241
242         final String localUUID;
243         if (preserveUUID) {
244             localUUID = uuid;
245         } else {
246             localUUID = UUID.randomUUID().toString();
247         }
248         window.setUUID(localUUID);
249
250         if (containsX) {
251             window.setLocation(((Integer) attrs.get("x")).intValue(), ((Integer) attrs.get("y")).intValue());
252         }
253
254         window.setSize(((Integer) attrs.get("width")).intValue(), ((Integer) attrs.get("height")).intValue());
255
256         return window;
257     }
258
259     /**
260      * Restore a window with a given uuid
261      *
262      * @param uuid
263      *            the uuid
264      * @param restoreTab
265      *            if true the tab is restored too
266      * @return the corresponding window
267      */
268     public static SwingScilabWindow restoreWindow(String uuid, String defaultTabUuid, boolean restoreTab, boolean requestFocus) {
269         readDocument();
270
271         final boolean nullUUID = uuid.equals(NULLUUID);
272
273         // create the window and preserve the uuid if not null
274         final SwingScilabWindow window = (SwingScilabWindow) createWindow(uuid, !nullUUID);
275         if (window == null) {
276             return null;
277         }
278
279         if (restoreTab) {
280             if (!nullUUID) {
281                 final LayoutNodeSerializer serializer = new LayoutNodeSerializer();
282                 final Element dockingPort = getDockingPort(uuid);
283                 LayoutNode layoutNode = (LayoutNode) serializer.deserialize(dockingPort);
284                 window.getDockingPort().importLayout(layoutNode);
285             } else if (defaultTabUuid != null && !defaultTabUuid.isEmpty()) {
286                 SwingScilabTab defaultTab = ScilabTabFactory.getInstance().getTab(defaultTabUuid);
287                 defaultTab.setParentWindowId(window.getId());
288                 DockingManager.dock(defaultTab, window.getDockingPort());
289             }
290
291             for (SwingScilabTab tab : (Set<SwingScilabTab>) window.getDockingPort().getDockables()) {
292                 tab.setParentWindowId(window.getId());
293             }
294
295             SwingScilabTab[] tabs = new SwingScilabTab[window.getNbDockedObjects()];
296             tabs = ((Set<SwingScilabTab>) window.getDockingPort().getDockables()).toArray(tabs);
297
298             // Be sur that the main tab will have the focus.
299             // Get the elder tab and activate it
300             final SwingScilabTab mainTab = ClosingOperationsManager.getElderTab(new ArrayList(Arrays.asList(tabs)));
301             BarUpdater.updateBars(mainTab.getParentWindowId(), mainTab.getMenuBar(), mainTab.getToolBar(), mainTab.getInfoBar(), mainTab.getName(), mainTab.getWindowIcon());
302
303             if (!ScilabConsole.isExistingConsole() && tabs.length == 1 && tabs[0].getPersistentId().equals(NULLUUID)) {
304                 // null uuid is reserved to the console and in NW mode, there is no console.
305                 return null;
306             }
307
308             for (SwingScilabTab tab : tabs) {
309                 // each tab has now a window so it can be useful for the tab to set an icon window or to center a dialog...
310                 EndedRestoration ended = endedRestoration.get(tab);
311                 if (ended != null) {
312                     ended.finish();
313                     endedRestoration.remove(ended);
314                 }
315             }
316
317             if (tabs.length == 1) {
318                 // we remove undock and close buttons when there is only one View in the DockingPort
319                 SwingScilabTab.removeActions(tabs[0]);
320             } else {
321                 // we add undock and close buttons
322                 for (SwingScilabTab tab : tabs) {
323                     SwingScilabTab.addActions(tab);
324                 }
325             }
326
327             window.setVisible(true);
328
329             // Return only when the window is displayable
330             while (!window.isDisplayable()) {
331                 Thread.yield();
332             }
333
334             if (requestFocus) {
335                 SwingUtilities.invokeLater(new Runnable() {
336                     @Override
337                     public void run() {
338                         final Thread t = new Thread(new Runnable() {
339                             @Override
340                             public void run() {
341                                 synchronized (currentlyRestored) {
342                                     while (currentlyRestored.size() > 0) {
343                                         try {
344                                             currentlyRestored.wait();
345                                         } catch (InterruptedException e) {
346                                             e.printStackTrace();
347                                         }
348                                     }
349                                 }
350
351                                 // Be sure that te main tab or one of its subcomponent
352                                 // will have the focus on start-up
353                                 Component owner = null;
354                                 while (owner == null && !mainTab.isAncestorOf(owner)) {
355                                     mainTab.requestFocus();
356                                     Thread.yield();
357
358                                     owner = window.getFocusOwner();
359                                 }
360                                 ActiveDockableTracker.requestDockableActivation(mainTab);
361                                 window.toFront();
362                             }
363                         });
364                         t.start();
365                     }
366                 });
367             }
368         }
369
370         return window;
371     }
372
373     private static final Element getDockingPort(final String winUUID) {
374         readDocument();
375
376         final Element root = doc.getDocumentElement();
377         final Element win = getElementWithUUID(root, "Window", winUUID);
378         if (win == null) {
379             return null;
380         }
381
382         final NodeList children = win.getElementsByTagName(PersistenceConstants.DOCKING_PORT_NODE_ELEMENT_NAME);
383         return (Element) children.item(0);
384     }
385
386
387     /**
388      * Must be called when the restoration is finished
389      * @param tab the tab
390      */
391     public static final void restorationFinished(SwingScilabTab tab) {
392         synchronized (currentlyRestored) {
393             currentlyRestored.remove(tab.getPersistentId());
394
395             // notify after remove
396             currentlyRestored.notify();
397         }
398     }
399
400     /**
401      * Remove a window from the already restored windows
402      * @param uuid the win uuid
403      */
404     public static final void removeWin(String uuid) {
405         alreadyRestoredWindows.remove(uuid);
406     }
407
408     /**
409      * Find all the dependencies of the given tab. The returned list contains parent before its children.
410      * @param uuid the tab's uuid
411      * @return a list of the elements with the given uuid
412      */
413     public static final Set<Element> getTabDependencies(String uuid) {
414         readDocument();
415         Element root = doc.getDocumentElement();
416
417         // Children
418         List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "depends", uuid);
419
420         Set<Element> list = new LinkedHashSet<Element>();
421         Element el = getElementWithUUID(doc.getDocumentElement(), uuid);
422         if (el != null) {
423             //We add the parent
424             list.add(el);
425             // We add the children and their own children
426             for (Element e : elements) {
427                 list.addAll(getTabDependencies(e.getAttribute("uuid")));
428             }
429         }
430
431         return list;
432     }
433
434     /**
435      * Create all the tabs depending of the tab with the given uuid.
436      * The creation will respect the convention: parent before children.
437      * @param uuid the tab uuid
438      * @return the list of all the uuids to restore
439      */
440     public static final Set<Element> createDescendantTabs(String uuid) {
441         Set<Element> list = getTabDependencies(uuid);
442         Dimension nullDims = new Dimension(0, 0);
443         for (Element e : list) {
444             // All the tabs created in the factory will be cached so when Flexdock will restore the docking
445             // it will use the same tab as created here.
446             ScilabTabFactory factory = ScilabTabFactory.getInstance();
447             factory.addTabFactory(e.getAttribute("load"), e.getAttribute("factory"));
448             currentlyRestored.add(e.getAttribute("uuid"));
449             SwingScilabTab tab = factory.getTab(e.getAttribute("uuid"));
450             if (!e.getAttribute("width").isEmpty() && !e.getAttribute("height").isEmpty()) {
451                 tab.setMinimumSize(nullDims);
452                 tab.setPreferredSize(new Dimension(Integer.parseInt(e.getAttribute("width")), Integer.parseInt(e.getAttribute("width"))));
453             }
454         }
455
456         return list;
457     }
458
459     /**
460      * Useful for the following case: you have 3 tabs, A, B and C, C depends of B, A and C are docked in the same window.
461      * You want to restore A, the window containing A needs to restore C too (and finally B).
462      * There is no direct dependency between A and C but the fact that they're in the same dockport
463      * implies a dependency.
464      * @param elems the elems corresponding to the tabs to restore
465      * @return tabs to restore
466      */
467     private static final Set<Element> createAdjacentTabs(Set<Element> elems) {
468         readDocument();
469
470         Element root = doc.getDocumentElement();
471         boolean jobFinished = true;
472         Set<Element> toAdd = new LinkedHashSet<Element>();
473         for (Element e : elems) {
474             String winuuid = e.getAttribute("winuuid");
475             if (!winuuid.isEmpty() && !winuuid.equals(NULLUUID)) {
476                 List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "winuuid", winuuid);
477                 elements.removeAll(elems);
478                 jobFinished = jobFinished && elements.size() == 0;
479                 toAdd.addAll(elements);
480             }
481         }
482
483         if (jobFinished) {
484             return elems;
485         }
486
487         for (Element ee : toAdd) {
488             elems.addAll(createDescendantTabs(getElderParent(ee).getAttribute("uuid")));
489         }
490
491
492         return createAdjacentTabs(elems);
493     }
494
495     /**
496      * @param e the element
497      * @return the elder parent of this element (elder for the attribute "depends")
498      */
499     private static final Element getElderParent(Element e) {
500         readDocument();
501
502         Element root = doc.getDocumentElement();
503         String dep = e.getAttribute("depends");
504         if (!dep.isEmpty()) {
505             return getElderParent(ScilabXMLUtilities.getElementsWithAttributeEquals(root, "uuid", dep).get(0));
506         }
507
508         return e;
509     }
510
511     /**
512      * Starts the restoration of the tab with the given uuid
513      * @param uuid the tab uuid to restore
514      */
515     private static final void startRestoration(String uuid) {
516         Set<Element> list = createDescendantTabs(uuid);
517         list = createAdjacentTabs(list);
518         List<String> wins = new ArrayList<String>();
519         List<String> tabsWithoutWin = new ArrayList<String>();
520         for (Element e : list) {
521             String winuuid = e.getAttribute("winuuid");
522             if (winuuid.equals(NULLUUID) || getElementWithUUID(winuuid) == null || !isDockableIdExisting(winuuid, e.getAttribute("uuid"))) {
523                 tabsWithoutWin.add(e.getAttribute("uuid"));
524             } else if (!wins.contains(winuuid)) {
525                 wins.add(winuuid);
526             }
527         }
528
529         boolean requestFocus = true;
530
531         for (String winuuid : wins) {
532             if (!alreadyRestoredWindows.contains(winuuid)) {
533                 restoreWindow(winuuid, uuid, true, requestFocus);
534                 alreadyRestoredWindows.add(winuuid);
535                 if (requestFocus) {
536                     requestFocus = false;
537                 }
538             }
539         }
540
541         for (String u : tabsWithoutWin) {
542             SwingScilabWindow window = restoreWindow(NULLUUID, u, true, requestFocus);
543             alreadyRestoredWindows.add(window.getUUID());
544             if (requestFocus) {
545                 requestFocus = false;
546             }
547         }
548     }
549
550     /**
551      * Search a node (child of root) with name nodeName and with a given uuid
552      * @param root the root element
553      * @param nodeName the node name
554      * @param uuid the uuid
555      * @return the corresponding element or null if it does not exist
556      */
557     public static final Element getElementWithUUID(Element root, String nodeName, String uuid) {
558         if (uuid == null || uuid.isEmpty()) {
559             return null;
560         }
561         NodeList list = root.getElementsByTagName(nodeName);
562         int length = getNodeListLength(list);
563         for (int i = 0; i < length; i++) {
564             Element elem = (Element) list.item(i);
565             if (elem.getAttribute("uuid").equals(uuid)) {
566                 return elem;
567             }
568         }
569
570         return null;
571     }
572
573     /**
574      * Search a node with a given uuid
575      * @param uuid the uuid
576      * @return the corresponding element or null if it does not exist
577      */
578     public static final Element getElementWithUUID(String uuid) {
579         readDocument();
580
581         return getElementWithUUID(doc.getDocumentElement(), uuid);
582     }
583
584     /**
585      * Search a node (child of root) with name nodeName and with a given uuid
586      * @param root the root element
587      * @param uuid the uuid
588      * @return the corresponding element or null if it does not exist
589      */
590     public static final Element getElementWithUUID(Element root, String uuid) {
591         if (uuid == null || uuid.isEmpty()) {
592             return null;
593         }
594         List<Element> list = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "uuid", uuid);
595         if (list.size() != 0) {
596             return list.get(0);
597         }
598
599         return null;
600     }
601
602     /**
603      * Check if there is a window which has a dockableID equals to the given uuid
604      * @param winuuid the uuid of the window
605      * @param uuid the uuid to test
606      * @return true if a dockableId exists
607      */
608     public static final boolean isDockableIdExisting(String winuuid, String uuid) {
609         if (winuuid == null || winuuid.isEmpty() || uuid == null || uuid.isEmpty()) {
610             return false;
611         }
612
613         Element win = getElementWithUUID(winuuid);
614         if (win != null) {
615             List<Element> list = ScilabXMLUtilities.getElementsWithAttributeEquals(win, "dockableId", uuid);
616             if (list.size() != 0) {
617                 return true;
618             }
619         }
620
621         return false;
622     }
623
624     /**
625      * Validate all the windows in checking if all the dockable nodes are ok.
626      * If a split node contains an invalid uuid, then the invalid dockable is removed
627      * and the split node is replaced by the valid dockable.
628      */
629     private static final void validateWindows() {
630         // We remove all the blanks and carriage return
631         try {
632             XPath xp = XPathFactory.newInstance().newXPath();
633             NodeList nodes = (NodeList) xp.compile("//text()").evaluate(doc, XPathConstants.NODESET);
634             for (int i = 0; i < nodes.getLength(); i++) {
635                 nodes.item(i).getParentNode().removeChild(nodes.item(i));
636             }
637         } catch (Exception e) {
638             e.printStackTrace();
639         }
640
641         Element root = doc.getDocumentElement();
642         NodeList list = root.getElementsByTagName("Window");
643
644         int length = getNodeListLength(list);
645         for (int i = 0; i < length; i++) {
646             validateWindow(((Element) list.item(i)).getAttribute("uuid"));
647         }
648     }
649
650     /**
651      * Validate a window element.
652      * The window element in the DOM is eventually replaced by a valid one.
653      * @param winuuid the window uuid
654      */
655     private static final void validateWindow(final String winuuid) {
656         if (winuuid == null || winuuid.isEmpty()) {
657             return;
658         }
659
660         Element win = getElementWithUUID(winuuid);
661         if (win == null) {
662             return;
663         }
664
665         Element dp = (Element) win.getFirstChild();
666         Element e = validateDockingPortNode(winuuid, dp);
667         if (e == null) {
668             win.removeChild(dp);
669             return;
670         }
671
672         win.removeChild(dp);
673         win.appendChild(e);
674     }
675
676     /**
677      * Validate a dockingport node element.
678      * @param winuuid the window uuid
679      * @param e the element to validate
680      * @return a valid element
681      */
682     private static final Element validateDockingPortNode(final String winuuid, final Element e) {
683         Element ee = (Element) e.getFirstChild();
684         if (ee.getTagName().equals("DockableNode")) {
685             ee = validateDockableNode(winuuid, ee);
686             if (ee == null) {
687                 return null;
688             }
689             return e;
690         } else {
691             ee = validateSplitNode(winuuid, ee);
692             if (ee == null) {
693                 return null;
694             }
695             Element eee = (Element) e.cloneNode(false);
696             eee.appendChild(ee);
697
698             return eee;
699         }
700     }
701
702     /**
703      * Validate a dockable node element.
704      * @param winuuid the window uuid
705      * @param e the element to validate
706      * @return a valid element
707      */
708     private static final Element validateDockableNode(final String winuuid, final Element e) {
709         String id = e.getAttribute("dockableId");
710         Element ee = getElementWithUUID(id);
711         if (ee == null || !ee.getAttribute("winuuid").equals(winuuid)) {
712             return null;
713         }
714
715         return e;
716     }
717
718     /**
719      * Validate a split node element.
720      * @param winuuid the window uuid
721      * @param e the element to validate
722      * @return a valid element
723      */
724     private static final Element validateSplitNode(final String winuuid, final Element e) {
725         NodeList set = e.getChildNodes();
726         Element c1 = validateDockingPortNode(winuuid, (Element) set.item(0));
727         Element c2 = validateDockingPortNode(winuuid, (Element) set.item(1));
728
729         if (c1 != null && c2 != null) {
730             Element ee = (Element) e.cloneNode(false);
731             ee.appendChild(c1);
732             ee.appendChild(c2);
733             return ee;
734         }
735
736         if (c1 == null && c2 == null) {
737             return null;
738         }
739
740         if (c1 == null) {
741             return (Element) c2.getFirstChild().cloneNode(true);
742         }
743
744         return (Element) c1.getFirstChild().cloneNode(true);
745     }
746
747     /**
748      * Remove a node with a given uuid
749      * @param parent the parent element
750      * @param nodeName the node name
751      * @param uuid the uuid
752      */
753     private static final void removeNode(Element parent, String nodeName, String uuid) {
754         if (uuid == null || uuid.isEmpty()) {
755             return;
756         }
757         Element e = getElementWithUUID(parent, nodeName, uuid);
758         if (e != null) {
759             parent.removeChild(e);
760         }
761     }
762
763     /**
764      * Remove a node with a given uuid
765      * @param nodeName the node name
766      * @param uuid the uuid
767      */
768     public static final void removeNode(String uuid) {
769         if (uuid == null || uuid.isEmpty()) {
770             return;
771         }
772         Element e = getElementWithUUID(uuid);
773         if (e != null && e.getParentNode() != null) {
774             e.getParentNode().removeChild(e);
775         }
776     }
777
778     /**
779      * Save the tab properties
780      * @param tab the tab
781      * @param nullWin if true, the winuuid will be set to 0 (the tab is not docked)
782      */
783     public static void saveTabProperties(SwingScilabTab tab, boolean nullWin) {
784         readDocument();
785
786         ScilabTabFactory factory = ScilabTabFactory.getInstance();
787         String uuid = tab.getPersistentId();
788         Element root = doc.getDocumentElement();
789
790         String app = factory.getApplication(uuid);
791         if (app.isEmpty()) {
792             return;
793         }
794
795         String winuuid;
796         if (nullWin) {
797             winuuid = NULLUUID;
798         } else {
799             winuuid = tab.getParentWindowUUID();
800         }
801
802         Dimension dim = tab.getSize();
803
804         createNode(root, app, new Object[] {"winuuid", winuuid,
805                                             "uuid", uuid,
806                                             "load", factory.getPackage(uuid),
807                                             "factory", factory.getClassName(uuid),
808                                             "width", (int) dim.getWidth(),
809                                             "height", (int) dim.getHeight()
810                                            });
811         writeDocument();
812     }
813
814     /**
815      * Clean the document in removing the useless tags
816      * and validate the different windows.
817      */
818     public static void clean() {
819         readDocument();
820
821         validateWindows();
822
823         Element root = doc.getDocumentElement();
824         NodeList list = root.getElementsByTagName("Window");
825         int len = getNodeListLength(list);
826         for (int i = 0; i < len; i++) {
827             if (list.item(i) instanceof Element) {
828                 String uuid = ((Element) list.item(i)).getAttribute("uuid");
829                 List<Element> elements = ScilabXMLUtilities.getElementsWithAttributeEquals(root, "winuuid", uuid);
830                 if (elements == null || elements.size() == 0) {
831                     root.removeChild(list.item(i));
832                     removeWin(uuid);
833                 }
834             }
835         }
836         writeDocument();
837     }
838
839     /**
840      * Make a dependency between two tabs
841      * @param parentUUID the parent tab uuid
842      * @param childUUID the child tab uuid
843      */
844     public static final void makeDependency(String parentUUID, String childUUID) {
845         readDocument();
846
847         Element e = getElementWithUUID(doc.getDocumentElement(), childUUID);
848         if (e != null) {
849             e.setAttribute("depends", parentUUID);
850         }
851         writeDocument();
852     }
853
854     /**
855      * Remove a dependency with the parent tab
856      * @param childUUID the child tab uuid
857      */
858     public static void removeDependency(String childUUID) {
859         readDocument();
860
861         Element e = getElementWithUUID(doc.getDocumentElement(), childUUID);
862         if (e != null) {
863             e.removeAttribute("depends");
864         }
865         writeDocument();
866     }
867
868     /**
869      * Restore an application by its uuid
870      * @param uuid the application uuid
871      * @return true if the operation succeded
872      */
873     public static boolean restoreUUID(String uuid) {
874         readDocument();
875         clean();
876
877         Element elem = getElementWithUUID(uuid);
878         if (elem == null) {
879             return false;
880         }
881
882         startRestoration(uuid);
883
884         writeDocument();
885         doc = null;
886
887         return true;
888     }
889
890     /**
891      * Get the uuids of an application
892      * @param name the application anem
893      * @return the corresponding uuids
894      */
895     public static String[] getApplicationUUIDs(String name) {
896         readDocument();
897
898         NodeList list = doc.getDocumentElement().getElementsByTagName(name);
899         String[] uuids = new String[getNodeListLength(list)];
900         for (int i = 0; i < uuids.length; i++) {
901             if (list.item(i) instanceof Element) {
902                 Element elem = (Element) list.item(i);
903                 uuids[i] = elem.getAttribute("uuid");
904             }
905         }
906
907         return uuids;
908     }
909
910     /**
911      * Get the length of a NodeList (this is a workaround for a f... java bug)
912      */
913     private static final int getNodeListLength(NodeList list) {
914         int length = 0;
915         try {
916             length = list.getLength();
917         } catch (NullPointerException e) { }
918
919         return length;
920     }
921
922
923     /**
924      * Inner interface used to have something to execute when the restoration is finished
925      */
926     public interface EndedRestoration {
927
928         /**
929          * Stuff to do when the restoration is ended
930          */
931         public void finish();
932     }
933 }