Coverage Report - org.simpleframework.xml.load.DefaultStrategy
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultStrategy
96%
43/45
100%
9/9
1.917
 
 1  
 /*
 2  
  * DefaultStrategy.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.stream.Node;
 24  
 import org.simpleframework.xml.stream.NodeMap;
 25  
 import java.lang.reflect.Array;
 26  
 import java.util.Map;
 27  
 
 28  
 /**
 29  
  * The <code>DefaultStrategy</code> object is used by the persister if
 30  
  * no strategy is specified. This will make use of a "class" attribute
 31  
  * by default to resolve the class to use for a given element during
 32  
  * the deserialization process. For the serialization process the 
 33  
  * "class" attribute will be added to the element specified. If there
 34  
  * is a need to use an attribute name other than "class" then the
 35  
  * name of the attribute to use can be specified.
 36  
  * 
 37  
  * @author Niall Gallagher
 38  
  */
 39  
 class DefaultStrategy implements Strategy {
 40  
 
 41  
    /**
 42  
     * This is used to specify the size of an array element instance.
 43  
     */
 44  
    private static final String LENGTH = "length";
 45  
    
 46  
    /**   
 47  
     * This is the attribute that is used to determine the real type.
 48  
     */
 49  
    private static final String LABEL = "class";
 50  
    
 51  
    /**
 52  
     * This is the attribute that is used to determine an array size.
 53  
     */
 54  
    private String length;
 55  
    
 56  
    /**   
 57  
     * This is the attribute that is used to determine the real type.
 58  
     */   
 59  
    private String label;
 60  
    
 61  
    /**
 62  
     * Constructor for the <code>DefaultStrategy</code> object. This 
 63  
     * is used to create a strategy that can resolve and load class
 64  
     * objects for deserialization using a "class" attribute. Also
 65  
     * for serialization this will add the appropriate "class" value.
 66  
     */
 67  
    public DefaultStrategy() {
 68  195
       this(LABEL, LENGTH);           
 69  195
    }        
 70  
    
 71  
    /**
 72  
     * Constructor for the <code>DefaultStrategy</code> object. This 
 73  
     * is used to create a strategy that can resolve and load class
 74  
     * objects for deserialization using the specified attribute. 
 75  
     * The attribute value can be any legal XML attribute name.
 76  
     * 
 77  
     * @param label this is the name of the attribute to use
 78  
     * @param length this is used to determine the array length
 79  
     */
 80  195
    public DefaultStrategy(String label, String length) {
 81  195
       this.length = length;
 82  195
       this.label = label;         
 83  195
    }
 84  
    
 85  
    /**
 86  
     * This is used to resolve and load a class for the given element.
 87  
     * Resolution of the class to used is done by inspecting the
 88  
     * XML element provided. If there is a "class" attribute on the
 89  
     * element then its value is used to resolve the class to use.
 90  
     * If no such attribute exists on the element this returns null.
 91  
     * 
 92  
     * @param field this is the type of the XML element expected
 93  
     * @param node this is the element used to resolve an override
 94  
     * @param map this is used to maintain contextual information
 95  
     * 
 96  
     * @return returns the class that should be used for the object
 97  
     * 
 98  
     * @throws Exception thrown if the class cannot be resolved
 99  
     */
 100  
    public Type getRoot(Class field, NodeMap node, Map map) throws Exception {
 101  30304
       return getElement(field, node, map);
 102  
    }  
 103  
    
 104  
    /**
 105  
     * This is used to resolve and load a class for the given element.
 106  
     * Resolution of the class to used is done by inspecting the
 107  
     * XML element provided. If there is a "class" attribute on the
 108  
     * element then its value is used to resolve the class to use.
 109  
     * If no such attribute exists on the element this returns null.
 110  
     * 
 111  
     * @param field this is the type of the XML element expected
 112  
     * @param node this is the element used to resolve an override
 113  
     * @param map this is used to maintain contextual information
 114  
     * 
 115  
     * @return returns the class that should be used for the object
 116  
     * 
 117  
     * @throws Exception thrown if the class cannot be resolved
 118  
     */
 119  
    public Type getElement(Class field, NodeMap node, Map map) throws Exception {
 120  752911
       Class type = getType(field, node);
 121  
       
 122  752910
       if(field.isArray()) {
 123  87
          return getArray(type, node);   
 124  
       }
 125  752823
       if(field != type) {
 126  30119
          return new ClassType(type);
 127  
       }
 128  722704
       return null;
 129  
    }
 130  
    
 131  
    /**
 132  
     * This is used to resolve and load a class for the given element.
 133  
     * Resolution of the class to used is done by inspecting the
 134  
     * XML element provided. If there is a "class" attribute on the
 135  
     * element then its value is used to resolve the class to use.
 136  
     * This also expects a "length" attribute for the array length.
 137  
     * 
 138  
     * @param type this is the type of the XML element expected
 139  
     * @param node this is the element used to resolve an override
 140  
     * 
 141  
     * @return returns the class that should be used for the object
 142  
     * 
 143  
     * @throws Exception thrown if the class cannot be resolved
 144  
     */   
 145  
    private Type getArray(Class type, NodeMap node) throws Exception {      
 146  87
       Node entry = node.remove(length);
 147  87
       int size = 0;
 148  
       
 149  87
       if(entry != null) {
 150  78
          String value = entry.getValue();
 151  78
          size = Integer.parseInt(value);
 152  
       }      
 153  87
       return new ArrayType(type, size);
 154  
    }
 155  
    
 156  
    /**
 157  
     * This is used to resolve and load a class for the given element.
 158  
     * Resolution of the class to used is done by inspecting the
 159  
     * XML element provided. If there is a "class" attribute on the
 160  
     * element then its value is used to resolve the class to use.
 161  
     * If no such attribute exists the specified field is returned,
 162  
     * or if the field type is an array then the component type.
 163  
     * 
 164  
     * @param field this is the type of the XML element expected
 165  
     * @param node this is the element used to resolve an override
 166  
     * 
 167  
     * @return returns the class that should be used for the object
 168  
     * 
 169  
     * @throws Exception thrown if the class cannot be resolved
 170  
     */   
 171  
    private Class getType(Class field, NodeMap node) throws Exception {      
 172  752911
       Node entry = node.remove(label);      
 173  752911
       Class type = field;
 174  
       
 175  752911
       if(field.isArray()) {
 176  87
          type = field.getComponentType();
 177  
       }
 178  752911
       if(entry != null) {
 179  30120
          String name = entry.getValue();
 180  30120
          type = getClass(name);
 181  
       }    
 182  752910
       return type;
 183  
    }     
 184  
    
 185  
    /**
 186  
     * This is used to attach a attribute to the provided element
 187  
     * that is used to identify the class. The attribute name is
 188  
     * "class" and has the value of the fully qualified class 
 189  
     * name for the object provided. This will only be invoked
 190  
     * if the object class is different from the field class.
 191  
     *  
 192  
     * @param field this is the declared class for the field used
 193  
     * @param value this is the instance variable being serialized
 194  
     * @param node this is the element used to represent the value
 195  
     * @param map this is used to maintain contextual information
 196  
     * 
 197  
     * @return this returns true if serialization is complete
 198  
     */
 199  
    public boolean setRoot(Class field, Object value, NodeMap node, Map map){
 200  10177
       return setElement(field, value, node, map);
 201  
    }   
 202  
    
 203  
    /**
 204  
     * This is used to attach a attribute to the provided element
 205  
     * that is used to identify the class. The attribute name is
 206  
     * "class" and has the value of the fully qualified class 
 207  
     * name for the object provided. This will only be invoked
 208  
     * if the object class is different from the field class.
 209  
     *
 210  
     * @param field this is the declared class for the field used
 211  
     * @param value this is the instance variable being serialized
 212  
     * @param node this is the element used to represent the value
 213  
     * @param map this is used to maintain contextual information
 214  
     * 
 215  
     * @return this returns true if serialization is complete
 216  
     */   
 217  
    public boolean setElement(Class field, Object value, NodeMap node, Map map){
 218  251983
       Class type = value.getClass();
 219  251983
       Class real = type;
 220  
       
 221  251983
       if(type.isArray()) {
 222  60
          real = setArray(field, value, node);
 223  
       }
 224  251983
       if(type != field) {
 225  10092
          node.put(label, real.getName());
 226  
       }       
 227  251983
       return false;
 228  
    }
 229  
    
 230  
    /**
 231  
     * This is used to add a length attribute to the element due to
 232  
     * the fact that the serialized value is an array. The length
 233  
     * of the array is acquired and inserted in to the attributes.
 234  
     * 
 235  
     * @param field this is the field type for the array to set
 236  
     * @param value this is the actual value for the array to set
 237  
     * @param node this is the map of attributes for the element
 238  
     * 
 239  
     * @return returns the array component type that is set
 240  
     */
 241  
    private Class setArray(Class field, Object value, NodeMap node){
 242  60
       int size = Array.getLength(value);
 243  
       
 244  60
       if(length != null) {       
 245  60
          node.put(length, String.valueOf(size));
 246  
       }
 247  60
       return field.getComponentType();
 248  
    }
 249  
    
 250  
    /**
 251  
     * This method is used to acquire the class of the specified name.
 252  
     * Loading is performed by the thread context class loader as this
 253  
     * will ensure that the class loading strategy can be changed as
 254  
     * requirements dictate. Typically the thread context class loader
 255  
     * can handle all serialization requirements.
 256  
     * 
 257  
     * @param name this is the name of the class that is to be loaded
 258  
     * 
 259  
     * @return this returns the class that has been loaded by this
 260  
     */
 261  
    private Class getClass(String type) throws Exception {
 262  30120
       ClassLoader loader = getClassLoader();
 263  
       
 264  30120
       if(loader == null) {
 265  0
          loader = getCallerClassLoader();
 266  
       }
 267  30120
       return loader.loadClass(type);      
 268  
    }
 269  
    
 270  
    /**
 271  
     * This is used to acquire the caller class loader for this object.
 272  
     * Typically this is only used if the thread context class loader
 273  
     * is set to null. This ensures that there is at least some class
 274  
     * loader available to the strategy to load the class.
 275  
     * 
 276  
     * @return this returns the loader that loaded this class     
 277  
     */
 278  
    private ClassLoader getCallerClassLoader() throws Exception {
 279  0
       return getClass().getClassLoader();
 280  
    }
 281  
 
 282  
    /**
 283  
     * This is used to acquire the thread context class loader. This
 284  
     * is the default class loader used by the cycle strategy. When
 285  
     * using the thread context class loader the caller can switch the
 286  
     * class loader in use, which allows class loading customization.
 287  
     * 
 288  
     * @return this returns the loader used by the calling thread
 289  
     */
 290  
    private static ClassLoader getClassLoader() throws Exception {
 291  30120
       return Thread.currentThread().getContextClassLoader();
 292  
    }
 293  
 }