Open Source Repository

Home /open-symphony/oscore-2.2.6 | Repository Home



com/opensymphony/util/XMLUtils.java
/*
 * Copyright (c) 2002-2003 by OpenSymphony
 * All rights reserved.
 */
package com.opensymphony.util;

import com.opensymphony.provider.ProviderFactory;
import com.opensymphony.provider.ProviderInvocationException;
import com.opensymphony.provider.XMLPrinterProvider;
import com.opensymphony.provider.XPathProvider;

import org.w3c.dom.*;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import java.io.*;

import java.net.URL;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;


/**
 * XMLUtils is a bunch of quick access utility methods to common XML operations.
 *
 <p>These include:</p>
 *
 <ul>
 <li>Parsing XML stream into org.w3c.dom.Document.
 <li>Creating blank Documents.
 <li>Serializing (pretty-printing) Document back to XML stream.
 <li>Extracting nodes using X-Path expressions.
 <li>Cloning nodes.
 <li>Performing XSL transformations.
 </ul>
 *
 <p>This class contains static methods only and is not meant to be instantiated. It also
 * contains only basic (common) functions - for more control access appropriate API directly.</p>
 *
 @author <a href="mailto:[email protected]">Joe Walnes</a>
 @author <a href="mailto:[email protected]">Hani Suleiman</a>
 @version $Revision: 125 $
 */
public class XMLUtils {
    //~ Static fields/initializers /////////////////////////////////////////////

    private static final XPathProvider xPathProvider;
    private static final XMLPrinterProvider xmlPrinterProvider;

    static {
        ProviderFactory factory = ProviderFactory.getInstance();
        xPathProvider = (XPathProviderfactory.getProvider("xpath.provider", com.opensymphony.provider.xpath.XalanXPathProvider.class.getName());
        xmlPrinterProvider = (XMLPrinterProviderfactory.getProvider("xmlprinter.provider"
            //      com.opensymphony.provider.xmlprinter.XalanXMLPrinterProvider.class.getName()
            com.opensymphony.provider.xmlprinter.DefaultXMLPrinterProvider.class.getName());
    }

    /**
     * The cache size for the XSL transforms
     */
    private static int cacheSize = 10;
    private static HashMap xslCache = new HashMap();
    private static LinkedList xslKeyList = new LinkedList();

    //~ Methods ////////////////////////////////////////////////////////////////

    /**
     * Return the contained text within an Element. Returns null if no text found.
     */
    public final static String getElementText(Element element) {
        NodeList nl = element.getChildNodes();

        for (int i = 0; i < nl.getLength(); i++) {
            Node c = nl.item(i);

            if (instanceof Text) {
                return ((Textc).getData();
            }
        }

        return null;
    }

    /**
     * Clone given Node into target Document. If targe is null, same Document will be used.
     * If deep is specified, all children below will also be cloned.
     */
    public final static Node cloneNode(Node node, Document target, boolean deepthrows DOMException {
        if ((target == null|| (node.getOwnerDocument() == target)) {
            // same Document
            return node.cloneNode(deep);
        else {
            //DOM level 2 provides this in Document, so once xalan switches to that,
            //we can take out all the below and just call target.importNode(node, deep);
            //For now, we implement based on the javadocs for importNode
            Node newNode;
            int nodeType = node.getNodeType();

            switch (nodeType) {
            case Node.ATTRIBUTE_NODE:
                newNode = target.createAttribute(node.getNodeName());

                break;

            case Node.DOCUMENT_FRAGMENT_NODE:
                newNode = target.createDocumentFragment();

                break;

            case Node.ELEMENT_NODE:

                Element newElement = target.createElement(node.getNodeName());
                NamedNodeMap nodeAttr = node.getAttributes();

                if (nodeAttr != null) {
                    for (int i = 0; i < nodeAttr.getLength(); i++) {
                        Attr attr = (AttrnodeAttr.item(i);

                        if (attr.getSpecified()) {
                            Attr newAttr = (AttrcloneNode(attr, target, true);
                            newElement.setAttributeNode(newAttr);
                        }
                    }
                }

                newNode = newElement;

                break;

            case Node.ENTITY_REFERENCE_NODE:
                newNode = target.createEntityReference(node.getNodeName());

                break;

            case Node.PROCESSING_INSTRUCTION_NODE:
                newNode = target.createProcessingInstruction(node.getNodeName(), node.getNodeValue());

                break;

            case Node.TEXT_NODE:
                newNode = target.createTextNode(node.getNodeValue());

                break;

            case Node.CDATA_SECTION_NODE:
                newNode = target.createCDATASection(node.getNodeValue());

                break;

            case Node.COMMENT_NODE:
                newNode = target.createComment(node.getNodeValue());

                break;

            case Node.NOTATION_NODE:
            case Node.ENTITY_NODE:
            case Node.DOCUMENT_TYPE_NODE:
            case Node.DOCUMENT_NODE:default:
                throw new IllegalArgumentException("Importing of " + node + " not supported yet");
            }

            if (deep) {
                for (Node child = node.getFirstChild(); child != null;
                        child = child.getNextSibling()) {
                    newNode.appendChild(cloneNode(child, target, true));
                }
            }

            return newNode;
        }
    }

    /**
     * Create blank Document.
     */
    public final static Document newDocument() throws ParserConfigurationException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        return builder.newDocument();
    }

    /**
     * Create blank Document, and insert root element with given name.
     */
    public final static Document newDocument(String rootElementNamethrows ParserConfigurationException {
        Document doc = newDocument();
        doc.appendChild(doc.createElement(rootElementName));

        return doc;
    }

    /**
     * Parse an InputSource of XML into Document.
     */
    public final static Document parse(InputSource inthrows ParserConfigurationException, IOException, SAXException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();

        return builder.parse(in);
    }

    /**
     * Parse an InputStream of XML into Document.
     */
    public final static Document parse(InputStream inthrows ParserConfigurationException, IOException, SAXException {
        return parse(new InputSource(in));
    }

    /**
     * Parse a Reader of XML into Document.
     */
    public final static Document parse(Reader inthrows ParserConfigurationException, IOException, SAXException {
        return parse(new InputSource(in));
    }

    /**
     * Parse a File of XML into Document.
     */
    public final static Document parse(File filethrows ParserConfigurationException, IOException, SAXException {
        return parse(new InputSource(new FileInputStream(file)));
    }

    /**
     * Parse the contents of a URL's XML into Document.
     */
    public final static Document parse(URL urlthrows ParserConfigurationException, IOException, SAXException {
        return parse(new InputSource(url.toString()));
    }

    /**
     * Parse a String containing XML data into a Document.
     * Note that String contains XML itself and is not URI.
     */
    public final static Document parse(String xmlthrows ParserConfigurationException, IOException, SAXException {
        return parse(new InputSource(new StringReader(xml)));
    }

    /**
     * Pretty-print a Document to Writer.
     */
    public final static void print(Document document, Writer outthrows IOException {
        xmlPrinterProvider.print(document, out);
    }

    /**
     * Pretty-print a Document to OutputStream.
     */
    public final static void print(Document document, OutputStream outthrows IOException {
        print(document, new OutputStreamWriter(out));
    }

    /**
     * Pretty-print a Document to File.
     */
    public final static void print(Document document, File filethrows IOException {
        print(document, new FileWriter(file));
    }

    /**
     * Pretty-print a Document back to String of XML.
     */
    public final static String print(Document documentthrows IOException {
        StringWriter result = new StringWriter();
        print(document, result);

        return result.toString();
    }

    /**
     * Perform XSL transformation.
     */
    public final static void transform(Reader xml, Reader xsl, Writer resultthrows TransformerException {
        transform(xml, xsl, result, null);
    }

    /**
     * Return single Node from base Node using X-Path expression.
     */
    public final static Node xpath(Node base, String xpaththrows TransformerException {
        try {
            return xPathProvider.getNode(base, xpath);
        catch (ProviderInvocationException e) {
            try {
                throw e.getCause();
            catch (TransformerException te) {
                throw te;
            catch (Throwable tw) {
                tw.printStackTrace();

                return null;
            }
        }
    }

    /**
     * Return multiple Nodes from base Node using X-Path expression.
     */
    public final static NodeList xpathList(Node base, String xpaththrows TransformerException {
        try {
            return xPathProvider.getNodes(base, xpath);
        catch (ProviderInvocationException e) {
            try {
                throw e.getCause();
            catch (TransformerException te) {
                throw te;
            catch (Throwable tw) {
                tw.printStackTrace();

                return null;
            }
        }
    }

    /**
     * Sets the internal cache size for XSL sheets
     @param newCacheSize
     */
    public static void setCacheSize(int newCacheSize) {
        cacheSize = newCacheSize;
    }

    /**
     * Accessor for the internal XSL transformer cache
     @return the cache size
     */
    public static int getCacheSize() {
        return cacheSize;
    }

    /**
     * This method applies an XSL sheet to an XML document.
     <p>2002/Apr/7, fixed bug 540875, first reported by Erik Weber, and
     * added configurable cache size.
     @param xml the XML source
     @param xsl the XSL source
     @param result where to put the response
     @param parameters a map consisting of params for the transformer
     @param xslkey a key used to refer to the XSL
     @throws TransformerException
     */
    public final static void transform(Reader xml, Reader xsl, Writer result, Map parameters, String xslkeythrows TransformerException {
        try {
            Transformer t;

            if ((null != xslkey&& (xslCache.containsKey(xslkey))) {
                t = (TransformerxslCache.get(xslkey);

                synchronized (xslKeyList) {
                    xslKeyList.remove(xslkey);
                    xslKeyList.add(xslkey);
                }
            else {
                TransformerFactory factory = TransformerFactory.newInstance();
                t = factory.newTransformer(new StreamSource(xsl));

                if (null != xslkey) {
                    xslCache.put(xslkey, t);
                    xslKeyList.add(xslkey);

                    synchronized (xslKeyList) {
                        int s = xslKeyList.size();
                        int cacheSize = getCacheSize();
                        int iterations = 1;

                        /* if the cache size was adjusted after the cache is initialized,
                         * we don't want to shrink TOO fast, just to be nice to runtime
                         * performance
                         *
                         * That's my story, and I'm stickin' to it
                         */
                        if (s > (cacheSize + 1)) {
                            iterations = 2;
                        }

                        while (iterations-- != 0) {
                            Object removalKey = xslKeyList.get(0);
                            xslKeyList.remove(0);
                            xslCache.remove(removalKey);
                        }
                    }
                }
            }

            if (parameters != null) {
                Iterator i = parameters.keySet().iterator();

                while (i.hasNext()) {
                    Object key = i.next();
                    Object value = parameters.get(key);
                    t.setParameter(key.toString(), value.toString());
                }
            }

            t.transform(new StreamSource(xml)new StreamResult(result));
        catch (TransformerConfigurationException tce) {
            throw new TransformerException(tce);
        }
    }

    /**
     * Perform XSL transformation, with params.
     */
    public final static void transform(Reader xml, Reader xsl, Writer result, Map parametersthrows TransformerException {
        transform(xml, xsl, result, parameters, xsl.toString());
    }

    /**
     * Perform XSL transformation.
     */
    public final static void transform(InputStream xml, InputStream xsl, OutputStream resultthrows TransformerException {
        transform(new InputStreamReader(xml)new InputStreamReader(xsl)new OutputStreamWriter(result));
    }

    /**
     * Perform XSL transformation. XML and XSL supplied in Strings and result returned as String.
     * Note that inputs are actual XML/XSL data, not URIs.
     */
    public final static String transform(String xml, String xslthrows TransformerException {
        StringWriter result = new StringWriter();
        transform(new StringReader(xml)new StringReader(xsl), result);

        return result.toString();
    }

    /**
     * Perform XSL transformations using given Documents and return new Document.
     */
    public final static Document transform(Document xml, Document xslthrows ParserConfigurationException, TransformerException {
        try {
            Document result = newDocument();
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer t = factory.newTransformer(new DOMSource(xsl));
            t.transform(new DOMSource(xml)new DOMResult(result));

            return result;
        catch (TransformerConfigurationException tce) {
            throw new TransformerException(tce);
        }
    }
}