Coverage Report - org.simpleframework.xml.core.ElementArrayLabel
 
Classes in this File Line Coverage Branch Coverage Complexity
ElementArrayLabel
94%
48/51
83%
10/12
1.588
 
 1  
 /*
 2  
  * ElementArrayLabel.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.core;
 20  
 
 21  
 import java.lang.annotation.Annotation;
 22  
 
 23  
 import org.simpleframework.xml.ElementArray;
 24  
 import org.simpleframework.xml.strategy.Type;
 25  
 import org.simpleframework.xml.stream.Format;
 26  
 import org.simpleframework.xml.stream.Style;
 27  
 
 28  
 /**
 29  
  * The <code>ElementArrayLabel</code> represents a label that is used
 30  
  * to represent an XML element array in a class schema. This element 
 31  
  * array label can be used to convert an XML node into an array of
 32  
  * composite or primitive objects. If the array is of primitive types
 33  
  * then the <code>entry</code> attribute must be specified so that 
 34  
  * the primitive values can be serialized in a structured manner.
 35  
  * 
 36  
  * @author Niall Gallagher
 37  
  * 
 38  
  * @see org.simpleframework.xml.ElementArray
 39  
  */
 40  
 class ElementArrayLabel extends TemplateLabel {
 41  
    
 42  
    /**
 43  
     * This is the decorator that is associated with the element.
 44  
     */
 45  
    private Decorator decorator;
 46  
 
 47  
    /**
 48  
     * This references the annotation that the field uses.
 49  
     */
 50  
    private ElementArray label;
 51  
    
 52  
    /**
 53  
     * This contains the details of the annotated contact object.
 54  
     */
 55  
    private Introspector detail;
 56  
    
 57  
    /**
 58  
     * This is the path that is used to represent this attribute.
 59  
     */
 60  
    private Expression path;
 61  
    
 62  
    /**
 63  
     * This is used to style the elements associated with this.
 64  
     */
 65  
    private Format format;
 66  
    
 67  
    /**
 68  
     * This is the type of array this label will represent.
 69  
     */
 70  
    private Class type;
 71  
    
 72  
    /**
 73  
     * This is the name of the XML entry from the annotation.
 74  
     */
 75  
    private String entry;
 76  
    
 77  
    /**
 78  
     * This is the name of the element for this label instance.
 79  
     */
 80  
    private String name;
 81  
    
 82  
    /**
 83  
     * This is used to determine if the element is required.
 84  
     */
 85  
    private boolean required;
 86  
    
 87  
    /**
 88  
     * This is used to determine if the element is data.
 89  
     */
 90  
    private boolean data;
 91  
    
 92  
    /**
 93  
     * Constructor for the <code>ElementArrayLabel</code> object. This
 94  
     * creates a label object, which can be used to convert an element
 95  
     * node to an array of XML serializable objects.
 96  
     * 
 97  
     * @param contact this is the contact that this label represents
 98  
     * @param label the annotation that contains the schema details
 99  
     * @param format this is used to style the elements for this label
 100  
     */
 101  95
    public ElementArrayLabel(Contact contact, ElementArray label, Format format) {
 102  95
       this.detail = new Introspector(contact, this, format);
 103  95
       this.decorator = new Qualifier(contact);
 104  95
       this.required = label.required();
 105  95
       this.type = contact.getType();
 106  95
       this.entry = label.entry();
 107  95
       this.data = label.data();
 108  95
       this.name = label.name();
 109  95
       this.format = format;
 110  95
       this.label = label;
 111  95
    }
 112  
    
 113  
    /**
 114  
     * This is used to acquire the <code>Decorator</code> for this.
 115  
     * A decorator is an object that adds various details to the
 116  
     * node without changing the overall structure of the node. For
 117  
     * example comments and namespaces can be added to the node with
 118  
     * a decorator as they do not affect the deserialization.
 119  
     * 
 120  
     * @return this returns the decorator associated with this
 121  
     */
 122  
    public Decorator getDecorator() throws Exception {
 123  275
       return decorator;
 124  
    }
 125  
 
 126  
    /**
 127  
     * This will create a <code>Converter</code> for transforming an XML
 128  
     * element into an array of XML serializable objects. The XML schema
 129  
     * class for these objects must present the element array annotation. 
 130  
     * 
 131  
     * @param context this is the context object used for serialization
 132  
     * 
 133  
     * @return this returns the converter for creating a collection 
 134  
     */
 135  
    public Converter getConverter(Context context) throws Exception {
 136  302
       Contact contact = getContact();
 137  302
       String entry = getEntry();
 138  
       
 139  302
       if(!type.isArray()) {
 140  0
          throw new InstantiationException("Type is not an array %s for %s", type, contact);
 141  
       }
 142  302
       return getConverter(context, entry);
 143  
    }  
 144  
       
 145  
    /**
 146  
     * This will create a <code>Converter</code> for transforming an XML
 147  
     * element into an array of XML serializable objects. The XML schema
 148  
     * class for these objects must present the element array annotation. 
 149  
     * 
 150  
     * @param context this is the context object used for serialization
 151  
     * @param name this is the name of the entry XML element to use
 152  
     * 
 153  
     * @return this returns the converter for creating a collection 
 154  
     */      
 155  
    private Converter getConverter(Context context, String name) throws Exception {
 156  302
       Type entry = getDependent();
 157  302
       Type type = getContact();
 158  
       
 159  302
       if(!context.isPrimitive(entry)) { 
 160  186
          return new CompositeArray(context, type, entry, name);        
 161  
       }
 162  116
       return new PrimitiveArray(context, type, entry, name);            
 163  
    }
 164  
 
 165  
    /**
 166  
     * This is used to provide a configured empty value used when the
 167  
     * annotated value is null. This ensures that XML can be created
 168  
     * with required details regardless of whether values are null or
 169  
     * not. It also provides a means for sensible default values.
 170  
     * 
 171  
     * @param context this is the context object for the serialization
 172  
     * 
 173  
     * @return this returns the string to use for default values
 174  
     */
 175  
    public Object getEmpty(Context context) throws Exception {
 176  105
       Type array = new ClassType(type);
 177  105
       Factory factory = new ArrayFactory(context, array);
 178  
       
 179  105
       if(!label.empty()) {
 180  3
          return factory.getInstance();
 181  
       }
 182  102
       return null;
 183  
    }
 184  
    
 185  
    /**
 186  
     * This is used to either provide the entry value provided within
 187  
     * the annotation or compute a entry value. If the entry string
 188  
     * is not provided the the entry value is calculated as the type
 189  
     * of primitive the object is as a simplified class name.
 190  
     * 
 191  
     * @return this returns the name of the XML entry element used 
 192  
     */
 193  
    public String getEntry() throws Exception {      
 194  1138
       Style style = format.getStyle();
 195  
       
 196  1138
       if(detail.isEmpty(entry)) {
 197  41
          entry = detail.getEntry();
 198  
       }
 199  1138
       return style.getElement(entry);
 200  
    }
 201  
    
 202  
    /**
 203  
     * This is used to acquire the name of the element or attribute
 204  
     * that is used by the class schema. The name is determined by
 205  
     * checking for an override within the annotation. If it contains
 206  
     * a name then that is used, if however the annotation does not
 207  
     * specify a name the the field or method name is used instead.
 208  
     * 
 209  
     * @return returns the name that is used for the XML property
 210  
     */
 211  
    public String getName() throws Exception{
 212  744
       Style style = format.getStyle();
 213  744
       String name = detail.getName();
 214  
      
 215  744
       return style.getElement(name);
 216  
    }
 217  
    
 218  
    /**
 219  
     * This is used to acquire the path of the element or attribute
 220  
     * that is used by the class schema. The path is determined by
 221  
     * acquiring the XPath expression and appending the name of the
 222  
     * label to form a fully qualified path.
 223  
     * 
 224  
     * @return returns the path that is used for the XML property
 225  
     */
 226  
    public String getPath() throws Exception {
 227  374
       Expression path = getExpression();
 228  374
       String name = getName();
 229  
    
 230  374
       return path.getElement(name);   
 231  
    }
 232  
   
 233  
    /**
 234  
     * This method is used to return an XPath expression that is 
 235  
     * used to represent the position of this label. If there is no
 236  
     * XPath expression associated with this then an empty path is
 237  
     * returned. This will never return a null expression.
 238  
     * 
 239  
     * @return the XPath expression identifying the location
 240  
     */
 241  
    public Expression getExpression() throws Exception {
 242  469
       if(path == null) {
 243  95
          path = detail.getExpression();
 244  
       }
 245  469
       return path;
 246  
    }   
 247  
    
 248  
    /**
 249  
     * This acquires the annotation associated with this label. This
 250  
     * is typically the annotation acquired from the field or method.
 251  
     * However, in the case of unions this will return the actual
 252  
     * annotation within the union group that this represents.
 253  
     * 
 254  
     * @return this returns the annotation that this represents
 255  
     */
 256  
    public Annotation getAnnotation() {
 257  92
       return label;
 258  
    }
 259  
    
 260  
    /**
 261  
     * This is used to acquire the dependent type for the annotated
 262  
     * array. This will simply return the type that the array is
 263  
     * composed to hold. This must be a serializable type, that is,
 264  
     * a type that is annotated with the <code>Root</code> class.
 265  
     * 
 266  
     * @return this returns the component type for the array
 267  
     */
 268  
    public Type getDependent() {
 269  435
       Class entry = type.getComponentType();
 270  
       
 271  435
       if(entry == null) {
 272  0
          return new ClassType(type);
 273  
       }
 274  435
       return new ClassType(entry);
 275  
    }
 276  
    
 277  
    /**
 278  
     * This acts as a convenience method used to determine the type of
 279  
     * contact this represents. This is used when an object is written
 280  
     * to XML. It determines whether a <code>class</code> attribute
 281  
     * is required within the serialized XML element, that is, if the
 282  
     * class returned by this is different from the actual value of the
 283  
     * object to be serialized then that type needs to be remembered.
 284  
     *  
 285  
     * @return this returns the type of the contact class
 286  
     */
 287  
    public Class getType() {
 288  95
       return type;      
 289  
    }
 290  
    
 291  
    /**
 292  
     * This is used to acquire the contact object for this label. The 
 293  
     * contact retrieved can be used to set any object or primitive that
 294  
     * has been deserialized, and can also be used to acquire values to
 295  
     * be serialized in the case of object persistence. All contacts
 296  
     * that are retrieved from this method will be accessible. 
 297  
     * 
 298  
     * @return returns the contact that this label is representing
 299  
     */   
 300  
    public Contact getContact() {
 301  879
       return detail.getContact();
 302  
    }
 303  
    
 304  
    /**
 305  
     * This is used to acquire the name of the element or attribute
 306  
     * as taken from the annotation. If the element or attribute
 307  
     * explicitly specifies a name then that name is used for the
 308  
     * XML element or attribute used. If however no overriding name
 309  
     * is provided then the method or field is used for the name. 
 310  
     * 
 311  
     * @return returns the name of the annotation for the contact
 312  
     */
 313  
    public String getOverride() {
 314  836
       return name;
 315  
    }
 316  
    
 317  
    /**
 318  
     * This is used to determine whether the XML element is required. 
 319  
     * This ensures that if an XML element is missing from a document
 320  
     * that deserialization can continue. Also, in the process of
 321  
     * serialization, if a value is null it does not need to be 
 322  
     * written to the resulting XML document.
 323  
     * 
 324  
     * @return true if the label represents a some required data
 325  
     */  
 326  
    public boolean isRequired() {
 327  92
       return required;
 328  
    }
 329  
    
 330  
    /**
 331  
     * This is used to determine whether the annotation requires it
 332  
     * and its children to be written as a CDATA block. This is done
 333  
     * when a primitive or other such element requires a text value
 334  
     * and that value needs to be encapsulated within a CDATA block.
 335  
     * 
 336  
     * @return this returns true if the element requires CDATA
 337  
     */
 338  
    public boolean isData() {
 339  257
       return data;
 340  
    }
 341  
    
 342  
    /**
 343  
     * This is used to describe the annotation and method or field
 344  
     * that this label represents. This is used to provide error
 345  
     * messages that can be used to debug issues that occur when
 346  
     * processing a method. This will provide enough information
 347  
     * such that the problem can be isolated correctly. 
 348  
     * 
 349  
     * @return this returns a string representation of the label
 350  
     */
 351  
    public String toString() {
 352  0
       return detail.toString();
 353  
    }   
 354  
 }