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