org.mule.module.xml.util.XMLUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.module.xml.util.XMLUtils.java

Source

/*
 * $Id$
 * --------------------------------------------------------------------------------------
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.module.xml.util;

import org.mule.RequestContext;
import org.mule.api.MuleContext;
import org.mule.api.transport.OutputHandler;
import org.mule.module.xml.stax.DelegateXMLStreamReader;
import org.mule.module.xml.stax.StaxSource;
import org.mule.module.xml.transformer.DelayedResult;
import org.mule.module.xml.transformer.XmlToDomDocument;
import org.mule.transformer.types.DataTypeFactory;
import org.mule.util.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.commons.lang.StringUtils;
import org.dom4j.DocumentException;
import org.dom4j.io.DOMReader;
import org.dom4j.io.DOMWriter;
import org.dom4j.io.DocumentSource;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * General utility methods for working with XML.
 */
public class XMLUtils extends org.mule.util.XMLUtils {
    public static final String TRANSFORMER_FACTORY_JDK5 = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl";

    // xml parser feature names for optional XSD validation
    public static final String APACHE_XML_FEATURES_VALIDATION_SCHEMA = "http://apache.org/xml/features/validation/schema";
    public static final String APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING = "http://apache.org/xml/features/validation/schema-full-checking";

    // JAXP property for specifying external XSD location
    public static final String JAXP_PROPERTIES_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";

    // JAXP properties for specifying external XSD language (as required by newer
    // JAXP implementation)
    public static final String JAXP_PROPERTIES_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
    public static final String JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE = "http://www.w3.org/2001/XMLSchema";

    /**
     * Converts a DOM to an XML string.
     * @param dom the dome object to convert
     * @return A string representation of the document
     */
    public static String toXml(Document dom) {
        return new DOMReader().read(dom).asXML();
    }

    /**
     * @return a new XSLT transformer
     * @throws TransformerConfigurationException if no TransformerFactory can be located in the
     * runtime environment.
     */
    public static Transformer getTransformer() throws TransformerConfigurationException {
        TransformerFactory tf;
        try {
            tf = TransformerFactory.newInstance();
        } catch (TransformerFactoryConfigurationError e) {
            System.setProperty("javax.xml.transform.TransformerFactory", TRANSFORMER_FACTORY_JDK5);
            tf = TransformerFactory.newInstance();
        }
        if (tf != null) {
            return tf.newTransformer();
        } else {
            throw new TransformerConfigurationException("Unable to instantiate a TransformerFactory");
        }
    }

    public static org.dom4j.Document toDocument(Object obj, MuleContext muleContext) throws Exception {
        return toDocument(obj, null, muleContext);
    }

    /**
     * Converts an object of unknown type to an org.dom4j.Document if possible.
     * @return null if object cannot be converted
     * @throws DocumentException if an error occurs while parsing
     */
    public static org.dom4j.Document toDocument(Object obj, String externalSchemaLocation, MuleContext muleContext)
            throws Exception {
        org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader();
        if (externalSchemaLocation != null) {
            reader.setValidation(true);
            reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA, true);
            reader.setFeature(APACHE_XML_FEATURES_VALIDATION_SCHEMA_FULL_CHECKING, true);

            InputStream xsdAsStream = IOUtils.getResourceAsStream(externalSchemaLocation, XMLUtils.class);
            if (xsdAsStream == null) {
                throw new IllegalArgumentException("Couldn't find schema at " + externalSchemaLocation);
            }

            // Set schema language property (must be done before the schemaSource
            // is set)
            reader.setProperty(JAXP_PROPERTIES_SCHEMA_LANGUAGE, JAXP_PROPERTIES_SCHEMA_LANGUAGE_VALUE);

            // Need this one to map schemaLocation to a physical location
            reader.setProperty(JAXP_PROPERTIES_SCHEMA_SOURCE, xsdAsStream);
        }

        if (obj instanceof org.dom4j.Document) {
            return (org.dom4j.Document) obj;
        } else if (obj instanceof org.w3c.dom.Document) {
            org.dom4j.io.DOMReader domReader = new org.dom4j.io.DOMReader();
            return domReader.read((org.w3c.dom.Document) obj);
        } else if (obj instanceof org.xml.sax.InputSource) {
            return reader.read((org.xml.sax.InputSource) obj);
        } else if (obj instanceof javax.xml.transform.Source || obj instanceof javax.xml.stream.XMLStreamReader) {
            // TODO Find a more direct way to do this
            XmlToDomDocument tr = new XmlToDomDocument();
            tr.setMuleContext(muleContext);
            tr.setReturnDataType(DataTypeFactory.create(org.dom4j.Document.class));
            return (org.dom4j.Document) tr.transform(obj);
        } else if (obj instanceof java.io.InputStream) {
            return reader.read((java.io.InputStream) obj);
        } else if (obj instanceof String) {
            return reader.read(new StringReader((String) obj));
        } else if (obj instanceof byte[]) {
            // TODO Handle encoding/charset somehow
            return reader.read(new StringReader(new String((byte[]) obj)));
        } else if (obj instanceof File) {
            return reader.read((File) obj);
        } else {
            return null;
        }
    }

    /**
     * Converts a payload to a {@link org.w3c.dom.Document} representation.
     * <p> Reproduces the behavior from {@link org.mule.module.xml.util.XMLUtils#toDocument(Object, MuleContext)}
     * which works converting to {@link org.dom4j.Document}.
     *
     * @param payload the payload to convert.
     * @return a document from the payload or null if the payload is not a valid XML document.
     */
    public static org.w3c.dom.Document toW3cDocument(Object payload) throws Exception {
        if (payload instanceof org.dom4j.Document) {
            DOMWriter writer = new DOMWriter();
            org.w3c.dom.Document w3cDocument = writer.write((org.dom4j.Document) payload);

            return w3cDocument;
        } else if (payload instanceof org.w3c.dom.Document) {
            return (org.w3c.dom.Document) payload;
        } else if (payload instanceof org.xml.sax.InputSource) {
            return parseXML((InputSource) payload);
        } else if (payload instanceof javax.xml.transform.Source
                || payload instanceof javax.xml.stream.XMLStreamReader) {
            DOMResult result = new DOMResult();
            Transformer idTransformer = getTransformer();
            Source source = (payload instanceof Source) ? (Source) payload : toXmlSource(null, true, payload);
            idTransformer.transform(source, result);
            return (Document) result.getNode();
        } else if (payload instanceof java.io.InputStream) {
            InputStreamReader input = new InputStreamReader((InputStream) payload);
            return parseXML(input);
        } else if (payload instanceof String) {
            Reader input = new StringReader((String) payload);

            return parseXML(input);
        } else if (payload instanceof byte[]) {
            // TODO Handle encoding/charset somehow
            Reader input = new StringReader(new String((byte[]) payload));
            return parseXML(input);
        } else if (payload instanceof File) {
            Reader input = new FileReader((File) payload);
            return parseXML(input);
        } else {
            return null;
        }
    }

    private static org.w3c.dom.Document parseXML(Reader source) throws Exception {
        return parseXML(new InputSource(source));
    }

    private static org.w3c.dom.Document parseXML(InputSource source) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        return factory.newDocumentBuilder().parse(source);
    }

    /**
     * Returns an XMLStreamReader for an object of unknown type if possible.
     * @return null if no XMLStreamReader can be created for the object type
     * @throws XMLStreamException
     */
    public static javax.xml.stream.XMLStreamReader toXMLStreamReader(javax.xml.stream.XMLInputFactory factory,
            Object obj) throws XMLStreamException {
        if (obj instanceof javax.xml.stream.XMLStreamReader) {
            return (javax.xml.stream.XMLStreamReader) obj;
        } else if (obj instanceof org.mule.module.xml.stax.StaxSource) {
            return ((org.mule.module.xml.stax.StaxSource) obj).getXMLStreamReader();
        } else if (obj instanceof javax.xml.transform.Source) {
            return factory.createXMLStreamReader((javax.xml.transform.Source) obj);
        } else if (obj instanceof org.xml.sax.InputSource) {
            return factory.createXMLStreamReader(((org.xml.sax.InputSource) obj).getByteStream());
        } else if (obj instanceof org.w3c.dom.Document) {
            return factory.createXMLStreamReader(new javax.xml.transform.dom.DOMSource((org.w3c.dom.Document) obj));
        } else if (obj instanceof org.dom4j.Document) {
            return factory.createXMLStreamReader(new org.dom4j.io.DocumentSource((org.dom4j.Document) obj));
        } else if (obj instanceof java.io.InputStream) {
            final InputStream is = (java.io.InputStream) obj;

            XMLStreamReader xsr = factory.createXMLStreamReader(is);
            return new DelegateXMLStreamReader(xsr) {
                @Override
                public void close() throws XMLStreamException {
                    super.close();

                    try {
                        is.close();
                    } catch (IOException e) {
                        throw new XMLStreamException(e);
                    }
                }

            };
        } else if (obj instanceof String) {
            return factory.createXMLStreamReader(new StringReader((String) obj));
        } else if (obj instanceof byte[]) {
            // TODO Handle encoding/charset?
            return factory.createXMLStreamReader(new ByteArrayInputStream((byte[]) obj));
        } else {
            return null;
        }
    }

    /**
     * Convert our object to a Source type efficiently.
     */
    public static javax.xml.transform.Source toXmlSource(javax.xml.stream.XMLInputFactory xmlInputFactory,
            boolean useStaxSource, Object src) throws Exception {
        if (src instanceof javax.xml.transform.Source) {
            return (Source) src;
        } else if (src instanceof byte[]) {
            ByteArrayInputStream stream = new ByteArrayInputStream((byte[]) src);
            return toStreamSource(xmlInputFactory, useStaxSource, stream);
        } else if (src instanceof InputStream) {
            return toStreamSource(xmlInputFactory, useStaxSource, (InputStream) src);
        } else if (src instanceof String) {
            if (useStaxSource) {
                return new StaxSource(xmlInputFactory.createXMLStreamReader(new StringReader((String) src)));
            } else {
                return new StreamSource(new StringReader((String) src));
            }
        } else if (src instanceof org.dom4j.Document) {
            return new DocumentSource((org.dom4j.Document) src);
        } else if (src instanceof org.xml.sax.InputSource) {
            return new SAXSource((InputSource) src);
        }
        // TODO MULE-3555
        else if (src instanceof XMLStreamReader) {
            XMLStreamReader xsr = (XMLStreamReader) src;

            // StaxSource requires that we advance to a start element/document event
            if (!xsr.isStartElement() && xsr.getEventType() != XMLStreamConstants.START_DOCUMENT) {
                xsr.nextTag();
            }

            return new StaxSource((XMLStreamReader) src);
        } else if (src instanceof org.w3c.dom.Document || src instanceof org.w3c.dom.Element) {
            return new DOMSource((org.w3c.dom.Node) src);
        } else if (src instanceof DelayedResult) {
            DelayedResult result = ((DelayedResult) src);
            DOMResult domResult = new DOMResult();
            result.write(domResult);
            return new DOMSource(domResult.getNode());
        } else if (src instanceof OutputHandler) {
            OutputHandler handler = ((OutputHandler) src);
            ByteArrayOutputStream output = new ByteArrayOutputStream();

            handler.write(RequestContext.getEvent(), output);

            return toStreamSource(xmlInputFactory, useStaxSource, new ByteArrayInputStream(output.toByteArray()));
        } else {
            return null;
        }
    }

    public static javax.xml.transform.Source toStreamSource(javax.xml.stream.XMLInputFactory xmlInputFactory,
            boolean useStaxSource, InputStream stream) throws XMLStreamException {
        if (useStaxSource) {
            return new org.mule.module.xml.stax.StaxSource(xmlInputFactory.createXMLStreamReader(stream));
        } else {
            return new javax.xml.transform.stream.StreamSource(stream);
        }
    }

    /**
     * Copies the reader to the writer. The start and end document methods must
     * be handled on the writer manually. TODO: if the namespace on the reader
     * has been declared previously to where we are in the stream, this probably
     * won't work.
     * 
     * @param reader
     * @param writer
     * @throws XMLStreamException
     */
    public static void copy(XMLStreamReader reader, XMLStreamWriter writer) throws XMLStreamException {
        copy(reader, writer, false);
    }

    public static void copy(XMLStreamReader reader, XMLStreamWriter writer, boolean fragment)
            throws XMLStreamException {
        // number of elements read in
        int read = 0;
        int event = reader.getEventType();

        while (reader.hasNext()) {
            switch (event) {
            case XMLStreamConstants.START_ELEMENT:
                read++;
                writeStartElement(reader, writer);
                break;
            case XMLStreamConstants.END_ELEMENT:
                writer.writeEndElement();
                read--;
                if (read <= 0 && !fragment) {
                    return;
                }
                break;
            case XMLStreamConstants.CHARACTERS:
                writer.writeCharacters(reader.getText());
                break;
            case XMLStreamConstants.START_DOCUMENT:
            case XMLStreamConstants.END_DOCUMENT:
            case XMLStreamConstants.ATTRIBUTE:
            case XMLStreamConstants.NAMESPACE:
                break;
            default:
                break;
            }
            event = reader.next();
        }
    }

    private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer)
            throws XMLStreamException {
        String local = reader.getLocalName();
        String uri = reader.getNamespaceURI();
        String prefix = reader.getPrefix();
        if (prefix == null) {
            prefix = "";
        }

        //        System.out.println("STAXUTILS:writeStartElement : node name : " + local +  " namespace URI" + uri);
        boolean writeElementNS = false;
        if (uri != null) {
            String boundPrefix = writer.getPrefix(uri);
            if (boundPrefix == null || !prefix.equals(boundPrefix)) {
                writeElementNS = true;
            }
        }

        // Write out the element name
        if (uri != null) {
            if (prefix.length() == 0 && StringUtils.isEmpty(uri)) {
                writer.writeStartElement(local);
                writer.setDefaultNamespace(uri);

            } else {
                writer.writeStartElement(prefix, local, uri);
                writer.setPrefix(prefix, uri);
            }
        } else {
            writer.writeStartElement(local);
        }

        // Write out the namespaces
        for (int i = 0; i < reader.getNamespaceCount(); i++) {
            String nsURI = reader.getNamespaceURI(i);
            String nsPrefix = reader.getNamespacePrefix(i);
            if (nsPrefix == null) {
                nsPrefix = "";
            }

            if (nsPrefix.length() == 0) {
                writer.writeDefaultNamespace(nsURI);
            } else {
                writer.writeNamespace(nsPrefix, nsURI);
            }

            if (nsURI.equals(uri) && nsPrefix.equals(prefix)) {
                writeElementNS = false;
            }
        }

        // Check if the namespace still needs to be written.
        // We need this check because namespace writing works
        // different on Woodstox and the RI.
        if (writeElementNS) {
            if (prefix.length() == 0) {
                writer.writeDefaultNamespace(uri);
            } else {
                writer.writeNamespace(prefix, uri);
            }
        }

        // Write out attributes
        for (int i = 0; i < reader.getAttributeCount(); i++) {
            String ns = reader.getAttributeNamespace(i);
            String nsPrefix = reader.getAttributePrefix(i);
            if (ns == null || ns.length() == 0) {
                writer.writeAttribute(reader.getAttributeLocalName(i), reader.getAttributeValue(i));
            } else if (nsPrefix == null || nsPrefix.length() == 0) {
                writer.writeAttribute(reader.getAttributeNamespace(i), reader.getAttributeLocalName(i),
                        reader.getAttributeValue(i));
            } else {
                writer.writeAttribute(reader.getAttributePrefix(i), reader.getAttributeNamespace(i),
                        reader.getAttributeLocalName(i), reader.getAttributeValue(i));
            }

        }
    }

    /**
     * Creates an XPath object with a custom NamespaceContext given the Node to operate on
     * @param node the Node or document to operate on.  Note that namespace handling will not work if a Node fragment is passed in
     * @return a new XPath object
     */
    private static XPath createXPath(Node node) {
        XPath xp = XPathFactory.newInstance().newXPath();
        if (node instanceof Document) {
            xp.setNamespaceContext(new XPathNamespaceContext((Document) node));
        }
        return xp;
    }

    /**
     * Select a single XML node using an Xpath
     * @param xpath the XPath expression to evaluate
     * @param node the node (or document) to exaluate on
     * @return the result of the evaluation.
     * @throws XPathExpressionException if the XPath expression is malformed and cannot be parsed
     */
    public static Node selectOne(String xpath, Node node) throws XPathExpressionException {
        XPath xp = createXPath(node);
        return (Node) xp.evaluate(xpath, node, XPathConstants.NODE);
    }

    /**
     * Select a single XML String value using an Xpath
     * @param xpath the XPath expression to evaluate
     * @param node the node (or document) to evaluate on
     * @return the result of the evaluation.
     * @throws XPathExpressionException if the XPath expression is malformed and cannot be parsed
     */
    public static String selectValue(String xpath, Node node) throws XPathExpressionException {
        XPath xp = createXPath(node);
        return (String) xp.evaluate(xpath, node, XPathConstants.STRING);
    }

    /**
     * Select a set of Node objects using the Xpath expression
     * @param xpath the XPath expression to evaluate
     * @param node the node (or document) to evaluate on
     * @return the result of the evaluation. 
     * @throws XPathExpressionException if the XPath expression is malformed and cannot be parsed
     */
    public static List<Node> select(String xpath, Node node) throws XPathExpressionException {
        XPath xp = createXPath(node);
        NodeList nl = (NodeList) xp.evaluate(xpath, node, XPathConstants.NODESET);
        List<Node> nodeList = new ArrayList<Node>(nl.getLength());
        for (int i = 0; i < nl.getLength(); i++) {
            nodeList.add(nl.item(i));
        }
        return nodeList;
    }

    /**
     * The default namespace context that will read namespaces from the current document if the
     * Node being processed is a Document
     */
    private static class XPathNamespaceContext implements NamespaceContext {
        private Document document;

        public XPathNamespaceContext(Document document) {
            this.document = document;
        }

        public String getNamespaceURI(String prefix) {
            if (prefix == null || prefix.equals("")) {
                return document.getDocumentElement().getNamespaceURI();
            } else {
                return document.lookupNamespaceURI(prefix);
            }
        }

        public String getPrefix(String namespaceURI) {
            return document.lookupPrefix(namespaceURI);
        }

        public Iterator<String> getPrefixes(String namespaceURI) {
            List<String> list = new ArrayList<String>();
            list.add(getPrefix(namespaceURI));
            return list.iterator();
        }
    }
}