org.jboss.seam.xml.model.ModelBuilder Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.seam.xml.model;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.enterprise.inject.Stereotype;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;
import javax.inject.Qualifier;
import javax.interceptor.InterceptorBinding;
import org.jboss.seam.xml.core.BeanResult;
import org.jboss.seam.xml.core.BeanResultType;
import org.jboss.seam.xml.core.GenericBeanResult;
import org.jboss.seam.xml.core.XmlResult;
import org.jboss.seam.xml.fieldset.FieldValueObject;
import org.jboss.seam.xml.parser.SaxNode;
import org.jboss.seam.xml.parser.namespace.CompositeNamespaceElementResolver;
import org.jboss.seam.xml.parser.namespace.NamespaceElementResolver;
import org.jboss.seam.xml.parser.namespace.RootNamespaceElementResolver;
import org.jboss.seam.xml.util.XmlConfigurationException;
import org.jboss.seam.xml.util.XmlObjectConverter;
import org.jboss.weld.extensions.annotated.AnnotatedTypeBuilder;
import org.jboss.weld.extensions.util.AnnotationInstanceProvider;
import org.jboss.weld.extensions.util.Reflections;
/**
* Builds an XML result from sax nodes
*
* @author stuart
*
*/
public class ModelBuilder
{
AnnotationInstanceProvider ac = new AnnotationInstanceProvider();
static final String ROOT_NAMESPACE = "urn:java:seam:core";
static final String BEANS_ROOT_NAMESPACE = "http://java.sun.com/xml/ns/javaee";
Map resolvers;
/**
* builds an XML result from a parsed xml document
*/
public XmlResult build(SaxNode root)
{
resolvers = new HashMap();
XmlResult ret = new XmlResult();
if (!root.getName().equals("beans"))
{
throw new XmlConfigurationException("Wrong root element for XML config file, expected: found:" + root.getName(), root.getDocument(), root.getLineNo());
}
if (!(ROOT_NAMESPACE.equals(root.getNamespaceUri()) || BEANS_ROOT_NAMESPACE.equals(root.getNamespaceUri())))
{
throw new XmlConfigurationException("Wrong root namespace for XML config file, expected:" + ROOT_NAMESPACE + " or " + BEANS_ROOT_NAMESPACE + " found:" + root.getNamespaceUri(), root.getDocument(), root.getLineNo());
}
resolvers.put(ROOT_NAMESPACE, new RootNamespaceElementResolver());
List children = root.getChildren();
for (SaxNode node : children)
{
try
{
// nodes with a null namespace are whitespace nodes etc
if (node.getNamespaceUri() != null)
{
// ignore etc
if (node.getNamespaceUri().equals(BEANS_ROOT_NAMESPACE))
{
continue;
}
XmlItem rb = resolveNode(node, null);
// validateXmlItem(rb);
addNodeToResult(ret, rb);
}
}
catch (Exception e)
{
ret.addProblem(e.getMessage());
e.printStackTrace();
}
}
return ret;
}
@SuppressWarnings("unchecked")
private void addNodeToResult(XmlResult ret, XmlItem rb)
{
if (rb.getType() == XmlItemType.CLASS || rb.getType() == XmlItemType.ANNOTATION)
{
ResultType type = getItemType(rb);
// if we are configuring a bean
if (type == ResultType.BEAN)
{
ClassXmlItem cxml = (ClassXmlItem) rb;
// get the AnnotatedType information
BeanResult> tp = buildAnnotatedType(cxml);
if (cxml.getJavaClass().isInterface())
{
ret.addInterface(tp);
}
else
{
ret.addBean(tp);
}
// or need to veto the bean
if (tp.getBeanType() != BeanResultType.ADD)
{
ret.addVeto(tp.getType());
}
// get all the field values from the bean
Set configuredFields = new HashSet();
List fields = new ArrayList();
for (FieldValueXmlItem xi : cxml.getChildrenOfType(FieldValueXmlItem.class))
{
FieldValueObject f = xi.getFieldValue();
if (f != null)
{
fields.add(f);
configuredFields.add(xi.getFieldName());
}
}
for (FieldValueXmlItem f : cxml.getShorthandFieldValues())
{
if (configuredFields.contains(f.getFieldName()))
{
throw new XmlConfigurationException("Field configured in two places: " + cxml.getJavaClass().getName() + "." + f.getFieldName(), cxml.getDocument(), cxml.getLineno());
}
fields.add(f.getFieldValue());
}
if (!fields.isEmpty())
{
if (rb.getJavaClass().isInterface())
{
ret.addInterfaceFieldValues(tp.getType(), fields);
}
else
{
ret.addFieldValue(tp, fields);
}
}
}
else if (type == ResultType.QUALIFIER)
{
ret.addQualifier((Class) rb.getJavaClass());
}
else if (type == ResultType.INTERCEPTOR_BINDING)
{
ret.addInterceptorBinding((Class) rb.getJavaClass());
}
else if (type == ResultType.STEREOTYPE)
{
addStereotypeToResult(ret, rb);
}
}
else if (rb.getType() == XmlItemType.GENERIC_BEAN)
{
GenericBeanXmlItem item = (GenericBeanXmlItem) rb;
Set> classes = new HashSet>();
for (ClassXmlItem c : rb.getChildrenOfType(ClassXmlItem.class))
{
BeanResult> br = buildAnnotatedType(c);
if (br.getBeanType() != BeanResultType.ADD)
{
ret.addVeto(br.getType());
}
classes.add(br);
}
ret.addGenericBean(new GenericBeanResult(item.getJavaClass(), classes));
}
}
/**
* resolves the appropriate java elements from the xml
*/
protected XmlItem resolveNode(SaxNode node, XmlItem parent)
{
NamespaceElementResolver resolver = resolveNamepsace(node.getNamespaceUri());
XmlItem ret = resolver.getItemForNamespace(node, parent);
if (ret == null)
{
throw new XmlConfigurationException("Could not resolve node " + node.getName() + " in namespace " + node.getNamespaceUri(), node.getDocument(), node.getLineNo());
}
List children = node.getChildren();
for (SaxNode n : children)
{
if (n.getNamespaceUri() != null)
{
XmlItem rb = resolveNode(n, ret);
ret.addChild(rb);
}
}
ret.resolveChildren();
return ret;
}
protected NamespaceElementResolver resolveNamepsace(String namespaceURI)
{
if (resolvers.containsKey(namespaceURI))
{
return resolvers.get(namespaceURI);
}
String ns = namespaceURI.replaceFirst("urn:java:", "");
CompositeNamespaceElementResolver res = new CompositeNamespaceElementResolver(ns.split(":"));
resolvers.put(namespaceURI, res);
return res;
}
/**
* Determines the type of an element by examining its child nodes
*/
protected ResultType getItemType(XmlItem item)
{
ResultType ret = null;
for (AnnotationXmlItem it : item.getChildrenOfType(AnnotationXmlItem.class))
{
if (it.getJavaClass() == InterceptorBinding.class)
{
if (ret != null)
{
throw new XmlConfigurationException("Element cannot be both an INTERCEPTOR_BINDING and a " + ret.toString(), item.getDocument(), item.getLineno());
}
else
{
ret = ResultType.INTERCEPTOR_BINDING;
}
}
else if (it.getJavaClass() == Qualifier.class)
{
if (ret != null)
{
throw new XmlConfigurationException("Element cannot be both an QUALIFIER and a " + ret.toString(), item.getDocument(), item.getLineno());
}
else
{
ret = ResultType.QUALIFIER;
}
}
else if (it.getJavaClass() == Stereotype.class)
{
if (ret != null)
{
throw new XmlConfigurationException("Element cannot be both an STEREOTYPE and a " + ret.toString(), item.getDocument(), item.getLineno());
}
else
{
ret = ResultType.STEREOTYPE;
}
}
}
if (ret == null)
{
ret = ResultType.BEAN;
}
return ret;
}
@SuppressWarnings("unchecked")
BeanResult> buildAnnotatedType(ClassXmlItem rb)
{
boolean override = !rb.getChildrenOfType(OverrideXmlItem.class).isEmpty();
boolean extend = !rb.getChildrenOfType(SpecializesXmlItem.class).isEmpty();
// if it is an extend we want to read the annotations from the underlying
// class
BeanResult> result = new BeanResult(rb.getJavaClass(), extend);
AnnotatedTypeBuilder> type = result.getBuilder();
// list of constructor arguments
List constList = new ArrayList();
if (override && extend)
{
throw new XmlConfigurationException("A bean may not both and an existing bean", rb.getDocument(), rb.getLineno());
}
if (override)
{
result.setBeanType(BeanResultType.OVERRIDE);
}
else if (extend)
{
result.setBeanType(BeanResultType.SPECIALISE);
}
for (AnnotationXmlItem item : rb.getChildrenOfType(AnnotationXmlItem.class))
{
Annotation a = createAnnotation(item);
type.addToClass(a);
}
List constructorParameters = rb.getChildrenOfType(ParametersXmlItem.class);
if (constructorParameters.size() > 1)
{
throw new XmlConfigurationException("A method may only have a single element", rb.getDocument(), rb.getLineno());
}
else if (!constructorParameters.isEmpty())
{
for (ParameterXmlItem item : constructorParameters.get(0).getChildrenOfType(ParameterXmlItem.class))
{
constList.add(item);
}
}
for (FieldXmlItem item : rb.getChildrenOfType(FieldXmlItem.class))
{
for (AnnotationXmlItem fi : item.getChildrenOfType(AnnotationXmlItem.class))
{
Annotation a = createAnnotation(fi);
type.addToField(item.getField(), a);
}
List types = item.getChildrenOfType(TypeXmlItem.class);
if (types.size() > 1)
{
throw new XmlConfigurationException("Only one element may be present on a field", rb.getDocument(), rb.getLineno());
}
if (!types.isEmpty())
{
List overridenTypes = types.get(0).getChildrenOfType(ClassXmlItem.class);
if (overridenTypes.size() != 1)
{
throw new XmlConfigurationException(" must have a single child element", rb.getDocument(), rb.getLineno());
}
type.overrideFieldType(item.getField(), overridenTypes.get(0).getJavaClass());
}
}
for (MethodXmlItem item : rb.getChildrenOfType(MethodXmlItem.class))
{
int paramCount = 0;
for (AnnotationXmlItem fi : item.getChildrenOfType(AnnotationXmlItem.class))
{
Annotation a = createAnnotation(fi);
type.addToMethod(item.getMethod(), a);
}
List parameters = item.getChildrenOfType(ParametersXmlItem.class);
if (parameters.size() > 1)
{
throw new XmlConfigurationException("A method may only have a single element", item.getDocument(), item.getLineno());
}
else if (!parameters.isEmpty())
{
for (ParameterXmlItem fi : parameters.get(0).getChildrenOfType(ParameterXmlItem.class))
{
int param = paramCount++;
for (AnnotationXmlItem pan : fi.getChildrenOfType(AnnotationXmlItem.class))
{
Annotation a = createAnnotation(pan);
type.addToMethodParameter(item.getMethod(), param, a);
}
List types = fi.getChildrenOfType(TypeXmlItem.class);
if (types.size() > 1)
{
throw new XmlConfigurationException("Only one element may be present on a parameter", rb.getDocument(), rb.getLineno());
}
if (!types.isEmpty())
{
List overridenTypes = types.get(0).getChildrenOfType(ClassXmlItem.class);
if (overridenTypes.size() != 1)
{
throw new XmlConfigurationException(" must have a single child element", rb.getDocument(), rb.getLineno());
}
type.overrideMethodParameterType(item.getMethod(), overridenTypes.get(0).getJavaClass(), param);
}
}
}
}
if (!constList.isEmpty())
{
int paramCount = 0;
Constructor> c = resolveConstructor(rb, constList);
// we automatically add inject to the constructor
type.addToConstructor((Constructor) c, new AnnotationLiteral()
{
});
for (ParameterXmlItem fi : constList)
{
int param = paramCount++;
for (AnnotationXmlItem pan : fi.getChildrenOfType(AnnotationXmlItem.class))
{
Annotation a = createAnnotation(pan);
type.addToConstructorParameter((Constructor) c, param, a);
}
List types = fi.getChildrenOfType(TypeXmlItem.class);
if (types.size() > 1)
{
throw new XmlConfigurationException("Only one element may be present on a parameter", rb.getDocument(), rb.getLineno());
}
if (!types.isEmpty())
{
List overridenTypes = types.get(0).getChildrenOfType(ClassXmlItem.class);
if (overridenTypes.size() != 1)
{
throw new XmlConfigurationException(" must have a single child element", rb.getDocument(), rb.getLineno());
}
type.overrideConstructorParameterType(c, overridenTypes.get(0).getJavaClass(), param);
}
}
}
return result;
}
protected static Constructor> resolveConstructor(ClassXmlItem bean, List constList)
{
Class>[] params = new Class[constList.size()];
for (int i = 0; i < constList.size(); ++i)
{
params[i] = constList.get(i).getJavaClass();
}
Constructor> ret = Reflections.getConstructor(bean.getJavaClass(), params);
if (ret == null)
{
throw new XmlConfigurationException("Could not resolve constructor for " + bean.getJavaClass() + " with arguments " + params, bean.getDocument(), bean.getLineno());
}
return ret;
}
@SuppressWarnings("unchecked")
void addStereotypeToResult(XmlResult ret, XmlItem rb)
{
Annotation[] values = new Annotation[rb.getChildren().size()];
int count = 0;
for (XmlItem item : rb.getChildren())
{
if (item.getType() == XmlItemType.ANNOTATION)
{
Annotation a = createAnnotation((AnnotationXmlItem) item);
values[count] = a;
}
else
{
throw new XmlConfigurationException("Setereotype " + rb.getJavaClass() + " has an item that does not represent an annotation in its XML configurations", rb.getDocument(), rb.getLineno());
}
count++;
}
ret.addStereotype((Class) rb.getJavaClass(), values);
}
@SuppressWarnings("unchecked")
Annotation createAnnotation(AnnotationXmlItem item)
{
Map typedVars = new HashMap();
Class> anClass = item.getJavaClass();
for (Entry e : item.getAttributes().entrySet())
{
String mname = e.getKey();
Method m;
try
{
m = anClass.getDeclaredMethod(mname);
}
catch (Exception e1)
{
throw new XmlConfigurationException("Annotation " + item.getJavaClass().getName() + " does not have a member named " + mname + " ,error in XML", item.getDocument(), item.getLineno());
}
Class> returnType = m.getReturnType();
typedVars.put(mname, XmlObjectConverter.convert(returnType, e.getValue()));
}
return ac.get((Class) item.getJavaClass(), typedVars);
}
public void validateXmlItem(XmlItem item)
{
Set allowed = item.getAllowedItem();
for (XmlItem i : item.getChildren())
{
if (!allowed.contains(item.getType()))
{
throw new XmlConfigurationException("Item " + item.getType() + " is not allowed to contain " + i.getType(), item.getDocument(), item.getLineno());
}
validateXmlItem(i);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy