org.waarp.common.xml.XmlUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.waarp.common.xml.XmlUtil.java

Source

/**
 * This file is part of Waarp Project.
 * 
 * Copyright 2009, Frederic Bregier, and individual contributors by the @author tags. See the
 * COPYRIGHT.txt in the distribution for a full listing of individual contributors.
 * 
 * All Waarp Project is free software: you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with Waarp . If not, see
 * <http://www.gnu.org/licenses/>.
 */
package org.waarp.common.xml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.waarp.common.exception.InvalidArgumentException;
import org.waarp.common.utility.WaarpStringUtils;

/**
 * XML utility that handles simple cases as:<br>
 * <ul>
 * <li>XPath as /x/y/z for a node and referring to root of Document</li>
 * <li>XPath as x/y/z for a node and referring to current referenced node</li>
 * <li>Any XPath can be a singleton (unique node referenced by the XPath) or multiple nodes (same XPath)</li>
 * <li>Only Element as Node: no Attribute or any other type within XML</li>
 * <li>Any other path is not supported: //x /x@y ./ ../</li>
 * <li>Supports special SubXml tree as element (singleton or multiple)</li>
 * </ul>
 * 
 * @author Frederic Bregier
 * 
 */
public class XmlUtil {

    /**
     * 
     * @param filename
     * @return Existing Document from filename
     * @throws IOException
     * @throws DocumentException
     */
    static public Document getDocument(String filename) throws IOException, DocumentException {
        File file = new File(filename);
        if (!file.canRead()) {
            throw new IOException("File is not readable: " + filename);
        }
        return getDocument(file);
    }

    /**
     * 
     * @param file
     * @return Existing Document from file
     * @throws IOException
     * @throws DocumentException
     */
    static public Document getDocument(File file) throws IOException, DocumentException {
        if (!file.canRead()) {
            throw new IOException("File is not readable: " + file.getPath());
        }
        SAXReader reader = new SAXReader();
        return reader.read(file);
    }

    /**
     * Read the document from the string
     * 
     * @param document
     *            as String
     * @return the Document
     * @throws DocumentException
     */
    static public Document readDocument(String document) throws DocumentException {
        return DocumentHelper.parseText(document);
    }

    /**
     * 
     * @param document
     * @return the document as an XML string
     */
    static public String writeToString(Document document) {
        return document.asXML();
    }

    /**
     * 
     * @param element
     * @return the element as an XML string
     */
    static public String writeToString(Element element) {
        return element.asXML();
    }

    /**
     * 
     * @return an empty new Document
     */
    static public Document createEmptyDocument() {
        return DocumentHelper.createDocument();
    }

    /**
     * Save the document into the file
     * 
     * @param filename
     * @param document
     * @throws IOException
     */
    static public void saveDocument(String filename, Document document) throws IOException {
        File file = new File(filename);
        saveDocument(file, document);
    }

    /**
     * Save the document into the file
     * 
     * @param file
     * @param document
     * @throws IOException
     */
    static public void saveDocument(File file, Document document) throws IOException {
        if (file.exists() && (!file.canWrite())) {
            throw new IOException("File is not writable: " + file.getPath());
        }
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding(WaarpStringUtils.UTF8.name());
        XMLWriter writer = new XMLWriter(new FileWriter(file), format);
        writer.write(document);
        writer.flush();
        writer.close();
    }

    /**
     * Save the branch from element into the file
     * 
     * @param filename
     * @param element
     * @throws IOException
     */
    static public void saveElement(String filename, Element element) throws IOException {
        File file = new File(filename);
        saveElement(file, element);
    }

    /**
     * Save the branch from element into the file
     * 
     * @param file
     * @param element
     * @throws IOException
     */
    static public void saveElement(File file, Element element) throws IOException {
        if (file.exists() && (!file.canWrite())) {
            throw new IOException("File is not writable: " + file.getPath());
        }
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding(WaarpStringUtils.UTF8.name());
        XMLWriter writer = new XMLWriter(new FileWriter(file), format);
        writer.write(element);
        writer.flush();
        writer.close();
    }

    /**
     * 
     * @param document
     * @return the root Element from the document
     */
    static public Element getRootElement(Document document) {
        return document.getRootElement();
    }

    /**
     * Add or Get (if already existing) an element given by the path relative to the referent
     * element and set the value
     * 
     * @param ref
     * @param path
     * @param value
     * @return the new added or already existing element with new value
     */
    static public Element addOrSetElement(Element ref, String path, String value) {
        Element current = addOrGetElement(ref, path);
        current.setText(value);
        return current;
    }

    /**
     * Add or Get (if already existing) an element given by the path relative to the referent
     * element
     * 
     * @param ref
     * @param path
     * @return the new added or already existing element
     */
    static public Element addOrGetElement(Element ref, String path) {
        String[] pathes = path.split("/");
        Element current = ref;
        for (String nodename : pathes) {
            if (!nodename.isEmpty()) {
                Element exist = current.element(nodename);
                if (exist == null) {
                    current = current.addElement(nodename);
                } else {
                    current = exist;
                }
            }
        }
        return current;
    }

    /**
     * Add an element given by the path relative to the referent element and set the value
     * 
     * @param ref
     * @param path
     * @param value
     * @return the new added element with value
     */
    static public Element addAndSetElementMultiple(Element ref, String path, String value) {
        Element current = addAndGetElementMultiple(ref, path);
        current.setText(value);
        return current;
    }

    /**
     * Add an element given by the path relative to the referent element
     * 
     * @param ref
     * @param path
     * @return the new added element
     */
    static public Element addAndGetElementMultiple(Element ref, String path) {
        String[] pathes = path.split("/");
        Element current = ref;
        for (int i = 0; i < pathes.length - 1; i++) {
            String nodename = pathes[i];
            if (!nodename.isEmpty()) {
                Element exist = current.element(nodename);
                if (exist == null) {
                    current = current.addElement(nodename);
                } else {
                    current = exist;
                }
            }
        }
        String nodename = pathes[pathes.length - 1];
        if (!nodename.isEmpty()) {
            current = current.addElement(nodename);
        }
        return current;
    }

    /**
     * 
     * @param ref
     * @param path
     * @return the parent element associated with the path relatively to the referent element
     * @throws DocumentException
     */
    static public Element getParentElement(Element ref, String path) throws DocumentException {
        String npath = path;
        while (npath.charAt(0) == '/') {// startsWith("/")) {
            npath = npath.substring(1);
        }
        Element current = (Element) ref.selectSingleNode(npath);
        if (current == null) {
            throw new DocumentException("Node not found: " + path);
        }
        return current.getParent();
    }

    /**
     * 
     * @param ref
     * @param path
     * @return the element associated with the path relatively to the referent element
     * @throws DocumentException
     */
    static public Element getElement(Element ref, String path) throws DocumentException {
        String npath = path;
        while (npath.charAt(0) == '/') {// .startsWith("/")) {
            npath = npath.substring(1);
        }
        Element current = (Element) ref.selectSingleNode(npath);
        if (current == null) {
            throw new DocumentException("Node not found: " + path);
        }
        return current;
    }

    /**
     * 
     * @param ref
     * @param path
     * @return the element associated with the path relatively to the referent element
     * @throws DocumentException
     */
    @SuppressWarnings("unchecked")
    static public List<Element> getElementMultiple(Element ref, String path) throws DocumentException {
        String npath = path;
        while (npath.charAt(0) == '/') {// .startsWith("/")) {
            npath = npath.substring(1);
        }
        List<Element> list = ref.selectNodes(npath);
        if (list == null || list.isEmpty()) {
            throw new DocumentException("Nodes not found: " + path);
        }
        return list;
    }

    /**
     * Add or Get (if already existing) an element given by the path relative to the document and
     * set the value
     * 
     * @param doc
     * @param path
     * @param value
     * @return the new added or already existing element with new value
     */
    static public Element addOrSetElement(Document doc, String path, String value) {
        Element current = addOrGetElement(doc, path);
        current.setText(value);
        return current;
    }

    /**
     * Add or Get (if already existing) an element given by the path relative to the document
     * 
     * @param doc
     * @param path
     * @return the new added or already existing element
     */
    static public Element addOrGetElement(Document doc, String path) {
        String[] pathes = path.split("/");
        int rank = 0;
        for (rank = 0; rank < pathes.length; rank++) {
            if (!pathes[rank].isEmpty()) {
                break; // found
            }
        }
        if (rank >= pathes.length) {
            return null; // Should not be !
        }
        Element current = (Element) doc.selectSingleNode(pathes[rank]);
        if (current == null) {
            current = doc.addElement(pathes[rank]);
        }
        for (int i = rank + 1; i < pathes.length; i++) {
            String nodename = pathes[i];
            if (!nodename.isEmpty()) {
                Element exist = current.element(nodename);
                if (exist == null) {
                    current = current.addElement(nodename);
                } else {
                    current = exist;
                }
            }
        }
        return current;
    }

    /**
     * Add an element given by the path relative to the document and set the value
     * 
     * @param doc
     * @param path
     * @param value
     * @return the new added element with value
     */
    static public Element addAndSetElementMultiple(Document doc, String path, String value) {
        Element current = addAndGetElementMultiple(doc, path);
        current.setText(value);
        return current;
    }

    /**
     * Add an element given by the path relative to the document
     * 
     * @param doc
     * @param path
     * @return the new added element
     */
    static public Element addAndGetElementMultiple(Document doc, String path) {
        String[] pathes = path.split("/");
        int rank = 0;
        for (rank = 0; rank < pathes.length; rank++) {
            if (!pathes[rank].isEmpty()) {
                break; // found
            }
        }
        if (rank >= pathes.length) {
            return null; // Should not be !
        }
        Element current = (Element) doc.selectSingleNode(pathes[rank]);
        if (current == null) {
            current = doc.addElement(pathes[rank]);
        }
        if (rank == pathes.length - 1) {
            // Last level is the root !!! No multiple root is allowed !!!
            // So just give back the root if it exists
            return current;
        }
        for (int i = rank + 1; i < pathes.length - 1; i++) {
            String nodename = pathes[i];
            if (!nodename.isEmpty()) {
                Element exist = current.element(nodename);
                if (exist == null) {
                    current = current.addElement(nodename);
                } else {
                    current = exist;
                }
            }
        }
        String nodename = pathes[pathes.length - 1];
        if (!nodename.isEmpty()) {
            current = current.addElement(nodename);
        }
        return current;
    }

    /**
     * 
     * @param doc
     * @param path
     * @return the Parent element associated with the path relatively to the document
     * @throws DocumentException
     */
    static public Element getParentElement(Document doc, String path) throws DocumentException {
        Element current = (Element) doc.selectSingleNode(path);
        if (current == null) {
            throw new DocumentException("Node not found: " + path);
        }
        return current.getParent();
    }

    /**
     * 
     * @param doc
     * @param path
     * @return the element associated with the path relatively to the document
     * @throws DocumentException
     */
    static public Element getElement(Document doc, String path) throws DocumentException {
        Element current = (Element) doc.selectSingleNode(path);
        if (current == null) {
            throw new DocumentException("Node not found: " + path);
        }
        return current;
    }

    /**
     * 
     * @param doc
     * @param path
     * @return the element associated with the path relatively to the document
     * @throws DocumentException
     */
    @SuppressWarnings("unchecked")
    static public List<Element> getElementMultiple(Document doc, String path) throws DocumentException {
        List<Element> list = doc.selectNodes(path);
        if (list == null || list.isEmpty()) {
            throw new DocumentException("Nodes not found: " + path);
        }
        return list;
    }

    /**
     * Create the XmlValues from the XmlDevls and the Document
     * 
     * @param doc
     * @param decls
     * @return XmlValues
     */
    static public XmlValue[] read(Document doc, XmlDecl[] decls) {
        XmlValue[] values = null;
        int len = decls.length;
        values = new XmlValue[len];
        for (int i = 0; i < len; i++) {
            XmlValue value = new XmlValue(decls[i]);
            values[i] = value;
            if (decls[i].isSubXml()) {
                if (decls[i].isMultiple()) {
                    List<Element> elts;
                    try {
                        elts = getElementMultiple(doc, decls[i].getXmlPath());
                    } catch (DocumentException e) {
                        continue;
                    }
                    for (Element element : elts) {
                        XmlValue[] newValue = read(element, decls[i].getSubXml());
                        if (newValue == null) {
                            continue;
                        }
                        try {
                            value.addValue(newValue);
                        } catch (InvalidObjectException e) {
                            continue;
                        }
                    }
                } else {
                    Element element;
                    try {
                        element = getElement(doc, decls[i].getXmlPath());
                    } catch (DocumentException e) {
                        continue;
                    }
                    XmlValue[] newValue = read(element, decls[i].getSubXml());
                    if (newValue == null) {
                        continue;
                    }
                    try {
                        value.setValue(newValue);
                    } catch (InvalidObjectException e) {
                        continue;
                    }
                }
            } else if (decls[i].isMultiple()) {
                List<Element> elts;
                try {
                    elts = getElementMultiple(doc, decls[i].getXmlPath());
                } catch (DocumentException e) {
                    continue;
                }
                for (Element element : elts) {
                    String svalue = element.getText();
                    try {
                        value.addFromString(svalue);
                    } catch (InvalidObjectException e) {
                        continue;
                    } catch (InvalidArgumentException e) {
                        continue;
                    }
                }
            } else {
                Element element;
                try {
                    element = getElement(doc, decls[i].getXmlPath());
                } catch (DocumentException e) {
                    continue;
                }
                String svalue = element.getText();
                try {
                    value.setFromString(svalue);
                } catch (InvalidArgumentException e) {
                    continue;
                }
            }
        }
        return values;
    }

