Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
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 2012 Sven Ewald
*
* 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 org.xmlbeam;
import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathVariableResolver;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xmlbeam.XBProjector.IOBuilder;
import org.xmlbeam.XBProjector.InternalProjection;
import org.xmlbeam.annotation.XBDelete;
import org.xmlbeam.annotation.XBDocURL;
import org.xmlbeam.annotation.XBOverride;
import org.xmlbeam.annotation.XBRead;
import org.xmlbeam.annotation.XBUpdate;
import org.xmlbeam.annotation.XBValue;
import org.xmlbeam.annotation.XBWrite;
import org.xmlbeam.dom.DOMAccess;
import org.xmlbeam.types.TypeConverter;
import org.xmlbeam.util.intern.DOMHelper;
import org.xmlbeam.util.intern.MethodParamVariableResolver;
import org.xmlbeam.util.intern.Preprocessor;
import org.xmlbeam.util.intern.ReflectionHelper;
import org.xmlbeam.util.intern.duplex.DuplexExpression;
import org.xmlbeam.util.intern.duplex.DuplexXPathParser;
import org.xmlbeam.util.intern.duplex.ExpressionType;
import org.xmlbeam.util.intern.duplex.XBPathParsingException;
/**
* This class implements the "magic" behind projection methods. Each projection is linked with a
* ProjectionInvocatonHandler which handles method invocations on the projections. Notice that this
* class is not part of the public API. You should not get in touch with this class at all. See
* {@link org.xmlbeam.XBProjector} for API usage.
*
* @author Sven Ewald
*/
@SuppressWarnings("serial")
final class ProjectionInvocationHandler implements InvocationHandler, Serializable {
private Map getDefaultInvokers(final Object defaultInvokerObject) {
final ReflectionInvoker reflectionInvoker = new ReflectionInvoker(defaultInvokerObject);
final Map invokers = new HashMap();
for (Method m : DOMAccess.class.getMethods()) {
invokers.put(MethodSignature.forMethod(m), reflectionInvoker);
}
invokers.put(MethodSignature.forVoidMethod("toString"), reflectionInvoker);
invokers.put(MethodSignature.forSingleParam("equals", Object.class), reflectionInvoker);
invokers.put(MethodSignature.forVoidMethod("hashCode"), reflectionInvoker);
return invokers;//Collections.unmodifiableMap(invokers);
}
private static class ReflectionInvoker implements InvocationHandler, Serializable {
protected final Object obj;
ReflectionInvoker(final Object obj) {
this.obj = obj;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
try {
return method.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getCause() == null ? e : e.getCause();
}
}
}
private static class MixinInvoker extends ReflectionInvoker {
private final Class> projectionInterface;
MixinInvoker(final Object obj, final Class> projectionInterface) {
super(obj);
this.projectionInterface = projectionInterface;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
injectMeAttribute((InternalProjection) proxy, obj, projectionInterface);
try {
return super.invoke(proxy, method, args);
} catch (InvocationTargetException e) {
throw e.getCause() == null ? e : e.getCause();
}
}
}
private static abstract class ProjectionMethodInvocationHandler implements InvocationHandler, Serializable {
private static final InvocationContext EMPTY_INVOCATION_CONTEXT = new InvocationContext(null, null, null, null, null);
static class InvocationContext {
public String getResolvedXPath() {
return resolvedXPath;
}
public XPath getxPath() {
return xPath;
}
public XPathExpression getxPathExpression() {
return xPathExpression;
}
public DuplexExpression getDuplexExpression() {
return duplexExpression;
}
public InvocationContext(final String resolvedXPath, final XPath xPath, final XPathExpression xPathExpression, final DuplexExpression duplexExpression, final MethodParamVariableResolver resolver) {
this.resolvedXPath = resolvedXPath;
this.xPath = xPath;
this.xPathExpression = xPathExpression;
this.duplexExpression = duplexExpression;
this.resolver = resolver;
}
final String resolvedXPath;
final XPath xPath;
final XPathExpression xPathExpression;
final DuplexExpression duplexExpression;
final MethodParamVariableResolver resolver;
/**
* @param resolvedXpath
* @return true if this invocation context may be reused for given path
*/
public boolean isStillValid(final String resolvedXpath) {
return resolvedXpath.equals(this.resolvedXPath);
}
/**
* @return Format pattern for the expression within with this context.
*/
public String getExpressionFormatPattern() {
return duplexExpression.getExpressionFormatPattern();
}
public void updateMethodArgs(final Object[] args) {
if (resolver != null) {
resolver.updateArgs(args);
}
}
}
protected final Method method;
protected final String annotationValue;
protected final XBProjector projector;
protected final Node node;
private final String docAnnotationValue;
private final boolean isVoidMethod;
protected InvocationContext lastInvocationContext = EMPTY_INVOCATION_CONTEXT;
protected final Map methodParameterIndexes;
ProjectionMethodInvocationHandler(final Node node, final Method method, final String annotationValue, final XBProjector projector) {
this.method = method;
this.annotationValue = annotationValue;
this.projector = projector;
this.node = node;
final XBDocURL annotation = method.getAnnotation(XBDocURL.class);
this.docAnnotationValue = annotation == null ? null : annotation.value();
this.isVoidMethod = !ReflectionHelper.hasReturnType(method);
methodParameterIndexes = ReflectionHelper.getMethodParameterIndexes(method);
}
protected Node getNodeForMethod(final Method method, final Object[] args) throws SAXException, IOException, ParserConfigurationException {
if (docAnnotationValue != null) {
String uri = projector.config().getExternalizer().resolveURL(docAnnotationValue, method, args);
final Map requestParams = ((IOBuilder) projector.io()).filterRequestParamsFromParams(uri, args);
uri = Preprocessor.applyParams(uri, methodParameterIndexes, args);
return DOMHelper.getDocumentFromURL(projector.config().createDocumentBuilder(), uri, requestParams, method.getDeclaringClass());
}
return node;
}
protected String resolveXPath(final Object[] args) {
return Preprocessor.applyParams(projector.config().getExternalizer().resolveXPath(annotationValue, method, args), methodParameterIndexes, args);
}
/**
* Determine a methods return value that does not depend on the methods execution. Possible
* values are void or the proxy itself (would be "this").
*
* @param method
* @return
*/
protected Object getProxyReturnValueForMethod(final Object proxy, final Method method, final Integer numberOfChanges) {
if (isVoidMethod) {
return null;
}
if (method.getReturnType().equals(method.getDeclaringClass())) {
return proxy;
}
if ((numberOfChanges != null) && (method.getReturnType().isAssignableFrom(Integer.class) || method.getReturnType().isAssignableFrom(int.class))) {
return numberOfChanges;
}
throw new IllegalArgumentException("Method " + method + " has illegal return type \"" + method.getReturnType() + "\". I don't know what to return. I expected void or " + method.getDeclaringClass().getSimpleName());
}
abstract protected Object invokeProjection(final String resolvedXpath, final Object proxy, final Object[] args) throws Throwable;
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final String xPath = resolveXPath(args);
final String resolvedXpath = Preprocessor.applyParams(xPath, methodParameterIndexes, args);
return invokeProjection(resolvedXpath, proxy, args);
}
}
private static abstract class XPathInvocationHandler extends ProjectionMethodInvocationHandler {
private XPathInvocationHandler(final Node node, final Method method, final String annotationValue, final XBProjector projector) {
super(node, method, annotationValue, projector);
}
@Override
final protected Object invokeProjection(final String resolvedXpath, final Object proxy, final Object[] args) throws Throwable {
final XPath xPath = projector.config().createXPath(DOMHelper.getOwnerDocumentFor(node));
if (!lastInvocationContext.isStillValid(resolvedXpath)) {
final DuplexExpression duplexExpression = new DuplexXPathParser().compile(resolvedXpath);
String strippedXPath = duplexExpression.getExpressionAsStringWithoutFormatPatterns();
MethodParamVariableResolver resolver = null;
if (duplexExpression.isUsingVariables()) {
XPathVariableResolver peviousResolver = xPath.getXPathVariableResolver();
resolver = new MethodParamVariableResolver(method, args, duplexExpression, projector.config().getStringRenderer(), peviousResolver);
xPath.setXPathVariableResolver(resolver);
}
final XPathExpression xPathExpression = xPath.compile(strippedXPath);
lastInvocationContext = new InvocationContext(resolvedXpath, xPath, xPathExpression, duplexExpression, resolver);
}
lastInvocationContext.updateMethodArgs(args);
return invokeXpathProjection(lastInvocationContext, proxy, args);
}
abstract protected Object invokeXpathProjection(final InvocationContext invocationContext, final Object proxy, final Object[] args) throws Throwable;
}
private static class ReadInvocationHandler extends XPathInvocationHandler {
private final boolean absentIsEmpty;
private final boolean wrappedInOptional;
private final Class> returnType;
private final Class> targetComponentType;
private final Class> exceptionType;
private final boolean isConvertable;
private final boolean isReturnAsNode;
private final boolean isEvaluateAsList;
private final boolean isEvaluateAsArray;
private final boolean isEvaluateAsSubProjection;
private final boolean isThrowIfAbsent;
ReadInvocationHandler(final Node node, final Method method, final String annotationValue, final XBProjector projector, final boolean absentIsEmpty) {
super(node, method, annotationValue, projector);
wrappedInOptional = ReflectionHelper.isOptional(method.getGenericReturnType());
returnType = wrappedInOptional ? ReflectionHelper.getParameterType(method.getGenericReturnType()) : method.getReturnType();
Class>[] exceptionTypes = method.getExceptionTypes();
exceptionType = exceptionTypes.length > 0 ? exceptionTypes[0] : null;
this.isConvertable = projector.config().getTypeConverter().isConvertable(returnType);
this.isReturnAsNode = Node.class.isAssignableFrom(returnType);
this.isEvaluateAsList = List.class.equals(returnType);
this.isEvaluateAsArray = returnType.isArray();
if (wrappedInOptional && (isEvaluateAsArray || isEvaluateAsList)) {
throw new IllegalArgumentException("Method " + method + " must not declare an optional return type of list or array. Lists and arrays may be empty but will never be null.");
}
this.targetComponentType = isEvaluateAsList || isEvaluateAsArray ? findTargetComponentType(method) : null;
this.isEvaluateAsSubProjection = returnType.isInterface();
this.isThrowIfAbsent = exceptionType != null;
// Throwing exception overrides empty default value.
this.absentIsEmpty = absentIsEmpty && (!isThrowIfAbsent);
}
/**
* When reading collections, determine the collection component type.
*
* @param method
* @return
*/
private static Class> findTargetComponentType(final Method method) {
if (method.getReturnType().isArray()) {
return method.getReturnType().getComponentType();
}
assert List.class.equals(method.getReturnType());
final Type type = method.getGenericReturnType();
if (!(type instanceof ParameterizedType) || (((ParameterizedType) type).getActualTypeArguments() == null) || (((ParameterizedType) type).getActualTypeArguments().length < 1)) {
throw new IllegalArgumentException("When using List as return type for method " + method + ", please specify a generic type for the List. Otherwise I do not know which type I should fill the List with.");
}
assert ((ParameterizedType) type).getActualTypeArguments().length == 1 : "";
Type componentType = ((ParameterizedType) type).getActualTypeArguments()[0];
if (!(componentType instanceof Class)) {
throw new IllegalArgumentException("I don't know how to instantiate the generic type for the return type of method " + method);
}
return (Class>) componentType;
}
private List> evaluateAsList(final XPathExpression expression, final Node node, final Method method, final InvocationContext invocationContext) throws XPathExpressionException {
assert targetComponentType != null;
final NodeList nodes = (NodeList) expression.evaluate(node, XPathConstants.NODESET);
final List