com.c4om.utils.xmlutils.XMLLibsShortcuts.java Source code

Java tutorial

Introduction

Here is the source code for com.c4om.utils.xmlutils.XMLLibsShortcuts.java

Source

/*
Copyright 2014 Universidad Politcnica de Madrid - Center for Open Middleware (http://www.centeropenmiddleware.com)
    
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 com.c4om.utils.xmlutils;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;

import net.sf.saxon.Configuration;
import net.sf.saxon.dom.DOMNodeWrapper;
import net.sf.saxon.dom.DocumentWrapper;
import net.sf.saxon.s9api.DOMDestination;
import net.sf.saxon.s9api.Destination;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmValue;
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.filter.Filter;
import org.jdom2.filter.Filters;
import org.jdom2.input.DOMBuilder;
import org.jdom2.output.DOMOutputter;
import org.jdom2.xpath.XPathBuilder;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;

/**
 * Static methods provided by this class act as shortcuts to Saxon, JAXEN (via JDOM2) and other XML librarys.
 * The aim is to provide simple methods to performs queries/transformations/etc. whithout having to take care 
 * of complicated configurations in simple situations.
 */
public class XMLLibsShortcuts {

    /**
     * JAXEN XPath factory
     */
    private static final XPathFactory XPATH_FACTORY = XPathFactory.instance();

    /**
     * Simple method that performs a XPath query over elements
     * @param path the path
     * @param context a context node
     * @return the list of results
     */
    public static List<Element> performJAXENXPath(String path, Object context) {
        return performJAXENXPath(path, context, Filters.element(), null);
    }

    /**
     * Simple method that performs a XPath query over elements
     * @param <T> The type of the XPath result.
     * @param path the path
     * @param context a context node
     * @param filter an appropiate filter. Its type will be the one of the result
     * @return the list of results
     */
    public static <T> List<T> performJAXENXPath(String path, Object context, Filter<T> filter) {
        return performJAXENXPath(path, context, filter, null);
    }

    /**
     * Simple method that performs a XPath query over elements
     * @param <T> The type of the XPath result.
     * @param path the path
     * @param context a context node
     * @param filter an appropiate filter. Its type will be the one of the result
     * @param namespaces namespaces made available to XPath engine
     * @return the list of results
     */
    public static <T> List<T> performJAXENXPath(String path, Object context, Filter<T> filter,
            Collection<Namespace> namespaces) {
        XPathBuilder<T> builder = new XPathBuilder<>(path, filter);
        if (namespaces != null) {
            builder.setNamespaces(namespaces);
        }
        XPathExpression<T> xpathExpression = builder.compileWith(XPATH_FACTORY);
        List<T> xpathResults = xpathExpression.evaluate(context);
        return xpathResults;
    }

    /**
     * This method takes an iterable as an input and returns an {@link XdmValue} object, whose internal {@link XdmItem} objects are generated 
     * from the ones obtained by iterating the iterable.
     * @param variableValueIterable an {@link Iterable}.
     * @param configuration Saxon {@link Configuration} used to build the {@link Processor} used.
     * @return a {@link XdmValue}, whose {@link XdmItem}s are representations of the provided values
     * @throws IllegalArgumentException if any value anywhere cannot be transformed into a suitable {@link XdmValue}.
     */
    private static XdmValue getXdmValueFromIterable(Iterable<?> variableValueIterable,
            Configuration configuration) {
        XdmValue xdmValueForVariable;
        List<XdmItem> items = new ArrayList<>();
        for (Object variableValueCurrentSubitem : variableValueIterable) {
            XdmValue currentItemXdmValue = generateXdmValueForObject(variableValueCurrentSubitem, configuration);
            if (!(currentItemXdmValue instanceof XdmItem)) {
                throw new IllegalArgumentException(
                        "Iterables or arrays are not allowed to contain other Iterables or arrays.");
            }
            XdmItem currentItem = (XdmItem) currentItemXdmValue;
            items.add(currentItem);
        }
        xdmValueForVariable = new XdmValue(items);
        return xdmValueForVariable;
    }