    /**
     * Create the XmlValues from the XmlDevls and the reference Element
     * 
     * @param ref
     * @param decls
     * @return XmlValues
     */
    static public XmlValue[] read(Element ref, XmlDecl[] decls) {
        XmlValue[] values = null;
        int len = decls.length;
        values = new XmlValue[len];
        for (int i = 0; i < len; i++) {
            XmlValue value = new XmlValue(decls[i]);
            values[i] = value;
            if (decls[i].isSubXml()) {
                if (decls[i].isMultiple()) {
                    List<Element> elts;
                    try {
                        elts = getElementMultiple(ref, decls[i].getXmlPath());
                    } catch (DocumentException e) {
                        continue;
                    }
                    for (Element element : elts) {
                        XmlValue[] newValue = read(element, decls[i].getSubXml());
                        if (newValue == null) {
                            continue;
                        }
                        try {
                            value.addValue(newValue);
                        } catch (InvalidObjectException e) {
                            continue;
                        }
                    }
                } else {
                    Element element;
                    try {
                        element = getElement(ref, decls[i].getXmlPath());
                    } catch (DocumentException e) {
                        continue;
                    }
                    XmlValue[] newValue = read(element, decls[i].getSubXml());
                    if (newValue == null) {
                        continue;
                    }
                    try {
                        value.setValue(newValue);
                    } catch (InvalidObjectException e) {
                        continue;
                    }
                }
            } else if (decls[i].isMultiple()) {
                List<Element> elts;
                try {
                    elts = getElementMultiple(ref, decls[i].getXmlPath());
                } catch (DocumentException e) {
                    continue;
                }
                for (Element element : elts) {
                    String svalue = element.getText();
                    try {
                        value.addFromString(svalue);
                    } catch (InvalidObjectException e) {
                        continue;
                    } catch (InvalidArgumentException e) {
                        continue;
                    }
                }
            } else {
                Element element;
                try {
                    element = getElement(ref, decls[i].getXmlPath());
                } catch (DocumentException e) {
                    continue;
                }
                String svalue = element.getText();
                try {
                    value.setFromString(svalue);
                } catch (InvalidArgumentException e) {
                    continue;
                }
            }
        }
        return values;
    }

    /**
     * Add all nodes from XmlValues into Document
     * 
     * @param doc
     * @param values
     */
    @SuppressWarnings("unchecked")
    public static void write(Document doc, XmlValue[] values) {
        int len = values.length;
        for (int i = 0; i < len; i++) {
            if (values[i] != null) {
                if (values[i].isSubXml()) {
                    if (values[i].isMultiple()) {
                        List<XmlValue[]> list = (List<XmlValue[]>) values[i].getList();
                        for (XmlValue[] object : list) {
                            Element ref = addAndGetElementMultiple(doc, values[i].getXmlPath());
                            write(ref, object);
                        }
                    } else {
                        Element ref = addOrGetElement(doc, values[i].getXmlPath());
                        write(ref, values[i].getSubXml());
                    }
                } else if (values[i].isMultiple()) {
                    List<?> list = values[i].getList();
                    for (Object object : list) {
                        addAndSetElementMultiple(doc, values[i].getXmlPath(), object.toString());
                    }
                } else {
                    addOrSetElement(doc, values[i].getXmlPath(), values[i].getIntoString());
                }
            }
        }
    }

    /**
     * Add all nodes from XmlValues from the referenced Element
     * 
     * @param ref
     * @param values
     */
    @SuppressWarnings("unchecked")
    public static void write(Element ref, XmlValue[] values) {
        int len = values.length;
        for (int i = 0; i < len; i++) {
            if (values[i] != null) {
                if (values[i].isSubXml()) {
                    if (values[i].isMultiple()) {
                        List<XmlValue[]> list = (List<XmlValue[]>) values[i].getList();
                        for (XmlValue[] object : list) {
                            Element newref = addAndGetElementMultiple(ref, values[i].getXmlPath());
                            write(newref, object);
                        }
                    } else {
                        Element newref = addOrGetElement(ref, values[i].getXmlPath());
                        write(newref, values[i].getSubXml());
                    }
                } else if (values[i].isMultiple()) {
                    List<?> list = values[i].getList();
                    for (Object object : list) {
                        addAndSetElementMultiple(ref, values[i].getXmlPath(), object.toString());
                    }
                } else {
                    addOrSetElement(ref, values[i].getXmlPath(), values[i].getIntoString());
                }
            }
        }
    }

