XMLUtils.java Source code

Java tutorial

Introduction

Here is the source code for XMLUtils.java

Source

// 
// Copyright 2007 Requea.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at 
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.Stack;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * Utilities to manipulate w3c DOM trees.
 * @author Pierre Dubois
 */
public class XMLUtils {

    private static DocumentBuilderFactory fDocumentFactory;
    private static Stack fParsersPool = new Stack();

    /**
     * Public Id and the Resource path (of the cached copy) 
     * of the DTDs for tag library descriptors. 
     */
    public static final String TAGLIB_DTD_PUBLIC_ID_11 = "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN";
    public static final String TAGLIB_DTD_RESOURCE_PATH_11 = "/javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd";
    public static final String TAGLIB_DTD_PUBLIC_ID_12 = "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN";
    public static final String TAGLIB_DTD_RESOURCE_PATH_12 = "/javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd";

    /**
     * Public Id and the Resource path (of the cached copy) 
     * of the DTDs for web application deployment descriptors
     */
    public static final String WEBAPP_DTD_PUBLIC_ID_22 = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN";
    public static final String WEBAPP_DTD_RESOURCE_PATH_22 = "/javax/servlet/resources/web-app_2_2.dtd";
    public static final String WEBAPP_DTD_PUBLIC_ID_23 = "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN";
    public static final String WEBAPP_DTD_RESOURCE_PATH_23 = "/javax/servlet/resources/web-app_2_3.dtd";

    /**
     * List of the Public IDs that we cache, and their
     * associated location. This is used by 
     * an EntityResolver to return the location of the
     * cached copy of a DTD.
     */
    public static final String[] CACHED_DTD_PUBLIC_IDS = { TAGLIB_DTD_PUBLIC_ID_11, TAGLIB_DTD_PUBLIC_ID_12,
            WEBAPP_DTD_PUBLIC_ID_22, WEBAPP_DTD_PUBLIC_ID_23, };
    public static final String[] CACHED_DTD_RESOURCE_PATHS = { TAGLIB_DTD_RESOURCE_PATH_11,
            TAGLIB_DTD_RESOURCE_PATH_12, WEBAPP_DTD_RESOURCE_PATH_22, WEBAPP_DTD_RESOURCE_PATH_23, };

    private static synchronized void initFactory() {
        if (fDocumentFactory != null)
            return;

        fDocumentFactory = DocumentBuilderFactory.newInstance();
        fDocumentFactory.setNamespaceAware(true);
        fDocumentFactory.setIgnoringElementContentWhitespace(true);
        fDocumentFactory.setValidating(false);
    }

    public static synchronized DocumentBuilder getParser() throws ParserConfigurationException {
        if (fParsersPool.isEmpty()) {
            // create a new parser
            initFactory();
            DocumentBuilder builder = fDocumentFactory.newDocumentBuilder();
            builder.setEntityResolver(entityResolver);
            return builder;
        } else {
            return (DocumentBuilder) fParsersPool.pop();
        }
    }

    public static synchronized void releaseParser(DocumentBuilder parser) {
        if (parser != null)
            fParsersPool.push(parser);
    }

    /**
     * Parse an xml document as an imput stream.
     * @param is
     * @return
     * @throws XMLException
     */
    public static Document parse(InputStream is) {
        DocumentBuilder builder = null;
        try {
            builder = getParser();
            // parse the document

            Document doc = builder.parse(is);
            return doc;

        } catch (Exception e) {

        } finally {
            releaseParser(builder);
        }
    }

    /**
     * Parses an xml document as a string.
     * @param xml
     * @return
     * @throws XMLException
     */
    public static Document parse(String xml) {
        DocumentBuilder builder = null;
        try {
            builder = getParser();
            // parse the document
            try {
                InputStream is = new ByteArrayInputStream(xml.getBytes());
                Document doc = builder.parse(is);
                return doc;
            } catch (SAXException e) {

            } catch (IOException e) {

            }
        } catch (ParserConfigurationException e) {

        } finally {
            releaseParser(builder);
        }
    }

    /**
     * Creates a new and empty document.
     * @return
     */
    public static Document newDocument() {
        DocumentBuilder builder = null;
        try {
            builder = getParser();
            return builder.newDocument();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } finally {
            releaseParser(builder);
        }
    }

    /**
     * Creates a new document with a document root element.
     * @param name
     * @return
     */
    public static Element newElement(String name) {
        DocumentBuilder builder = null;
        try {
            builder = getParser();
            Document doc = builder.newDocument();
            Element el = doc.createElement(name);
            doc.appendChild(el);
            return el;
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        } finally {
            releaseParser(builder);
        }
    }

    /**
     * Serialize an element to a string.
     * @param element
     * @return
     * @throws XMLException 
     */
    public static String ElementToString(Element element) {
        return privateElementToString(element, true, true);
    }

    public static String ElementToString(Element element, boolean pretty) {
        return privateElementToString(element, true, pretty);
    }

    /**
     * Serialize a document to a string
     * @param doc
     * @return
     */
    public static String DocumentToString(Document doc) {
        return privateElementToString(doc.getDocumentElement(), false, true);
    }

    public static String DocumentToString(Document doc, boolean pretty) {
        return privateElementToString(doc.getDocumentElement(), false, pretty);
    }

    /**
     * Get the first child element to match a given name. 
     * Look for a child element in the same namespace as the parent.
     * 
     * @param parent
     * @param name
     * @return
     */
    public static Element getChild(Element parent, String name) {
        Element child = getFirstChild(parent);
        while (child != null) {
            String tagName = child.getTagName();
            if (tagName != null && tagName.equals(name)) {
                return child;
            }
            if (child.getPrefix() != null && child.getPrefix().equals(parent.getPrefix())
                    && child.getLocalName() != null && child.getLocalName().equals(name)) {
                return child;
            }
            child = getNext(child);
        }
        return child;
    }

    /**
     * Get the first child element to match a given name. 
     * Look for a child element in the same namespace as the parent.
     * 
     * @param parent
     * @param name
     * @return
     */
    public static Element getChild(Element parent, String ns, String name) {
        Element child = getFirstChild(parent);
        while (child != null) {
            if (child.getLocalName().equals(name)) {
                if (ns == null && child.getNamespaceURI() == null) {
                    return child;
                } else if (ns != null && ns.equals(child.getNamespaceURI())) {
                    return child;
                }
            }
            child = getNext(child);
        }
        return child;
    }

    /**
     * Get the first child element of an element.
     * @param el
     * @return
     */
    public static Element getFirstChild(Element el) {
        if (el == null) {
            return null;
        }
        NodeList lst = el.getChildNodes();
        int len = lst.getLength();
        for (int i = 0; i < len; i++) {
            Node n = lst.item(i);
            if (n instanceof Element)
                return (Element) n;
        }
        return null;
    }

    /**
     * Get the next sibling element of a given element.
     * @param el
     * @return
     */
    public static Element getNext(Element el) {
        Node n = el.getNextSibling();
        while (n != null && !(n instanceof Element)) {
            // get the next one
            n = n.getNextSibling();
        }

        if (n instanceof Element) {
            return (Element) n;
        }
        // else, nothing to return
        return null;
    }

    /**
     * Get the next sibling element of a given element.
     * @param el
     * @return
     */
    public static Element getNextSibling(Element el) {
        String tagName = el.getTagName();
        if (tagName == null) {
            return null;
        }
        Node n = el.getNextSibling();
        while (n != null && (!(n instanceof Element) || !tagName.equals(((Element) n).getTagName()))) {
            // get the next one
            n = n.getNextSibling();
        }

        if (n instanceof Element) {
            return (Element) n;
        } else {
            // else, nothing to return
            return null;
        }
    }

    /**
     * Get the previous sibling element of a given element.
     * @param el
     * @return
     */
    public static Element getPrevious(Element el) {
        Node n = el.getPreviousSibling();
        while (n != null && !(n instanceof Element)) {
            // get the next one
            n = n.getPreviousSibling();
        }

        if (n instanceof Element) {
            return (Element) n;
        } else {
            // else, nothing to return
            return null;
        }
    }

    /**
     * Get the previous sibling element of a given element.
     * @param el
     * @return
     */
    public static Element getPreviousSibling(Element el) {
        Node n = el.getPreviousSibling();
        while (n != null && (!(n instanceof Element) || !el.getTagName().equals(((Element) n).getTagName()))) {
            // get the next one
            n = n.getPreviousSibling();
        }

        if (n instanceof Element) {
            return (Element) n;
        } else {
            // else, nothing to return
            return null;
        }
    }

    /**
     * Returns the text value of an element.
     * @param el
     * @return
     */
    public static String getTextValue(Element el) {
        StringBuffer b = new StringBuffer();
        // retrieve the text node child
        NodeList nl = el.getChildNodes();
        int len = nl.getLength();
        for (int i = 0; i < len; i++) {
            Node n = nl.item(i);
            if (n instanceof Text) {
                Text t = (Text) n;
                b.append(t.getData());
            }
        }
        // trim the result, ignoring the first spaces and cariage return
        int iFirst = 0;
        for (; iFirst < b.length(); iFirst++) {
            char c = b.charAt(iFirst);
            if (c != ' ' && c != '\r' && c != '\n' && c != '\t') {
                break;
            }
        }
        // start by the end as well
        int iLast = b.length() - 1;
        for (; iLast >= 0; iLast--) {
            char c = b.charAt(iLast);
            if (c != ' ' && c != '\r' && c != '\n' && c != '\t') {
                break;
            }
        }
        return b.substring(iFirst, iLast + 1);
    }

