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

org.xmlbeam.DefaultXPathBinder Maven / Gradle / Ivy

Go to download

The coolest XML library for Java around. Define typesafe views (projections) to xml. Use XPath to read and write XML. Bind XML to Java collections. Requires at least Java6, supports Java8 features and has no further runtime dependencies.

There is a newer version: 1.4.24
Show newest version
/**
 *    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.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 RuntimeException(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 RuntimeException(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 - 2024 Weber Informatics LLC | Privacy Policy