org.xmlbeam.DefaultXPathBinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xmlprojector Show documentation
Show all versions of xmlprojector Show documentation
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.
/**
* 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);
}
}