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

org.simpleframework.xml.core.ConstructorScanner Maven / Gradle / Ivy

Go to download

Simple is a high performance XML serialization and configuration framework for Java

There is a newer version: 2.7.1
Show newest version
/*
 * ConstructorScanner.java July 2009
 *
 * Copyright (C) 2009, Niall Gallagher 
 *
 * 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.
 *
 * 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
 */

package org.simpleframework.xml.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;

import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementArray;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.ElementMap;

/**
 * The ConstructorScanner object is used to scan all 
 * all constructors that have XML annotations for their parameters. 
 * parameters. Each constructor scanned is converted in to a 
 * Builder object. In order to ensure consistency
 * amongst the annotated parameters each named parameter must have
 * the exact same type and annotation attributes across the 
 * constructors. This ensures a consistent XML representation.
 *
 * @author Niall Gallagher
 * 
 * @see org.simpleframework.xml.core.Scanner
 */
class ConstructorScanner {

   /**
    * This contains a list of all the builders for the class.
    */
   private List list;
   
   /**
    * This represents the default no argument constructor used.
    */
   private Builder primary;
   
   /**
    * This is used to acquire a parameter by the parameter name.
    */
   private Index index;
   
   /**
    * This is the type that is scanner for annotated constructors.
    */
   private Class type;
   
   /**
    * Constructor for the ConstructorScanner object. 
    * This is used to scan the specified class for constructors that
    * can be used to instantiate the class. Only constructors that
    * have all parameters annotated will be considered.
    * 
    * @param type this is the type that is to be scanned
    */
   public ConstructorScanner(Class type) throws Exception {
      this.list = new ArrayList();
      this.index = new Index(type);
      this.type = type;
      this.scan(type);
   }
   
   /**
    * This is used to create the object instance. It does this by
    * either delegating to the default no argument constructor or by
    * using one of the annotated constructors for the object. This
    * allows deserialized values to be injected in to the created
    * object if that is required by the class schema.
    * 
    * @return this returns the creator for the class object
    */
   public Creator getCreator() {
      return new ClassCreator(list, index, primary);
   }
   
   /**
    * This is used to scan the specified class for constructors that
    * can be used to instantiate the class. Only constructors that
    * have all parameters annotated will be considered.
    * 
    * @param type this is the type that is to be scanned
    */
   private void scan(Class type) throws Exception {
      Constructor[] array = type.getDeclaredConstructors();
      
      for(Constructor factory: array){
         Index index = new Index(type);
         
         if(!type.isPrimitive()) {
            scan(factory, index);
         }
      } 
   }
   
   /**
    * This is used to scan the specified constructor for annotations
    * that it contains. Each parameter annotation is evaluated and 
    * if it is an XML annotation it is considered to be a valid
    * parameter and is added to the parameter map.
    * 
    * @param factory this is the constructor that is to be scanned
    * @param map this is the parameter map that contains parameters
    */
   private void scan(Constructor factory, Index map) throws Exception {
      Annotation[][] labels = factory.getParameterAnnotations();
      Class[] types = factory.getParameterTypes();

      for(int i = 0; i < types.length; i++) {         
         for(int j = 0; j < labels[i].length; j++) {
            Parameter value = process(factory, labels[i][j], i);
            
            if(value != null) {
               String name = value.getName();
               
               if(map.containsKey(name)) {
                  throw new PersistenceException("Parameter '%s' is a duplicate in %s", name, factory);
               }
               index.put(name, value);
               map.put(name, value);
            }
         }
      }
      if(types.length == map.size()) {
         build(factory, map);
      }
   }
   
   /**
    * This is used to build the Builder object that is
    * to be used to instantiate the object. The builder contains 
    * the constructor at the parameters in the declaration order.
    * 
    * @param factory this is the constructor that is to be scanned
    * @param map this is the parameter map that contains parameters
    */
   private void build(Constructor factory, Index map) throws Exception {
      Builder builder = new Builder(factory, map);
      
      if(builder.isDefault()) {
         primary = builder;
      }
      list.add(builder);   
   }
   
   /**
    * This is used to create a Parameter object which is
    * used to represent a parameter to a constructor. Each parameter
    * contains an annotation an the index it appears in.
    * 
    * @param factory this is the constructor the parameter is in
    * @param label this is the annotation used for the parameter
    * @param ordinal this is the position the parameter appears at
    * 
    * @return this returns the parameter for the constructor
    */
   private Parameter process(Constructor factory, Annotation label, int ordinal) throws Exception{
      if(label instanceof Attribute) {
         return create(factory, label, ordinal);
      }
      if(label instanceof ElementList) {
         return create(factory, label, ordinal);
      }     
      if(label instanceof ElementArray) {
         return create(factory, label, ordinal);
      }
      if(label instanceof ElementMap) {
         return create(factory, label, ordinal);
      }
      if(label instanceof Element) {
         return create(factory, label, ordinal);
      }
      return null;
   }
   
   /**
    * This is used to create a Parameter object which is
    * used to represent a parameter to a constructor. Each parameter
    * contains an annotation an the index it appears in.
    * 
    * @param factory this is the constructor the parameter is in
    * @param label this is the annotation used for the parameter
    * @param ordinal this is the position the parameter appears at
    * 
    * @return this returns the parameter for the constructor
    */
   private Parameter create(Constructor factory, Annotation label, int ordinal) throws Exception {
      Parameter value = ParameterFactory.getInstance(factory, label, ordinal);
      String name = value.getName(); 
      
      if(index.containsKey(name)) {
         validate(value, name);
      }
      return value;
   }
   
   /**
    * This is used to validate the parameter against all the other
    * parameters for the class. Validating each of the parameters
    * ensures that the annotations for the parameters remain
    * consistent throughout the class.
    * 
    * @param parameter this is the parameter to be validated
    * @param name this is the name of the parameter to validate
    */
   private void validate(Parameter parameter, String name) throws Exception {
      Parameter other = index.get(name);
      Annotation label = other.getAnnotation();
      
      if(!parameter.getAnnotation().equals(label)) {
         throw new MethodException("Annotations do not match for '%s' in %s", name, type);
      }
      Class expect = other.getType();
      
      if(expect != parameter.getType()) {
         throw new MethodException("Method types do not match for '%s' in %s", name, type);
      }
   }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy