/*
* ============================================================================
* GNU Lesser General Public License
* ============================================================================
*
* Beanlet - JSE Application Container.
* Copyright (C) 2006 Leon van Zantvoort
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Leon van Zantvoort
* 243 Acalanes Drive #11
* Sunnyvale, CA 94086
* USA
*
* zantvoort@users.sourceforge.net
* http://beanlet.org
*/
package org.beanlet.impl;
import static org.beanlet.common.BeanletConstants.*;
import java.security.PrivilegedAction;
import java.util.concurrent.atomic.AtomicInteger;
import java.lang.annotation.Annotation;
import java.lang.annotation.Target;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.beanlet.BeanletDefinitionException;
import org.beanlet.BeanletValidationException;
import org.beanlet.annotation.AbstractAnnotationDomain;
import org.beanlet.annotation.AnnotationDomain;
import org.beanlet.annotation.ElementAnnotation;
import org.beanlet.annotation.PackageElement;
import org.beanlet.plugin.ClassResolver;
import org.beanlet.plugin.ElementAnnotationContext;
import org.beanlet.plugin.ElementAnnotationFactory;
import org.beanlet.plugin.NestedBeanletFactory;
import org.beanlet.plugin.XMLElementAnnotation;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* First, an intermediate {@code AnnotationDomain} is created from the
* annotations declared by the beanlet type and underlying package.
* This includes annotatations defined by XML and Java code. Next, classes are
* extracted from this {@code AnnotationDomain} by the {@code ClassResolver}.
* Finally, annotations are read from XML and these classes
* (and underlying packages). These annotations are added to the final
* {@code AnnotationDomain}.
*
* @author Leon van Zantvoort
*/
public final class XMLAnnotationDomain<T> extends AbstractAnnotationDomain {
// PENDING: This class requires some clean up, I know!
private static final ConcurrentMap<String, AtomicInteger> anonymousCounterMap;
private static final XPath xpath;
private static final XPathExpression BEANLET_EXPRESSION;
private static final XPathExpression GLOBAL_PACKAGE_ANNOTATIONS_EXPRESSION;
private static final XPathExpression GLOBAL_ANNOTATIONS_EXPRESSION;
private static final XPathExpression MERGE_EXPRESSION;
private static final XPathExpression ALL_EXPRESSION;
static {
// Records in this map are never cleared!
anonymousCounterMap = new ConcurrentHashMap<String, AtomicInteger>();
xpath = XPathFactory.newInstance().newXPath();
xpath.setNamespaceContext(BEANLET_NAMESPACE_CONTEXT);
try {
BEANLET_EXPRESSION = xpath.compile("/:beanlets/:beanlet");
GLOBAL_PACKAGE_ANNOTATIONS_EXPRESSION = xpath.compile(
"/:beanlets/:package-annotations[not(@package)]");
GLOBAL_ANNOTATIONS_EXPRESSION = xpath.compile(
"/:beanlets/:annotations[not(@type)]");
MERGE_EXPRESSION = xpath.compile("@merge");
ALL_EXPRESSION = xpath.compile("*");
} catch (XPathExpressionException e) {
throw new AssertionError(e);
}
}
static String getAnonymousBeanletName(String prefix) {
if (prefix == null) {
prefix = Object.class.getName();
}
AtomicInteger counter = anonymousCounterMap.get(prefix);
if (counter == null) {
counter = new AtomicInteger();
anonymousCounterMap.putIfAbsent(prefix, counter);
counter = anonymousCounterMap.get(prefix);
}
assert counter != null;
return prefix + "$" + counter.getAndIncrement();
}
/**
* Returns a beanlet name for a top level beanlet.
*/
private static String getBeanletName(Node beanletNode,
XMLAnnotationDomain parentDomain) {
NamedNodeMap attributes = beanletNode.getAttributes();
Node nameNode = attributes.getNamedItem("name");
String beanletName = nameNode == null ? null : nameNode.getNodeValue();
if (beanletName == null) {
Node typeNode = attributes.getNamedItem("type");
String className = typeNode == null ? null : typeNode.getNodeValue();
if (className == null) {
if (parentDomain != null) {
className = parentDomain.getBeanletType().getName();
} else {
className = Object.class.getName();
}
}
if (beanletName == null) {
beanletName = getAnonymousBeanletName(className);
}
} else {
if (beanletName.contains("$")) {
throw new BeanletDefinitionException(beanletName,
"$ is a reserved character.");
}
}
return beanletName;
}
/**
* Returns an unmodifiable list of all beanlet annotation domains expressed
* by the specified document.
*/
public static List<XMLAnnotationDomain<?>> list(List<Document> documents,
ClassLoader loader, ElementAnnotationFactory annotationFactory,
ClassResolver resolver) throws BeanletDefinitionException {
try {
List<NestedBeanletFactoryImpl> nestedFactories =
new ArrayList<NestedBeanletFactoryImpl>();
Queue<Node> derived =
new LinkedList<Node>();
Map<String, XMLAnnotationDomain<?>> map =
new HashMap<String, XMLAnnotationDomain<?>>();
Set<String> names = new HashSet<String>();
for (Document document : documents) {
NodeList nodes = (NodeList) BEANLET_EXPRESSION.evaluate(document,
XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
Node beanletNode = nodes.item(i);
NamedNodeMap attributes = beanletNode.getAttributes();
String beanletName = attributes.getNamedItem("name") == null
? null : attributes.getNamedItem("name").getNodeValue();
Node parentNode = attributes.getNamedItem("parent");
String parent = parentNode == null ? null : parentNode.getNodeValue();
if (parent != null) {
if (beanletName != null && beanletName.equals(parent)) {
throw new BeanletDefinitionException(beanletName, "Cyclic inheritance.");
}
derived.offer(beanletNode);
continue;
} else {
if (beanletName == null) {
beanletName = getBeanletName(beanletNode, null);
}
}
Map<String, XMLAnnotationDomain<?>> parentDomains =
new HashMap<String, XMLAnnotationDomain<?>>(map);
NestedBeanletFactoryImpl nestedFactory =
new NestedBeanletFactoryImpl(beanletName, loader,
annotationFactory, resolver, parentDomains);
XMLAnnotationDomain<?> domain = createNestedDomain(beanletName,
beanletNode, loader, annotationFactory, resolver,
null, nestedFactory);
if (map.put(beanletName, domain) != null) {
throw new BeanletDefinitionException(beanletName,
"Duplicate definition of beanlet.");
}
parentDomains.put(beanletName, domain);
nestedFactories.add(nestedFactory);
}
}
String beanletName = null;
Node beanletNode = null;
while ((beanletNode = derived.poll()) != null) {
NamedNodeMap attributes = beanletNode.getAttributes();
String parent = attributes.getNamedItem("parent").getNodeValue();
XMLAnnotationDomain<?> parentDomain =
(XMLAnnotationDomain<?>) map.get(parent);
if (parentDomain == null) {
if (!names.contains(parent)) {
throw new BeanletDefinitionException(
getBeanletName(beanletNode, null),
"Parent does not exist: '" + parent + "'.");
} else {
derived.add(beanletNode);
}
continue;
}
beanletName = getBeanletName(beanletNode, parentDomain);
Map<String, XMLAnnotationDomain<?>> parentDomains =
new HashMap<String, XMLAnnotationDomain<?>>(map);
NestedBeanletFactoryImpl nestedFactory =
new NestedBeanletFactoryImpl(beanletName, loader,
annotationFactory, resolver, parentDomains);
XMLAnnotationDomain<?> domain = createNestedDomain(beanletName,
beanletNode, loader, annotationFactory, resolver,
parentDomain, nestedFactory);
map.put(beanletName, domain);
parentDomains.put(beanletName, domain);
nestedFactories.add(nestedFactory);
}
List<XMLAnnotationDomain<?>> list =
new ArrayList<XMLAnnotationDomain<?>>(map.values());
for (NestedBeanletFactoryImpl nestedFactory : nestedFactories) {
list.addAll(nestedFactory.get());
}
return Collections.unmodifiableList(list);
} catch (XPathExpressionException e) {
throw new AssertionError(e);
}
}
static <T> XMLAnnotationDomain<? extends T> createNestedDomain(
String beanletName, Node beanletNode, ClassLoader loader,
ElementAnnotationFactory annotationFactory, ClassResolver resolver,
XMLAnnotationDomain<T> parentDomain,
NestedBeanletFactory nestedFactory) throws
BeanletDefinitionException {
NamedNodeMap attributes = beanletNode.getAttributes();
Node typeNode = attributes.getNamedItem("type");
Node descriptionNode = attributes.getNamedItem("description");
Node factoryNode = attributes.getNamedItem("factory");
Node factoryMethodNode = attributes.getNamedItem("factory-method");
Node abstractNode = attributes.getNamedItem("abstract");
Node parentNode = attributes.getNamedItem("parent");
String className = typeNode == null ? null : typeNode.getNodeValue();
String description = descriptionNode == null ? null : descriptionNode.getNodeValue();
String factory = factoryNode == null ? null : factoryNode.getNodeValue();
String factoryMethod = factoryMethodNode == null ? null : factoryMethodNode.getNodeValue();
boolean abstr = abstractNode == null ? false : Boolean.valueOf(abstractNode.getNodeValue());
String parent = parentNode == null ? null : parentNode.getNodeValue();
final Class<? extends T> beanletType;
if (className == null) {
if (parentDomain != null) {
beanletType = parentDomain.getBeanletType();
} else {
@SuppressWarnings("unchecked")
Class<? extends T> tmp = (Class<? extends T>) Object.class;
beanletType = tmp;
}
} else {
try {
@SuppressWarnings("unchecked")
Class<? extends T> tmp = (Class<? extends T>)
loader.loadClass(className);
beanletType = tmp;
} catch (ClassNotFoundException e) {
throw new BeanletDefinitionException(beanletName,
"Type not found: " + className + ".");
}
}
@SuppressWarnings("unchecked")
XMLAnnotationDomain<? extends T> domain =
(XMLAnnotationDomain<? extends T>)
new XMLAnnotationDomain(
beanletNode, beanletName, beanletType,
description, abstr, parent, factory, factoryMethod,
loader, annotationFactory, resolver, parentDomain,
nestedFactory);
if (parentDomain != null) {
domain = domain.merge(parentDomain);
}
return domain;
}
private final String beanletName;
private final Class<T> beanletType;
private final String description;
private final boolean abstr;
private final String parent;
private final String factory;
private final String factoryMethod;
private final ClassLoader loader;
private final NestedBeanletFactory nestedFactory;
private XMLAnnotationDomain(Node beanletNode, String beanletName,
Class<T> beanletType, String description, boolean abstr,
String parent, String factory, String factoryMethod,
ClassLoader loader, ElementAnnotationFactory annotationFactory,
ClassResolver resolver, AnnotationDomain parentDomain,
NestedBeanletFactory nestedFactory) {
this(getElementAnnotations(beanletNode, beanletName, beanletType,
description, abstr, parent, factory, factoryMethod, loader,
annotationFactory, resolver, parentDomain, nestedFactory),
beanletName, beanletType, description, abstr, parent, factory,
factoryMethod, loader, nestedFactory, true);
}
private XMLAnnotationDomain(List<ElementAnnotation> elementAnnotations,
String beanletName, Class<T> beanletType, String description, boolean abstr,
String parent, String factory, String factoryMethod,
ClassLoader loader, NestedBeanletFactory
nestedFactory, boolean validate) {
super(elementAnnotations);
this.beanletName = beanletName;
this.beanletType = beanletType;
this.description = description;
this.abstr = abstr;
this.parent = parent;
this.factory = factory;
this.factoryMethod = factoryMethod;
this.loader = loader;
this.nestedFactory = nestedFactory;
if (validate) {
for (ElementAnnotation ea : elementAnnotations) {
validate(ea);
}
}
}
public String getBeanletName() {
return beanletName;
}
public Class<T> getBeanletType() {
return beanletType;
}
public String getDescription() {
return description;
}
public boolean isAbstract() {
return abstr;
}
public String getParent() {
return parent;
}
public String getFactory() {
return factory;
}
public String getFactoryMethod() {
return factoryMethod;
}
public ClassLoader getClassLoader() {
return loader;
}
private void validate(ElementAnnotation ea) {
Target target = ea.getAnnotation().annotationType().getAnnotation(
Target.class);
if (target != null) {
if (!Arrays.asList(target.value()).contains(
ea.getElement().getElementType())) {
throw new BeanletValidationException(getBeanletName(),
"Invalid element for annotation type. Element: " +
ea.getElement() + ". Annotation: " +
ea.getAnnotation().annotationType() + ".");
}
}
}
/**
* Returns the package-annotations node that represents the specified
* package. If a package-annotations node exists for the specified
* {@code packageName}, this node is selected. If not, the
* package-annotations node without the package attribute is selected, if
* available. In all other cases, {@code null} is returned.
*
* @return package-annotations node for the specified package, or
* {@code null} if node does not exist.
*/
private static Node getGlobalPackageAnnotationsNode(Node beanletNode,
String packageName) throws XPathExpressionException {
Node node = (Node) xpath.evaluate(
"/:beanlets/:package-annotations[@package='" +
packageName + "']", beanletNode, XPathConstants.NODE);
if (node == null) {
node = (Node) GLOBAL_PACKAGE_ANNOTATIONS_EXPRESSION.evaluate(
beanletNode, XPathConstants.NODE);
}
return node;
}
private static Node getGlobalAnnotationsNode(Node beanletNode,
String typeName) throws XPathExpressionException {
Node node = (Node) xpath.evaluate(
"/:beanlets/:annotations[@type='" +
typeName + "']", beanletNode, XPathConstants.NODE);
if (node == null) {
node = (Node) GLOBAL_ANNOTATIONS_EXPRESSION.evaluate(
beanletNode, XPathConstants.NODE);
}
return node;
}
/**
* Returns the annotations node that represents the specified class.
* If a annotations node exist for the specified {@code typeName}, this node
* is selected. If {@code typeName} matches the {@code beanletType}, and no
* node has been selected yet, the annotations node without a type attribute
* is selected. In all other cases, {@code null} is returned.
*
* @return package-annotations node for the specified package, or
* {@code null} if node does not exist.
*/
private static Node getLocalAnnotationsNode(Node beanletNode,
String beanletName, Class<?> beanletType, String typeName) throws
XPathExpressionException {
// PENDING: update this method's javadoc.
Node node = (Node) xpath.evaluate(
"./:annotations[@type='" + typeName + "']",
beanletNode, XPathConstants.NODE);
if (beanletType.getName().equals(typeName)) {
NodeList list = (NodeList) xpath.evaluate("./:annotations",
beanletNode, XPathConstants.NODESET);
final Node tmp;
if (list.getLength() == 0) {
tmp = beanletNode;
} else {
tmp = (Node) xpath.evaluate("./:annotations[not(@type)]",
beanletNode, XPathConstants.NODE);
}
if (node == null) {
node = tmp;
} else if (tmp != null) {
throw new BeanletValidationException(beanletName,
"Ambiguous annotation element.");
}
}
return node;
}
/**
* Returns all {@code ElementAnnotation}s from XML and the underlying class.
*/
private static List<ElementAnnotation> getElementAnnotations(
Node beanletNode, String beanletName, Class<?> beanletType,
String description, boolean abstr, String parent, String factory,
String factoryMethod, ClassLoader loader,
ElementAnnotationFactory annotationFactory,
ClassResolver resolver, AnnotationDomain parentDomain,
NestedBeanletFactory nestedFactory) {
Set<Class> dupes = new HashSet<Class>();
dupes.add(beanletType);
@SuppressWarnings("unchecked")
XMLAnnotationDomain<?> intermediate =
(XMLAnnotationDomain<?>)
new XMLAnnotationDomain(getElementAnnotations(
beanletNode, beanletName, beanletType, beanletType, description,
abstr, parent, factory, factoryMethod, loader,
annotationFactory, parentDomain, nestedFactory),
beanletName, beanletType, description, abstr, parent, factory,
factoryMethod, loader, nestedFactory, false);
// Add annotations for other classes specified by XML.
String className = null;
try {
NodeList list = (NodeList) xpath.evaluate("./:annotations/@type",
beanletNode, XPathConstants.NODESET);
for (int i = 0; i < list.getLength(); i++) {
className = list.item(i).getNodeValue();
Class<?> cls = loader.loadClass(className);
if (dupes.add(cls)) {
intermediate = intermediate.mergeDomain(
getElementAnnotations(beanletNode, beanletName,
beanletType, cls, description, abstr, parent,
factory, factoryMethod, loader, annotationFactory,
parentDomain, nestedFactory));
}
}
} catch (ClassNotFoundException e) {
throw new BeanletValidationException(beanletName,
"Class not found: " + className + ".");
} catch (XPathExpressionException e) {
throw new AssertionError(e);
}
// Add annotations for resolved classes.
for (Class<?> cls : resolver.getClasses(intermediate)) {
if (dupes.add(cls)) {
intermediate = intermediate.mergeDomain(
getElementAnnotations(beanletNode, beanletName,
beanletType, cls, description, abstr, parent, factory,
factoryMethod, loader, annotationFactory, parentDomain,
nestedFactory));
}
}
return intermediate.getElements();
}
/**
* Returns all {@code ElementAnnotation}s from XML and the underlying class.
*/
private static List<ElementAnnotation> getElementAnnotations(
Node beanletNode, String beanletName, Class<?> beanletType,
final Class<?> cls, String description, boolean abstr, String parent,
String factory, String factoryMethod, ClassLoader loader,
ElementAnnotationFactory annotationFactory,
AnnotationDomain parentDomain,
NestedBeanletFactory nestedFactory) {
try {
if (parentDomain == null) {
parentDomain = AccessController.doPrivileged(new PrivilegedAction<AnnotationDomain>() {
public AnnotationDomain run() {
return AbstractAnnotationDomain.instance(cls);
}
});
}
List<ElementAnnotation> list = new ArrayList<ElementAnnotation>();
Package pkg = cls.getPackage();
if (pkg != null) {
Node node = getGlobalPackageAnnotationsNode(
beanletNode, pkg.getName());
if (node != null) {
list.addAll(getPackageElementAnnotations(beanletName, node,
pkg, loader, annotationFactory, parentDomain,
nestedFactory));
}
}
Node localNode = getLocalAnnotationsNode(beanletNode, beanletName,
beanletType, cls.getName());
if (localNode != null) {
list.addAll(getClassElementAnnotations(beanletName, localNode,
cls, loader, annotationFactory, parentDomain,
nestedFactory));
}
@SuppressWarnings("unchecked")
XMLAnnotationDomain<?> tmp = (XMLAnnotationDomain<?>)
new XMLAnnotationDomain(
list, beanletName, beanletType, description, abstr, parent,
factory, factoryMethod, loader, nestedFactory, false);
Node globalNode = getGlobalAnnotationsNode(
beanletNode, cls.getName());
if (globalNode != null) {
tmp = tmp.mergeDomain(getClassElementAnnotations(beanletName,
globalNode, cls, loader, annotationFactory,
parentDomain, nestedFactory));
}
AnnotationDomain domain = AccessController.doPrivileged(
new PrivilegedAction<AnnotationDomain>() {
public AnnotationDomain run() {
return AbstractAnnotationDomain.instance(cls);
}
});
return tmp.mergeList(domain.getElements());
} catch (XPathExpressionException e) {
assert false : e;
return Collections.emptyList();
}
}
private XMLAnnotationDomain<T> merge(
XMLAnnotationDomain<? super T> domain)
throws BeanletDefinitionException {
assert getParent() != null;
assert this != domain;
assert domain.getBeanletName().endsWith(getParent());
Class<T> beanletType = getBeanletType();
if (beanletType == null) {
@SuppressWarnings("unchecked")
Class<T> tmp = (Class<T>) domain.getBeanletType();
beanletType = tmp;
} else if (!domain.getBeanletType().isAssignableFrom(beanletType)) {
// PENDING: this check might be removed in the future.
throw new BeanletDefinitionException(getBeanletName(),
"Parent's beanlet type MUST be assignable from beanlet type.");
}
String description = getDescription();
if (description == null) {
description = domain.getDescription();
}
String factory = getFactory();
if (factory == null) {
factory = domain.getFactory();
}
String factoryMethod = getFactoryMethod();
if (factoryMethod == null) {
factoryMethod = domain.getFactoryMethod();
}
// The annotation merge boolean values are not inherited!
return new XMLAnnotationDomain<T>(mergeList(domain.getElements()),
getBeanletName(), beanletType, description, isAbstract(),
getParent(), factory, factoryMethod, getClassLoader(),
nestedFactory, false);
}
/**
* Returns a new {@code BeanletXMLAnnotationDomain} as a result of the
* merger of {@code this} domain and the specified {@code domain}. The
* specified {@code list} overrides non {@code XMLElementAnnotation}s of
* {@code this} domain.
*/
private XMLAnnotationDomain<T> mergeDomain(List<ElementAnnotation> elementAnnotations) {
return new XMLAnnotationDomain<T>(mergeList(elementAnnotations),
getBeanletName(), getBeanletType(), getDescription(),
isAbstract(), getParent(), getFactory(), getFactoryMethod(),
getClassLoader(), nestedFactory, false);
}
/**
* Returns a list of {@code ElementAnnotation}s as a result of the
* merger of the element annotations of {@code this} domain and the
* specified {@code elementAnnotations}. The specified {@code list}
* overrides non {@code XMLElementAnnotation}s of {@code this} domain.
*/
private List<ElementAnnotation> mergeList(List<ElementAnnotation> list) {
Set<ElementAnnotation> orgList = new HashSet<ElementAnnotation>(
getElements());
List<ElementAnnotation> newList = new ArrayList<ElementAnnotation>();
for (Iterator<ElementAnnotation> i = list.iterator(); i.hasNext();) {
ElementAnnotation ea = i.next();
ElementAnnotation org = getElementAnnotation(ea.getElement(),
ea.getAnnotation().annotationType());
if (org == null) {
newList.add(ea);
} else if (!(org instanceof XMLElementAnnotation)) {
newList.add(ea);
boolean removed = orgList.remove(org);
assert removed;
}
}
newList.addAll(orgList);
return newList;
}
/**
* Returns all package specific {@code ElementAnnotation} objects for the
* specified {@code pkgNode}.
*/
private static List<ElementAnnotation<PackageElement, Annotation>>
getPackageElementAnnotations(String beanletName, Node pkgNode,
Package pkg, ClassLoader loader, ElementAnnotationFactory factory,
AnnotationDomain parent,
NestedBeanletFactory nestedFactory) {
List<ElementAnnotation<PackageElement, Annotation>> eas =
new ArrayList<ElementAnnotation<PackageElement, Annotation>>();
try {
NodeList nodeList = (NodeList) ALL_EXPRESSION.evaluate(pkgNode,
XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node annotationNode = nodeList.item(i);
Node mergeNode = (Node) MERGE_EXPRESSION.evaluate(
annotationNode, XPathConstants.NODE);
boolean merge = mergeNode == null ? false : Boolean.valueOf(
mergeNode.getNodeValue());
ElementAnnotationContext ctx =
new ElementAnnotationContextImpl(
beanletName, loader, merge ? parent : null,
nestedFactory);
@SuppressWarnings("unchecked")
ElementAnnotation<PackageElement, Annotation> e = factory.
getElementAnnotation(annotationNode, pkg, ctx);
if (e != null) {
eas.add(e);
}
}
} catch (XPathExpressionException e) {
assert false : e;
}
return eas;
}
/**
* Returns all class specific {@code ElementAnnotation} objects for the
* specified {@code node}.
*
* @param node
* @param cls
*/
private static List<ElementAnnotation> getClassElementAnnotations(
String beanletName, Node node, Class<?> cls, ClassLoader loader,
ElementAnnotationFactory factory, AnnotationDomain parent,
NestedBeanletFactory nestedFactory) {
List<ElementAnnotation> eas = new ArrayList<ElementAnnotation>();
try {
NodeList nodeList = (NodeList) ALL_EXPRESSION.evaluate(node,
XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node annotationNode = nodeList.item(i);
Node mergeNode = (Node) MERGE_EXPRESSION.evaluate(
annotationNode, XPathConstants.NODE);
boolean merge = mergeNode == null ? false : Boolean.valueOf(
mergeNode.getNodeValue());
ElementAnnotationContext ctx =
new ElementAnnotationContextImpl(
beanletName, loader, merge ? parent : null,
nestedFactory);
@SuppressWarnings("unchecked")
ElementAnnotation e = factory.getElementAnnotation(
annotationNode, cls, ctx);
if (e != null) {
eas.add(e);
}
}
} catch (XPathExpressionException e) {
assert false : e;
}
return eas;
}
}