org.alfresco.web.config.XmlUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.web.config.XmlUtils.java

Source

/**
 * Copyright (C) 2005-2009 Alfresco Software Limited.
 *
 * This file is part of the Spring Surf Extension project.
 *
 * 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.
 */
package org.alfresco.web.config;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.springframework.util.Assert;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

/**
 * Utilities related to DOM and XML usage.
 * 
 * @author Stefan Schmidt
 * @author Ben Alex
 * @since 1.0
 * 
 */
public abstract class XmlUtils {

    private static final Map<String, XPathExpression> compiledExpressionCache = new HashMap<String, XPathExpression>();
    private static final XPath xpath = XPathFactory.newInstance().newXPath();
    private static final TransformerFactory transformerFactory = TransformerFactory.newInstance();
    private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

    public static final void writeXml(OutputStream outputEntry, Document document) {
        writeXml(createIndentingTransformer(), outputEntry, document);
    }

    public static final void writeMalformedXml(OutputStream outputEntry, NodeList nodes) {
        writeMalformedXml(createIndentingTransformer(), outputEntry, nodes);
    }

    public static final void writeXml(Transformer transformer, OutputStream outputEntry, Document document) {
        Assert.notNull(transformer, "Transformer required");
        Assert.notNull(outputEntry, "Output entry required");
        Assert.notNull(document, "Document required");

        transformer.setOutputProperty(OutputKeys.METHOD, "xml");

        try {
            transformer.transform(new DOMSource(document), createUnixStreamResultForEntry(outputEntry));
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }

    public static final void writeMalformedXml(Transformer transformer, OutputStream outputEntry, NodeList nodes) {
        Assert.notNull(transformer, "Transformer required");
        Assert.notNull(outputEntry, "Output entry required");
        Assert.notNull(nodes, "NodeList required");

        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");

        try {
            for (int i = 0; i < nodes.getLength(); i++) {
                transformer.transform(new DOMSource(nodes.item(i)), createUnixStreamResultForEntry(outputEntry));
            }
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }

    /**
     * Creates a {@link StreamResult} by wrapping the given outputEntry in an
     * {@link OutputStreamWriter} that transforms Windows line endings (\r\n) 
     * into Unix line endings (\n) on Windows for consistency with Roo's templates.  
     * @param outputEntry
     * @return StreamResult 
     * @throws UnsupportedEncodingException 
     */
    private static StreamResult createUnixStreamResultForEntry(OutputStream outputEntry)
            throws UnsupportedEncodingException {
        final Writer writer;
        if (System.getProperty("line.separator").equals("\r\n")) {
            writer = new OutputStreamWriter(outputEntry, "ISO-8859-1") {
                public void write(char[] cbuf, int off, int len) throws IOException {
                    for (int i = off; i < off + len; i++) {
                        if (cbuf[i] != '\r' || (i < cbuf.length - 1 && cbuf[i + 1] != '\n')) {
                            super.write(cbuf[i]);
                        }
                    }
                }

                public void write(int c) throws IOException {
                    if (c != '\r')
                        super.write(c);
                }

                public void write(String str, int off, int len) throws IOException {
                    String orig = str.substring(off, off + len);
                    String filtered = orig.replace("\r\n", "\n");
                    int lengthDiff = orig.length() - filtered.length();
                    if (filtered.endsWith("\r")) {
                        super.write(filtered.substring(0, filtered.length() - 1), 0, len - lengthDiff - 1);
                    } else {
                        super.write(filtered, 0, len - lengthDiff);
                    }
                }
            };
        } else {
            writer = new OutputStreamWriter(outputEntry, "ISO-8859-1");
        }
        return new StreamResult(writer);
    }

    /**
     * Checks in under a given root element whether it can find a child element
     * which matches the XPath expression supplied. Returns {@link Element} if
     * exists.
     * 
     * Please note that the XPath parser used is NOT namespace aware. So if you
     * want to find a element <beans><sec:http> you need to use the following
     * XPath expression '/beans/http'.
     * 
     * @param xPathExpression the xPathExpression (required)
     * @param root the parent DOM element (required)
     * 
     * @return the Element if discovered (null if not found)
     */
    public static Element findFirstElement(String xPathExpression, Element root) {
        if (xPathExpression == null || root == null || xPathExpression.length() == 0) {
            throw new IllegalArgumentException("Xpath expression and root element required");
        }

        Element rootElement = null;
        try {

            XPathExpression expr = compiledExpressionCache.get(xPathExpression);
            if (expr == null) {
                expr = xpath.compile(xPathExpression);
                compiledExpressionCache.put(xPathExpression, expr);
            }
            rootElement = (Element) expr.evaluate(root, XPathConstants.NODE);
        } catch (XPathExpressionException e) {
            throw new IllegalArgumentException("Unable evaluate xpath expression", e);
        }
        return rootElement;
    }

    /**
     * Checks in under a given root element whether it can find a child element
     * which matches the name supplied. Returns {@link Element} if exists.
     * 
     * @param name the Element name (required)
     * @param root the parent DOM element (required)
     * 
     * @return the Element if discovered
     */
    public static Element findFirstElementByName(String name, Element root) {
        Assert.hasText(name, "Element name required");
        Assert.notNull(root, "Root element required");
        return (Element) root.getElementsByTagName(name).item(0);
    }

    /**
     * Checks in under a given root element whether it can find a child element
     * which matches the XPath expression supplied. The {@link Element} must
     * exist. Returns {@link Element} if exists.
     * 
     * Please note that the XPath parser used is NOT namespace aware. So if you
     * want to find a element <beans><sec:http> you need to use the following
     * XPath expression '/beans/http'.
     * 
     * @param xPathExpression the xPathExpression (required)
     * @param root the parent DOM element (required)
     * 
     * @return the Element if discovered (never null; an exception is thrown if
     *         cannot be found)
     */
    public static Element findRequiredElement(String xPathExpression, Element root) {
        Assert.hasText(xPathExpression, "XPath expression required");
        Assert.notNull(root, "Root element required");
        Element element = findFirstElement(xPathExpression, root);
        Assert.notNull(element,
                "Unable to obtain required element '" + xPathExpression + "' from element '" + root + "'");
        return element;
    }

    /**
     * Checks in under a given root element whether it can find a child elements
     * which match the XPath expression supplied. Returns a {@link List} of
     * {@link Element} if they exist.
     * 
     * Please note that the XPath parser used is NOT namespace aware. So if you
     * want to find a element <beans><sec:http> you need to use the following
     * XPath expression '/beans/http'.
     * 
     * @param xPathExpression the xPathExpression
     * @param root the parent DOM element
     * 
     * @return a {@link List} of type {@link Element} if discovered, otherwise null
     */
    public static List<Element> findElements(String xPathExpression, Element root) {
        List<Element> elements = new ArrayList<Element>();

        NodeList nodes = null;

        try {
            XPathExpression expr = xpath.compile(xPathExpression);
            nodes = (NodeList) expr.evaluate(root, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
            throw new IllegalArgumentException("Unable evaluate xpath expression", e);
        }

        for (int i = 0; i < nodes.getLength(); i++) {
            elements.add((Element) nodes.item(i));
        }
        return elements;
    }

    /**
     * @return a transformer that indents entries by 4 characters (never null)
     */
    public static final Transformer createIndentingTransformer() {
        Transformer xformer;
        try {
            xformer = transformerFactory.newTransformer();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        xformer.setOutputProperty(OutputKeys.INDENT, "yes");
        xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        return xformer;
    }

    /**
     * @return a new document builder (never null)
     */
    public static final DocumentBuilder getDocumentBuilder() {
        //      factory.setNamespaceAware(true);
        try {
            return factory.newDocumentBuilder();
        } catch (ParserConfigurationException ex) {
            throw new IllegalStateException(ex);
        }
    }
}