    /**
     * Example (run test)
     * 
     * @param args
     */
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
        XmlDecl[] decls = { new XmlDecl("n1", XmlType.BOOLEAN, "/root/bool", false),
                new XmlDecl("n2", XmlType.FLOAT, "/root/float", false),
                new XmlDecl("n3", XmlType.STRING, "/root/string", false),
                new XmlDecl("n4", XmlType.INTEGER, "/root/int", false),
                new XmlDecl("n5", XmlType.BOOLEAN, "/root/sub1/sub2/bool", false),
                new XmlDecl("n6", XmlType.BOOLEAN, "/root/sub1/sub3/bool", true),
                new XmlDecl("n7", XmlType.BOOLEAN, "/root/sub4/sub5/sub6/sub7/bool", false) };
        String xmltree = "<root><bool>true</bool><float>1.2</float><string>my string to read</string><int>5</int>"
                + "<sub1>" + "<sub2><bool>1</bool></sub2>" + "<sub3>"
                + "<bool>false</bool><bool>true</bool><bool>0</bool>" + "</sub3>" + "</sub1>"
                + "<sub4><sub5><sub6><sub7><bool>True</bool></sub7></sub6></sub5></sub4>" + "</root>";
        // Test with only single XmlType (no XVAL)
        Document document = null;
        try {
            document = readDocument(xmltree);
        } catch (DocumentException e) {
            e.printStackTrace();
            return;
        }
        try {
            saveDocument("D:\\Tools\\test-src.xml", document);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        XmlValue[] values = read(document, decls);
        Document newDoc = createEmptyDocument();
        write(newDoc, values);
        String text = writeToString(newDoc);
        System.err.println(text);
        try {
            saveDocument("D:\\Tools\\test.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            newDoc = getDocument("D:\\Tools\\test.xml");
        } catch (IOException e) {
            e.printStackTrace();
            return;
        } catch (DocumentException e) {
            e.printStackTrace();
            return;
        }
        values = read(newDoc, decls);
        XmlHash hash = new XmlHash(values);
        XmlValue value = hash.get("n6");
        if (value != null) {
            if (value.isMultiple()) {
                try {
                    value.addFromString("true");
                } catch (InvalidObjectException e) {
                    e.printStackTrace();
                } catch (InvalidArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        value = hash.get("n3");
        if (value != null) {
            if (!value.isMultiple()) {
                try {
                    value.setValue("New String as replacement");
                } catch (InvalidObjectException e) {
                    e.printStackTrace();
                }
            }
        }
        newDoc = createEmptyDocument();
        write(newDoc, values);
        try {
            saveDocument("D:\\Tools\\test2.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        Element elt = null;
        try {
            elt = getElement(newDoc, "/root/sub4");
        } catch (DocumentException e1) {
            e1.printStackTrace();
            return;
        }
        XmlValue[] oldValues = read(document, decls);
        write(elt, oldValues);
        try {
            saveDocument("D:\\Tools\\test3.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        write(newDoc, oldValues);
        try {
            saveDocument("D:\\Tools\\test4.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            saveElement("D:\\Tools\\test5.xml", (Element) elt.selectSingleNode("root"));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        // Now test XmlType.XVAL
        System.err.println("\n\nXVAL TESTING\n");
        XmlDecl[] subdecls1 = { new XmlDecl("n11", XmlType.BOOLEAN, "/n11/bool", false),
                new XmlDecl("n12", XmlType.FLOAT, "/n11/float", true) };
        XmlDecl[] subdecls2 = { new XmlDecl("n21", XmlType.BOOLEAN, "/n21/bool", false),
                new XmlDecl("n22", XmlType.FLOAT, "/n21/float", true) };
        XmlDecl[] maindecls = { new XmlDecl("n1", XmlType.BOOLEAN, "/root/bool", false),
                new XmlDecl("n2", XmlType.FLOAT, "/root/float", false),
                new XmlDecl("n3", XmlType.STRING, "/root/string", false),
                new XmlDecl("n4", XmlType.INTEGER, "/root/int", false),
                new XmlDecl("n5", XmlType.BOOLEAN, "/root/sub1/sub2/bool", false),
                new XmlDecl("n6", XmlType.BOOLEAN, "/root/sub1/sub3/bool", true),
                new XmlDecl("n7", XmlType.BOOLEAN, "/root/sub4/sub5/sub6/sub7/bool", false),
                new XmlDecl("n10", XmlType.XVAL, "/root/sub8", subdecls1, false),
                new XmlDecl("n20", XmlType.XVAL, "/root/sub9", subdecls2, true) };
        xmltree = "<root><bool>true</bool><float>1.2</float><string>my string to read</string><int>5</int>"
                + "<sub1>" + "<sub2><bool>1</bool></sub2>" + "<sub3>"
                + "<bool>false</bool><bool>true</bool><bool>0</bool>" + "</sub3>" + "</sub1>"
                + "<sub4><sub5><sub6><sub7><bool>True</bool></sub7></sub6></sub5></sub4>"
                + "<sub8><n11><bool>true</bool><float>1.2</float><float>1.3</float><float>1.4</float></n11></sub8>"
                + "<sub9><n21><bool>true</bool><float>2.2</float><float>2.3</float><float>2.4</float></n21></sub9>"
                + "<sub9><n21><bool>false</bool><float>3.2</float><float>3.3</float><float>3.4</float></n21></sub9>"
                + "<sub9><n21><bool>true</bool><float>4.2</float><float>4.3</float><float>4.4</float></n21></sub9>"
                + "</root>";
        document = null;
        try {
            document = readDocument(xmltree);
        } catch (DocumentException e) {
            e.printStackTrace();
            return;
        }
        try {
            saveDocument("D:\\Tools\\xtest-src.xml", document);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        values = read(document, maindecls);
        newDoc = createEmptyDocument();
        write(newDoc, values);
        text = writeToString(newDoc);
        System.err.println(text);
        try {
            saveDocument("D:\\Tools\\xtest.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            newDoc = getDocument("D:\\Tools\\xtest.xml");
        } catch (IOException e) {
            e.printStackTrace();
            return;
        } catch (DocumentException e) {
            e.printStackTrace();
            return;
        }
        values = read(newDoc, maindecls);
        hash = new XmlHash(values);
        value = hash.get("n10");
        if (value != null) {
            if (!value.isMultiple()) {
                XmlValue[] subvalues = value.getSubXml();
                XmlHash subhash = new XmlHash(subvalues);
                value = subhash.get("n12");
                try {
                    value.addFromString("10.3");
                } catch (InvalidObjectException e) {
                    e.printStackTrace();
                } catch (InvalidArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        value = hash.get("n20");
        if (value != null) {
            if (value.isMultiple()) {
                List<XmlValue[]> listvalues = (List<XmlValue[]>) value.getList();
                XmlValue[] subvalues = listvalues.get(0);
                XmlValue[] subvalues2 = new XmlValue[subvalues.length];
                // Create a perfect clone
                for (int i = 0; i < subvalues.length; i++) {
                    subvalues2[i] = new XmlValue(subvalues[i]);
                }
                // Add it at the end
                listvalues.add(subvalues2);
                XmlHash subhash = new XmlHash(subvalues2);
                value = subhash.get("n21");
                try {
                    value.setValue(Boolean.FALSE);
                } catch (InvalidObjectException e) {
                    e.printStackTrace();
                }
            }
        }
        newDoc = createEmptyDocument();
        write(newDoc, values);
        try {
            saveDocument("D:\\Tools\\xtest2.xml", newDoc);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        elt = null;
        try {
            elt = getElement(newDoc, "/root/sub9");
        } catch (DocumentException e1) {
            e1.printStackTrace();
            return;
        }
        try {
            saveElement("D:\\Tools\\xtest5.xml", elt);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }

    /**
     * Write the given XML document to filename using the encoding
     * 
     * @param filename
     * @param encoding
     *            if null, default encoding UTF-8 will be used
     * @param document
     * @throws IOException
     */
    public static void writeXML(String filename, String encoding, Document document) throws IOException {
        OutputFormat format = OutputFormat.createPrettyPrint();
        if (encoding != null) {
            format.setEncoding(encoding);
        } else {
            format.setEncoding(WaarpStringUtils.UTF8.name());
        }
        XMLWriter writer = null;
        writer = new XMLWriter(new FileWriter(filename), format);
        writer.write(document);
        try {
            writer.close();
        } catch (IOException e) {
        }
    }
}