Coverage Report - org.simpleframework.xml.load.Entry
 
Classes in this File Line Coverage Branch Coverage Complexity
Entry
92%
47/51
100%
14/14
2.75
 
 1  
 /*
 2  
  * Entry.java July 2007
 3  
  *
 4  
  * Copyright (C) 2007, 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.ElementMap;
 24  
 
 25  
 /**
 26  
  * The <code>Entry</code> object is used to provide configuration for
 27  
  * the serialization and deserialization of a map. Values taken from
 28  
  * the <code>ElementMap</code> annotation provide a means to specify
 29  
  * how to read and write the map as an XML element. Key and value
 30  
  * objects can be written as composite or primitive values. Primitive
 31  
  * key values can be written as attributes of the resulting entry
 32  
  * and value objects can be written inline if desired.
 33  
  * 
 34  
  * @author Niall Gallagher
 35  
  */
 36  
 class Entry {
 37  
    
 38  
    /**
 39  
     * Provides the default name for entry XML elements of the map.
 40  
     */
 41  
    private static final String DEFAULT_NAME = "entry";
 42  
      
 43  
    /**
 44  
     * Represents the annotation that the map object is labeled with.
 45  
     */
 46  
    private ElementMap label;
 47  
    
 48  
    /**
 49  
     * Provides the point of contact in the object to the map.
 50  
     */
 51  
    private Contact contact;
 52  
    
 53  
    /**
 54  
     * Determines whether the key object is written as an attribute.
 55  
     */
 56  
    private boolean attribute;
 57  
    
 58  
    /**
 59  
     * Provides the class XML schema used for the value objects.
 60  
     */
 61  
    private Class valueType;
 62  
    
 63  
    /**
 64  
     * Provides the class XML schema used for the key objects.
 65  
     */
 66  
    private Class keyType;  
 67  
    
 68  
    /**
 69  
     * Specifies the name of the XML entry element used by the map.
 70  
     */
 71  
    private String entry;
 72  
    
 73  
    /**
 74  
     * Specifies the name of the XML value element used by the map.
 75  
     */
 76  
    private String value;
 77  
    
 78  
    /**
 79  
     * Specifies the name of the XML key node used by the map.
 80  
     */
 81  
    private String key;
 82  
 
 83  
    /**
 84  
     * Constructor for the <code>Entry</code> object. This takes the
 85  
     * element map annotation that provides configuration as to how
 86  
     * the map is serialized and deserialized from the XML document. 
 87  
     * The entry object provides a convenient means to access the XML
 88  
     * schema configuration using defaults where necessary.
 89  
     * 
 90  
     * @param contact this is the point of contact to the map object
 91  
     * @param label the annotation the map method or field uses
 92  
     */
 93  38
    public Entry(Contact contact, ElementMap label) {  
 94  38
       this.attribute = label.attribute();   
 95  38
       this.entry = label.entry();
 96  38
       this.value = label.value();
 97  38
       this.key = label.key();
 98  38
       this.key = label.key();
 99  38
       this.contact = contact;
 100  38
       this.label = label;
 101  38
    }
 102  
    
 103  
    /**
 104  
     * This is used to acquire the dependant key for the annotated
 105  
     * map. This will simply return the type that the map object is
 106  
     * composed to hold. This must be a serializable type, that is,
 107  
     * it must be a composite or supported primitive type.
 108  
     * 
 109  
     * @return this returns the key object type for the map object
 110  
     */
 111  
    protected Class getKeyType() throws Exception  {
 112  102
       if(keyType != null) {
 113  0
          return keyType;
 114  
       }
 115  102
       Class keyType = label.keyType();
 116  
       
 117  102
       if(keyType == void.class) {
 118  91
          keyType = getDependant(0);
 119  
       }
 120  102
       return keyType;
 121  
    }
 122  
    
 123  
    /**
 124  
     * This is used to get the key converter for the entry. This knows
 125  
     * whether the key type is a primitive or composite object and will
 126  
     * provide the appropriate converter implementation. This allows 
 127  
     * the root composite map converter to concern itself with only the
 128  
     * details of the surrounding entry object. 
 129  
     * 
 130  
     * @param root this is the root context for the serialization
 131  
     * 
 132  
     * @return returns the converter used for serializing the key
 133  
     */
 134  
    public Converter getKey(Source root) throws Exception {
 135  96
       Class type = getKeyType();
 136  
 
 137  96
       if(Factory.isPrimitive(type)) {        
 138  64
          return new PrimitiveKey(root, this, type);
 139  
       }
 140  32
       return new CompositeKey(root, this, type);
 141  
    }   
 142  
    
 143  
    /**
 144  
     * Represents whether the key value is to be an attribute or an
 145  
     * element. This allows the key to be embedded within the entry
 146  
     * XML element allowing for a more compact representation. Only
 147  
     * primitive key objects can be represented as an attribute. For
 148  
     * example a <code>java.util.Date</code> or a string could be
 149  
     * represented as an attribute key for the generated XML. 
 150  
     *  
 151  
     * @return true if the key is to be inlined as an attribute
 152  
     */
 153  
    public boolean isAttribute() {
 154  420
       return attribute;
 155  
    }
 156  
    
 157  
    /**
 158  
     * This is used to acquire the dependant value for the annotated
 159  
     * map. This will simply return the type that the map object is
 160  
     * composed to hold. This must be a serializable type, that is,
 161  
     * it must be a composite or supported primitive type.
 162  
     * 
 163  
     * @return this returns the value object type for the map object
 164  
     */
 165  
    protected Class getValueType() throws Exception {
 166  102
       if(valueType != null) {
 167  0
          return valueType;
 168  
       }
 169  102
       Class valueType = label.valueType();
 170  
       
 171  102
       if(valueType == void.class) {
 172  91
          valueType = getDependant(1);
 173  
       }
 174  102
       return valueType;
 175  
    }
 176  
    
 177  
    /**
 178  
     * This is used to get the value converter for the entry. This knows
 179  
     * whether the value type is a primitive or composite object and will
 180  
     * provide the appropriate converter implementation. This allows 
 181  
     * the root composite map converter to concern itself with only the
 182  
     * details of the surrounding entry object. 
 183  
     * 
 184  
     * @param root this is the root context for the serialization
 185  
     * 
 186  
     * @return returns the converter used for serializing the value
 187  
     */
 188  
    public Converter getValue(Source root) throws Exception {
 189  96
       Class value = getValueType();
 190  
           
 191  96
       if(Factory.isPrimitive(value)) {
 192  55
          return new PrimitiveValue(root, this, value);
 193  
       }
 194  41
       return new CompositeValue(root, this, value);
 195  
    }
 196  
    
 197  
    /**
 198  
     * Represents whether the value is to be written as an inline text
 199  
     * value within the element. This is only possible if the key has
 200  
     * been specified as an attribute. Also, the value can only be
 201  
     * inline if there is no wrapping value XML element specified.
 202  
     * 
 203  
     * @return this returns true if the value can be written inline
 204  
     */
 205  
    public boolean isInline() throws Exception {
 206  156
       return isAttribute();
 207  
    }
 208  
    
 209  
    /**
 210  
     * Provides the dependant class for the map as taken from the 
 211  
     * specified index. This allows the entry to fall back on generic
 212  
     * declarations of the map if no explicit dependant types are 
 213  
     * given within the element map annotation.
 214  
     * 
 215  
     * @param index this is the index to acquire the parameter from
 216  
     * 
 217  
     * @return this returns the generic type at the specified index
 218  
     */
 219  
    private Class getDependant(int index) throws Exception {
 220  182
       Class[] list = contact.getDependants();
 221  
       
 222  182
       if(list.length < index) {
 223  0
          throw new PersistenceException("Could not find dependant at index %s", index);
 224  
       }
 225  182
       return list[index];
 226  
    }
 227  
    
 228  
    /**
 229  
     * This is used to provide a key XML element for each of the
 230  
     * keys within the map. This essentially wraps the entity to
 231  
     * be serialized such that there is an extra XML element present.
 232  
     * This can be used to override the default names of primitive
 233  
     * keys, however it can also be used to wrap composite keys. 
 234  
     * 
 235  
     * @return this returns the key XML element for each key
 236  
     */
 237  
    public String getKey() throws Exception {
 238  269
       if(key == null) {
 239  172
          return key;
 240  
       }    
 241  97
       if(isEmpty(key)) {
 242  22
          key = null;
 243  
       }      
 244  97
       return key;
 245  
    } 
 246  
    
 247  
    /**
 248  
     * This is used to provide a value XML element for each of the
 249  
     * values within the map. This essentially wraps the entity to
 250  
     * be serialized such that there is an extra XML element present.
 251  
     * This can be used to override the default names of primitive
 252  
     * values, however it can also be used to wrap composite values. 
 253  
     * 
 254  
     * @return this returns the value XML element for each value
 255  
     */
 256  
    public String getValue() throws Exception {
 257  240
       if(value == null) {
 258  163
          return value;
 259  
       }
 260  77
       if(isEmpty(value)) {
 261  17
          value = null;
 262  
       }
 263  77
       return value;
 264  
    }
 265  
    
 266  
    /**
 267  
     * This is used to provide a the name of the entry XML element 
 268  
     * that wraps the key and value elements. If specified the entry
 269  
     * value specified will be used instead of the default name of 
 270  
     * the element. This is used to ensure the resulting XML is 
 271  
     * configurable to the requirements of the generated XML. 
 272  
     * 
 273  
     * @return this returns the entry XML element for each entry
 274  
     */
 275  
    public String getEntry() throws Exception {
 276  101
       if(entry == null) {
 277  0
          return entry;
 278  
       }
 279  101
       if(isEmpty(entry)) {
 280  15
          entry = DEFAULT_NAME;
 281  
       }      
 282  101
       return entry;
 283  
    } 
 284  
    
 285  
    /**
 286  
     * This method is used to determine if a root annotation value is
 287  
     * an empty value. Rather than determining if a string is empty
 288  
     * be comparing it to an empty string this method allows for the
 289  
     * value an empty string represents to be changed in future.
 290  
     * 
 291  
     * @param value this is the value to determine if it is empty
 292  
     * 
 293  
     * @return true if the string value specified is an empty value
 294  
     */
 295  
    private boolean isEmpty(String value) {
 296  275
       return value.length() == 0;
 297  
    }  
 298  
 }