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

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