    /**
     * This method takes an {@link Object} and tries to get an equivalent Saxon {@link XdmValue}. This is 
     * useful, for example, if the value is going to be set to a variable in a XQuery query.<p/>
     * Currently, those object kinds are supported:
     * <ol>
     * <li>Any of the ones suitable to construct a {@link XdmAtomicValue} (look at {@link XdmAtomicValue} constructors for details), except for the Saxon-specific ones (AtomicValue, QName).</li>
     * <li>A {@link org.w3c.dom.Document} or a {@link org.w3c.dom.Node} <b>whose {@link org.w3c.dom.Node#getOwnerDocument()} returns a valid owner document.</b></li>
     * <li>An array of the types described at points 1 and 2.</li>
     * <li>An {@link Iterable} whose returned objects are of the types described at points 1 or 2 (it means, an {@link Iterable} that first returns an {@link String} and then a {@link Boolean} would be valid, for example).</li>
     * </ol>
     * @param variableValue any object following the aforementioned restrictions.
     * @param configuration Saxon {@link Configuration} used to build the {@link Processor} used.
     * @return An equivalent {@link XdmValue}
     * @throws IllegalArgumentException if the value or any of it subvalues (if it was an array or an {@link Iterable}) cannot be transformed into a suitable {@link XdmValue}.
     */
    protected static XdmValue generateXdmValueForObject(Object variableValue, Configuration configuration) {
        XdmValue xdmValueForVariable;
        if (variableValue instanceof XdmValue) {
            xdmValueForVariable = (XdmValue) variableValue;
        } else if (variableValue instanceof BigDecimal) {
            xdmValueForVariable = new XdmAtomicValue((BigDecimal) variableValue);
        } else if (variableValue instanceof Boolean) {
            xdmValueForVariable = new XdmAtomicValue((Boolean) variableValue);
        } else if (variableValue instanceof Double) {
            xdmValueForVariable = new XdmAtomicValue((Double) variableValue);
        } else if (variableValue instanceof Float) {
            xdmValueForVariable = new XdmAtomicValue((Float) variableValue);
        } else if (variableValue instanceof Long) {
            xdmValueForVariable = new XdmAtomicValue((Long) variableValue);
        } else if (variableValue instanceof String) {
            xdmValueForVariable = new XdmAtomicValue((String) variableValue);
        } else if (variableValue instanceof URI) {
            xdmValueForVariable = new XdmAtomicValue((URI) variableValue);
        } else if (variableValue instanceof BigDecimal[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof Boolean[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof Double[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof Float[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof Long[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof String[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof URI[]) {
            List<?> variableValueList = Arrays.asList(variableValue);
            xdmValueForVariable = getXdmValueFromIterable(variableValueList, configuration);
        } else if (variableValue instanceof org.w3c.dom.Document) {
            org.w3c.dom.Document variableValueDocument = (org.w3c.dom.Document) variableValue;
            DocumentWrapper wrapper = new DocumentWrapper(variableValueDocument, variableValueDocument.getBaseURI(),
                    configuration);
            xdmValueForVariable = new XdmNode(wrapper);
        } else if (variableValue instanceof org.w3c.dom.Node) {
            org.w3c.dom.Node variableValueNode = (org.w3c.dom.Node) variableValue;
            DocumentWrapper documentWrapper = new DocumentWrapper(variableValueNode.getOwnerDocument(),
                    variableValueNode.getBaseURI(), configuration);
            DOMNodeWrapper wrapper = documentWrapper.wrap(variableValueNode);
            xdmValueForVariable = new XdmNode(wrapper);
        } else if (variableValue instanceof Iterable<?>) {
            Iterable<?> variableValueIterable = (Iterable<?>) variableValue;
            xdmValueForVariable = getXdmValueFromIterable(variableValueIterable, configuration);
        } else {
            throw new IllegalArgumentException("Non recognized variable value type.");
        }
        return xdmValueForVariable;
    }

    /**
     * This method performs a XQuery query via Saxon
     * @param query the query, as a String
     * @param variables external variables passed to the query. It is a {@link Map} between the variable names (identified by {@link QName}s) and their values. For more information about valid values, have a look at {@link XMLLibsShortcuts#generateXdmValueForObject(Object, Configuration)}
     * @return the results, as a String
     * @throws SaxonApiException if there is any problem when performing the query
     */
    public static String saxonQuery(String query, Map<QName, ?> variables) throws SaxonApiException {
        Configuration configuration = new Configuration();
        Processor processor = new Processor(configuration);
        XQueryCompiler xqueryCompiler = processor.newXQueryCompiler();
        XQueryExecutable xqueryExecutable = xqueryCompiler.compile(query);
        XQueryEvaluator xqueryEvaluator = xqueryExecutable.load();
        if (variables != null) {
            for (QName variableKey : variables.keySet()) {
                Object variableValue = variables.get(variableKey);
                XdmValue xdmValueForVariable = generateXdmValueForObject(variableValue, configuration);
                xqueryEvaluator.setExternalVariable(new net.sf.saxon.s9api.QName(variableKey), xdmValueForVariable);
            }
        }
        XdmValue resultAsXdmValue = xqueryEvaluator.evaluate();
        StringWriter resultsWriter = new StringWriter();
        Serializer serializer = new Serializer(resultsWriter);
        serializer.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, "yes");
        serializer.setProcessor(processor);
        serializer.serializeXdmValue(resultAsXdmValue);
        String results = resultsWriter.toString();
        return results;

    }

    /**
     * This method performs a XQuery query via Saxon (specifying no external variables).
     * @param query the query, as a String
     * @return the results, as a String
     * @throws SaxonApiException if there is any problem when performing the query
     */
    public static String saxonQuery(String query) throws SaxonApiException {
        return saxonQuery(query, null);
    }

    /**
     * This method takes an input JDOM2 {@link Document} and a XSLT (as a JDOM2 {@link Document}) 
     * and applies the sheet to the document. The result is returned as a JDOM2 {@link Document}. 
     * @param inputDocument the input document
     * @param xslt the XSLT sheet
     * @return the result of applying the sheet to the document
     * @throws JDOMException if there are problems at JDOM2 parsing
     * @throws SaxonApiException if there are problems related to JDOM2 documents
     * @throws ParserConfigurationException if there are problems at building internal {@link org.w3c.dom.Document} objects used to interact with Saxon API.
     */
    public static Document performXSLT(Document inputDocument, Document xslt)
            throws JDOMException, SaxonApiException, ParserConfigurationException {
        //First, we convert JDOM2 documents to org.w3c.dom.Document
        DOMOutputter domOutputter = new DOMOutputter();
        org.w3c.dom.Document w3cXSLT = domOutputter.output(xslt);
        org.w3c.dom.Document w3cInput = domOutputter.output(inputDocument);
        Processor processor = new Processor(false);

        DocumentBuilderFactory w3cDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
        w3cDocumentBuilderFactory.setNamespaceAware(true);
        DocumentBuilder documentBuilder = w3cDocumentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document w3cDestinationDocument = documentBuilder.newDocument();

        Source w3cXSLTSource = new DOMSource(w3cXSLT);
        Source w3cInputSource = new DOMSource(w3cInput);
        Destination destination = new DOMDestination(w3cDestinationDocument);

        XsltCompiler comp = processor.newXsltCompiler();
        XsltExecutable executable = comp.compile(w3cXSLTSource);
        XsltTransformer transformer = executable.load();
        XdmNode xdmInput = processor.newDocumentBuilder().build(w3cInputSource);
        transformer.setInitialContextNode(xdmInput);
        transformer.setDestination(destination);
        transformer.transform();

        DOMBuilder domBuilder = new DOMBuilder();
        Document destinationDocument = domBuilder.build(w3cDestinationDocument);

        return destinationDocument;
    }

    /**
     * Private constructor, to avoid instancing.
     */
    private XMLLibsShortcuts() {
    }
}