Coverage Report - org.simpleframework.xml.strategy.TreeStrategy
 
Classes in this File Line Coverage Branch Coverage Complexity
TreeStrategy
100%
40/40
93%
15/16
2.429
 
 1  
 /*
 2  
  * TreeStrategy.java July 2006
 3  
  *
 4  
  * Copyright (C) 2006, Niall Gallagher <niallg@users.sf.net>
 5  
  *
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 15  
  * implied. See the License for the specific language governing 
 16  
  * permissions and limitations under the License.
 17  
  */
 18  
 
 19  
 package org.simpleframework.xml.strategy;
 20  
 
 21  
 import static org.simpleframework.xml.strategy.Name.LABEL;
 22  
 import static org.simpleframework.xml.strategy.Name.LENGTH;
 23  
 
 24  
 import java.lang.reflect.Array;
 25  
 import java.util.Map;
 26  
 
 27  
 import org.simpleframework.xml.stream.Node;
 28  
 import org.simpleframework.xml.stream.NodeMap;
 29  
 
 30  
 /**
 31  
  * The <code>TreeStrategy</code> object is used to provide a simple
 32  
  * strategy for handling object graphs in a tree structure. This does
 33  
  * not resolve cycles in the object graph. This will make use of the
 34  
  * specified class attribute to resolve the class to use for a given 
 35  
  * element during the deserialization process. For the serialization 
 36  
  * process the "class" attribute will be added to the element specified.
 37  
  * If there is a need to use an attribute name other than "class" then 
 38  
  * the name of the attribute to use can be specified.
 39  
  * 
 40  
  * @author Niall Gallagher
 41  
  * 
 42  
  * @see org.simpleframework.xml.strategy.CycleStrategy
 43  
  */
 44  
 public class TreeStrategy implements Strategy {
 45  
    
 46  
    /**
 47  
     * This is the loader that is used to load the specified class.
 48  
     */
 49  
    private final Loader loader;
 50  
    
 51  
    /**
 52  
     * This is the attribute that is used to determine an array size.
 53  
     */
 54  
    private final String length;
 55  
    
 56  
    /**   
 57  
     * This is the attribute that is used to determine the real type.
 58  
     */   
 59  
    private final String label;
 60  
    
 61  
    /**
 62  
     * Constructor for the <code>TreeStrategy</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 TreeStrategy() {
 68  413
       this(LABEL, LENGTH);           
 69  413
    }        
 70  
    
 71  
    /**
 72  
     * Constructor for the <code>TreeStrategy</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  419
    public TreeStrategy(String label, String length) {
 81  419
       this.loader = new Loader();
 82  419
       this.length = length;
 83  419
       this.label = label;         
 84  419
    } 
 85  
    
 86  
    /**
 87  
     * This is used to resolve and load a class for the given element.
 88  
     * Resolution of the class to used is done by inspecting the
 89  
     * XML element provided. If there is a "class" attribute on the
 90  
     * element then its value is used to resolve the class to use.
 91  
     * If no such attribute exists on the element this returns null.
 92  
     * 
 93  
     * @param type this is the type of the XML element expected
 94  
     * @param node this is the element used to resolve an override
 95  
     * @param map this is used to maintain contextual information
 96  
     * 
 97  
     * @return returns the class that should be used for the object
 98  
     * 
 99  
     * @throws Exception thrown if the class cannot be resolved
 100  
     */
 101  
    public Value read(Type type, NodeMap node, Map map) throws Exception {
 102  1775560
       Class actual = readValue(type, node);
 103  1775559
       Class expect = type.getType();
 104  
       
 105  1775559
       if(expect.isArray()) {
 106  70
          return readArray(actual, node);   
 107  
       }
 108  1775489
       if(expect != actual) {
 109  70249
          return new ObjectValue(actual);
 110  
       }
 111  1705240
       return null;
 112  
    }
 113  
    
 114  
    /**
 115  
     * This is used to resolve and load a class for the given element.
 116  
     * Resolution of the class to used is done by inspecting the
 117  
     * XML element provided. If there is a "class" attribute on the
 118  
     * element then its value is used to resolve the class to use.
 119  
     * This also expects a "length" attribute for the array length.
 120  
     * 
 121  
     * @param type this is the type of the XML element expected
 122  
     * @param node this is the element used to resolve an override
 123  
     * 
 124  
     * @return returns the class that should be used for the object
 125  
     * 
 126  
     * @throws Exception thrown if the class cannot be resolved
 127  
     */   
 128  
    private Value readArray(Class type, NodeMap node) throws Exception {      
 129  70
       Node entry = node.remove(length);
 130  70
       int size = 0;
 131  
       
 132  70
       if(entry != null) {
 133  63
          String value = entry.getValue();
 134  63
          size = Integer.parseInt(value);
 135  
       }      
 136  70
       return new ArrayValue(type, size);
 137  
    }
 138  
    
 139  
    /**
 140  
     * This is used to resolve and load a class for the given element.
 141  
     * Resolution of the class to used is done by inspecting the
 142  
     * XML element provided. If there is a "class" attribute on the
 143  
     * element then its value is used to resolve the class to use.
 144  
     * If no such attribute exists the specified field is returned,
 145  
     * or if the field type is an array then the component type.
 146  
     * 
 147  
     * @param type this is the type of the XML element expected
 148  
     * @param node this is the element used to resolve an override
 149  
     * 
 150  
     * @return returns the class that should be used for the object
 151  
     * 
 152  
     * @throws Exception thrown if the class cannot be resolved
 153  
     */   
 154  
    private Class readValue(Type type, NodeMap node) throws Exception {      
 155  1775560
       Node entry = node.remove(label);      
 156  1775560
       Class expect = type.getType();
 157  
       
 158  1775560
       if(expect.isArray()) {
 159  70
          expect = expect.getComponentType();
 160  
       }
 161  1775560
       if(entry != null) {
 162  70256
          String name = entry.getValue();
 163  70256
          expect = loader.load(name);
 164  
       }    
 165  1775559
       return expect;
 166  
    }       
 167  
    
 168  
    /**
 169  
     * This is used to attach a attribute to the provided element
 170  
     * that is used to identify the class. The attribute name is
 171  
     * "class" and has the value of the fully qualified class 
 172  
     * name for the object provided. This will only be invoked
 173  
     * if the object class is different from the field class.
 174  
     *
 175  
     * @param type this is the declared class for the field used
 176  
     * @param value this is the instance variable being serialized
 177  
     * @param node this is the element used to represent the value
 178  
     * @param map this is used to maintain contextual information
 179  
     * 
 180  
     * @return this returns true if serialization is complete
 181  
     */   
 182  
    public boolean write(Type type, Object value, NodeMap node, Map map){
 183  520161
       Class actual = value.getClass();
 184  520161
       Class expect = type.getType();
 185  520161
       Class real = actual;
 186  
       
 187  520161
       if(actual.isArray()) {
 188  49
          real = writeArray(expect, value, node);
 189  
       }
 190  520161
       if(actual != expect) {
 191  20276
          node.put(label, real.getName());
 192  
       }       
 193  520161
       return false;
 194  
    }
 195  
    
 196  
    /**
 197  
     * This is used to add a length attribute to the element due to
 198  
     * the fact that the serialized value is an array. The length
 199  
     * of the array is acquired and inserted in to the attributes.
 200  
     * 
 201  
     * @param field this is the field type for the array to set
 202  
     * @param value this is the actual value for the array to set
 203  
     * @param node this is the map of attributes for the element
 204  
     * 
 205  
     * @return returns the array component type that is set
 206  
     */
 207  
    private Class writeArray(Class field, Object value, NodeMap node){
 208  49
       int size = Array.getLength(value);
 209  
       
 210  49
       if(length != null) {       
 211  49
          node.put(length, String.valueOf(size));
 212  
       }
 213  49
       return field.getComponentType();
 214  
    }
 215  
 }