    /**
     * Get the text value of a child element with a given name. 
     * @param parent
     * @param name
     * @return
     */
    public static String getChildText(Element parent, String name) {
        Element child = getChild(parent, name);
        if (child != null) {
            return getTextValue(child);
        }
        return null;
    }

    /**
     * Get the text value of a child element with a given name. 
     * @param parent
     * @param name
     * @return
     */
    public static String getChildText(Element parent, String ns, String name) {
        Element child = getChild(parent, ns, name);
        if (child != null) {
            return getTextValue(child);
        }
        return null;
    }

    /**
     * Adds an element as a child of a given element. 
     * The child is created with the same namespace as the parent. 
     * @param parent
     * @param name
     * @return
     */
    public static Element addElement(Element parent, String name) {
        Document doc = parent.getOwnerDocument();
        String qname;
        if (parent.getPrefix() != null) {
            qname = parent.getPrefix() + ":" + name;
        } else {
            qname = name;
        }
        Element child = doc.createElementNS(parent.getNamespaceURI(), qname);
        parent.appendChild(child);
        return child;
    }

    /**
     * Adds an element as a child of a given element and sets the text value.
     * The child is created with the same namespace as the parent. 
     * 
     * @param parent
     * @param name
     * @param textValue
     * @return
     */
    public static Element addElement(Element parent, String name, String textValue) {

        Element child = addElement(parent, name);
        // create a text node
        if (textValue == null) {
            textValue = "";
        }
        Text txt = child.getOwnerDocument().createTextNode(textValue);
        child.appendChild(txt);
        return child;
    }

    /**
     * Sets the text value for a given element.
     * @param el
     * @param value
     */
    public static void setText(Element el, String value) {
        // remove the children if already exist
        while (el.getFirstChild() != null) {
            el.removeChild(el.getFirstChild());
        }
        if (value == null) {
            value = "";
        }
        Text txt = el.getOwnerDocument().createTextNode(value);
        el.appendChild(txt);
    }

    /**
     * Sets the text value for a given element as a CDATA section
     * @param el
     * @param value
     */
    public static void setCDATA(Element el, String value) {
        // remove the children if already exist
        while (el.getFirstChild() != null) {
            el.removeChild(el.getFirstChild());
        }
        if (value == null) {
            value = "";
        }
        CDATASection txt = el.getOwnerDocument().createCDATASection(value);
        el.appendChild(txt);
    }

    /**
     * Retrieve the namespace for a given prefix. 
     * Does a lookup into the parent hierarchy.
     * @param el
     * @param prefix
     * @return
     */
    public static String getNamespace(Element el, String prefix) {
        Element parent = el;
        while (parent != null) {
            String ns = parent.getAttribute("xmlns:" + prefix);
            if (ns != null && ns.length() > 0) {
                return ns;
            }
            // get the parent
            Node n = parent.getParentNode();
            if (n instanceof Element) {
                parent = (Element) n;
            } else {
                parent = null;
            }
        }
        // nothing found
        return null;
    }

    /*
     * serialize an element to a string.
     */
    private static String privateElementToString(Element element, boolean omitXMLDecl, boolean pretty) {
        try {
            Source source = new DOMSource(element);
            StringWriter out = new StringWriter();

            StreamResult result = new StreamResult(out);
            Transformer xformer = TransformerFactory.newInstance().newTransformer();
            xformer.setOutputProperty("indent", "yes");
            xformer.transform(source, result);
            return out.toString();
        } catch (Exception e) {

        }
    }

    public static String getAttribute(Element el, String att) {
        String str = el.getAttribute(att);
        if (str == null || str.length() == 0) {
            return null;
        } else {
            return str;
        }
    }

    static EntityResolver entityResolver = new MyEntityResolver();

    static private class MyEntityResolver implements EntityResolver {

        public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
            for (int i = 0; i < CACHED_DTD_PUBLIC_IDS.length; i++) {
                String cachedDtdPublicId = CACHED_DTD_PUBLIC_IDS[i];
                if (cachedDtdPublicId.equals(publicId)) {
                    String resourcePath = CACHED_DTD_RESOURCE_PATHS[i];
                    InputStream input = this.getClass().getResourceAsStream(resourcePath);
                    if (input == null) {
                        throw new SAXException("file.not.found" + resourcePath);
                    }
                    InputSource isrc = new InputSource(input);
                    return isrc;
                }
            }

            return null;
        }
    }

}