82b134bcb7d11d05f5f37205cce916029837634e
[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.FileNotFoundException;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.StringReader;
30 import java.net.URI;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Stack;
35 import java.util.StringTokenizer;
36
37 import javax.swing.JDialog;
38 import javax.swing.SwingUtilities;
39 import javax.swing.border.Border;
40 import javax.xml.parsers.DocumentBuilder;
41 import javax.xml.parsers.DocumentBuilderFactory;
42 import javax.xml.parsers.ParserConfigurationException;
43 import javax.xml.transform.OutputKeys;
44 import javax.xml.transform.Source;
45 import javax.xml.transform.Transformer;
46 import javax.xml.transform.TransformerConfigurationException;
47 import javax.xml.transform.TransformerException;
48 import javax.xml.transform.TransformerFactory;
49 import javax.xml.transform.TransformerFactoryConfigurationError;
50 import javax.xml.transform.URIResolver;
51 import javax.xml.transform.dom.DOMResult;
52 import javax.xml.transform.dom.DOMSource;
53 import javax.xml.transform.stream.StreamResult;
54 import javax.xml.transform.stream.StreamSource;
55
56 import org.w3c.dom.Document;
57 import org.w3c.dom.DocumentFragment;
58 import org.w3c.dom.Element;
59 import org.w3c.dom.NamedNodeMap;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
64
65 import org.scilab.modules.localization.Messages;
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:import href=\"").append(SCI).append("/modules/preferences/src/xslt/XConfiguration.xsl").append("\"/>\n");
303
304             FilenameFilter filter = new FilenameFilter() {
305                 public boolean accept(File dir, String name) {
306                     return name.endsWith(".xsl") && name.startsWith("XConfiguration");
307                 }
308             };
309
310             // Include standard Scilab xsl files
311             for (File etc : etcs) {
312                 File[] xsls = etc.listFiles(filter);
313                 for (File xsl : xsls) {
314                     try {
315                         buffer.append("<xsl:import href=\"").append(xsl.getCanonicalPath()).append("\"/>\n");
316                     } catch (IOException e) {
317                         buffer.append("<xsl:import href=\"").append(xsl.getAbsolutePath()).append("\"/>\n");
318                     }
319                 }
320             }
321
322             // Include toolboxes xsl files
323             List<ScilabPreferences.ToolboxInfos> infos = ScilabPreferences.getToolboxesInfos();
324             filter = new FilenameFilter() {
325                 public boolean accept(File dir, String name) {
326                     return name.endsWith(".xsl");
327                 }
328             };
329             for (ScilabPreferences.ToolboxInfos i : infos) {
330                 File etc = new File(i.getPrefFile()).getParentFile();
331                 File[] xsls = etc.listFiles(filter);
332                 for (File xsl : xsls) {
333                     try {
334                         buffer.append("<xsl:import href=\"").append(xsl.getCanonicalPath()).append("\"/>\n");
335                     } catch (IOException e) {
336                         buffer.append("<xsl:import href=\"").append(xsl.getAbsolutePath()).append("\"/>\n");
337                     }
338                 }
339             }
340
341             buffer.append("</xsl:stylesheet>");
342
343             XSLCODE = buffer.toString();
344         }
345
346         return XSLCODE;
347     }
348
349     public static void invalidateXSL() {
350         XSLCODE = null;
351     }
352
353     /**
354      * Load XSL as XSL Transformer.
355      */
356     protected static void reloadTransformer(String address) {
357         try {
358             StreamSource source = new StreamSource(new StringReader(createXSLFile()));
359             transformer = factory.newTransformer(source);
360         } catch (TransformerConfigurationException e1) {
361             System.err.println(ERROR_READ + address);
362         } catch (TransformerFactoryConfigurationError e1) {
363             System.err.println(ERROR_READ + address);
364         }
365     }
366
367     /**
368      * Generate view by application of XSL on XConfiguration file.
369      * @return View DOM.
370      */
371     private static DOMResult generateViewDOM() {
372         DOMResult result = new DOMResult();
373         DOMSource source = new DOMSource(document);
374         try {
375             transformer.transform(source, result);
376         } catch (TransformerException e) {
377             // Just highlight clear transformer output.
378             System.out.println("\n");
379         }
380         return result;
381     }
382
383     /**
384      * Identify an element with its context string.
385      * @see XConfiguration.xsl#context
386      * @param context : the context string used to catch the element.
387      * @return the corresponding node
388      */
389     public static Element getElementByContext(final String context) {
390         String[] ids = context.split("/");
391         Element element = document.getDocumentElement();
392         for (int i = 0; i < ids.length; i++) {
393             int index = Integer.parseInt(ids[i]);
394             // get the element with corresponding index (filter text nodes)
395             NodeList childNodes = element.getChildNodes();
396             Node node = null;
397             for (int j = 0; index > 0 && j < childNodes.getLength(); j++) {
398                 node = childNodes.item(j);
399                 if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
400                     index--;
401                 }
402             }
403             if (index == 0) {
404                 element = (Element) node;
405             } else {
406                 System.err.println("'" + context + "' out of document!");
407                 return null;
408             }
409         }
410         return element;
411     }
412
413     /**
414      * Interpret action.
415      * @param action : to be interpreted.
416      * @param source : component source of the action (only class is needed).
417      * @return if the event is handled here
418      */
419     protected static boolean generixEvent(final Node[] actions, final Component source) {
420         if (actions.length == 0) {
421             return false;
422         }
423
424         Node action = actions[0];
425         // All actions must be of the same kind.
426
427         boolean enable = getAttribute(action, "enable").equals(NAV) || getAttribute(action, "enable").equals("true");
428
429         if (!getAttribute(action, "set").equals(NAV) && enable) {
430             for (int i = 0; i < actions.length; i++) {
431                 action = actions[i];
432                 String context = getAttribute(action, "context");
433                 Element element = getElementByContext(context);
434                 String value = getAttribute(action, "value");
435                 String attribute = getAttribute(action, "set");
436                 if (element != null) {
437                     element.setAttribute(attribute, value);
438                     XConfiguration.addModifiedPath(getNodePath(element));
439                 }
440             }
441             refreshDisplay();
442             updated = true;
443             return true;
444         }
445
446         if (!getAttribute(action, "insert").equals(NAV) && enable) {
447             for (int i = 0; i < actions.length; i++) {
448                 action = actions[i];
449                 String context = getAttribute(action, "context");
450                 Element element = getElementByContext(context);
451                 String insertValue = XCommonManager.getAttribute(action, "insert");
452                 int insert = 0;
453                 try {
454                     insert = Integer.decode(insertValue);
455                 } catch (NumberFormatException e) {
456                     XChooser chooser = (XChooser) source;
457                     insertValue = chooser.choose().toString();
458                     insert = Integer.decode(insertValue) + 1;
459                 }
460                 Node hook = null;
461                 NodeList nodelist = element.getChildNodes();
462                 for (int xi = 0; xi < nodelist.getLength(); xi++) {
463                     Node node = nodelist.item(xi);
464                     if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
465                         if (insert == 1) {
466                             hook = node;
467                             break;
468                         }
469                         insert --;
470                     }
471                 }
472                 DocumentFragment fragment = document.createDocumentFragment();
473                 while (action.hasChildNodes()) {
474                     Node transferred = action.getFirstChild();
475                     action.removeChild(transferred);
476                     transferred = document.importNode(transferred, true);
477                     fragment.appendChild(transferred);
478                 }
479                 if (element != null) {
480                     element.insertBefore(fragment, hook);
481                 }
482             }
483             refreshDisplay();
484             updated = true;
485
486             return true;
487         }
488
489         if (!getAttribute(action, "delete").equals(NAV) && enable) {
490             for (int i = 0; i < actions.length; i++) {
491                 action = actions[i];
492                 String context = getAttribute(action, "context");
493                 Element element = getElementByContext(context);
494                 String xDelete = XCommonManager.getAttribute(action, "delete");
495                 int delete = 0;
496                 try {
497                     delete = Integer.decode(xDelete);
498                 } catch (NumberFormatException e) {
499                     if (source == null) {
500                         return false;
501                     }
502                     XChooser chooser = (XChooser) source;
503                     xDelete = chooser.choose().toString();
504                     delete = Integer.decode(xDelete);
505                 }
506                 Node deleted = null;
507                 NodeList nodelist = element.getChildNodes();
508                 for (int xi = 0; xi < nodelist.getLength(); xi++) {
509                     Node node = nodelist.item(xi);
510                     if (!node.getNodeName().equals("#text") && !node.getNodeName().equals("#comment")) {
511                         if (delete == 1) {
512                             deleted = node;
513                             break;
514                         }
515                         delete--;
516                     }
517                 }
518                 if (element != null && deleted != null) {
519                     element.removeChild(deleted);
520                 }
521             }
522             refreshDisplay();
523             updated = true;
524
525             return true;
526         }
527
528         if (!getAttribute(action, "choose").equals(NAV) && enable) {
529             String context = getAttribute(action, "context");
530             Element element = getElementByContext(context);
531
532             if (source == null) {
533                 return false;
534             }
535             if (source instanceof XChooser) {
536                 XChooser chooser = (XChooser) source;
537                 Object value = chooser.choose();
538                 if (value != null) {
539                     String attribute = getAttribute(action, "choose");
540                     if (element != null) {
541                         if (value instanceof String[]) {
542                             String[] values = (String[]) value;
543                             StringTokenizer toks = new StringTokenizer(attribute, ",");
544                             int n = Math.min(toks.countTokens(), values.length);
545                             for (int i = 0; i < n; i++) {
546                                 String attr = toks.nextToken().trim();
547                                 element.setAttribute(attr, values[i]);
548                             }
549                         } else {
550                             element.setAttribute(attribute, value.toString());
551                         }
552                         XConfiguration.addModifiedPath(getNodePath(element));
553                     }
554                     refreshDisplay();
555                     updated = true;
556                 }
557             } else {
558                 System.err.println("@choose attribute only valid on choosers " + "(SELECT, COLOR, FILE, ENTRY,...)");
559             }
560             return true;
561         }
562         return false;
563     }
564
565     public static final String getNodePath(Node node) {
566         StringBuilder buffer = new StringBuilder("/");
567         Stack<String> stack = new Stack<String>();
568         Node n = node;
569
570         while (n != null) {
571             String nname = n.getNodeName();
572             NamedNodeMap attrs = n.getAttributes();
573             if (attrs != null && attrs.getLength() != 0) {
574                 Node attr = attrs.getNamedItem("xconf-uid");
575                 if (attr != null) {
576                     nname += "[@xconf-uid=\"" + attr.getNodeValue() + "\"]";
577                 }
578             }
579             stack.push(nname);
580             n = n.getParentNode();
581         }
582
583         if (stack.size() >= 3) {
584             stack.pop();
585             stack.pop();
586         } else {
587             return null;
588         }
589
590         while (!stack.empty()) {
591             buffer.append("/");
592             buffer.append(stack.pop());
593         }
594
595         return buffer.toString();
596     }
597
598     /**
599      * Sentinel string for attribute consulting.
600      */
601     public static final String NAV = "\"not an value'";
602
603     /**
604      * Attribute consulting with default.
605      * @param node : consulted node
606      * @param name : attribute key
607      * @param value : default value
608      * @return the consulted value
609      */
610     public static String getAttribute(final Node node, final String name, final String value) {
611         String response = getAttribute(node, name);
612         if (response == NAV) {
613             return value;
614         }
615
616         return response;
617     }
618
619     /**
620      * Attribute consulting without default.
621      * @param node : consulted node.
622      * @param name : attribute key.
623      * @return the consulted value (or NAV if attribute key is absent)
624      */
625     public static String getAttribute(final Node node, final String name) {
626         NamedNodeMap attrs = node.getAttributes();
627         if (attrs == null) {
628             return NAV;
629         }
630         Node attr = attrs.getNamedItem(name);
631         if (attr == null) {
632             return NAV;
633         }
634
635         return attr.getNodeValue();
636     }
637
638     /**
639      * Typed attribute consulting with default.
640      * @param node : consulted node.
641      * @param name : attribute key.
642      * @param value : default value.
643      * @return the value.
644      */
645     public static int getInt(final Node node, final String name, final int value) {
646         String response = getAttribute(node, name);
647         if (response.equals(NAV) || response.equals("")) {
648             return value;
649         }
650
651         try {
652             return Integer.parseInt(response);
653         } catch (NumberFormatException e) {
654             return 0;
655         }
656     }
657
658     /**
659      * Typed attribute consulting with default.
660      * @param node : consulted node.
661      * @param name : attribute key.
662      * @param value : default value.
663      * @return the value.
664      */
665     public static double getDouble(final Node node, final String name, final double value) {
666         String response = getAttribute(node, name);
667         if (response.equals(NAV) || response.equals("")) {
668             return value;
669         }
670
671         try {
672             return Double.parseDouble(response);
673         } catch (NumberFormatException e) {
674             return 0;
675         }
676     }
677
678     /**
679      * Typed attribute consulting with default.
680      * @param node : consulted node.
681      * @param name : attribute key.
682      * @param value : default value.
683      * @return the value.
684      */
685     public static boolean getBoolean(final Node node, final String name, final boolean value) {
686         String response = getAttribute(node, name);
687         if (response.equals(NAV) || response.equals("")) {
688             return value;
689         }
690
691         return response.equalsIgnoreCase("true");
692     }
693
694     /**
695      * Manage color representation.
696      * @param source : the color.
697      * @return the string representation.
698      */
699     public static String getColor(final Color source) {
700         if (source == null) {
701             return "#000000";
702         }
703
704         String hexStr = Integer.toHexString(source.getRGB());
705         return "#" + hexStr.substring(2);
706     }
707
708     /**
709      * Manage color representation.
710      * @param source : the string representation.
711      * @return the corresponding color
712      */
713     public static Color getColor(final String source) {
714         return Color.decode(source);
715     }
716
717     /**
718      * Get top level window for correct dialog opening.
719      * @return top-level frame.
720      */
721     public static Frame getTopLevel() {
722         Container main = (Container) ScilabConsole.getConsole().getAsSimpleConsole();
723         return (Frame) SwingUtilities.getAncestorOfClass(Frame.class, main);
724     }
725
726     /**
727      * Set a dimension for a component.
728      * @param component : the resized component.
729      * @param peer : the node having the dimension information.
730      */
731     public static void setDimension(final Component component, final Node peer) {
732         int height = XConfigManager.getInt(peer, "height", 0);
733         int width = XConfigManager.getInt(peer, "width",  0);
734         if (height > 0 && width > 0) {
735             component.setPreferredSize(new Dimension(width, height));
736         }
737     }
738
739     /**
740      * Create a copy of Scilab configuration file in the user directory.
741      */
742     public static void createUserCopy(String original, String copy) {
743         File fileConfig = new File(copy);
744         if (!fileConfig.exists()) {
745             refreshUserCopy(original, copy);
746         }
747     }
748
749     /**
750      * Refresh configuration file in the user directory with Scilab defaults.
751      */
752     public static void refreshUserCopy(String original, String copy) {
753         /* Create a local copy of the configuration file */
754         try {
755             copyFile(new File(original), new File(copy));
756         } catch (FileNotFoundException e) {
757             System.err.println(ERROR_READ + copy);
758         }
759     }
760
761     /**
762      * Copy a file
763      * @param in src file
764      * @param out dest file
765      * @throws FileNotFoundException
766      */
767     private static void copyFile(final File in, final File out) throws FileNotFoundException {
768         FileInputStream fis = new FileInputStream(in);
769         FileOutputStream fos = new FileOutputStream(out);
770
771         byte[] buf = new byte[BUFSIZE];
772         int i = 0;
773         try {
774             while ((i = fis.read(buf)) != -1) {
775                 fos.write(buf, 0, i);
776             }
777             fis.close();
778             fos.close();
779         } catch (IOException e) {
780             e.printStackTrace();
781         }
782     }
783
784     /**
785      * Read the file to modify
786      */
787     protected static Document readDocument(final String fileName) {
788         File xml = new File(fileName);
789         DocumentBuilder docBuilder = null;
790
791         try {
792             DocumentBuilderFactory factory = ScilabDocumentBuilderFactory.newInstance();
793             docBuilder = factory.newDocumentBuilder();
794             return docBuilder.parse(xml);
795         } catch (ParserConfigurationException pce) {
796             System.err.println(ERROR_READ + fileName);
797         } catch (SAXException se) {
798             System.err.println(ERROR_READ + fileName);
799         } catch (IOException ioe) {
800             System.err.println(ERROR_READ + fileName);
801         }
802         return null;
803     }
804
805     /**
806      * Save the modifications
807      */
808     protected static void writeDocument(String filename, Node written) {
809         Transformer transformer = null;
810         try {
811             transformer = ScilabTransformerFactory.newInstance().newTransformer();
812         } catch (TransformerConfigurationException e1) {
813             System.err.println(ERROR_WRITE + filename);
814         } catch (TransformerFactoryConfigurationError e1) {
815             System.err.println(ERROR_WRITE + filename);
816         }
817         transformer.setOutputProperty(OutputKeys.INDENT, "yes");
818
819         StreamResult result = new StreamResult(new File(filename));
820         DOMSource source = new DOMSource(written);
821         try {
822             transformer.transform(source, result);
823         } catch (TransformerException e) {
824             System.err.println(ERROR_WRITE + filename);
825         }
826     }
827 }