
com.tangosol.config.xml.DefaultProcessingContext Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2020, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.config.xml;
import com.tangosol.config.ConfigurationException;
import com.tangosol.config.annotation.Injectable;
import com.tangosol.config.expression.Expression;
import com.tangosol.config.expression.ExpressionParser;
import com.tangosol.config.expression.LiteralExpression;
import com.tangosol.config.expression.Parameter;
import com.tangosol.config.expression.ParameterResolver;
import com.tangosol.config.expression.Value;
import com.tangosol.config.xml.DocumentProcessor.Dependencies;
import com.tangosol.run.xml.QualifiedName;
import com.tangosol.run.xml.SimpleAttribute;
import com.tangosol.run.xml.XmlAttribute;
import com.tangosol.run.xml.XmlElement;
import com.tangosol.run.xml.XmlHelper;
import com.tangosol.run.xml.XmlValue;
import com.tangosol.util.ClassHelper;
import com.tangosol.util.ExternalizableHelper;
import com.tangosol.util.ResourceRegistry;
import com.tangosol.util.UUID;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.ParseException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeSet;
/**
* The default implementation of a {@link ProcessingContext}.
*
* @author bo 2011.06.14
* @since Coherence 12.1.2
*/
public class DefaultProcessingContext
implements ProcessingContext, AutoCloseable
{
// ----- constructors ----------------------------------------------------
/**
* Constructs a root {@link ProcessingContext} using default {@link DocumentProcessor} {@link DocumentProcessor.Dependencies}.
*/
public DefaultProcessingContext()
{
this((Dependencies) null);
}
/**
* Constructs a root {@link ProcessingContext} with the specified {@link DocumentProcessor} {@link DocumentProcessor.Dependencies}.
*
* @param dependencies the {@link DocumentProcessor.Dependencies} for the {@link ProcessingContext}
*/
public DefaultProcessingContext(Dependencies dependencies)
{
m_dependencies = dependencies == null ? new DocumentProcessor.DefaultDependencies() : dependencies;
m_ctxParent = null;
m_xmlElement = null;
m_mapNamespaceURIsByPrefix = new LinkedHashMap();
m_mapNamespaceHandlersByURI = new LinkedHashMap();
m_mapCookiesByType = new HashMap, HashMap>();
m_mapPropertyPaths = new HashMap();
m_mapAttributeProcessorsByType = new HashMap, AttributeProcessor>>();
m_mapElementProcessorsByType = new HashMap, ElementProcessor>>();
m_setProcessedChildElements = new HashSet();
}
/**
* Constructs a root {@link ProcessingContext} for a given {@link XmlElement} using default DocumentProcessor
* dependencies.
*
* @param xmlElement the {@link XmlElement} for the {@link ProcessingContext}
*/
public DefaultProcessingContext(XmlElement xmlElement)
{
this(new DocumentProcessor.DefaultDependencies());
m_ctxParent = null;
m_xmlElement = xmlElement;
}
/**
* Constructs a sub-{@link ProcessingContext} of another {@link ProcessingContext}.
*
* @param ctxParent the parent {@link ProcessingContext} for this {@link ProcessingContext}
* @param xmlElement the {@link XmlElement} for the sub-{@link ProcessingContext}
*/
public DefaultProcessingContext(DefaultProcessingContext ctxParent, XmlElement xmlElement)
{
this(ctxParent.getDependencies());
m_ctxParent = ctxParent;
m_xmlElement = xmlElement;
}
/**
* Constructs a root {@link ProcessingContext} for a given {@link XmlElement}.
*
* @param dependencies the {@link DocumentProcessor.Dependencies} for the {@link ProcessingContext}
* @param xmlElement the {@link XmlElement} for the {@link ProcessingContext}
*/
public DefaultProcessingContext(Dependencies dependencies, XmlElement xmlElement)
{
this(dependencies);
m_ctxParent = null;
m_xmlElement = xmlElement;
}
// ----- ResourceResolver interface -------------------------------------
/**
* {@inheritDoc}
*/
@Override
public R getResource(Class clsResource)
{
R resource = getCookie(clsResource);
if (resource == null && getResourceRegistry() != null)
{
resource = getResourceRegistry().getResource(clsResource);
}
return resource;
}
/**
* {@inheritDoc}
*/
@Override
public R getResource(Class clsResource, String sResourceName)
{
R resource = getCookie(clsResource, sResourceName);
if (resource == null && getResourceRegistry() != null)
{
resource = getResourceRegistry().getResource(clsResource, sResourceName);
}
return resource;
}
// ----- ProcessingContext interface ------------------------------------
/**
* {@inheritDoc}
*/
@Override
public ResourceRegistry getResourceRegistry()
{
return m_dependencies.getResourceRegistry();
}
/**
* {@inheritDoc}
*/
@Override
public ParameterResolver getDefaultParameterResolver()
{
return m_dependencies.getDefaultParameterResolver();
}
/**
* {@inheritDoc}
*/
@Override
public ClassLoader getContextClassLoader()
{
return m_dependencies.getContextClassLoader();
}
/**
* {@inheritDoc}
*/
@Override
public void addCookie(Class clzCookie, String sName, T value)
{
HashMap mapCookiesByName = m_mapCookiesByType.get(clzCookie);
if (mapCookiesByName == null)
{
mapCookiesByName = new HashMap();
m_mapCookiesByType.put(clzCookie, mapCookiesByName);
}
mapCookiesByName.put(sName, value);
}
/**
* {@inheritDoc}
*/
@Override
public void addCookie(Class clzCookie, T cookie)
{
addCookie(clzCookie, clzCookie.getName(), cookie);
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public T getCookie(Class clzCookie, String sName)
{
HashMap mapCookiesByName = m_mapCookiesByType.get(clzCookie);
T cookie = mapCookiesByName == null ? null : (T) mapCookiesByName.get(sName);
return cookie == null ? (isRootContext() ? null : m_ctxParent.getCookie(clzCookie, sName)) : cookie;
}
/**
* {@inheritDoc}
*/
@Override
public T getCookie(Class clzCookie)
{
return clzCookie == null ? null : getCookie(clzCookie, clzCookie.getName());
}
/**
* {@inheritDoc}
*/
@Override
public void definePropertyPath(String sBeanPropertyName, String sXmlPath)
{
m_mapPropertyPaths.put(sBeanPropertyName, sXmlPath);
}
/**
* {@inheritDoc}
*/
@Override
public void registerProcessor(Class clzType, AttributeProcessor processor)
{
m_mapAttributeProcessorsByType.put(clzType, processor);
}
/**
* {@inheritDoc}
*/
@Override
public void registerProcessor(Class clzType, ElementProcessor processor)
{
m_mapElementProcessorsByType.put(clzType, processor);
}
/**
* {@inheritDoc}
*/
@Override
public void registerAttributeType(Class clzType)
{
registerProcessor(clzType, new SimpleAttributeProcessor(clzType));
}
/**
* {@inheritDoc}
*/
@Override
public void registerElementType(Class clzType)
{
registerProcessor(clzType, new SimpleElementProcessor(clzType));
}
/**
* {@inheritDoc}
*/
@Override
public ExpressionParser getExpressionParser()
{
return m_dependencies.getExpressionParser();
}
/**
* {@inheritDoc}
*/
public Object processDocument(XmlElement xmlElement)
throws ConfigurationException
{
return processElement(xmlElement);
}
/**
* {@inheritDoc}
*/
public Object processDocumentAt(URI uri)
throws ConfigurationException
{
return processDocument(XmlHelper.loadFileOrResource(uri.toString(), "cache configuration",
m_dependencies.getContextClassLoader()));
}
/**
* {@inheritDoc}
*/
public Object processDocumentAt(String sLocation)
throws ConfigurationException
{
return processDocument(XmlHelper.loadFileOrResource(sLocation, "cache configuration",
m_dependencies.getContextClassLoader()));
}
/**
* {@inheritDoc}
*/
public Object processDocument(String sXml)
throws ConfigurationException
{
return processDocument(XmlHelper.loadXml(sXml));
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Object processElement(XmlElement xmlElement)
throws ConfigurationException
{
// NOTE: Seven sequential tasks are required to "process" an XmlElement. These must
// be performed in a specific order as they are dependent on each other.
Object oResult = null;
// we need a list of XmlAttributes for the XmlElement as we may use these in multiple places.
ArrayList lstAttributes = new ArrayList(xmlElement.getAttributeMap().size());
for (String sAttributeName : ((Set) xmlElement.getAttributeMap().keySet()))
{
lstAttributes.add(new SimpleAttribute(xmlElement, sAttributeName, xmlElement.getAttribute(sAttributeName)));
}
// we need to know if a new namespace was loaded or if this is the root
// element to determine if the element requires preprocessing
boolean fElementRequiresPreprocessing = isRootContext() || xmlElement.getParent() == null;
// TASK 1: Start a new sub context for the element we're about to process.
DefaultProcessingContext context = new DefaultProcessingContext(this, xmlElement);
// TASK 2: Ensure that all of NamespaceHandler(s) declared in the element (using xmlns) are defined
// in the sub-context.
for (XmlAttribute attribute : lstAttributes)
{
QualifiedName qnAttribute = attribute.getQualifiedName();
// only process "xmlns" declarations
if (qnAttribute.getLocalName().equals("xmlns"))
{
String sURI = attribute.getXmlValue().getString();
try
{
// ensure that the declared Xml Namespaces are available in the context of this element.
context.ensureNamespaceHandler(qnAttribute.getPrefix(), new URI(sURI));
// a new namespace was loaded so we must perform pre-processing
fElementRequiresPreprocessing = true;
}
catch (URISyntaxException uriSyntaxException)
{
throw new ConfigurationException(String.format("Invalid URI '%s' specified for Xml Namespace '%s'",
sURI, qnAttribute.getPrefix()), "You must specify a valid URI for the Xml Namespace.",
uriSyntaxException);
}
}
}
// TASK 3: Pre-process the element using all visible NamespaceHandler
// DocumentPreprocessors until no more pre-precessing is required
if (fElementRequiresPreprocessing)
{
Iterable namespaceHandlers = context.getNamespaceHandlers();
boolean fRevisit;
do
{
fRevisit = false;
for (NamespaceHandler namespaceHandler : namespaceHandlers)
{
DocumentPreprocessor preprocessor = namespaceHandler.getDocumentPreprocessor();
if (preprocessor != null)
{
fRevisit = fRevisit || preprocessor.preprocess(context, xmlElement);
if (fRevisit)
{
break;
}
}
}
}
while (fRevisit);
}
// TASK 4: Find an appropriate ElementProcessor for the element we're about to process
QualifiedName qnElement = xmlElement.getQualifiedName();
NamespaceHandler nsElement = context.getNamespaceHandler(qnElement.getPrefix());
ElementProcessor> procElement = nsElement == null ? null : nsElement.getElementProcessor(xmlElement);
if (nsElement == null)
{
throw new ConfigurationException(String.format(
"A NamespaceHandler could not be located for the namespace [%s] in the element [%s]",
qnElement.getPrefix(),
xmlElement), "A NamespaceHandler implementation for the namespace must be defined.");
}
else if (procElement == null)
{
throw new ConfigurationException(String.format("An ElementProcessor could not be located for the element [%s]",
qnElement), "The specified element is unknown to the NamespaceHandler implementation. " + "Perhaps the xml element is foreign to the Xml Namespace?");
}
else
{
// TASK 5: Process all of the xml attributes declared in the element.
for (XmlAttribute attribute : lstAttributes)
{
QualifiedName qnAttribute = attribute.getQualifiedName();
NamespaceHandler nsAttribute;
// locate the AttributeProcessor for the XmlAttribute using the appropriate NamespaceHandler
if (qnAttribute.hasPrefix())
{
// when an attribute is defined in a specific namespace, use the namespace of the attribute
nsAttribute = getNamespaceHandler(qnAttribute.getPrefix());
}
else
{
// when an attribute is not defined in a specific namespace, use the namespace of the element.
nsAttribute = nsElement;
}
AttributeProcessor> procAttribute = nsAttribute == null
? null : nsAttribute.getAttributeProcessor(attribute);
if (nsAttribute == null || procAttribute == null)
{
// SKIP: when we don't have an NamespaceHandler or AttributeProcessor for the attribute
// we simply ignore the request because the dependency injection framework may/can try to access
// the content directly.
}
else
{
procAttribute.process(context, attribute);
}
}
// TASK 6: Use the located ElementProcessor to process the element in it's context.
ConditionalElementProcessor> procConditional = procElement instanceof ConditionalElementProcessor
? ((ConditionalElementProcessor) procElement) : null;
if (procConditional == null || (procConditional != null && procConditional.accepts(context, xmlElement)))
{
oResult = procElement.process(context, xmlElement);
}
}
// TASK 7: Terminate the current context as it's now out of scope.
context.terminate();
// remember that we've processed this element so that if a call to
// processRemainingElements is made, we can skip this one
m_setProcessedChildElements.add(xmlElement);
return oResult;
}
/**
* {@inheritDoc}
*/
public Object processElement(String sXml)
throws ConfigurationException
{
return processElement(XmlHelper.loadXml(sXml));
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T processOnlyElementOf(XmlElement xmlElement)
throws ConfigurationException
{
// the xmlElement must only have a single child
if (xmlElement.getElementList().size() == 1)
{
// process the child element
return (T) processElement((XmlElement) xmlElement.getElementList().get(0));
}
else
{
// expected only a single element in custom-provider
throw new ConfigurationException(String.format("Only a single element is permitted in the %s element.",
xmlElement), String.format("Please consult the documentation regarding use of the '%s' namespace",
new QualifiedName(xmlElement).getPrefix()));
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Map processElementsOf(XmlElement xmlElement)
throws ConfigurationException
{
// process all of the children of the xmlElement
LinkedHashMap mapResult = new LinkedHashMap();
for (Iterator children = xmlElement.getElementList().iterator(); children.hasNext(); )
{
XmlElement xmlChild = children.next();
String sId = xmlChild.getAttributeMap().containsKey("id")
? xmlChild.getAttribute("id").getString() : new UUID().toString();
if (sId.trim().length() == 0)
{
sId = new UUID().toString();
}
mapResult.put(sId, processElement(xmlChild));
}
return mapResult;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public Map processForeignElementsOf(XmlElement xmlElement)
throws ConfigurationException
{
String sPrefix = xmlElement.getQualifiedName().getPrefix();
// process all of the children of the xmlElement
LinkedHashMap mapResult = new LinkedHashMap();
for (Iterator children = xmlElement.getElementList().iterator(); children.hasNext(); )
{
XmlElement xmlChild = children.next();
if (!xmlChild.getQualifiedName().getPrefix().equals(sPrefix))
{
String sId = xmlChild.getAttributeMap().containsKey("id")
? xmlChild.getAttribute("id").getString() : new UUID().toString();
if (sId.trim().length() == 0)
{
sId = new UUID().toString();
}
mapResult.put(sId, processElement(xmlChild));
}
}
return mapResult;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public Map processRemainingElementsOf(XmlElement xmlElement)
throws ConfigurationException
{
// process all of the children of the xmlElement that aren't in the set of elements we've already processed
LinkedHashMap mapResult = new LinkedHashMap();
for (Iterator children = xmlElement.getElementList().iterator(); children.hasNext(); )
{
XmlElement xmlChild = children.next();
if (!m_setProcessedChildElements.contains(xmlChild))
{
String sId = xmlChild.getAttributeMap().containsKey("id")
? xmlChild.getAttribute("id").getString() : new UUID().toString();
if (sId.trim().length() == 0)
{
sId = new UUID().toString();
}
mapResult.put(sId, processElement(xmlChild));
}
}
return mapResult;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public T processRemainingElementOf(XmlElement xmlElement)
throws ConfigurationException
{
Map mapResults = processRemainingElementsOf(xmlElement);
int cSize = mapResults.size();
if (cSize == 1)
{
return (T) mapResults.values().iterator().next();
}
else
{
throw new ConfigurationException(String.format(
"Expected a single remaining element to process in %s after processing the elements %s but there were %d elements remaining",
xmlElement, xmlElement.getQualifiedName(), m_setProcessedChildElements.size()), String.format(
"The ElementProcessor implementation for %s makes an incorrect assumption about the number of remaining elements.",
xmlElement.getQualifiedName()));
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isPropertyDefined(String sPropertyName, XmlElement xmlParent)
throws ConfigurationException
{
if (sPropertyName == null || xmlParent == null)
{
return false;
}
// assume that a property in "this context" (ie: .) is always found
boolean fPropertyFound = sPropertyName.equals(".");
// STEP 1: Attempt to find the property value using an XmlAttribute defined by the XmlElement
if (!fPropertyFound)
{
fPropertyFound = getPropertyAttribute(xmlParent, sPropertyName) != null;
}
// STEP 2: Attempt to find the property value using content defined by the XmlElement
if (!fPropertyFound)
{
fPropertyFound = getPropertyElement(xmlParent, sPropertyName) != null;
}
return fPropertyFound;
}
/**
* {@inheritDoc}
*/
@Override
public B inject(B bean, XmlElement xmlElement)
throws ConfigurationException
{
for (Method method : bean.getClass().getMethods())
{
Type[] aParameterTypes = method.getGenericParameterTypes();
// we can only inject into methods annotated with @Injectable
Injectable annInjectable = method.getAnnotation(Injectable.class);
if (annInjectable != null)
{
if (aParameterTypes.length == 0 && !method.getReturnType().equals(Void.TYPE))
{
// perform getter injection (ie: inject into the value returned by the getter)
Object oInjectable = null;
try
{
// determine the injectable from the getter
oInjectable = method.invoke(bean);
// we can only inject into an object that's not a bean
if (oInjectable != null && oInjectable != bean)
{
// decide how to locate the XmlElement containing the content to use for injection
String sPropertyName = annInjectable.value();
XmlElement xmlProperty;
if (sPropertyName.isEmpty())
{
// when no name is specified we (automatically)
// determine the property name based on the method name
sPropertyName = getPropertyName(method);
}
if (sPropertyName.equals("."))
{
// when a "." we use "this" context for injection content
xmlProperty = xmlElement;
}
else
{
// find the element based on the name of the property
xmlProperty = getPropertyElement(xmlElement, sPropertyName);
}
if (xmlProperty != null)
{
inject(oInjectable, xmlProperty);
}
}
}
catch (ConfigurationException e)
{
throw e;
}
catch (Exception e)
{
throw new ConfigurationException(String.format(
"Could not inject a value into the instance '%s' using reflection "
+ " produced by the annotated method '%s' of '%s'", oInjectable, method,
bean.getClass().getName()), "Please resolve the causing exception.", e);
}
}
else if (aParameterTypes.length == 1)
{
// perform setter injection (ie: call a setter with a value)
// use the method name (or @Injectable) to determine the property name
String sPropertyName = getPropertyName(method);
// use the setter parameter type to determine the property type
// the type of the property
Type typeProperty = aParameterTypes[0];
// the value of the property
Value value = getPropertyValue(sPropertyName, typeProperty, xmlElement, false);
if (value != null)
{
// use the defined value to inject
Object oPropertyValue = value.get();
{
try
{
method.invoke(bean, oPropertyValue);
}
catch (Exception e)
{
throw new ConfigurationException(String.format(
"Could not inject the property '%s' using reflection "
+ "with the annotated method '%s' of '%s'", sPropertyName, method,
bean.getClass().getName()), "Please resolve the causing exception.", e);
}
}
}
}
}
}
return bean;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public T getMandatoryProperty(String sPath, Type typeProperty, XmlElement xmlParent)
throws ConfigurationException
{
Value value = getPropertyValue(sPath, typeProperty, xmlParent, true);
if (value == null)
{
// when we can't find a value as an attribute or element we must throw an exception.
throw new ConfigurationException(String.format(
"The expected property [%s] is not defined in element [%s].", sPath, xmlParent), String.format(
"Please consult the documentation for the use of the %s namespace",
m_xmlElement.getQualifiedName().getPrefix()));
}
else
{
return (T) value.get();
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public T getOptionalProperty(String sPropertyName, Type typeProperty, T defaultValue, XmlElement xmlElement)
throws ConfigurationException
{
Value value = getPropertyValue(sPropertyName, typeProperty, xmlElement, true);
if (value == null)
{
return defaultValue;
}
else
{
return (T) value.get();
}
}
/**
* {@inheritDoc}
*/
@Override
public NamespaceHandler ensureNamespaceHandler(String sPrefix, NamespaceHandler handler)
throws ConfigurationException
{
URI uri = m_mapNamespaceURIsByPrefix.get(sPrefix);
if (uri == null)
{
try
{
uri = new URI("class://" + handler.getClass().getName());
}
catch (URISyntaxException e)
{
throw new ConfigurationException(String.format(
"Failed to create a valid URI for the specified namespace class [%s] with prefix [%s]",
handler.getClass().getName(), sPrefix), "The implemented URI encoding is invalid", e);
}
m_mapNamespaceURIsByPrefix.put(sPrefix, uri);
m_mapNamespaceHandlersByURI.put(uri, handler);
// call-back the NamespaceHandler
handler.onStartNamespace(this, m_xmlElement, sPrefix, uri);
return handler;
}
else
{
return m_mapNamespaceHandlersByURI.get(uri);
}
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public NamespaceHandler ensureNamespaceHandler(String sPrefix, URI uri)
throws ConfigurationException
{
// determine if the NamespaceHandler for the specified URI is already defined
NamespaceHandler namespaceHandler = getNamespaceHandler(uri);
if ((namespaceHandler == null) || ((namespaceHandler != null) && !getNamespaceURI(sPrefix).equals(uri)))
{
String sScheme = uri.getScheme();
// ensure that we don't already have a NamespaceHandler with the specified prefix in the context
if (m_mapNamespaceURIsByPrefix.containsKey(sPrefix))
{
throw new ConfigurationException(String.format(
"Duplicate definition for the namespace prefix [%s] and URI [%s] encountered in the element [%s]",
sPrefix, uri, m_xmlElement), "Duplicate definitions of namespaces is not permitted.");
}
else if (sScheme.equals("class"))
{
String sClassName = (uri.getHost() == null) ? uri.getSchemeSpecificPart() : uri.getHost();
try
{
Class> clzNamespaceHandler = ExternalizableHelper.loadClass(sClassName,
m_dependencies.getContextClassLoader(), null);
// ensure that the class is a NamespaceHandler
if (NamespaceHandler.class.isAssignableFrom(clzNamespaceHandler))
{
try
{
// find the no args constructor for the NamespaceHandler
Constructor constructor =
(Constructor) clzNamespaceHandler.getConstructor();
// instantiate the NamespaceHandler
namespaceHandler = constructor.newInstance();
// register the NamespaceHandler with this context
m_mapNamespaceHandlersByURI.put(uri, namespaceHandler);
// register the prefix for the NamespaceHandler
m_mapNamespaceURIsByPrefix.put(sPrefix, uri);
// call-back the NamespaceHandler
namespaceHandler.onStartNamespace(this, m_xmlElement, sPrefix, uri);
return namespaceHandler;
}
catch (Exception exception)
{
throw new ConfigurationException(String.format("Can't instantiate the NamespaceHandler [%s]\n",
sClassName), "Please ensure that the specified class is public and has a no-args constructor", exception);
}
}
else
{
throw new ConfigurationException(String
.format("The declared class [%s] does not implement the %s interface", sClassName, NamespaceHandler.class
.getName()), "To use a class as a NamespaceHandler it must implement the appropriate interface.");
}
}
catch (ClassNotFoundException e)
{
throw new ConfigurationException(String.format(
"Can't instantiate the NamespaceHandler [%s] as the class is not found\n",
sClassName), "Please ensure that the specified class is an instance of NamespaceHandler interface", e);
}
}
else if (sScheme.equalsIgnoreCase("http") || sScheme.equalsIgnoreCase("https"))
{
// for http/https-based schemes, we assume there is a NamespaceHandler already registered
return getNamespaceHandler(sPrefix);
}
else
{
throw new ConfigurationException(String.format(
"Can't instantiate a suitable NamespaceHandler as the URI [%s] scheme is unknown.\n",
uri), "Please ensure that the specified URI refers to a class that implements the NamespaceHandler interface");
}
}
else
{
return namespaceHandler;
}
}
/**
* {@inheritDoc}
*/
@Override
public NamespaceHandler getNamespaceHandler(String sPrefix)
{
URI uri = getNamespaceURI(sPrefix);
return uri == null ? null : getNamespaceHandler(uri);
}
/**
* {@inheritDoc}
*/
@Override
public NamespaceHandler getNamespaceHandler(URI uri)
{
NamespaceHandler namespaceHandler = m_mapNamespaceHandlersByURI.get(uri);
return namespaceHandler == null
? (isRootContext() ? null : m_ctxParent.getNamespaceHandler(uri)) : namespaceHandler;
}
/**
* {@inheritDoc}
*/
@Override
public URI getNamespaceURI(String sPrefix)
{
URI uri = m_mapNamespaceURIsByPrefix.get(sPrefix);
return uri == null ? (isRootContext() ? null : m_ctxParent.getNamespaceURI(sPrefix)) : uri;
}
/**
* {@inheritDoc}
*/
@Override
public Iterable getNamespaceHandlers()
{
LinkedHashMap mapNamespaceHandlersByURI = new LinkedHashMap();
DefaultProcessingContext ctx = this;
while (ctx != null)
{
for (Entry e : ctx.m_mapNamespaceHandlersByURI.entrySet())
{
if (!mapNamespaceHandlersByURI.containsKey(e.getKey()))
{
mapNamespaceHandlersByURI.put(e.getKey(), e.getValue());
}
}
ctx = ctx.m_ctxParent;
}
return mapNamespaceHandlersByURI.values();
}
// ----- DefaultProcessingContext methods -------------------------------
/**
* Obtains the {@link DocumentProcessor} {@link DocumentProcessor.Dependencies} for the {@link ProcessingContext}.
*
* @return the {@link DocumentProcessor.Dependencies}
*/
public Dependencies getDependencies()
{
return m_dependencies;
}
/**
* Attempts to resolve the named property of the specified type in the current context and
* if required will parse the specified {@link XmlElement} in order to do so.
*
* @param sPropertyName the name or xml path to the property
* @param typeProperty the required type of the property value
* @param xmlParent the parent element in which the property may be found
* @param fOnlyUsePropertyName when true
the specified property name must be
* used resolve the property value. when false
* attempts may be made to resolve the property just the type
* name if the specified property name doesn't resolve a property
*
* @return The {@link Value} representing the property or null
if the property
* could not be located
* @throws ConfigurationException if the property was but could not be processed or is
* of the incorrect type
*/
public Value getPropertyValue(String sPropertyName, Type typeProperty, XmlElement xmlParent,
boolean fOnlyUsePropertyName)
throws ConfigurationException
{
if (sPropertyName == null)
{
throw new NullPointerException("Property Name can't be null");
}
if (typeProperty == null)
{
throw new NullPointerException("Property Type can't be null");
}
if (xmlParent == null)
{
throw new NullPointerException("XmlElement in which to locate a property can't be null");
}
// assume we haven't resolved the property value
Object oValue = null;
boolean fValueResolved = false;
// determine concrete class for the property
Class> clzProperty = ClassHelper.getClass(typeProperty);
// STEP 1: Attempt to resolve the property value using an XmlAttribute defined by the XmlElement
if (!fValueResolved && !sPropertyName.equals("."))
{
XmlAttribute xmlValue = getPropertyAttribute(xmlParent, sPropertyName);
if (xmlValue != null)
{
// no matter what happens now we assume we can and have resolved the property value
fValueResolved = true;
// determine the AttributeProcessor to process the XmlAttribute content into the property value
NamespaceHandler nsAttribute = getNamespaceHandler(xmlValue.getQualifiedName().getPrefix());
AttributeProcessor> procAttribute = nsAttribute == null
? null : nsAttribute.getAttributeProcessor(xmlValue);
// when there is no AttributeProcessor, attempt to locate a type-based one
if (procAttribute == null)
{
procAttribute = getAttributeProcessor(clzProperty);
}
if (procAttribute == null)
{
// when we can't find a suitable AttributeProcessor, assume we can use the attribute as a String
oValue = xmlValue.getXmlValue().getString();
}
else
{
oValue = procAttribute.process(this, xmlValue);
}
}
}
// STEP 2: Attempt to resolve the property value using content defined by the XmlElement
if (!fValueResolved)
{
XmlElement xmlValue = sPropertyName.equals(".") ? xmlParent : getPropertyElement(xmlParent, sPropertyName);
if (xmlValue != null)
{
// remember that we've processed this element (when it's not the parent!)
if (xmlValue != xmlParent)
{
m_setProcessedChildElements.add(xmlValue);
}
// determine the ElementProcessor to process the XmlElement content into the property value
NamespaceHandler nsElement = getNamespaceHandler(xmlValue.getQualifiedName().getPrefix());
ElementProcessor> procElement = nsElement == null ? null : nsElement.getElementProcessor(xmlValue);
// when there is no ElementProcessor, attempt to locate a type-based one
if (procElement == null)
{
procElement = getElementProcessor(clzProperty);
}
// when we can't find a suitable ElementProcessor, try to resolve the value another way
// (perhaps it's a collection, an array or just plain xml)
if (procElement == null)
{
// determine if we are dealing with a collection (or array) of some type?
Class> clzComponent = ClassHelper.getComponentType(typeProperty);
// the style of processing is somewhat dependent on the number of children in the element
int cChildren = xmlValue.getElementList().size();
if (clzComponent == null)
{
if (cChildren == 0)
{
// when there is no processor for the element and the element value is simply an XmlValue
// assume we can use the value as a String (if it's not empty)
if (!XmlHelper.isEmpty(xmlValue))
{
oValue = xmlValue.getString();
// we have now resolved the property value
fValueResolved = true;
}
}
else if (cChildren == 1)
{
// when there is no processor for the property but the property value contains a single
// element, process the element itself
oValue = processOnlyElementOf(xmlValue);
// we have now resolved the property value
fValueResolved = true;
}
else
{
// when there is no processor and there's a collection, we assume the required value is
// just the element (xml).
oValue = xmlValue;
// we have now resolved the property value
fValueResolved = true;
}
}
else
{
// create a collection from the element based on the children
// we have now resolved the property value
fValueResolved = true;
// instantiate the collection for the property (or something that is assignment compatible)
boolean fArray = clzProperty.isArray();
if (fArray)
{
oValue = Array.newInstance(clzComponent, cChildren);
}
else if (clzProperty.isInterface())
{
if (clzProperty.equals(List.class))
{
oValue = new ArrayList(cChildren);
}
else if (clzProperty.equals(Set.class))
{
oValue = new LinkedHashSet(cChildren);
}
else if (clzProperty.equals(Queue.class))
{
oValue = new ArrayDeque(cChildren);
}
else if (clzProperty.equals(SortedSet.class))
{
oValue = new TreeSet();
}
else
{
throw new ConfigurationException(String.format(
"Unsupported collection type [%s] encountered with the property [%s] in [%s]",
typeProperty, sPropertyName,
xmlValue), "The specified interface type is not supported. Please use a more specific type.");
}
}
else
{
try
{
oValue = clzProperty.newInstance();
}
catch (Exception e)
{
throw new ConfigurationException(String.format(
"Failed to instantiate the required type [%s] for the property [%s] in [%s]",
typeProperty, sPropertyName,
xmlValue), "Ensure that the specified type has a public no-args constructor", e);
}
}
// process each of the children, adding them to the collection and ensuring they
// are of the correct type
int idx = 0;
for (Iterator children = xmlValue.getElementList().iterator(); children.hasNext(); )
{
XmlElement xmlChild = children.next();
Object oChild = processElement(xmlChild);
// ensure the child value is compatible with the component type
if (clzComponent.isInstance(oChild))
{
if (fArray)
{
((Object[]) oValue)[idx] = oChild;
}
else
{
((Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy