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

org.xmlbeam.XBProjector Maven / Gradle / Ivy

/**
 *  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.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URISyntaxException;
import java.text.Format;
import java.text.MessageFormat;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xmlbeam.annotation.XBAuto;
import org.xmlbeam.annotation.XBDelete;
import org.xmlbeam.annotation.XBDocURL;
import org.xmlbeam.annotation.XBRead;
import org.xmlbeam.annotation.XBUpdate;
import org.xmlbeam.annotation.XBValue;
import org.xmlbeam.annotation.XBWrite;
import org.xmlbeam.config.DefaultXMLFactoriesConfig;
import org.xmlbeam.config.XMLFactoriesConfig;
import org.xmlbeam.dom.DOMAccess;
import org.xmlbeam.evaluation.CanEvaluateOrProject;
import org.xmlbeam.evaluation.DefaultXPathEvaluator;
import org.xmlbeam.evaluation.DocumentResolver;
import org.xmlbeam.evaluation.InvocationContext;
import org.xmlbeam.evaluation.XPathEvaluator;
import org.xmlbeam.exceptions.XBException;
import org.xmlbeam.exceptions.XBIOException;
import org.xmlbeam.externalizer.Externalizer;
import org.xmlbeam.externalizer.ExternalizerAdapter;
import org.xmlbeam.intern.DOMChangeListener;
import org.xmlbeam.io.FileIO;
import org.xmlbeam.io.ProjectionIO;
import org.xmlbeam.io.StreamInput;
import org.xmlbeam.io.StreamOutput;
import org.xmlbeam.io.UrlIO;
import org.xmlbeam.types.DefaultTypeConverter;
import org.xmlbeam.types.StringRenderer;
import org.xmlbeam.types.TypeConverter;
import org.xmlbeam.types.XBAutoMap;
import org.xmlbeam.util.IOHelper;
import org.xmlbeam.util.intern.DOMHelper;
import org.xmlbeam.util.intern.DocScope;
import org.xmlbeam.util.intern.ReflectionHelper;
import org.xmlbeam.util.intern.Scope;

/**
 * 

* Overview
* The class XMLProjector is a tool to create, read or write so called "projections". Projections * are Java interfaces associated to XML documents. Projections may contain methods annotated with * XPath selectors. These XPath expressions define the subset of XML data which is "projected" to * Java values and objects. *

*

* Getters
* For getter methods (methods with a name-prefix "get", returning a value) the XPath-selected nodes * are converted to the method return type. This works with all java primitive types, Strings, and * lists or arrays containing primitives or Strings. *

*

* Setters
* Setter methods (method with a name starting with "set", having a parameter) can be defined to * modify the content of the associated XML document. Not all XPath capabilities define writable * projections, so the syntax is limited to selectors of elements and attributes. In contrast to * Java Beans a setter method may define a return value with the projection interface type. If this * return value is defined, the current projection instance is returned. This allows the definition * of projections according to the fluent interface pattern (aka Builder Pattern). *

*

* Sub Projections
* For the purpose of accessing structured data elements in the XML document you may define * "sub projections" which are projections associated to elements instead to documents. Sub * projections can be used as return type of getters and as parameters of setters. This works even * in arrays or lists. Because of the infamous Java type erasure you have to specify the component * type of the sub projection for a getter returning a list of sub projections. This type is defined * as second parameter "targetType" in the {@link XBRead} annotation. *

*

* Dynamic Projections
* XPath expressions are evaluated during runtime when the corresponding methods are called. Its * possible to use placeholder ("{0}, {1}, {2},... ) in the expression that will be substituted with * method parameters before the expression is evaluated. Therefore getters and setters may have * multiple parameters which will be applied via a {@link MessageFormat} to build up the final XPath * expression. The first parameter of a setter will be used for both, setting the document value and * replacing the placeholder "{0}". *

*

* Projection Mixins
* A mixin is defined as an object implementing a super interface of a projection. You may associate * a mixin with a projection type to add your own code to a projection. This way you can implement * validators, make a projection comparable or even share common business logic between multiple * projections. *

* * @author Sven Ewald */ @SuppressWarnings("serial") public class XBProjector implements Serializable, ProjectionFactory { private static final Externalizer NOOP_EXTERNALIZER = new ExternalizerAdapter(); private final ConfigBuilder configBuilder = new ConfigBuilder(); private Externalizer externalizer = NOOP_EXTERNALIZER; private final Set flags; /** * A variation of the builder pattern. All methods to configure the projector are hidden in this * builder class. */ public class ConfigBuilder implements ProjectionFactoryConfig { /** * Access the {@link XMLFactoriesConfig} as the given subtype to conveniently access * additional methods. * * @param clazz * @return casted XMLFactoriesConfig */ public T as(final Class clazz) { return clazz.cast(xMLFactoriesConfig); } /** * {@inheritDoc} */ @Override public TypeConverter getTypeConverter() { return XBProjector.this.typeConverter; } /** * Cast the type converter to the current type. * * @param clazz * @return Type converter casted down to clazz. */ public T getTypeConverterAs(final Class clazz) { return clazz.cast(getTypeConverter()); } /** * {@inheritDoc} */ @Override public ConfigBuilder setTypeConverter(final TypeConverter converter) { XBProjector.this.typeConverter = converter; return this; } /** * {@inheritDoc} */ @Override public ConfigBuilder setExternalizer(final Externalizer e10r) { XBProjector.this.externalizer = e10r == null ? NOOP_EXTERNALIZER : e10r; return this; } /** * {@inheritDoc} */ @Override public Externalizer getExternalizer() { return XBProjector.this.externalizer; } /** * @param clazz * @return Externalizer cast down to the type clazz */ public T getExternalizerAs(final Class clazz) { return clazz.cast(getExternalizer()); } /** * {@inheritDoc} */ @Override @Deprecated public TransformerFactory createTransformerFactory() { return XBProjector.this.xMLFactoriesConfig.createTransformerFactory(); } /** * {@inheritDoc} */ @Override @Deprecated public DocumentBuilderFactory createDocumentBuilderFactory() { return XBProjector.this.xMLFactoriesConfig.createDocumentBuilderFactory(); } /** * {@inheritDoc} */ @Override @Deprecated public XPathFactory createXPathFactory() { return XBProjector.this.xMLFactoriesConfig.createXPathFactory(); } /** * {@inheritDoc} */ @Override @Deprecated public Transformer createTransformer(final Document... document) { return XBProjector.this.xMLFactoriesConfig.createTransformer(document); } /** * {@inheritDoc} */ @Override @Deprecated public DocumentBuilder createDocumentBuilder() { return XBProjector.this.xMLFactoriesConfig.createDocumentBuilder(); } /** * {@inheritDoc} */ @Override @Deprecated public XPath createXPath(final Document... document) { return XBProjector.this.xMLFactoriesConfig.createXPath(document); } /** * @return StringRenderer used to convert objects into strings */ public StringRenderer getStringRenderer() { return XBProjector.this.stringRenderer; } /** * Cast the type StringRenderer to the current type. * * @param clazz * @return StringRenderer casted down to clazz. */ public T getStringRendererAs(final Class clazz) { return clazz.cast(getTypeConverter()); } /** * @param renderer * to be used to convert objects into strings * @return this for convenience */ public ConfigBuilder setStringRenderer(final StringRenderer renderer) { XBProjector.this.stringRenderer = renderer; return this; } @Override @Deprecated public Map getUserDefinedNamespaceMapping() { return xMLFactoriesConfig.getUserDefinedNamespaceMapping(); } } /** * A variation of the builder pattern. Mixin related methods are grouped behind this builder * class. */ class MixinBuilder implements MixinHolder { /** * {@inheritDoc} */ @Override public XBProjector addProjectionMixin(final Class

projectionInterface, final M mixinImplementation) { ensureIsValidProjectionInterface(projectionInterface); Map, Object> map = mixins.containsKey(projectionInterface) ? mixins.get(projectionInterface) : new HashMap, Object>(); for (Class type : ReflectionHelper.findAllCommonSuperInterfaces(projectionInterface, mixinImplementation.getClass())) { map.put(type, mixinImplementation); } mixins.put(projectionInterface, map); return XBProjector.this; } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public M getProjectionMixin(final Class

projectionInterface, final Class mixinInterface) { if (!mixins.containsKey(projectionInterface)) { return null; } return (M) mixins.get(projectionInterface).get(mixinInterface); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public M removeProjectionMixin(final Class

projectionInterface, final Class mixinInterface) { if (!mixins.containsKey(projectionInterface)) { return null; } return (M) mixins.get(projectionInterface).remove(mixinInterface); } } /** * A variation of the builder pattern. IO related methods are grouped behind this builder class. */ class IOBuilder implements ProjectionIO { /** * {@inheritDoc} */ @Override public FileIO file(final File file) { return new DefaultFileIO(XBProjector.this, file); } /** * {@inheritDoc} */ @Override public FileIO file(final String fileName) { return new DefaultFileIO(XBProjector.this, fileName); } /** * {@inheritDoc} */ @Override public UrlIO url(final String url) { return new UrlIO(XBProjector.this, url); } /** * {@inheritDoc} */ @Override public StreamInput stream(final InputStream is) { return new StreamInput(XBProjector.this, is); } /** * {@inheritDoc} */ @Override public StreamOutput stream(final OutputStream os) { return new StreamOutput(XBProjector.this, os); } /** * {@inheritDoc} */ @Override public T fromURLAnnotation(final Class projectionInterface, final Object... optionalParams) throws IOException { org.xmlbeam.annotation.XBDocURL doc = projectionInterface.getAnnotation(org.xmlbeam.annotation.XBDocURL.class); if (doc == null) { throw new IllegalArgumentException("Class " + projectionInterface.getCanonicalName() + " must have the " + XBDocURL.class.getName() + " annotation linking to the document source."); } UrlIO urlIO = url(MessageFormat.format(doc.value(), optionalParams)); urlIO.addRequestProperties(filterRequestParamsFromParams(doc.value(), optionalParams)); return urlIO.read(projectionInterface); } /** * @param projectionInterface * @param optionalParams * @return */ @SuppressWarnings("unchecked") Map filterRequestParamsFromParams(final String url, final Object... optionalParams) { Map requestParams = new HashMap(); Format[] formats = new MessageFormat(url).getFormatsByArgumentIndex(); for (int i = 0; i < optionalParams.length; ++i) { if (i >= formats.length) { if ((optionalParams[i] instanceof Map)) { requestParams.putAll((Map) optionalParams[i]); } continue; } if (formats[i] == null) { if ((optionalParams[i] instanceof Map)) { requestParams.putAll((Map) optionalParams[i]); } } } return requestParams; } /** * {@inheritDoc} */ @Override public String toURLAnnotationViaPOST(final Object projection, final Object... optionalParams) throws IOException, URISyntaxException { Class projectionInterface = checkProjectionInstance(projection).getProjectionInterface(); org.xmlbeam.annotation.XBDocURL doc = projectionInterface.getAnnotation(org.xmlbeam.annotation.XBDocURL.class); if (doc == null) { throw new IllegalArgumentException("Class " + projectionInterface.getCanonicalName() + " must have the " + XBDocURL.class.getName() + " annotation linking to the document source."); } UrlIO urlIO = url(MessageFormat.format(doc.value(), optionalParams)); urlIO.addRequestProperties(filterRequestParamsFromParams(doc.value(), optionalParams)); return urlIO.write(projection); } } /** * {@inheritDoc} */ @Override @Scope(DocScope.IO) public T projectEmptyDocument(final Class projectionInterface) { Document document = xMLFactoriesConfig.createDocumentBuilder().newDocument(); return projectDOMNode(document, projectionInterface); } /** * {@inheritDoc} */ @Override @Scope(DocScope.IO) public T projectEmptyElement(final String name, final Class projectionInterface) { Document document = xMLFactoriesConfig.createDocumentBuilder().newDocument(); Element element = document.createElement(name); return projectDOMNode(element, projectionInterface); } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") @Scope(DocScope.IO) public T projectDOMNode(final Node documentOrElement, final Class projectionInterface) { ensureIsValidProjectionInterface(projectionInterface); if (documentOrElement == null) { throw new IllegalArgumentException("Parameter node must not be null"); } final Map, Object> mixinsForProjection = mixins.containsKey(projectionInterface) ? Collections.unmodifiableMap(mixins.get(projectionInterface)) : Collections., Object> emptyMap(); final ProjectionInvocationHandler projectionInvocationHandler = new ProjectionInvocationHandler(XBProjector.this, documentOrElement, projectionInterface, mixinsForProjection, flags.contains(Flags.TO_STRING_RENDERS_XML), flags.contains(Flags.ABSENT_IS_EMPTY)); final Set> interfaces = new HashSet>(); interfaces.add(projectionInterface); interfaces.add(DOMAccess.class); interfaces.add(Serializable.class); if (flags.contains(Flags.SYNCHRONIZE_ON_DOCUMENTS)) { final Document document = DOMHelper.getOwnerDocumentFor(documentOrElement); InvocationHandler synchronizedInvocationHandler = new InvocationHandler() { @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { synchronized (document) { return projectionInvocationHandler.invoke(proxy, method, args); } } }; return ((T) Proxy.newProxyInstance(projectionInterface.getClassLoader(), interfaces.toArray(new Class[interfaces.size()]), synchronizedInvocationHandler)); } return ((T) Proxy.newProxyInstance(projectionInterface.getClassLoader(), interfaces.toArray(new Class[interfaces.size()]), projectionInvocationHandler)); } /** * {@inheritDoc} */ @Override @Scope(DocScope.IO) public T projectXMLString(final String xmlContent, final Class projectionInterface) { try { final ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlContent.getBytes("utf-8")); return new StreamInput(this, inputStream).read(projectionInterface); } catch (IOException e) { throw new XBIOException(e); } } /** * @param xmlContent * @return {@link DefaultXPathEvaluator} */ @Scope(DocScope.IO) public CanEvaluateOrProject onXMLString(final String xmlContent) { try { final ByteArrayInputStream inputStream = new ByteArrayInputStream(xmlContent.getBytes("utf-8")); return new CanEvaluateOrProject() { @Override public XPathEvaluator evalXPath(final String xpath) { return new DefaultXPathEvaluator(XBProjector.this, new DocumentResolver() { @Override public Document resolve(final Class... resourceAwareClass) { return IOHelper.loadDocument(XBProjector.this, inputStream); } }, xpath); } @Override public T createProjection(final Class projectionInterface) { return projectXMLString(xmlContent, projectionInterface); } @Override public XBAutoMap createMapOf(final Class valueType) { final Document document = IOHelper.loadDocument(XBProjector.this, inputStream); return createAutoMapForDocument(valueType, document); } }; } catch (IOException e) { throw new XBIOException(e); } } private final XMLFactoriesConfig xMLFactoriesConfig; private final Map, Map, Object>> mixins = new HashMap, Map, Object>>(); private TypeConverter typeConverter = new DefaultTypeConverter(Locale.getDefault(), TimeZone.getTimeZone("GMT")); private StringRenderer stringRenderer = (StringRenderer) typeConverter; private final List> domChangeListeners = new LinkedList>(); /** * Global projector configuration options. */ public enum Flags { /** * Enables thread safety by removing concurrent DOM access. Useful if the underlying DOM * implementation is not thread safe. */ SYNCHRONIZE_ON_DOCUMENTS, /** * Let the projections toString() method render the projection target as XML. Be careful if * your documents get large. toString() might be used frequently by the IDE your debugging * in. */ TO_STRING_RENDERS_XML, /** * Option to strip empty nodes from the result. */ OMIT_EMPTY_NODES, /** * If a node is not present, handle it like it is empty. */ ABSENT_IS_EMPTY } /** * Constructor. Use me to create a projector with defaults. * * @param optionalFlags */ public XBProjector(final Flags... optionalFlags) { this(new DefaultXMLFactoriesConfig(), optionalFlags); } private static > Set unfold(final T[] array) { if ((array == null) || (array.length == 0)) { return Collections.emptySet(); } EnumSet enumSet = EnumSet.of(array[0]); for (int i = 1; i < array.length; ++i) { enumSet.add(array[i]); } return enumSet; } /** * @param xMLFactoriesConfig * @param optionalFlags */ public XBProjector(final XMLFactoriesConfig xMLFactoriesConfig, final Flags... optionalFlags) { this.xMLFactoriesConfig = xMLFactoriesConfig; this.flags = unfold(optionalFlags); } /** * Shortcut for creating a {@link ConfigBuilder} object to change the projectors configuration. * * @return a new ConfigBuilder for this projector. */ public ConfigBuilder config() { return configBuilder; } /** * Shortcut for creating a {@link MixinHolder} object add or remove mixins to projections. * * @return a new MixinBuilder for this projector. */ public MixinHolder mixins() { return new MixinBuilder(); } /** * Ensures that the given object is a projection created by a projector. * * @param projection * @return */ private DOMAccess checkProjectionInstance(final Object projection) { if (java.lang.reflect.Proxy.isProxyClass(projection.getClass())) { InvocationHandler invocationHandler = java.lang.reflect.Proxy.getInvocationHandler(projection); if (invocationHandler instanceof ProjectionInvocationHandler) { if (projection instanceof DOMAccess) { return (DOMAccess) projection; } } } throw new IllegalArgumentException("Given object " + projection + " is not a projection."); } /** * @param projectionInterface */ private void ensureIsValidProjectionInterface(final Class projectionInterface) { if (projectionInterface == null) { throw new IllegalArgumentException("Parameter projectionInterface must not be null, but is.", new NullPointerException()); } if ((!projectionInterface.isInterface())) { throw new IllegalArgumentException("Parameter " + projectionInterface + " is not an interface"); } if (projectionInterface.isAnnotation()) { throw new IllegalArgumentException("Parameter " + projectionInterface + " is an annotation interface. Remove the @ and try again."); } for (Method method : projectionInterface.getMethods()) { final boolean isRead = (method.getAnnotation(XBRead.class) != null); final boolean isWrite = (method.getAnnotation(XBWrite.class) != null); final boolean isDelete = (method.getAnnotation(XBDelete.class) != null); final boolean isUpdate = (method.getAnnotation(XBUpdate.class) != null); final boolean isBind = (method.getAnnotation(XBAuto.class) != null); final boolean isExternal = (method.getAnnotation(XBDocURL.class) != null); final boolean isThrowsException = (method.getExceptionTypes().length > 0); if (countTrue(isRead, isWrite, isDelete, isUpdate, isBind) > 1) { throw new IllegalArgumentException("Method " + method + " has to many annotations. Decide for one of @" + XBRead.class.getSimpleName() + ", @" + XBWrite.class.getSimpleName() + ", @" + XBUpdate.class.getSimpleName() + ", or @" + XBDelete.class.getSimpleName() + ", or @" + XBAuto.class.getSimpleName()); } if (isExternal && (isWrite || isUpdate || isDelete)) { throw new IllegalArgumentException("Method " + method + " was declared as writing projection but has a @" + XBDocURL.class.getSimpleName() + " annotation. Defining external projections is only possible when reading because there is no DOM attached."); } if (isRead) { if (!ReflectionHelper.hasReturnType(method)) { throw new IllegalArgumentException("Method " + method + " has @" + XBRead.class.getSimpleName() + " annotation, but has no return type."); } if (ReflectionHelper.isRawType(method.getGenericReturnType())) { throw new IllegalArgumentException("Method " + method + " has @" + XBRead.class.getSimpleName() + " annotation, but has a raw return type."); } if (method.getExceptionTypes().length > 1) { throw new IllegalArgumentException("Method " + method + " has @" + XBRead.class.getSimpleName() + " annotation, but declares to throw multiple exceptions. Which one should I throw?"); } if (ReflectionHelper.isOptional(method.getReturnType()) && isThrowsException) { throw new IllegalArgumentException("Method " + method + " has an Optional<> return type, but declares to throw an exception. Exception will never be thrown because return value must not be null."); } } if (isWrite && isThrowsException) { throw new IllegalArgumentException("Method " + method + " declares to throw exception " + method.getExceptionTypes()[0].getSimpleName() + " but is not a reading projection method. When should this exception be thrown?"); } if (isWrite) { if (!ReflectionHelper.hasParameters(method)) { throw new IllegalArgumentException("Method " + method + " has @" + XBWrite.class.getSimpleName() + " annotaion, but has no paramerter"); } } if (isUpdate) { if (!ReflectionHelper.hasParameters(method)) { throw new IllegalArgumentException("Method " + method + " has @" + XBUpdate.class.getSimpleName() + " annotaion, but has no paramerter"); } } for (Class clazz : method.getParameterTypes()) { if (ReflectionHelper.isOptional(clazz)) { throw new IllegalArgumentException("Method " + method + " has java.util.Optional as a parameter type. You simply never should not do this."); } } int count = 0; for (Annotation[] paramAnnotations : method.getParameterAnnotations()) { for (Annotation a : paramAnnotations) { if (XBValue.class.equals(a.annotationType())) { if (!(isWrite || isUpdate)) { throw new IllegalArgumentException("Method " + method + " is not a writing projection method, but has an @" + XBValue.class.getSimpleName() + " annotaion."); } if (count > 0) { throw new IllegalArgumentException("Method " + method + " has multiple @" + XBValue.class.getSimpleName() + " annotaions."); } ++count; } } } } } /** * Count how many parameters are true. * * @return number of true values in parameter list. */ private static int countTrue(final boolean... b) { if (b == null) { return 0; } int count = 0; for (boolean bb : b) { if (bb) { ++count; } } return count; } /** * Access to the input/output features of this projector. * * @return A new IOBuilder providing methods to read or write projections. */ @Override @Scope(DocScope.IO) public ProjectionIO io() { return new IOBuilder(); } /** * @param projection * @return an XML string of the projection target. */ @SuppressWarnings("rawtypes") @Override public String asString(final Object projection) { // TODO: create interface for getNode, use this instead. if (projection instanceof AutoValue) { return DOMHelper.toXMLString(this, ((AutoValue) projection).getNode()); } if (projection instanceof AutoList) { return DOMHelper.toXMLString(this, ((AutoList) projection).getNode()); } if (projection instanceof AutoMap) { return DOMHelper.toXMLString(this, ((AutoMap) projection).getNode()); } if (!(projection instanceof DOMAccess)) { throw new IllegalArgumentException("Argument is not a projection."); } final DOMAccess domAccess = (DOMAccess) projection; return domAccess.asString(); } /** * read only access to flags. Use constructor to set. * * @return flags. */ public Set getFlags() { return Collections.unmodifiableSet(flags); } void addDOMChangeListener(final DOMChangeListener listener) { domChangeListeners.add(new WeakReference(listener)); } /** * */ void notifyDOMChangeListeners() { for (ListIterator> i = domChangeListeners.listIterator(); i.hasNext();) { DOMChangeListener listener = i.next().get(); if (listener == null) { i.remove(); continue; } listener.domChanged(); } } /** * Create an empty document and bind an XBAutoMap to it. * * @param valueType * component type of map * @return an empty Map view to the document */ public XBAutoMap autoMapEmptyDocument(final Class valueType) { Document document = xMLFactoriesConfig.createDocumentBuilder().newDocument(); return createAutoMapForDocument(valueType, document); } private XBAutoMap createAutoMapForDocument(final Class valueType, final Document document) { InvocationContext invocationContext = new InvocationContext(null, null, null, null, null, valueType, this); return new AutoMap(document, invocationContext, valueType); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy