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