All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.crabshue.commons.xpath.XPathEvaluator Maven / Gradle / Ivy

package com.crabshue.commons.xpath;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.lang3.StringUtils;
import org.xml.sax.InputSource;

import com.crabshue.commons.exceptions.ApplicationException;
import com.crabshue.commons.xml.inputsource.InputSourceBuilder;
import com.crabshue.commons.xpath.exceptions.XPathErrorContext;
import com.crabshue.commons.xpath.exceptions.XPathErrorType;
import com.crabshue.commons.xpath.saxon.NodeInfoUtils;

/**
 * XPath expression evaluator.
 *
 * @author vinh
 */
public class XPathEvaluator {


    private InputSource inputSource;

    private Map variables = new HashMap<>();

    private String xpathExpression;

    private QName returnType = XPathConstants.STRING;

    /**
     * Prepare an XPath evaluator with no context ({@code }.
     *
     * @return instance.
     */
    public static XPathEvaluator noContext() {
        return XPathEvaluator.of("");
    }

    /**
     * Prepare an XPath evaluator with an {@link InputSource}.
     *
     * @param inputSource the input source.
     * @return instance.
     */
    public static XPathEvaluator of(final InputSource inputSource) {
        final XPathEvaluator ret = new XPathEvaluator();
        ret.inputSource = inputSource;
        return ret;
    }

    /**
     * Prepare an XPath evaluator with an XML document as {@link String}.
     *
     * @param xmlContent the XML document as {@link String}.
     * @return instance.
     * @see #of(InputSource)
     */
    public static XPathEvaluator of(final String xmlContent) {
        return XPathEvaluator.of(InputSourceBuilder.newInputSource(xmlContent));
    }

    /**
     * Prepare an XPath evaluator with an XML {@link File}.
     *
     * @param xmlFile the XML file.
     * @return instance.
     * @see #of(InputSource)
     */
    public static XPathEvaluator of(final File xmlFile) {
        return XPathEvaluator.of(InputSourceBuilder.newInputSource(xmlFile));
    }

    /**
     * Prepare an XPath evaluator with a byte array.
     *
     * @param xmlByteArray the byte array
     * @return instance.
     * @see #of(InputSource)
     */
    public static XPathEvaluator of(final byte[] xmlByteArray) {
        return XPathEvaluator.of(InputSourceBuilder.newInputSource(xmlByteArray));
    }

    /**
     * Set the XPath expression for XPath evaluation.
     *
     * @param xpathExpression the XPath expression.
     * @return instance.
     */
    public XPathEvaluator withXpathExpression(final String xpathExpression) {
        this.xpathExpression = xpathExpression;
        return this;
    }

    /**
     * Add variables for XPath evaluation.
     *
     * @param variables the variables map.
     * @return instance.
     */
    public XPathEvaluator withVariables(Map variables) {
        this.variables.putAll(variables);
        return this;
    }

    /**
     * Set the return type for the XPath evaluation.
     * 

The default return type is {@link XPathConstants#STRING}

* * @param returnType the return type. * @return instance. */ public XPathEvaluator withReturnType(final QName returnType) { this.returnType = returnType; return this; } /** * Evaluate the XPath. * * @param the return type. * @return the value. */ public T evaluate() { final XPath xPath = XPathFactoryUtils.newXPath(this.inputSource, variables); try { xPath.compile(this.xpathExpression); } catch (XPathExpressionException e) { throw new ApplicationException(XPathErrorType.ERROR_XPATH_EVALUATION, e) .addContextValue(XPathErrorContext.XPATH, this.xpathExpression); } try { return (T) xPath.evaluate(xpathExpression, this.inputSource, this.returnType); } catch (XPathExpressionException e) { throw new ApplicationException(XPathErrorType.ERROR_XPATH_EVALUATION, e); } } /** * Evaluate the XPath and return a collection of values. * * @return the collection of values. */ public Collection evaluateCollection() { List ret = new ArrayList<>(); this.returnType = XPathConstants.NODESET; List result = this.evaluate(); for (Object elem : result) { final String resValue = NodeInfoUtils.extractValue(elem); if (StringUtils.isNotBlank(resValue)) { ret.add(resValue); } } return ret; } /** * Evaluate the XPath as a boolean expression. * * @return the result. */ public boolean evaluateBoolean() { this.returnType = XPathConstants.BOOLEAN; this.xpathExpression = "fn:deep-equal(fn:true(), " + this.xpathExpression + ")"; return this.evaluate(); } /** * Hidden constructor. */ private XPathEvaluator() { } // public static Map evaluateXpathPair(final InputSource inputSource, final String xpathStr, final String xpathStrPair) { // return evaluateXpathPair(inputSource, xpathStr, xpathStrPair, new HashMap<>()); // } // // /** // * Evaluate a given XPath expression on a given input, with a map of variables for variable resolution. // * // * @param inputSource the input // * @param xpathStr the xpath expression // * @param variables the variables to resolve // * @return the result of the XPath evaluation, as a collection of strings // */ // public static Map evaluateXpathPair(final InputSource inputSource, final String xpathStr, final String xpathStrPair, // final Map variables) { // Validate.notNull(inputSource); // Validate.notNull(xpathStr); // Validate.notNull(variables); // // // // keep content in a byte array, because needed twice (namespace retrieval + xpath evaluation) // byte[] content; // try { // content = IOUtils.toByteArray(inputSource.getByteStream()); // } catch (Exception e) { // throw new SystemException(XmlErrorType.ERROR_READING_FILE, e); // } // // Map ret = new HashMap<>(); // try (InputStream is = new ByteArrayInputStream(content)) { // XPath xpath = XPathFactoryUtils.newXPath(content, variables); // XPathExpression xPathExpression = xpath.compile(xpathStr); // // List res = (List) xPathExpression.evaluate(new InputSource(is), XPathConstants.NODESET); // // // use result of previous xpath evaluation as context for next xpath evaluation // for (Object elem : res) { // XPathExpression xPathExpression2 = xpath.compile(xpathStrPair); // String res2 = (String) xPathExpression2.evaluate(elem, XPathConstants.STRING); // // final String resValue = extractValue(elem); // if (StringUtils.isNotBlank(resValue)) { // ret.put(resValue, res2); // } // } // return ret; // } catch (ApplicationException e) { // throw e; // } catch (XPathExpressionException e) { // // saxon exception is not serializable (JSON) // throw new SystemException(XPathErrorType.ERROR_XPATH_EVALUATION, e.getMessage()) // .addContextValue(XPathErrorContext.XPATH, xpathStr) // .addContextValue(CommonErrorContext.CAUSE, e.getMessage()); // } catch (Exception e) { // throw new SystemException(XPathErrorType.ERROR_XPATH_EVALUATION, e) // .addContextValue(XPathErrorContext.XPATH, xpathStr); // } // // // } // /** // * Evaluate an XPath expression on a given input, with a folder as used as XPath variable 'base'. // * // * @param inputSource the input // * @param xpathStr the XPath expression // * @param folder the folder to use as base // * @return the XPath evaluation result, as a collection of strings // */ // public static Collection evaluate(final InputSource inputSource, final String xpathStr, final File folder) { // Validate.notNull(inputSource); // Validate.notNull(xpathStr); // Validate.notNull(folder); // // final Map variables = new HashMap<>(); // variables.put("base", folder.toURI().toString()); // // return evaluateXpath(inputSource, xpathStr, variables); // } // /** // * Resolve an XPath expression with given variables, out of any XML context. // *

The XPath is actually computed on the only tag {@code }.

// * // * @param xpath the expression // * @param variables the variables // * @return the first result // */ // public static String evaluateXpathExpressionNoXmlContext(final String xpath, final Map variables) { // Validate.notNull(xpath); // Validate.notNull(variables); // // try (InputStream is = IOUtils.toInputStream("", StandardCharsets.UTF_8)) { // final Collection values = evaluate(new InputSource(is), xpath, XPathConstants.STRING, variables); // if (values.isEmpty()) { // throw new ApplicationException(XPathErrorType.XPATH_RETURNS_NO_RESULT, "No result for computed value") // .addContextValue(XPathErrorContext.XPATH, xpath) // .addContextValue(XPathErrorContext.VARIABLES, ExceptionMessageUtils.printMap(variables, String::toString, String::toString)); // } // return values.iterator().next(); // } catch (IOException e) { // throw new SystemException(XmlErrorType.ERROR_READING_FILE, e); // } // } }