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

org.xmlbeam.DefaultXPathBinder Maven / Gradle / Ivy

/**
 *    Copyright 2015 Sven Ewald
 *
 *    This file is part of JSONBeam.
 *
 *    JSONBeam is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, any
 *    later version.
 *
 *    JSONBeam is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with JSONBeam.  If not, see .
 */
package org.xmlbeam;

import java.util.Collection;
import java.util.Date;

import java.io.Closeable;
import java.io.IOException;

import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.xmlbeam.evaluation.DocumentResolver;
import org.xmlbeam.evaluation.InvocationContext;
import org.xmlbeam.evaluation.XPathBinder;
import org.xmlbeam.exceptions.XBException;
import org.xmlbeam.exceptions.XBIOException;
import org.xmlbeam.types.CloseableList;
import org.xmlbeam.types.CloseableMap;
import org.xmlbeam.types.CloseableValue;
import org.xmlbeam.util.intern.ReflectionHelper;
import org.xmlbeam.util.intern.duplex.DuplexExpression;
import org.xmlbeam.util.intern.duplex.DuplexXPathParser;

/**
 * This class is used to provide an fluid interface for direct evaluation of XPath expressions.
 *
 * @author sven
 */
// FIXME: refactor and reduce visibility to default again
public final class DefaultXPathBinder implements XPathBinder {

    private final DocumentResolver documentProvider;
    private final DuplexExpression duplexExpression;
    private final XBProjector projector;
    private final Closeable documentWriter;

    /**
     * Constructor for DefaultXPathEvaluator.
     *
     * @param projector
     * @param documentProvider
     * @param xpath
     * @param documentWriter
     */
    public DefaultXPathBinder(final XBProjector projector, final DocumentResolver documentProvider, final String xpath, final Closeable documentWriter) {
        this.projector = projector;
        this.documentProvider = documentProvider;
        this.duplexExpression = new DuplexXPathParser(projector.config().getUserDefinedNamespaceMapping()).compile(xpath);
        this.documentWriter = documentWriter;
    }

    /**
     * Evaluates the XPath as a boolean value. This method is just a shortcut for as(Boolean.TYPE);
     *
     * @return true when the selected value equals (ignoring case) 'true'
     */
    @Override
    public CloseableValue asBoolean() {
        final Class callerClass = ReflectionHelper.getDirectCallerClass();
        return bindSingeValue(Boolean.TYPE, callerClass);
    }

    /**
     * Evaluates the XPath as a int value. This method is just a shortcut for as(Integer.TYPE);
     *
     * @return int value of evaluation result.
     */
    @Override
    public CloseableValue asInt() {
        final Class callerClass = ReflectionHelper.getDirectCallerClass();
        return bindSingeValue(Integer.TYPE, callerClass);
    }

    /**
     * Evaluates the XPath as a String value. This method is just a shortcut for as(String.class);
     *
     * @return String value of evaluation result.
     */
    @Override
    public CloseableValue asString() {
        final Class callerClass = ReflectionHelper.getDirectCallerClass();
        return bindSingeValue(String.class, callerClass);
    }

    /**
     * Evaluates the XPath as a Date value. This method is just a shortcut for as(Date.class); You
     * probably want to specify ' using ' followed by some formatting pattern consecutive to the
     * XPAth.
     *
     * @return Date value of evaluation result.
     */
    @Override
    public CloseableValue asDate() {
        final Class callerClass = ReflectionHelper.getDirectCallerClass();
        return bindSingeValue(Date.class, callerClass);
    }

    /**
     * Evaluate the XPath as a value of the given type.
     *
     * @param returnType
     *            Possible values: primitive types (e.g. Short.Type), Projection interfaces, any
     *            class with a String constructor or a String factory method, and org.w3c.Node
     * @return a value of return type that reflects the evaluation result.
     */
    @Override
    public  CloseableValue as(final Class returnType) {
        validateEvaluationType(returnType);
        final Class callerClass = ReflectionHelper.getDirectCallerClass();
        return bindSingeValue(returnType, callerClass);
    }

    private  CloseableValue bindSingeValue(final Class returnType, final Class callerClass) {
        validateEvaluationType(returnType);
        try {
            Document document = documentProvider.resolve(returnType, callerClass);

            XPathExpression expression = projector.config().createXPath(document).compile(duplexExpression.getExpressionAsStringWithoutFormatPatterns());

            InvocationContext invocationContext = new InvocationContext(duplexExpression.getExpressionAsStringWithoutFormatPatterns(), //
                    null, expression, duplexExpression, null, returnType, projector);

            return new DefaultFileValue(document, invocationContext, documentWriter);
        } catch (IOException e) {
            throw new XBIOException(e);
        } catch (XPathExpressionException e) {
            throw new XBException("Error during binding", e);
        }

    }

    public static  void validateEvaluationType(final Class returnType) {
        if (ReflectionHelper.isOptional(returnType)) {
            throw new IllegalArgumentException("Type Optional is only allowed as a method return type.");
        }
        if (Collection.class.isAssignableFrom(returnType)) {
            throw new IllegalArgumentException("A collection type can not be component type.");
        }
    }

    private enum CollectionType {
        LIST, MAP;
    }

    /**
     * Evaluate the XPath as a list of the given type.
     *
     * @param componentType
     *            Possible values: primitive types (e.g. Short.Type), Projection interfaces, any
     *            class with a String constructor or a String factory method, and org.w3c.Node
     * @return List of return type that reflects the evaluation result.
     */
    @SuppressWarnings("unchecked")
    @Override
    public  CloseableList asListOf(final Class componentType) {
        Class callerClass = ReflectionHelper.getDirectCallerClass();
        return (CloseableList) bindMultiValues(componentType, callerClass, CollectionType.LIST);
    }

    @SuppressWarnings("resource")
    private  Closeable bindMultiValues(final Class componentType, final Class callerClass, final CollectionType collectionType) {
        validateEvaluationType(componentType);
        try {
            Document document = documentProvider.resolve(componentType, callerClass);

            XPathExpression expression = projector.config().createXPath(document).compile(duplexExpression.getExpressionAsStringWithoutFormatPatterns());

            InvocationContext invocationContext = new InvocationContext(duplexExpression.getExpressionAsStringWithoutFormatPatterns(), //
                    null, expression, duplexExpression, null, componentType, projector);

            return collectionType == CollectionType.LIST ? new DefaultFileList(document, invocationContext, documentWriter) : new DefaultFileMap(document, invocationContext, documentWriter, componentType);
        } catch (IOException e) {
            throw new XBIOException(e);
        } catch (XPathExpressionException e) {
            throw new XBException("Error during evaluation", e);
        }
    }

    /**
     * @param valueType
     * @return map with value type
     * @see org.xmlbeam.evaluation.XPathBinder#asMapOf(java.lang.Class)
     */
    @SuppressWarnings("unchecked")
    @Override
    public  CloseableMap asMapOf(final Class valueType) {
        Class callerClass = ReflectionHelper.getDirectCallerClass();
        return (CloseableMap) bindMultiValues(valueType, callerClass, CollectionType.MAP);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy