Prefs: add a function preferences()
[scilab.git] / scilab / modules / preferences / src / java / org / scilab / modules / preferences / XCommonManager.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2011 - INRIA - Vincent COUVERT
4  * Copyright (C) 2011 -         Pierre GRADIT
5  * Copyright (C) 2012 - Scilab Enterprises - Calixte DENIZET
6  *
7  * This file must be used under the terms of the CeCILL.
8  * This source file is licensed as described in the file COPYING, which
9  * you should have received as part of this distribution.  The terms
10  * are also available at
11  * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
12  *
13  */
14
15 package org.scilab.modules.preferences;
16
17 import java.awt.Color;
18 import java.awt.Component;
19 import java.awt.Container;
20 import java.awt.Dimension;
21 import java.awt.Frame;
22 import java.io.File;
23 import java.io.FilenameFilter;
24 import java.io.FileFilter;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.StringReader;
29 import java.net.URI;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Stack;
34 import java.util.StringTokenizer;
35
36 import javax.swing.JDialog;
37 import javax.swing.SwingUtilities;
38 import javax.swing.border.Border;
39 import javax.xml.parsers.DocumentBuilder;
40 import javax.xml.parsers.DocumentBuilderFactory;
41 import javax.xml.parsers.ParserConfigurationException;
42 import javax.xml.transform.OutputKeys;
43 import javax.xml.transform.Source;
44 import javax.xml.transform.Transformer;
45 import javax.xml.transform.TransformerConfigurationException;
46 import javax.xml.transform.TransformerException;
47 import javax.xml.transform.TransformerFactory;
48 import javax.xml.transform.TransformerFactoryConfigurationError;
49 import javax.xml.transform.URIResolver;
50 import javax.xml.transform.dom.DOMResult;
51 import javax.xml.transform.dom.DOMSource;
52 import javax.xml.transform.stream.StreamResult;
53 import javax.xml.transform.stream.StreamSource;
54
55 import org.w3c.dom.Document;
56 import org.w3c.dom.DocumentFragment;
57 import org.w3c.dom.Element;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.InputSource;
62 import org.xml.sax.SAXException;
63
64 import org.scilab.modules.localization.Messages;
65 import org.scilab.modules.commons.OS;
66 import org.scilab.modules.commons.ScilabCommons;
67 import org.scilab.modules.commons.xml.ScilabDocumentBuilderFactory;
68 import org.scilab.modules.commons.xml.ScilabTransformerFactory;
69 import org.scilab.modules.commons.xml.XConfiguration;
70 import org.scilab.modules.gui.console.ScilabConsole;
71
72 /* This class is the common ancestor to both X_manager.
73  */
74 public abstract class XCommonManager {
75
76     /** XConfiguration management verbosity.*/
77     public static final boolean performances = true;
78
79     /** XConfiguration management verbosity.*/
80     public static final boolean differential = true;
81
82     /** Message for read error.*/
83     protected static final String ERROR_READ = "Could not load file: ";
84
85     /** Message for write error.*/
86     protected static final String ERROR_WRITE = "Could not save file: ";
87
88     /** Buffer size.*/
89     protected static final int BUFSIZE = 1024;
90
91     /** Main dialog.*/
92     protected static JDialog dialog;
93
94     /** DOM Document.*/
95     protected static Document document;
96
97     /** DOM Document.*/
98     protected static String documentAddress;
99
100     /** Up-to-date flag.*/
101     protected static boolean  updated = false;
102
103     /** Top level DOM Node.*/
104     protected static Node topDOM;
105
106     /** Top level Swing container.*/
107     protected static Container topSwing;
108
109     /** Container-Sentinel correspondence.*/
110     protected static Map<Component, XSentinel> correspondance;
111
112     /** Last visitor.*/
113     protected static XUpdateVisitor visitor;
114
115     /** Last current time.*/
116     protected static long time = System.currentTimeMillis();
117
118     /**
119      * XSL Transformer factory.
120      */
121     protected static TransformerFactory factory;
122
123     /**
124      * XML Document builder factory.
125      */
126     protected static DocumentBuilderFactory builder = ScilabDocumentBuilderFactory.newInstance();
127
128     /**
129      * XSL Transformer.
130      */
131     protected static Transformer transformer = null;
132
133     private static final String SCI = System.getenv("SCI");
134
135     /** Scilab configuration file.*/
136     private static final String SCILAB_CONFIG_FILE = SCI + "/modules/preferences/etc/XConfiguration.xml";
137
138     private static String XSLCODE;
139
140     static {
141         factory = ScilabTransformerFactory.newInstance();
142         factory.setURIResolver(new URIResolver() {
143             public Source resolve(String href, String base) throws TransformerException {
144                 if (href.startsWith("$SCI")) {
145                     href = href.replace("$SCI", SCI);
146                     base = null;
147                 }
148
149                 try {
150                     File baseDir = null;
151                     if (base != null && !base.isEmpty()) {
152                         baseDir = new File(new URI(base)).getParentFile();
153                     }
154                     File f;
155                     if (baseDir != null) {
156                         f = new File(baseDir, href);
157                     } else {
158                         f = new File(href);
159                     }
160
161                     if (f.exists() && f.canRead()) {
162                         return new StreamSource(f);
163                     }
164                 } catch (Exception e) {
165                     System.out.println(e);
166                 }
167
168                 throw new TransformerException("Cannot find the file " + href + "::" + base);
169             }
170         });
171     }
172
173     /**
174      * Monitor time between calls.
175      */
176     public static void printTimeStamp(final String msg) {
177         long nextTime  = System.currentTimeMillis();
178         long deltaTime = nextTime - time;
179         if (performances) {
180             System.out.println((msg.startsWith("*") ? "" : " |  ") + msg + " in " + deltaTime + " ms.");
181         }
182         time = nextTime;
183     }
184
185     /** Launch swing hierarchy update.
186      *
187      * @return whether XSL return a node or not.
188      */
189     public static boolean refreshDisplay() {
190         topDOM = generateViewDOM().getNode().getFirstChild();//System.out.println(XConfiguration.dumpNode(generateViewDOM().getNode()));
191         if (topDOM == null) {
192             System.err.println("XSL does not give a node!");
193             return false;
194         }
195
196         // Refresh correspondence
197         //    TODO top layout changes
198         visitor = new XUpdateVisitor(correspondance);
199         visitor.visit(topSwing, topDOM);
200         setDimension(dialog, topDOM);
201         dialog.pack();
202
203         return true;
204     }
205
206     /**
207      * Horizontal space between parent and child in String representations.
208      */
209     public static final String INCREMENT = "    ";
210
211     /**
212      * Compute recursive string representation of DOM view tree.
213      * @param node : current node
214      * @param indent : current indentation
215      * @return node representation
216      *
217      */
218     private static String viewDOM(final Node node, final String indent) {
219         String signature = indent;
220         if (node.hasAttributes()) {
221             signature += XSentinel.signature(node, new String[0]);
222         } else {
223             String single = node.getNodeName() + ": " + node.getNodeValue();
224             signature += single.replaceAll("[ \t\n]+", " ");
225         }
226         signature += "\n";
227         NodeList nodelist = node.getChildNodes();
228         for (int i = 0; i < nodelist.getLength(); i++) {
229             Node item  = nodelist.item(i);
230             signature += viewDOM(item, indent + INCREMENT);
231         }
232
233         return signature;
234     }
235
236     /**
237      * Compute top-level string representation of DOM view tree.
238      * @return document representation
239      */
240     protected static String viewDOM() {
241         return viewDOM(topDOM, "");
242     }
243
244     /**
245      * Compute recursive string representation of Swing tree.
246      * @param component : current component
247      * @param indent : current indentation
248      * @return component representation
249      *
250      */
251     private static String swingComposite(final Component component, final String indent) {
252         String signature = indent;
253         signature += component.toString() + "\n";
254         if (component instanceof Container) {
255             Container container = (Container) component;
256             Component [] components = container.getComponents();
257             for (int i = 0; i < components.length; i++) {
258                 Component child = components[i];
259                 signature += swingComposite(child, indent + INCREMENT);
260             }
261         }
262         return signature;
263     }
264
265     /**
266      * Compute top-level string representation of swing composite.
267      * @return Graphical interface representation
268      */
269     protected static String swingComposite() {
270         return swingComposite(topSwing, "");
271     }
272
273     private static List<File> getEtcDir() {
274         List<File> list = new ArrayList<File>();
275         File modulesDir = new File(SCI + "/modules/");
276         File[] modules = modulesDir.listFiles(new FileFilter() {
277             public boolean accept(File f) {
278                 return f.isDirectory();
279             }
280         });
281
282         for (File module : modules) {
283             File etc = new File(module, "/etc/");
284             if (etc.exists() && etc.isDirectory()) {
285                 list.add(etc);
286             }
287         }
288
289         return list;
290     }
291
292     /**
293      * Create a XSL string in using the XConfiguration-*.xsl found in SCI/modules/MODULE_NAME/etc/
294      * @return the buit XSL string.
295      */
296     protected static String createXSLFile() {
297         if (XSLCODE == null) {
298             List<File> etcs = XConfiguration.getEtcDir();
299
300             StringBuilder buffer = new StringBuilder("<?xml version='1.0' encoding='utf-8'?>\n");
301             buffer.append("<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n");
302             buffer.append("<xsl:param name=\"OS\"/>\n");
303             buffer.append("<xsl:import href=\"").append(SCI).append("/modules/preferences/src/xslt/XConfiguration.xsl").append("\"/>\n");
304
305             FilenameFilter filter = new FilenameFilter() {
306                 public boolean accept(File dir, String name) {
307                     return name.endsWith(".xsl") && name.startsWith("XConfiguration");
308                 }
309             };
310
311             // Include standard Scilab xsl files
312             for (File etc : etcs) {
313                 File[] xsls = etc.listFiles(filter);
314                 for (File xsl : xsls) {
315                     try {
316                         buffer.append("<xsl:import href=\"").append(xsl.getCanonicalPath()).append("\"/>\n");
317                     } catch (IOException e) {
318                         buffer.append("<xsl:import href=\"").append(xsl.getAbsolutePath()).append("\"/>\n");
319                     }
320                 }
321             }
322
323             // Include toolboxes xsl files
324             List<ScilabPreferences.ToolboxInfos> infos = ScilabPreferences.getToolboxesInfos();
325             filter = new FilenameFilter() {
326                 public boolean accept(File dir, String name) {
327                     return name.endsWith(".xsl");
328                 }
329             };
330             for (ScilabPreferences.ToolboxInfos i : infos) {
331                 File etc = new File(i.getPrefFile()).getParentFile();
332                 File[] xsls = etc.listFiles(filter);
333                 for (File xsl : xsls) {
334                     try {
335                         buffer.append("<xsl:import href=\"").append(xsl.getCanonicalPath()).append("\"/>\n");
336                     } catch (IOException e) {
337                         buffer.append("<xsl:import href=\"").append(xsl.getAbsolutePath()).append("\"/>\n");
338                     }
339                 }
340             }
341
342             buffer.append("</xsl:stylesheet>");
343
344             XSLCODE = buffer.toString();
345         }
346
347         return XSLCODE;
348     }
349
350     public static void invalidateXSL() {
351         XSLCODE = null;
352     }
353
354     /**
355      * Load XSL as XSL Transformer.
356      */
357     protected static void reloadTransformer(String address) {
358         try {
359             StreamSource source = new StreamSource(new StringReader(createXSLFile()));
360             transformer = factory.newTransformer(source);
361             transformer.setParameter("OS", OS.getVersionName());
362         } catch (TransformerConfigurationException e1) {
363             System.err.println(ERROR_READ + address);
364         } catch (TransformerFactoryConfigurationError e1) {
365             System.err.println(ERROR_READ + address);
366         }
367     }
368
369     /**
370      * Generate view by application of XSL on XConfiguration file.
371      * @return View DOM.
372      */
373     private static DOMResult generateViewDOM() {
374         DOMResult result = new DOMResult();
375         DOMSource source = new DOMSource(document);
376         try {
377             transformer.transform(source, result);
378         } catch (TransformerException e) {
379             // Just highlight clear transformer output.
380             System.out.println("\n");
381         }
382         return result;
383     }
384
385     /**
386      * Identify an element with its context string.
387      * @see XConfiguration.xsl#context
388      * @param context : the context string used to catch the element.
389      * @return the corresponding node
390      */
391     public static Element getElementByContext(final String context) {
392         String[] ids = context.split("/");
393         Element element = document.getDocumentElement();
394         for (int i = 0; i < ids.length; i++) {
395             int index = Integer.parseInt(ids[i]);
396             // get the element with corresponding index (filter text nodes)
397             NodeList childNodes = element.getChildNodes();
398             Node node = null;
399             for (int j = 0; index > 0 && j < childNodes.getLength(); j++) {
400                 node = childNodes.item(j);
401                 if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
402                     index--;
403                 }
404             }
405             if (index == 0) {
406                 element = (Element) node;
407             } else {
408                 System.err.println("'" + context + "' out of document!");
409                 return null;
410             }
411         }
412         return element;
413     }
414
415     /**
416      * Interpret action.
417      * @param action : to be interpreted.
418      * @param source : component source of the action (only class is needed).
419      * @return if the event is handled here
420      */
421     protected static boolean generixEvent(final Node[] actions, final Component source) {
422         if (actions.length == 0) {
423             return false;
424         }
425
426         Node action = actions[0];
427         // All actions must be of the same kind.
428
429         boolean enable = getAttribute(action, "enable").equals(NAV) || getAttribute(action, "enable").equals("true");
430
431         if (!getAttribute(action, "set").equals(NAV) && enable) {
432             for (int i = 0; i < actions.length; i++) {
433                 action = actions[i];
434                 String context = getAttribute(action, "context");
435                 Element element = getElementByContext(context);
436                 String value = getAttribute(action, "value");
437                 String attribute = getAttribute(action, "set");
438                 if (element != null) {
439                     element.setAttribute(attribute, value);
440                     XConfiguration.addModifiedPath(getNodePath(element));
441                 }
442             }
443             refreshDisplay();
444             updated = true;
445             return true;
446         }
447
448         if (!getAttribute(action, "insert").equals(NAV) && enable) {
449             for (int i = 0; i < actions.length; i++) {
450                 action = actions[i];
451                 String context = getAttribute(action, "context");
452                 Element element = getElementByContext(context);
453                 String insertValue = XCommonManager.getAttribute(action, "insert");
454                 int insert = 0;
455                 try {
456                     insert = Integer.decode(insertValue);
457                 } catch (NumberFormatException e) {
458                     XChooser chooser = (XChooser) source;
459                     insertValue = chooser.choose().toString();
460                     insert = Integer.decode(insertValue) + 1;
461                 }
462                 Node hook = null;
463                 NodeList nodelist = element.getChildNodes();
464                 for (int xi = 0; xi < nodelist.getLength(); xi++) {
465                     Node node = nodelist.item(xi);
466                     if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
467                         if (insert == 1) {
468                             hook = node;
469                             break;
470                         }
471                         insert --;
472                     }
473                 }
474                 DocumentFragment fragment = document.createDocumentFragment();
475                 while (action.hasChildNodes()) {
476                     Node transferred = action.getFirstChild();
477                     action.removeChild(transferred);
478                     transferred = document.importNode(transferred, true);
479                     fragment.appendChild(transferred);
480                 }
481                 if (element != null) {
482                     element.insertBefore(fragment, hook);
483                 }
484             }
485             refreshDisplay();
486             updated = true;
487
488             return true;
489         }
490
491         if (!getAttribute(action, "delete").equals(NAV) && enable) {
492             for (int i = 0; i < actions.length; i++) {
493                 action = actions[i];
494                 String context = getAttribute(action, "context");
495                 Element element = getElementByContext(context);
496                 String xDelete = XCommonManager.getAttribute(action, "delete");
497                 int delete = 0;
498                 try {
499                     delete = Integer.decode(xDelete);
500                 } catch (NumberFormatException e) {
501                     if (source == null) {
502                         return false;
503                     }
504                     XChooser chooser = (XChooser) source;
505                     xDelete = chooser.choose().toString();
506                     delete = Integer.decode(xDelete);
507                 }
508                 Node deleted = null;
509                 NodeList nodelist = element.getChildNodes();
510                 for (int xi = 0; xi < nodelist.getLength(); xi++) {
511                     Node node = nodelist.item(xi);
512                     if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
513                         if (delete == 1) {
514                             deleted = node;
515                             break;
516                         }
517                         delete--;
518                     }
519                 }
520                 if (element != null && deleted != null) {
521                     element.removeChild(deleted);
522                 }
523             }
524             refreshDisplay();
525             updated = true;
526
527             return true;
528         }
529
530         if (!getAttribute(action, "choose").equals(NAV) && enable) {
531             String context = getAttribute(action, "context");
532             Element element = getElementByContext(context);
533
534             if (source == null) {
535                 return false;
536             }
537             if (source instanceof XChooser) {
538                 XChooser chooser = (XChooser) source;
539                 Object value = chooser.choose();
540                 if (value != null) {
541                     String attribute = getAttribute(action, "choose");
542                     if (element != null) {
543                         if (value instanceof String[]) {
544                             String[] values = (String[]) value;
545                             StringTokenizer toks = new StringTokenizer(attribute, ",");
546                             int n = Math.min(toks.countTokens(), values.length);
547                             for (int i = 0; i < n; i++) {
548                                 String attr = toks.nextToken().trim();
549                                 element.setAttribute(attr, values[i]);
550                             }
551                         } else {
552                             element.setAttribute(attribute, value.toString());
553                         }
554                         XConfiguration.addModifiedPath(getNodePath(element));
555                     }
556                     refreshDisplay();
557                     updated = true;
558                 }
559             } else {
560                 System.err.println("@choose attribute only valid on choosers " + "(SELECT, COLOR, FILE, ENTRY,...)");
561             }
562             return true;
563         }
564         return false;
565     }
566
567     public static final String getNodePath(Node node) {
568         StringBuilder buffer = new StringBuilder("/");
569         Stack<String> stack = new Stack<String>();
570         Node n = node;
571
572         while (n != null) {
573             String nname = n.getNodeName();
574             NamedNodeMap attrs = n.getAttributes();
575             if (attrs != null && attrs.getLength() != 0) {
576                 Node attr = attrs.getNamedItem("xconf-uid");
577                 if (attr != null) {
578                     nname += "[@xconf-uid=\"" + attr.getNodeValue() + "\"]";
579                 }
580             }
581             stack.push(nname);
582             n = n.getParentNode();
583         }
584
585         if (stack.size() >= 3) {
586             stack.pop();
587             stack.pop();
588         } else {
589             return null;
590         }
591
592         while (!stack.empty()) {
593             buffer.append("/");
594             buffer.append(stack.pop());
595         }
596
597         return buffer.toString();
598     }
599
600     /**
601      * Sentinel string for attribute consulting.
602      */
603     public static final String NAV = "\"not an value'";
604
605     /**
606      * Attribute consulting with default.
607      * @param node : consulted node
608      * @param name : attribute key
609      * @param value : default value
610      * @return the consulted value
611      */
612     public static String getAttribute(final Node node, final String name, final String value) {
613         String response = getAttribute(node, name);
614         if (response == NAV) {
615             return value;
616         }
617
618         return response;
619     }
620
621     /**
622      * Attribute consulting without default.
623      * @param node : consulted node.
624      * @param name : attribute key.
625      * @return the consulted value (or NAV if attribute key is absent)
626      */
627     public static String getAttribute(final Node node, final String name) {
628         NamedNodeMap attrs = node.getAttributes();
629         if (attrs == null) {
630             return NAV;
631         }
632         Node attr = attrs.getNamedItem(name);
633         if (attr == null) {
634             return NAV;
635         }
636
637         return attr.getNodeValue();
638     }
639
640     /**
641      * Typed attribute consulting with default.
642      * @param node : consulted node.
643      * @param name : attribute key.
644      * @param value : default value.
645      * @return the value.
646      */
647     public static int getInt(final Node node, final String name, final int value) {
648         String response = getAttribute(node, name);
649         if (response.equals(NAV) || response.equals("")) {
650             return value;
651         }
652
653         try {
654             return Integer.parseInt(response);
655         } catch (NumberFormatException e) {
656             return 0;
657         }
658     }
659
660     /**
661      * Typed attribute consulting with default.
662      * @param node : consulted node.
663      * @param name : attribute key.
664      * @param value : default value.
665      * @return the value.
666      */
667     public static double getDouble(final Node node, final String name, final double value) {
668         String response = getAttribute(node, name);
669         if (response.equals(NAV) || response.equals("")) {
670             return value;
671         }
672
673         try {
674             return Double.parseDouble(response);
675         } catch (NumberFormatException e) {
676             return 0;
677         }
678     }
679
680     /**
681      * Typed attribute consulting with default.
682      * @param node : consulted node.
683      * @param name : attribute key.
684      * @param value : default value.
685      * @return the value.
686      */
687     public static boolean getBoolean(final Node node, final String name, final boolean value) {
688         String response = getAttribute(node, name);
689         if (response.equals(NAV) || response.equals("")) {
690             return value;
691         }
692
693         return response.equalsIgnoreCase("true");
694     }
695
696     /**
697      * Manage color representation.
698      * @param source : the color.
699      * @return the string representation.
700      */
701     public static String getColor(final Color source) {
702         if (source == null) {
703             return "#000000";
704         }
705
706         String hexStr = Integer.toHexString(source.getRGB());
707         return "#" + hexStr.substring(2);
708     }
709
710     /**
711      * Manage color representation.
712      * @param source : the string representation.
713      * @return the corresponding color
714      */
715     public static Color getColor(final String source) {
716         return Color.decode(source);
717     }
718
719     /**
720      * Get top level window for correct dialog opening.
721      * @return top-level frame.
722      */
723     public static Frame getTopLevel() {
724         if (ScilabConsole.isExistingConsole()) {
725             Container main = (Container) ScilabConsole.getConsole().getAsSimpleConsole();
726             return (Frame) SwingUtilities.getAncestorOfClass(Frame.class, main);
727         }
728
729         return null;
730     }
731
732     /**
733      * Set a dimension for a component.
734      * @param component : the resized component.
735      * @param peer : the node having the dimension information.
736      */
737     public static void setDimension(final Component component, final Node peer) {
738         int height = XConfigManager.getInt(peer, "height", 0);
739         int width = XConfigManager.getInt(peer, "width",  0);
740         if (height > 0 && width > 0) {
741             component.setPreferredSize(new Dimension(width, height));
742         }
743     }
744
745     /**
746      * Create a copy of Scilab configuration file in the user directory.
747      */
748     public static void createUserCopy(String original, String copy) {
749         File fileConfig = new File(copy);
750         if (!fileConfig.exists()) {
751             refreshUserCopy(original, copy);
752         }
753     }
754
755     /**
756      * Refresh configuration file in the user directory with Scilab defaults.
757      */
758     public static void refreshUserCopy(String original, String copy) {
759         /* Create a local copy of the configuration file */
760         try {
761             copyFile(new File(original), new File(copy));
762         } catch (IOException e) {
763             System.err.println(ERROR_READ + copy);
764         }
765     }
766
767     /**
768      * Copy a file
769      * @param in src file
770      * @param out dest file
771      * @throws FileNotFoundException
772      */
773     protected static void copyFile(final File in, final File out) throws IOException {
774         FileInputStream fis = new FileInputStream(in);
775         FileOutputStream fos = new FileOutputStream(out);
776
777         byte[] buf = new byte[BUFSIZE];
778         int i = 0;
779         try {
780             while ((i = fis.read(buf)) != -1) {
781                 fos.write(buf, 0, i);
782             }
783             fis.close();
784             fos.close();
785         } catch (IOException e) {
786             e.printStackTrace();
787         }
788     }
789
790     /**
791      * Read the file to modify
792      */
793     protected static Document readDocument(final String fileName) {
794         File xml = new File(fileName);
795         DocumentBuilder docBuilder = null;
796
797         try {
798             DocumentBuilderFactory factory = ScilabDocumentBuilderFactory.newInstance();
799             docBuilder = factory.newDocumentBuilder();
800             return docBuilder.parse(xml);
801         } catch (ParserConfigurationException pce) {
802             System.err.println(ERROR_READ + fileName);
803         } catch (SAXException se) {
804             System.err.println(ERROR_READ + fileName);
805         } catch (IOException ioe) {
806             System.err.println(ERROR_READ + fileName);
807         }
808         return null;
809     }
810
811     /**
812      * Save the modifications
813      */
814     protected static void writeDocument(String filename, Node written) {
815         Transformer transformer = null;
816         try {
817             transformer = ScilabTransformerFactory.newInstance().newTransformer();
818         } catch (TransformerConfigurationException e1) {
819             System.err.println(ERROR_WRITE + filename);
820         } catch (TransformerFactoryConfigurationError e1) {
821             System.err.println(ERROR_WRITE + filename);
822         }
823         transformer.setOutputProperty(OutputKeys.INDENT, "yes");
824
825         StreamResult result = new StreamResult(new File(filename));
826         DOMSource source = new DOMSource(written);
827         try {
828             transformer.transform(source, result);
829         } catch (TransformerException e) {
830             System.err.println(ERROR_WRITE + filename);
831         }
832     }
833 }