com.elsevier.xml.XPathProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.elsevier.xml.XPathProcessor.java

Source

/*
 * Copyright (c)2015 Elsevier, Inc.
    
 * 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.elsevier.xml;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;

import javax.xml.transform.stream.StreamSource;

import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.Processor;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.Serializer;
import net.sf.saxon.s9api.XPathCompiler;
import net.sf.saxon.s9api.XPathSelector;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.s9api.XdmValue;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Class with static methods to filter an XPath expression (return a TRUE/FALSE)
 * against a string of arbitrary xml content and evaluate an XPath expression (return 
 * a serialized response) against a string of arbitrary xml content.
 * 
 * @author Darin McBeath
 * 
 */
public class XPathProcessor {

    // Logger
    private static Log log = LogFactory.getLog(XPathProcessor.class);

    /**
     * Init the NamespaceContext. Set the prefix to namespace mappings specified 
     * in the passed HashMap.  
     * 
     * @param prefixesNamespaces HashMap of namespace prefix / namespace uri mappings
     */
    public static void init(HashMap<String, String> prefixesNamespaces) throws IOException {

        NamespaceContextMappings.init(prefixesNamespaces);

    }

    /**
     * Clear namespace prefix / namespace uri mappings. Useful if you want to use a 
     * different set of prefix/namespace mappings.
     */
    public static void clear() {

        NamespaceContextMappings.emptyCache();

    }

    /**
     * Filter the xpathExpression to the specified string.
     * 
     * @param content
     *            String to which the xpathExpression will be applied
     * @param xpathExpression
     *            XPath expression to apply to the content
     * @return TRUE if the xpathExpression evaluates to true, FALSE otherwise
     */
    public static boolean filterString(String content, String xpathExpression) {

        try {

            return filter(new StreamSource(IOUtils.toInputStream(content, CharEncoding.UTF_8)), xpathExpression);

        } catch (IOException e) {

            log.error("Problems processing the content.  " + e.getMessage(), e);
            return false;

        }

    }

    /**
     * Apply the xpathExpression to the specified string and return a serialized
     * response.
     * 
     * @param content
     *            String to which the xpathExpression will be applied
     * @param xpathExpression
     *            XPath expression to apply to the content
     * @return Serialized response from the evaluation.  If an error, the response will be "<error/>".
     */
    public static String evaluateString(String content, String xpathExpression) {

        try {

            return evaluate(new StreamSource(IOUtils.toInputStream(content, CharEncoding.UTF_8)), xpathExpression);

        } catch (IOException e) {

            log.error("Problems processing the content.  " + e.getMessage(), e);
            return "<error/>";

        }

    }

    /**
     * Filter the xpathExpression to the specified content
     * 
     * @param content
     *            content to which the xpathExpression will be applied
     * @param xpathExpression
     *            XPath expression to apply to the content
     * @return TRUE if the xpathExpression evaluates to true, FALSE otherwise
     */
    private static boolean filter(StreamSource content, String xpathExpression) {

        try {

            // Get the processor
            Processor proc = new Processor(false);

            // Get the xpath compiler
            XPathCompiler xpathCompiler = proc.newXPathCompiler();

            // Set the namespace to prefix mappings
            setPrefixNamespaceMappings(xpathCompiler);

            XPathSelector xsel = xpathCompiler.compile(xpathExpression).load();

            // Set the source document for which the xpath expression should be
            // applied
            DocumentBuilder builder = proc.newDocumentBuilder();
            XdmNode xmlDoc = builder.build(content);
            xsel.setContextItem(xmlDoc);

            // Evaluate return the boolean value for the xpath expression
            return xsel.effectiveBooleanValue();

        } catch (SaxonApiException e) {

            log.error("Problems processing the content.  EXPRESSION:" + xpathExpression + " " + e.getMessage(), e);
            return false;

        }

    }

    /**
     * Apply the xpathExpression to the specified content and return a serialized
     * response.
     * 
     * @param content
     *            content to which the xpathExpression will be applied
     * @param xpathExpression
     *            XPath expression to apply to the content
     * @return Serialized response from the evaluation.  If an error, the response will be "<error/>".
     */
    private static String evaluate(StreamSource content, String xpathExpression) {

        try {

            // Get the processor
            Processor proc = new Processor(false);

            // Get the xpath compiler
            XPathCompiler xpathCompiler = proc.newXPathCompiler();

            // Set the namespace to prefix mappings
            setPrefixNamespaceMappings(xpathCompiler);

            XPathSelector xsel = xpathCompiler.compile(xpathExpression).load();

            // Set the source document for which the xpath expression should be applied
            DocumentBuilder builder = proc.newDocumentBuilder();
            XdmNode xmlDoc = builder.build(content);
            xsel.setContextItem(xmlDoc);

            // Create an output serializer
            Serializer out = new Serializer();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            out.setOutputStream(baos);
            // Appears ok to always set output property to xml (even if we are just returning a text string)
            out.setOutputProperty(Serializer.Property.METHOD, "xml");
            out.setOutputProperty(Serializer.Property.OMIT_XML_DECLARATION, "yes");
            out.setProcessor(proc);

            // Evaluate the xpath expression
            XdmValue results = xsel.evaluate();
            Iterator<XdmItem> it = results.iterator();
            while (it.hasNext()) {
                XdmItem item = it.next();
                out.serializeXdmValue(item);
            }

            // Return the results
            return new String(baos.toByteArray(), CharEncoding.UTF_8);

        } catch (IOException e) {

            log.error("Problems processing the content.  EXPRESSION:" + xpathExpression + " " + e.getMessage(), e);
            return "<error/>";

        } catch (SaxonApiException e) {

            log.error("Problems processing the content.  EXPRESSION:" + xpathExpression + " " + e.getMessage(), e);
            return "<error/>";

        }

    }

    /**
     * Set the namespaces in the XPathCompiler.
     * 
     * @param xpathCompiler
     */
    private static void setPrefixNamespaceMappings(XPathCompiler xpathCompiler) {

        // Get the mappings
        Set<Entry<String, String>> mappings = NamespaceContextMappings.getMappings();

        // If mappings exist, set the namespaces
        if (mappings != null) {

            Iterator<Entry<String, String>> it = mappings.iterator();
            while (it.hasNext()) {
                Entry<String, String> entry = it.next();
                xpathCompiler.declareNamespace(entry.getKey(), entry.getValue());
            }
        }

    }

}