Coverage Report - org.simpleframework.xml.load.Factory
 
Classes in this File Line Coverage Branch Coverage Complexity
Factory
95%
36/38
100%
10/10
2.5
 
 1  
 /*
 2  
  * Factory.java July 2006
 3  
  *
 4  
  * Copyright (C) 2006, Niall Gallagher <niallg@users.sf.net>
 5  
  *
 6  
  * This library is free software; you can redistribute it and/or
 7  
  * modify it under the terms of the GNU Lesser General Public
 8  
  * License as published by the Free Software Foundation.
 9  
  *
 10  
  * This library is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  
  * GNU Lesser General Public License for more details.
 14  
  *
 15  
  * You should have received a copy of the GNU Lesser General 
 16  
  * Public License along with this library; if not, write to the 
 17  
  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
 18  
  * Boston, MA  02111-1307  USA
 19  
  */
 20  
 
 21  
 package org.simpleframework.xml.load;
 22  
 
 23  
 import org.simpleframework.xml.transform.Transformer;
 24  
 import org.simpleframework.xml.stream.InputNode;
 25  
 import org.simpleframework.xml.stream.OutputNode;
 26  
 import java.lang.reflect.Modifier;
 27  
 import java.beans.Introspector;
 28  
 
 29  
 /**
 30  
  * The <code>Factory</code> object provides a base class for factories 
 31  
  * used to produce field values from XML elements. The goal of this 
 32  
  * type of factory is to make use of the <code>Strategy</code> object
 33  
  * to determine the type of the field value. The strategy class must be 
 34  
  * assignable to the field class type, that is, it must extend it or
 35  
  * implement it if it represents an interface. If the strategy returns
 36  
  * a null <code>Type</code> then the subclass implementation determines 
 37  
  * the type used to populate the object field value.
 38  
  * 
 39  
  * @author Niall Gallagher
 40  
  */
 41  
 abstract class Factory {
 42  
    
 43  
    /**
 44  
     * Caches the constructors used to transform primitive types.
 45  
     * 
 46  
     * @see org.simpleframework.xml.load.Primitive
 47  
     */
 48  
    protected static Transformer transform;
 49  
 
 50  
    static {
 51  54
       transform = new Transformer();           
 52  54
    } 
 53  
    
 54  
    /**
 55  
     * This is the source object used for the serialization process.
 56  
     */
 57  
    protected Source source;
 58  
    
 59  
    /**
 60  
     * This is the field type that the class must be assignable to.
 61  
     */
 62  
    protected Class field;        
 63  
 
 64  
    /**
 65  
     * Constructor for the <code>Factory</code> object. This is given 
 66  
     * the class type for the field that this factory will determine
 67  
     * the actual type for. The actual type must be assignable to the
 68  
     * field type to insure that any instance can be set. 
 69  
     * 
 70  
     * @param source the contextual object used by the persister
 71  
     * @param field this is the field type to determine the value of
 72  
     */
 73  1333167
    protected Factory(Source source, Class field) {
 74  1333167
       this.source = source;           
 75  1333167
       this.field = field;           
 76  1333167
    }
 77  
 
 78  
    /**
 79  
     * This is used to get a possible override from the provided node.
 80  
     * If the node provided is an element then this checks for a  
 81  
     * specific class override using the <code>Strategy</code> object.
 82  
     * If the strategy cannot resolve a class then this will return 
 83  
     * null. If the resolved <code>Type</code> is not assignable to 
 84  
     * the field then this will thrown an exception.
 85  
     * 
 86  
     * @param node this is the node used to search for the override
 87  
     * 
 88  
     * @return this returns null if no override type can be found
 89  
     * 
 90  
     * @throws Exception if the override type is not compatible
 91  
     */
 92  
    public Type getOverride(InputNode node) throws Exception {
 93  766301
       Type type = getConversion(node);      
 94  
 
 95  766300
       if(type != null) { 
 96  43590
          Class real = type.getType();
 97  
      
 98  43590
          if(!isCompatible(field, real)) {
 99  3
             throw new InstantiationException("Type %s is not compatible with %s", real, field);              
 100  
          }
 101  
       }         
 102  766297
       return type; 
 103  
    }
 104  
    
 105  
    /**
 106  
     * This method is used to set the override class within an element.
 107  
     * This delegates to the <code>Strategy</code> implementation, which
 108  
     * depending on the implementation may add an attribute of a child
 109  
     * element to describe the type of the object provided to this.
 110  
     * 
 111  
     * @param field this is the class of the field type being serialized
 112  
     * @param node the XML element that is to be given the details
 113  
     *
 114  
     * @throws Exception thrown if an error occurs within the strategy
 115  
     */
 116  
    public boolean setOverride(Class field, Object value, OutputNode node) throws Exception {
 117  197337
       if(!field.isPrimitive()) {
 118  197240
          return source.setOverride(field, value, node);
 119  
       }
 120  97
       return false;
 121  
    }
 122  
 
 123  
    /**
 124  
     * This performs the conversion from the element node to a type. This
 125  
     * is where the <code>Strategy</code> object is consulted and asked
 126  
     * for a class that will represent the provided XML element. This will,
 127  
     * depending on the strategy implementation, make use of attributes
 128  
     * and/or elements to determine the type for the field.
 129  
     * 
 130  
     * @param node this is the element used to extract the override
 131  
     * 
 132  
     * @return this returns null if no override type can be found
 133  
     * 
 134  
     * @throws Exception thrown if the override class cannot be loaded    
 135  
     */ 
 136  
    public Type getConversion(InputNode node) throws Exception {
 137  766301
       return source.getOverride(field, node);
 138  
    }
 139  
    
 140  
    /**
 141  
     * This creates a <code>Scanner</code> object that can be used to
 142  
     * examine the fields within the XML class schema. The scanner
 143  
     * maintains information when a field from within the scanner is
 144  
     * visited, this allows the serialization and deserialization
 145  
     * process to determine if all required XML annotations are used.
 146  
     * 
 147  
     * @param type the schema class the scanner is created for
 148  
     * 
 149  
     * @return a scanner that can maintains information on the type
 150  
     * 
 151  
     * @throws Exception if the class contains an illegal schema 
 152  
     */ 
 153  
    private static Scanner getScanner(Class type) throws Exception {
 154  200012
       return ScannerFactory.getInstance(type);
 155  
    }
 156  
    
 157  
    /**
 158  
     * This is used to acquire the name of the specified type using
 159  
     * the <code>Root</code> annotation for the class. This will 
 160  
     * use either the name explicitly provided by the annotation or
 161  
     * it will use the name of the class that the annotation was
 162  
     * placed on if there is no explicit name for the root.
 163  
     * 
 164  
     * @param type this is the type to acquire the root name for
 165  
     * 
 166  
     * @return this returns the name of the type from the root
 167  
     * 
 168  
     * @throws Exception if the class contains an illegal schema
 169  
     */
 170  
    public static String getName(Class type) throws Exception {
 171  200012
       Scanner schema = getScanner(type);
 172  200012
       String name = schema.getName();
 173  
       
 174  200012
       if(name != null) {
 175  199781
          return name;
 176  
       }
 177  231
       return getClassName(type);
 178  
    }
 179  
    
 180  
    /**
 181  
     * This returns the name of the class specified. If there is a root
 182  
     * annotation on the type, then this is ignored in favour of the 
 183  
     * actual class name. This is typically used when the type is a
 184  
     * primitive or if there is no <code>Root</code> annotation present. 
 185  
     * 
 186  
     * @param type this is the type to acquire the root name for
 187  
     * 
 188  
     * @return this returns the name of the type from the root
 189  
     */
 190  
    public static String getClassName(Class type) throws Exception {
 191  231
       if(type.isArray()) {
 192  0
          type = type.getComponentType();
 193  
       }      
 194  231
       String name = type.getSimpleName();
 195  
       
 196  231
       if(type.isPrimitive()) {
 197  0
          return name;
 198  
       }
 199  231
       return Introspector.decapitalize(name);
 200  
    }
 201  
    
 202  
    /**
 203  
     * This is used to determine whether the scanned class represents
 204  
     * a primitive type. A primitive type is a type that contains no
 205  
     * XML annotations and so cannot be serialized with an XML form.
 206  
     * Instead primitives a serialized using transformations.
 207  
     *
 208  
     * @param type this is the type to determine if it is primitive
 209  
     * 
 210  
     * @return this returns true if no XML annotations were found
 211  
     */
 212  
    public static boolean isPrimitive(Class type) throws Exception {
 213  1078369
       if(type.isEnum()) {
 214  98
          return true;
 215  
       }
 216  1078271
       return transform.valid(type);
 217  
    }
 218  
    
 219  
    /**
 220  
     * This is used to determine whether the provided base class can be
 221  
     * assigned from the issued type. For an override to be compatible
 222  
     * with the field type an instance of the override type must be 
 223  
     * assignable to the field value. 
 224  
     * 
 225  
     * @param field this is the field value present the the object    
 226  
     * @param type this is the specialized type that will be assigned
 227  
     * 
 228  
     * @return true if the field type can be assigned the type value
 229  
     */
 230  
    public static boolean isCompatible(Class field, Class type) {
 231  43590
       if(field.isArray()) {
 232  124
          field = field.getComponentType();
 233  
       }
 234  43590
       return field.isAssignableFrom(type);           
 235  
    }
 236  
 
 237  
    /**
 238  
     * This is used to determine whether the type given is instantiable,
 239  
     * that is, this determines if an instance of that type can be
 240  
     * created. If the type is an interface or an abstract class then 
 241  
     * this will return false.
 242  
     * 
 243  
     * @param type this is the type to check the modifiers of
 244  
     * 
 245  
     * @return false if the type is an interface or an abstract class
 246  
     */
 247  
    public static boolean isInstantiable(Class type) {
 248  332539
       int modifiers = type.getModifiers();
 249  
 
 250  332539
       if(Modifier.isAbstract(modifiers)) {
 251  56
          return false;              
 252  
       }              
 253  332483
       return !Modifier.isInterface(modifiers);
 254  
    } 
 255  
 }