View Javadoc

1   /**
2      This file is part of GoldenGate Project (named also GoldenGate or GG).
3   
4      Copyright 2009, Frederic Bregier, and individual contributors by the @author
5      tags. See the COPYRIGHT.txt in the distribution for a full listing of
6      individual contributors.
7   
8      All GoldenGate Project is free software: you can redistribute it and/or 
9      modify it under the terms of the GNU General Public License as published 
10     by the Free Software Foundation, either version 3 of the License, or
11     (at your option) any later version.
12  
13     GoldenGate is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17  
18     You should have received a copy of the GNU General Public License
19     along with GoldenGate .  If not, see <http://www.gnu.org/licenses/>.
20   */
21  package goldengate.common.xml;
22  
23  import java.io.File;
24  import java.io.FileWriter;
25  import java.io.IOException;
26  import java.io.InvalidObjectException;
27  import java.util.List;
28  
29  import org.dom4j.Document;
30  import org.dom4j.DocumentException;
31  import org.dom4j.DocumentHelper;
32  import org.dom4j.Element;
33  import org.dom4j.io.OutputFormat;
34  import org.dom4j.io.SAXReader;
35  import org.dom4j.io.XMLWriter;
36  
37  /**
38   * XML utility that handles simple cases as:<br>
39   * <ul>
40   * <li>XPath as /x/y/z for a node and referring to root of Document</li>
41   * <li>XPath as x/y/z for a node and referring to current referenced node</li>
42   * <li>Any XPath can be a singleton (unique node referenced by the XPath) or
43   * multiple nodes (same XPath)</li>
44   * <li>Only Element as Node: no Attribute or any other type within XML</li>
45   * <li>Any other path is not supported: //x /x@y ./ ../</li>
46   * <li>Supports special SubXml tree as element (singleton or multiple)</li>
47   * </ul>
48   * 
49   * @author Frederic Bregier
50   * 
51   */
52  public class XmlUtil {
53  
54      /**
55       * 
56       * @param filename
57       * @return Existing Document from filename
58       * @throws IOException
59       * @throws DocumentException
60       */
61      static public Document getDocument(String filename) throws IOException,
62              DocumentException {
63          File file = new File(filename);
64          if (!file.canRead()) {
65              throw new IOException("File is not readable: " + filename);
66          }
67          return getDocument(file);
68      }
69  
70      /**
71       * 
72       * @param file
73       * @return Existing Document from file
74       * @throws IOException
75       * @throws DocumentException
76       */
77      static public Document getDocument(File file) throws IOException,
78              DocumentException {
79          if (!file.canRead()) {
80              throw new IOException("File is not readable: " + file.getPath());
81          }
82          SAXReader reader = new SAXReader();
83          return reader.read(file);
84      }
85  
86      /**
87       * Read the document from the string
88       * 
89       * @param document
90       *            as String
91       * @return the Document
92       * @throws DocumentException
93       */
94      static public Document readDocument(String document)
95              throws DocumentException {
96          return DocumentHelper.parseText(document);
97      }
98  
99      /**
100      * 
101      * @param document
102      * @return the document as an XML string
103      */
104     static public String writeToString(Document document) {
105         return document.asXML();
106     }
107 
108     /**
109      * 
110      * @param element
111      * @return the element as an XML string
112      */
113     static public String writeToString(Element element) {
114         return element.asXML();
115     }
116 
117     /**
118      * 
119      * @return an empty new Document
120      */
121     static public Document createEmptyDocument() {
122         return DocumentHelper.createDocument();
123     }
124 
125     /**
126      * Save the document into the file
127      * 
128      * @param filename
129      * @param document
130      * @throws IOException
131      */
132     static public void saveDocument(String filename, Document document)
133             throws IOException {
134         File file = new File(filename);
135         saveDocument(file, document);
136     }
137 
138     /**
139      * Save the document into the file
140      * 
141      * @param file
142      * @param document
143      * @throws IOException
144      */
145     static public void saveDocument(File file, Document document)
146             throws IOException {
147         if (file.exists() && (!file.canWrite())) {
148             throw new IOException("File is not writable: " + file.getPath());
149         }
150         OutputFormat format = OutputFormat.createPrettyPrint();
151         XMLWriter writer = new XMLWriter(new FileWriter(file), format);
152         writer.write(document);
153         writer.flush();
154         writer.close();
155     }
156 
157     /**
158      * Save the branch from element into the file
159      * 
160      * @param filename
161      * @param element
162      * @throws IOException
163      */
164     static public void saveElement(String filename, Element element)
165             throws IOException {
166         File file = new File(filename);
167         saveElement(file, element);
168     }
169 
170     /**
171      * Save the branch from element into the file
172      * 
173      * @param file
174      * @param element
175      * @throws IOException
176      */
177     static public void saveElement(File file, Element element)
178             throws IOException {
179         if (file.exists() && (!file.canWrite())) {
180             throw new IOException("File is not writable: " + file.getPath());
181         }
182         OutputFormat format = OutputFormat.createPrettyPrint();
183         XMLWriter writer = new XMLWriter(new FileWriter(file), format);
184         writer.write(element);
185         writer.flush();
186         writer.close();
187     }
188 
189     /**
190      * 
191      * @param document
192      * @return the root Element from the document
193      */
194     static public Element getRootElement(Document document) {
195         return document.getRootElement();
196     }
197 
198     /**
199      * Add or Get (if already existing) an element given by the path relative to
200      * the referent element and set the value
201      * 
202      * @param ref
203      * @param path
204      * @param value
205      * @return the new added or already existing element with new value
206      */
207     static public Element addOrSetElement(Element ref, String path, String value) {
208         Element current = addOrGetElement(ref, path);
209         current.setText(value);
210         return current;
211     }
212 
213     /**
214      * Add or Get (if already existing) an element given by the path relative to
215      * the referent element
216      * 
217      * @param ref
218      * @param path
219      * @return the new added or already existing element
220      */
221     static public Element addOrGetElement(Element ref, String path) {
222         String[] pathes = path.split("/");
223         Element current = ref;
224         for (String nodename: pathes) {
225             if (nodename.length() > 0) {
226                 Element exist = current.element(nodename);
227                 if (exist == null) {
228                     current = current.addElement(nodename);
229                 } else {
230                     current = exist;
231                 }
232             }
233         }
234         return current;
235     }
236 
237     /**
238      * Add an element given by the path relative to the referent element and set the value
239      * 
240      * @param ref
241      * @param path
242      * @param value
243      * @return the new added element with value
244      */
245     static public Element addAndSetElementMultiple(Element ref, String path,
246             String value) {
247         Element current = addAndGetElementMultiple(ref, path);
248         current.setText(value);
249         return current;
250     }
251 
252     /**
253      * Add an element given by the path relative to the referent element
254      * 
255      * @param ref
256      * @param path
257      * @return the new added element
258      */
259     static public Element addAndGetElementMultiple(Element ref, String path) {
260         String[] pathes = path.split("/");
261         Element current = ref;
262         for (int i = 0; i < pathes.length - 1; i ++) {
263             String nodename = pathes[i];
264             if (nodename.length() > 0) {
265                 Element exist = current.element(nodename);
266                 if (exist == null) {
267                     current = current.addElement(nodename);
268                 } else {
269                     current = exist;
270                 }
271             }
272         }
273         String nodename = pathes[pathes.length - 1];
274         if (nodename.length() > 0) {
275             current = current.addElement(nodename);
276         }
277         return current;
278     }
279 
280     /**
281      * 
282      * @param ref
283      * @param path
284      * @return the parent element associated with the path relatively to the
285      *         referent element
286      * @throws DocumentException
287      */
288     static public Element getParentElement(Element ref, String path)
289             throws DocumentException {
290         String npath = path;
291         while (npath.charAt(0) == '/') {//startsWith("/")) {
292             npath = npath.substring(1);
293         }
294         Element current = (Element) ref.selectSingleNode(npath);
295         if (current == null) {
296             throw new DocumentException("Node not found: " + path);
297         }
298         return current.getParent();
299     }
300 
301     /**
302      * 
303      * @param ref
304      * @param path
305      * @return the element associated with the path relatively to the referent
306      *         element
307      * @throws DocumentException
308      */
309     static public Element getElement(Element ref, String path)
310             throws DocumentException {
311         String npath = path;
312         while (npath.charAt(0) == '/') {//.startsWith("/")) {
313             npath = npath.substring(1);
314         }
315         Element current = (Element) ref.selectSingleNode(npath);
316         if (current == null) {
317             throw new DocumentException("Node not found: " + path);
318         }
319         return current;
320     }
321 
322     /**
323      * 
324      * @param ref
325      * @param path
326      * @return the element associated with the path relatively to the referent
327      *         element
328      * @throws DocumentException
329      */
330     @SuppressWarnings("unchecked")
331     static public List<Element> getElementMultiple(Element ref, String path)
332             throws DocumentException {
333         String npath = path;
334         while (npath.charAt(0) == '/') {//.startsWith("/")) {
335             npath = npath.substring(1);
336         }
337         List<Element> list = ref.selectNodes(npath);
338         if (list == null || list.isEmpty()) {
339             throw new DocumentException("Nodes not found: " + path);
340         }
341         return list;
342     }
343 
344     /**
345      * Add or Get (if already existing) an element given by the path relative to
346      * the document and set the value
347      * 
348      * @param doc
349      * @param path
350      * @param value
351      * @return the new added or already existing element with new value
352      */
353     static public Element addOrSetElement(Document doc, String path,
354             String value) {
355         Element current = addOrGetElement(doc, path);
356         current.setText(value);
357         return current;
358     }
359 
360     /**
361      * Add or Get (if already existing) an element given by the path relative to
362      * the document
363      * 
364      * @param doc
365      * @param path
366      * @return the new added or already existing element
367      */
368     static public Element addOrGetElement(Document doc, String path) {
369         String[] pathes = path.split("/");
370         int rank = 0;
371         for (rank = 0; rank < pathes.length; rank ++) {
372             if (pathes[rank].length() > 0) {
373                 break; // found
374             }
375         }
376         if (rank >= pathes.length) {
377             return null; // Should not be !
378         }
379         Element current = (Element) doc.selectSingleNode(pathes[rank]);
380         if (current == null) {
381             current = doc.addElement(pathes[rank]);
382         }
383         for (int i = rank + 1; i < pathes.length; i ++) {
384             String nodename = pathes[i];
385             if (nodename.length() > 0) {
386                 Element exist = current.element(nodename);
387                 if (exist == null) {
388                     current = current.addElement(nodename);
389                 } else {
390                     current = exist;
391                 }
392             }
393         }
394         return current;
395     }
396 
397     /**
398      * Add an element given by the path relative to the document and set the value
399      * 
400      * @param doc
401      * @param path
402      * @param value
403      * @return the new added element with value
404      */
405     static public Element addAndSetElementMultiple(Document doc, String path,
406             String value) {
407         Element current = addAndGetElementMultiple(doc, path);
408         current.setText(value);
409         return current;
410     }
411 
412     /**
413      * Add an element given by the path relative to the document
414      * 
415      * @param doc
416      * @param path
417      * @return the new added element
418      */
419     static public Element addAndGetElementMultiple(Document doc, String path) {
420         String[] pathes = path.split("/");
421         int rank = 0;
422         for (rank = 0; rank < pathes.length; rank ++) {
423             if (pathes[rank].length() > 0) {
424                 break; // found
425             }
426         }
427         if (rank >= pathes.length) {
428             return null; // Should not be !
429         }
430         Element current = (Element) doc.selectSingleNode(pathes[rank]);
431         if (current == null) {
432             current = doc.addElement(pathes[rank]);
433         }
434         if (rank == pathes.length - 1) {
435             // Last level is the root !!! No multiple root is allowed !!!
436             // So just give back the root if it exists
437             return current;
438         }
439         for (int i = rank + 1; i < pathes.length - 1; i ++) {
440             String nodename = pathes[i];
441             if (nodename.length() > 0) {
442                 Element exist = current.element(nodename);
443                 if (exist == null) {
444                     current = current.addElement(nodename);
445                 } else {
446                     current = exist;
447                 }
448             }
449         }
450         String nodename = pathes[pathes.length - 1];
451         if (nodename.length() > 0) {
452             current = current.addElement(nodename);
453         }
454         return current;
455     }
456 
457     /**
458      * 
459      * @param doc
460      * @param path
461      * @return the Parent element associated with the path relatively to the
462      *         document
463      * @throws DocumentException
464      */
465     static public Element getParentElement(Document doc, String path)
466             throws DocumentException {
467         Element current = (Element) doc.selectSingleNode(path);
468         if (current == null) {
469             throw new DocumentException("Node not found: " + path);
470         }
471         return current.getParent();
472     }
473 
474     /**
475      * 
476      * @param doc
477      * @param path
478      * @return the element associated with the path relatively to the document
479      * @throws DocumentException
480      */
481     static public Element getElement(Document doc, String path)
482             throws DocumentException {
483         Element current = (Element) doc.selectSingleNode(path);
484         if (current == null) {
485             throw new DocumentException("Node not found: " + path);
486         }
487         return current;
488     }
489 
490     /**
491      * 
492      * @param doc
493      * @param path
494      * @return the element associated with the path relatively to the document
495      * @throws DocumentException
496      */
497     @SuppressWarnings("unchecked")
498     static public List<Element> getElementMultiple(Document doc, String path)
499             throws DocumentException {
500         List<Element> list = doc.selectNodes(path);
501         if (list == null || list.isEmpty()) {
502             throw new DocumentException("Nodes not found: " + path);
503         }
504         return list;
505     }
506 
507     /**
508      * Create the XmlValues from the XmlDevls and the Document
509      * 
510      * @param doc
511      * @param decls
512      * @return XmlValues
513      */
514     static public XmlValue[] read(Document doc, XmlDecl[] decls) {
515         XmlValue[] values = null;
516         int len = decls.length;
517         values = new XmlValue[len];
518         for (int i = 0; i < len; i ++) {
519             XmlValue value = new XmlValue(decls[i]);
520             values[i] = value;
521             if (decls[i].isSubXml()) {
522                 if (decls[i].isMultiple()) {
523                     List<Element> elts;
524                     try {
525                         elts = getElementMultiple(doc, decls[i].getXmlPath());
526                     } catch (DocumentException e) {
527                         continue;
528                     }
529                     for (Element element: elts) {
530                         XmlValue[] newValue = read(element,
531                                 decls[i].getSubXml());
532                         if (newValue == null) {
533                             continue;
534                         }
535                         try {
536                             value.addValue(newValue);
537                         } catch (InvalidObjectException e) {
538                             continue;
539                         }
540                     }
541                 } else {
542                     Element element;
543                     try {
544                         element = getElement(doc, decls[i].getXmlPath());
545                     } catch (DocumentException e) {
546                         continue;
547                     }
548                     XmlValue[] newValue = read(element, decls[i].getSubXml());
549                     if (newValue == null) {
550                         continue;
551                     }
552                     try {
553                         value.setValue(newValue);
554                     } catch (InvalidObjectException e) {
555                         continue;
556                     }
557                 }
558             } else if (decls[i].isMultiple()) {
559                 List<Element> elts;
560                 try {
561                     elts = getElementMultiple(doc, decls[i].getXmlPath());
562                 } catch (DocumentException e) {
563                     continue;
564                 }
565                 for (Element element: elts) {
566                     String svalue = element.getText();
567                     try {
568                         value.addFromString(svalue);
569                     } catch (InvalidObjectException e) {
570                         continue;
571                     }
572                 }
573             } else {
574                 Element element;
575                 try {
576                     element = getElement(doc, decls[i].getXmlPath());
577                 } catch (DocumentException e) {
578                     continue;
579                 }
580                 String svalue = element.getText();
581                 value.setFromString(svalue);
582             }
583         }
584         return values;
585     }
586 
587     /**
588      * Create the XmlValues from the XmlDevls and the reference Element
589      * 
590      * @param ref
591      * @param decls
592      * @return XmlValues
593      */
594     static public XmlValue[] read(Element ref, XmlDecl[] decls) {
595         XmlValue[] values = null;
596         int len = decls.length;
597         values = new XmlValue[len];
598         for (int i = 0; i < len; i ++) {
599             XmlValue value = new XmlValue(decls[i]);
600             values[i] = value;
601             if (decls[i].isSubXml()) {
602                 if (decls[i].isMultiple()) {
603                     List<Element> elts;
604                     try {
605                         elts = getElementMultiple(ref, decls[i].getXmlPath());
606                     } catch (DocumentException e) {
607                         continue;
608                     }
609                     for (Element element: elts) {
610                         XmlValue[] newValue = read(element,
611                                 decls[i].getSubXml());
612                         if (newValue == null) {
613                             continue;
614                         }
615                         try {
616                             value.addValue(newValue);
617                         } catch (InvalidObjectException e) {
618                             continue;
619                         }
620                     }
621                 } else {
622                     Element element;
623                     try {
624                         element = getElement(ref, decls[i].getXmlPath());
625                     } catch (DocumentException e) {
626                         continue;
627                     }
628                     XmlValue[] newValue = read(element, decls[i].getSubXml());
629                     if (newValue == null) {
630                         continue;
631                     }
632                     try {
633                         value.setValue(newValue);
634                     } catch (InvalidObjectException e) {
635                         continue;
636                     }
637                 }
638             } else if (decls[i].isMultiple()) {
639                 List<Element> elts;
640                 try {
641                     elts = getElementMultiple(ref, decls[i].getXmlPath());
642                 } catch (DocumentException e) {
643                     continue;
644                 }
645                 for (Element element: elts) {
646                     String svalue = element.getText();
647                     try {
648                         value.addFromString(svalue);
649                     } catch (InvalidObjectException e) {
650                         continue;
651                     }
652                 }
653             } else {
654                 Element element;
655                 try {
656                     element = getElement(ref, decls[i].getXmlPath());
657                 } catch (DocumentException e) {
658                     continue;
659                 }
660                 String svalue = element.getText();
661                 value.setFromString(svalue);
662             }
663         }
664         return values;
665     }
666 
667     /**
668      * Add all nodes from XmlValues into Document
669      * 
670      * @param doc
671      * @param values
672      */
673     @SuppressWarnings("unchecked")
674     public static void write(Document doc, XmlValue[] values) {
675         int len = values.length;
676         for (int i = 0; i < len; i ++) {
677             if (values[i] != null) {
678                 if (values[i].isSubXml()) {
679                     if (values[i].isMultiple()) {
680                         List<XmlValue[]> list = (List<XmlValue[]>) values[i]
681                                 .getList();
682                         for (XmlValue[] object: list) {
683                             Element ref = addAndGetElementMultiple(doc,
684                                     values[i].getXmlPath());
685                             write(ref, object);
686                         }
687                     } else {
688                         Element ref = addOrGetElement(doc,
689                                 values[i].getXmlPath());
690                         write(ref, values[i].getSubXml());
691                     }
692                 } else if (values[i].isMultiple()) {
693                     List<?> list = values[i].getList();
694                     for (Object object: list) {
695                         addAndSetElementMultiple(doc, values[i].getXmlPath(),
696                                 object.toString());
697                     }
698                 } else {
699                     addOrSetElement(doc, values[i].getXmlPath(),
700                             values[i].getIntoString());
701                 }
702             }
703         }
704     }
705 
706     /**
707      * Add all nodes from XmlValues from the referenced Element
708      * 
709      * @param ref
710      * @param values
711      */
712     @SuppressWarnings("unchecked")
713     public static void write(Element ref, XmlValue[] values) {
714         int len = values.length;
715         for (int i = 0; i < len; i ++) {
716             if (values[i] != null) {
717                 if (values[i].isSubXml()) {
718                     if (values[i].isMultiple()) {
719                         List<XmlValue[]> list = (List<XmlValue[]>) values[i]
720                                 .getList();
721                         for (XmlValue[] object: list) {
722                             Element newref = addAndGetElementMultiple(ref,
723                                     values[i].getXmlPath());
724                             write(newref, object);
725                         }
726                     } else {
727                         Element newref = addOrGetElement(ref,
728                                 values[i].getXmlPath());
729                         write(newref, values[i].getSubXml());
730                     }
731                 } else if (values[i].isMultiple()) {
732                     List<?> list = values[i].getList();
733                     for (Object object: list) {
734                         addAndSetElementMultiple(ref, values[i].getXmlPath(),
735                                 object.toString());
736                     }
737                 } else {
738                     addOrSetElement(ref, values[i].getXmlPath(),
739                             values[i].getIntoString());
740                 }
741             }
742         }
743     }
744 
745     /**
746      * Example (run test)
747      * @param args
748      */
749     @SuppressWarnings("unchecked")
750     public static void main(String[] args) {
751         XmlDecl[] decls = {
752                 new XmlDecl("n1", XmlType.BOOLEAN, "/root/bool", false),
753                 new XmlDecl("n2", XmlType.FLOAT, "/root/float", false),
754                 new XmlDecl("n3", XmlType.STRING, "/root/string", false),
755                 new XmlDecl("n4", XmlType.INTEGER, "/root/int", false),
756                 new XmlDecl("n5", XmlType.BOOLEAN, "/root/sub1/sub2/bool",
757                         false),
758                 new XmlDecl("n6", XmlType.BOOLEAN, "/root/sub1/sub3/bool", true),
759                 new XmlDecl("n7", XmlType.BOOLEAN,
760                         "/root/sub4/sub5/sub6/sub7/bool", false) };
761         String xmltree = "<root><bool>true</bool><float>1.2</float><string>my string to read</string><int>5</int>"
762                 + "<sub1>"
763                 + "<sub2><bool>1</bool></sub2>"
764                 + "<sub3>"
765                 + "<bool>false</bool><bool>true</bool><bool>0</bool>"
766                 + "</sub3>"
767                 + "</sub1>"
768                 + "<sub4><sub5><sub6><sub7><bool>True</bool></sub7></sub6></sub5></sub4>"
769                 + "</root>";
770         // Test with only single XmlType (no XVAL)
771         Document document = null;
772         try {
773             document = readDocument(xmltree);
774         } catch (DocumentException e) {
775             e.printStackTrace();
776             return;
777         }
778         try {
779             saveDocument("D:\\Tools\\test-src.xml", document);
780         } catch (IOException e) {
781             e.printStackTrace();
782             return;
783         }
784         XmlValue[] values = read(document, decls);
785         Document newDoc = createEmptyDocument();
786         write(newDoc, values);
787         String text = writeToString(newDoc);
788         System.err.println(text);
789         try {
790             saveDocument("D:\\Tools\\test.xml", newDoc);
791         } catch (IOException e) {
792             e.printStackTrace();
793             return;
794         }
795         try {
796             newDoc = getDocument("D:\\Tools\\test.xml");
797         } catch (IOException e) {
798             e.printStackTrace();
799             return;
800         } catch (DocumentException e) {
801             e.printStackTrace();
802             return;
803         }
804         values = read(newDoc, decls);
805         XmlHash hash = new XmlHash(values);
806         XmlValue value = hash.get("n6");
807         if (value != null) {
808             if (value.isMultiple()) {
809                 try {
810                     value.addFromString("true");
811                 } catch (InvalidObjectException e) {
812                     e.printStackTrace();
813                 }
814             }
815         }
816         value = hash.get("n3");
817         if (value != null) {
818             if (!value.isMultiple()) {
819                 try {
820                     value.setValue("New String as replacement");
821                 } catch (InvalidObjectException e) {
822                     e.printStackTrace();
823                 }
824             }
825         }
826         newDoc = createEmptyDocument();
827         write(newDoc, values);
828         try {
829             saveDocument("D:\\Tools\\test2.xml", newDoc);
830         } catch (IOException e) {
831             e.printStackTrace();
832             return;
833         }
834         Element elt = null;
835         try {
836             elt = getElement(newDoc, "/root/sub4");
837         } catch (DocumentException e1) {
838             e1.printStackTrace();
839             return;
840         }
841         XmlValue[] oldValues = read(document, decls);
842         write(elt, oldValues);
843         try {
844             saveDocument("D:\\Tools\\test3.xml", newDoc);
845         } catch (IOException e) {
846             e.printStackTrace();
847             return;
848         }
849         write(newDoc, oldValues);
850         try {
851             saveDocument("D:\\Tools\\test4.xml", newDoc);
852         } catch (IOException e) {
853             e.printStackTrace();
854             return;
855         }
856         try {
857             saveElement("D:\\Tools\\test5.xml",
858                     (Element) elt.selectSingleNode("root"));
859         } catch (IOException e) {
860             e.printStackTrace();
861             return;
862         }
863         // Now test XmlType.XVAL
864         System.err.println("\n\nXVAL TESTING\n");
865         XmlDecl[] subdecls1 = {
866                 new XmlDecl("n11", XmlType.BOOLEAN, "/n11/bool", false),
867                 new XmlDecl("n12", XmlType.FLOAT, "/n11/float", true) };
868         XmlDecl[] subdecls2 = {
869                 new XmlDecl("n21", XmlType.BOOLEAN, "/n21/bool", false),
870                 new XmlDecl("n22", XmlType.FLOAT, "/n21/float", true) };
871         XmlDecl[] maindecls = {
872                 new XmlDecl("n1", XmlType.BOOLEAN, "/root/bool", false),
873                 new XmlDecl("n2", XmlType.FLOAT, "/root/float", false),
874                 new XmlDecl("n3", XmlType.STRING, "/root/string", false),
875                 new XmlDecl("n4", XmlType.INTEGER, "/root/int", false),
876                 new XmlDecl("n5", XmlType.BOOLEAN, "/root/sub1/sub2/bool",
877                         false),
878                 new XmlDecl("n6", XmlType.BOOLEAN, "/root/sub1/sub3/bool", true),
879                 new XmlDecl("n7", XmlType.BOOLEAN,
880                         "/root/sub4/sub5/sub6/sub7/bool", false),
881                 new XmlDecl("n10", XmlType.XVAL, "/root/sub8", subdecls1, false),
882                 new XmlDecl("n20", XmlType.XVAL, "/root/sub9", subdecls2, true) };
883         xmltree = "<root><bool>true</bool><float>1.2</float><string>my string to read</string><int>5</int>"
884                 + "<sub1>"
885                 + "<sub2><bool>1</bool></sub2>"
886                 + "<sub3>"
887                 + "<bool>false</bool><bool>true</bool><bool>0</bool>"
888                 + "</sub3>"
889                 + "</sub1>"
890                 + "<sub4><sub5><sub6><sub7><bool>True</bool></sub7></sub6></sub5></sub4>"
891                 + "<sub8><n11><bool>true</bool><float>1.2</float><float>1.3</float><float>1.4</float></n11></sub8>"
892                 + "<sub9><n21><bool>true</bool><float>2.2</float><float>2.3</float><float>2.4</float></n21></sub9>"
893                 + "<sub9><n21><bool>false</bool><float>3.2</float><float>3.3</float><float>3.4</float></n21></sub9>"
894                 + "<sub9><n21><bool>true</bool><float>4.2</float><float>4.3</float><float>4.4</float></n21></sub9>"
895                 + "</root>";
896         document = null;
897         try {
898             document = readDocument(xmltree);
899         } catch (DocumentException e) {
900             e.printStackTrace();
901             return;
902         }
903         try {
904             saveDocument("D:\\Tools\\xtest-src.xml", document);
905         } catch (IOException e) {
906             e.printStackTrace();
907             return;
908         }
909         values = read(document, maindecls);
910         newDoc = createEmptyDocument();
911         write(newDoc, values);
912         text = writeToString(newDoc);
913         System.err.println(text);
914         try {
915             saveDocument("D:\\Tools\\xtest.xml", newDoc);
916         } catch (IOException e) {
917             e.printStackTrace();
918             return;
919         }
920         try {
921             newDoc = getDocument("D:\\Tools\\xtest.xml");
922         } catch (IOException e) {
923             e.printStackTrace();
924             return;
925         } catch (DocumentException e) {
926             e.printStackTrace();
927             return;
928         }
929         values = read(newDoc, maindecls);
930         hash = new XmlHash(values);
931         value = hash.get("n10");
932         if (value != null) {
933             if (!value.isMultiple()) {
934                 XmlValue[] subvalues = value.getSubXml();
935                 XmlHash subhash = new XmlHash(subvalues);
936                 value = subhash.get("n12");
937                 try {
938                     value.addFromString("10.3");
939                 } catch (InvalidObjectException e) {
940                     e.printStackTrace();
941                 }
942             }
943         }
944         value = hash.get("n20");
945         if (value != null) {
946             if (value.isMultiple()) {
947                 List<XmlValue[]> listvalues = (List<XmlValue[]>)value.getList();
948                 XmlValue[] subvalues = listvalues.get(0);
949                 XmlValue[] subvalues2 = new XmlValue[subvalues.length];
950                 // Create a perfect clone
951                 for (int i = 0; i < subvalues.length; i++) {
952                     subvalues2[i] = new XmlValue(subvalues[i]);
953                 }
954                 // Add it at the end
955                 listvalues.add(subvalues2);
956                 XmlHash subhash = new XmlHash(subvalues2);
957                 value = subhash.get("n21");
958                 try {
959                     value.setValue(Boolean.FALSE);
960                 } catch (InvalidObjectException e) {
961                     e.printStackTrace();
962                 }
963             }
964         }
965         newDoc = createEmptyDocument();
966         write(newDoc, values);
967         try {
968             saveDocument("D:\\Tools\\xtest2.xml", newDoc);
969         } catch (IOException e) {
970             e.printStackTrace();
971             return;
972         }
973         elt = null;
974         try {
975             elt = getElement(newDoc, "/root/sub9");
976         } catch (DocumentException e1) {
977             e1.printStackTrace();
978             return;
979         }
980         try {
981             saveElement("D:\\Tools\\xtest5.xml",
982                     elt);
983         } catch (IOException e) {
984             e.printStackTrace();
985             return;
986         }
987     }
988 
989     /**
990      * Write the given XML document to filename using the encoding
991      * @param filename
992      * @param encoding if null, default encoding ISO-8859-1 will be used
993      * @param document
994      * @throws IOException 
995      */
996     public static void writeXML(String filename, String encoding, Document document) throws IOException {
997         OutputFormat format = OutputFormat.createPrettyPrint();
998         if (encoding != null) {
999             format.setEncoding(encoding);
1000         } else {
1001             format.setEncoding("ISO-8859-1");
1002         }
1003         XMLWriter writer = null;
1004         writer = new XMLWriter(new FileWriter(filename), format);
1005         writer.write(document);
1006         try {
1007             writer.close();
1008         } catch (IOException e) {
1009         }
1010     }
1011 }