Coverage Report - org.simpleframework.xml.core.ElementListLabel
 
Classes in this File Line Coverage Branch Coverage Complexity
ElementListLabel
98%
64/65
95%
19/20
1.75
 
 1  
 /*
 2  
  * ElementListLabel.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.ElementList;
 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>ElementListLabel</code> represents a label that is used
 30  
  * to represent an XML element list in a class schema. This element 
 31  
  * list label can be used to convert an XML node into a collection of
 32  
  * composite objects. Each element converted with the converter this
 33  
  * creates must be an XML serializable element.
 34  
  * 
 35  
  * @author Niall Gallagher
 36  
  * 
 37  
  *  @see org.simpleframework.xml.ElementList
 38  
  */
 39  
 class ElementListLabel extends TemplateLabel {
 40  
    
 41  
    /**
 42  
     * This is the decorator that is associated with the element.
 43  
     */
 44  
    private Decorator decorator; 
 45  
    
 46  
    /**
 47  
     * This contains the details of the annotated contact object.
 48  
     */
 49  
    private Introspector detail;
 50  
    
 51  
    /**
 52  
     * This references the annotation that the field uses.
 53  
     */
 54  
    private ElementList label;
 55  
    
 56  
    /**
 57  
     * This is a cache of the expression for this element list.
 58  
     */
 59  
    private Expression cache;
 60  
    
 61  
    /**
 62  
     * This is the format used to style the elements in the list.
 63  
     */  
 64  
    private Format format;
 65  
    
 66  
    /**
 67  
     * This is the name of the element for this label instance.
 68  
     */
 69  
    private String override;  
 70  
    
 71  
    /**
 72  
     * This is the name of the XML entry from the annotation.
 73  
     */
 74  
    private String entry;
 75  
    
 76  
    /**
 77  
     * This is the name of the XML element from the annotation.
 78  
     */
 79  
    private String name;
 80  
    
 81  
    /**
 82  
     * This is the path of the XML entry from the annotation.
 83  
     */
 84  
    private String path;
 85  
    
 86  
    /**
 87  
     * This is the type of collection this list will instantiate.
 88  
     */
 89  
    private Class type;
 90  
    
 91  
    /**
 92  
     * Represents the type of objects this list will hold.
 93  
     */
 94  
    private Class item;
 95  
    
 96  
    /**
 97  
     * This is used to determine if the attribute is required.
 98  
     */
 99  
    private boolean required;
 100  
    
 101  
    /**
 102  
     * This is used to determine if the attribute is inline.
 103  
     */
 104  
    private boolean inline;
 105  
    
 106  
    /**
 107  
     * This is used to determine if the attribute is data.
 108  
     */
 109  
    private boolean data;
 110  
 
 111  
    /**
 112  
     * Constructor for the <code>ElementListLabel</code> object. This
 113  
     * creates a label object, which can be used to convert an XML 
 114  
     * node to a <code>Collection</code> of XML serializable objects.
 115  
     * 
 116  
     * @param contact this is the contact that this label represents
 117  
     * @param label the annotation that contains the schema details
 118  
     * @param format this is used to style the elements in the list
 119  
     */
 120  1205
    public ElementListLabel(Contact contact, ElementList label, Format format) {
 121  1205
       this.detail = new Introspector(contact, this, format);
 122  1205
       this.decorator = new Qualifier(contact);
 123  1205
       this.required = label.required();
 124  1205
       this.type = contact.getType();
 125  1205
       this.override = label.name();  
 126  1205
       this.inline = label.inline();
 127  1205
       this.entry = label.entry();
 128  1205
       this.data = label.data();
 129  1205
       this.item = label.type();
 130  1205
       this.format = format;
 131  1205
       this.label = label;
 132  1205
    }
 133  
    
 134  
    /**
 135  
     * This is used to acquire the <code>Decorator</code> for this.
 136  
     * A decorator is an object that adds various details to the
 137  
     * node without changing the overall structure of the node. For
 138  
     * example comments and namespaces can be added to the node with
 139  
     * a decorator as they do not affect the deserialization.
 140  
     * 
 141  
     * @return this returns the decorator associated with this
 142  
     */
 143  
    public Decorator getDecorator() throws Exception {
 144  22577
       return decorator;
 145  
    }
 146  
 
 147  
    /**
 148  
     * This will create a <code>Converter</code> for transforming an XML
 149  
     * element into a collection of XML serializable objects. The XML
 150  
     * schema class for these objects must be present the element list
 151  
     * annotation. 
 152  
     * 
 153  
     * @param context this is the context object used for serialization
 154  
     * 
 155  
     * @return this returns the converter for creating a collection 
 156  
     */
 157  
    public Converter getConverter(Context context) throws Exception {
 158  94662
       String entry = getEntry();
 159  
       
 160  94662
       if(!label.inline()) {
 161  92715
          return getConverter(context, entry);
 162  
       }
 163  1947
       return getInlineConverter(context, entry);      
 164  
    }
 165  
 
 166  
    /**
 167  
     * This will create a <code>Converter</code> for transforming an XML
 168  
     * element into a collection of XML serializable objects. The XML
 169  
     * schema class for these objects must be present the element list
 170  
     * annotation. 
 171  
     * 
 172  
     * @param context this is the context object used for serialization
 173  
     * @param name this is the name of the XML entry element to use
 174  
     * 
 175  
     * @return this returns the converter for creating a collection 
 176  
     */
 177  
    private Converter getConverter(Context context, String name) throws Exception {  
 178  92715
       Type item = getDependent();
 179  92715
       Type type = getContact();
 180  
       
 181  92715
       if(!context.isPrimitive(item)) {
 182  92558
          return new CompositeList(context, type, item, name);
 183  
       }
 184  157
       return new PrimitiveList(context, type, item, name);      
 185  
    }
 186  
    
 187  
    /**
 188  
     * This will create a <code>Converter</code> for transforming an XML
 189  
     * element into a collection of XML serializable objects. The XML
 190  
     * schema class for these objects must be present the element list
 191  
     * annotation. 
 192  
     * 
 193  
     * @param context this is the context object used for serialization
 194  
     * 
 195  
     * @return this returns the converter for creating a collection 
 196  
     */
 197  
    private Converter getInlineConverter(Context context, String name) throws Exception {  
 198  1947
       Type item = getDependent();
 199  1947
       Type type = getContact();
 200  
       
 201  1947
       if(!context.isPrimitive(item)) {
 202  1720
          return new CompositeInlineList(context, type, item, name);
 203  
       }
 204  227
       return new PrimitiveInlineList(context, type, item, name);      
 205  
    }
 206  
    
 207  
    /**
 208  
     * This is used to provide a configured empty value used when the
 209  
     * annotated value is null. This ensures that XML can be created
 210  
     * with required details regardless of whether values are null or
 211  
     * not. It also provides a means for sensible default values.
 212  
     * 
 213  
     * @param context this is the context object for the serialization
 214  
     * 
 215  
     * @return this returns the string to use for default values
 216  
     */
 217  
    public Object getEmpty(Context context) throws Exception {
 218  72509
       Type list = new ClassType(type);
 219  72509
       Factory factory = new CollectionFactory(context, list);
 220  
       
 221  72509
       if(!label.empty()) {
 222  3
          return factory.getInstance();
 223  
       }
 224  72506
       return null;
 225  
    }
 226  
    
 227  
    /**
 228  
     * This is used to acquire the dependent type for the annotated
 229  
     * list. This will simply return the type that the collection is
 230  
     * composed to hold. This must be a serializable type, that is,
 231  
     * a type that is annotated with the <code>Root</code> class.
 232  
     * 
 233  
     * @return this returns the component type for the collection
 234  
     */
 235  
    public Type getDependent() throws Exception  {      
 236  96192
       Contact contact = getContact();
 237  
      
 238  96192
       if(item == void.class) {
 239  252
          item = contact.getDependent();
 240  
       }        
 241  96192
       if(item == null) {
 242  0
          throw new ElementException("Unable to determine generic type for %s", contact);           
 243  
       }     
 244  96192
       return new ClassType(item);
 245  
    }
 246  
    
 247  
    /**
 248  
     * This is used to either provide the entry value provided within
 249  
     * the annotation or compute a entry value. If the entry string
 250  
     * is not provided the the entry value is calculated as the type
 251  
     * of primitive the object is as a simplified class name.
 252  
     * 
 253  
     * @return this returns the name of the XML entry element used 
 254  
     */
 255  
    public String getEntry() throws Exception {
 256  97032
       Style style = format.getStyle();
 257  
       
 258  97032
       if(detail.isEmpty(entry)) {
 259  365
          entry = detail.getEntry();
 260  
       }
 261  97032
       return style.getElement(entry);
 262  
    }
 263  
    
 264  
    /**
 265  
     * This is used to acquire the name of the element or attribute
 266  
     * that is used by the class schema. The name is determined by
 267  
     * checking for an override within the annotation. If it contains
 268  
     * a name then that is used, if however the annotation does not
 269  
     * specify a name the the field or method name is used instead.
 270  
     * 
 271  
     * @return returns the name that is used for the XML property
 272  
     */
 273  
    public String getName() throws Exception{
 274  25104
       if(name == null) {
 275  1205
          Style style = format.getStyle();
 276  1205
          String value = detail.getName();
 277  
          
 278  1205
          name = style.getElement(value);
 279  
       }
 280  25104
       return name;
 281  
    }
 282  
    
 283  
    /**
 284  
     * This is used to acquire the path of the element or attribute
 285  
     * that is used by the class schema. The path is determined by
 286  
     * acquiring the XPath expression and appending the name of the
 287  
     * label to form a fully qualified path.
 288  
     * 
 289  
     * @return returns the path that is used for the XML property
 290  
     */
 291  
    public String getPath() throws Exception {
 292  4144
       if(path == null) {
 293  1205
          Expression expression = getExpression();
 294  1205
          String name = getName();
 295  
          
 296  1205
          path = expression.getElement(name);  
 297  
       }
 298  4144
       return path;
 299  
    }
 300  
    
 301  
    /**
 302  
     * This method is used to return an XPath expression that is 
 303  
     * used to represent the position of this label. If there is no
 304  
     * XPath expression associated with this then an empty path is
 305  
     * returned. This will never return a null expression.
 306  
     * 
 307  
     * @return the XPath expression identifying the location
 308  
     */
 309  
    public Expression getExpression() throws Exception {
 310  2410
       if(cache == null) {
 311  1205
          cache = detail.getExpression();
 312  
       }
 313  2410
       return cache;
 314  
    }
 315  
    
 316  
    /**
 317  
     * This acquires the annotation associated with this label. This
 318  
     * is typically the annotation acquired from the field or method.
 319  
     * However, in the case of unions this will return the actual
 320  
     * annotation within the union group that this represents.
 321  
     * 
 322  
     * @return this returns the annotation that this represents
 323  
     */
 324  
    public Annotation getAnnotation() {
 325  1165
       return label;
 326  
    }
 327  
    
 328  
    /**
 329  
     * This acts as a convenience method used to determine the type of
 330  
     * contact this represents. This is used when an object is written
 331  
     * to XML. It determines whether a <code>class</code> attribute
 332  
     * is required within the serialized XML element, that is, if the
 333  
     * class returned by this is different from the actual value of the
 334  
     * object to be serialized then that type needs to be remembered.
 335  
     *  
 336  
     * @return this returns the type of the contact class
 337  
     */ 
 338  
    public Class getType() {
 339  1205
       return type;      
 340  
    }
 341  
    
 342  
    /**
 343  
     * This is used to acquire the contact object for this label. The 
 344  
     * contact retrieved can be used to set any object or primitive that
 345  
     * has been deserialized, and can also be used to acquire values to
 346  
     * be serialized in the case of object persistence. All contacts 
 347  
     * that are retrieved from this method will be accessible. 
 348  
     * 
 349  
     * @return returns the contact that this label is representing
 350  
     */
 351  
    public Contact getContact() {
 352  213427
       return detail.getContact();
 353  
    }
 354  
    
 355  
    /**
 356  
     * This is used to acquire the name of the element or attribute
 357  
     * as taken from the annotation. If the element or attribute
 358  
     * explicitly specifies a name then that name is used for the
 359  
     * XML element or attribute used. If however no overriding name
 360  
     * is provided then the method or field is used for the name. 
 361  
     * 
 362  
     * @return returns the name of the annotation for the contact
 363  
     */
 364  
    public String getOverride() {
 365  1533
       return override;
 366  
    }
 367  
    
 368  
    /**
 369  
     * This is used to determine whether the annotation requires it
 370  
     * and its children to be written as a CDATA block. This is done
 371  
     * when a primitive or other such element requires a text value
 372  
     * and that value needs to be encapsulated within a CDATA block.
 373  
     * 
 374  
     * @return currently the element list does not require CDATA
 375  
     */
 376  
    public boolean isData() {
 377  22879
       return data;
 378  
    }
 379  
    
 380  
    /**
 381  
     * This is used to determine if the label is a collection. If the
 382  
     * label represents a collection then any original assignment to
 383  
     * the field or method can be written to without the need to 
 384  
     * create a new collection. This allows obscure collections to be
 385  
     * used and also allows initial entries to be maintained.
 386  
     * 
 387  
     * @return true if the label represents a collection value
 388  
     */
 389  
    public boolean isCollection() {
 390  1165
       return true;
 391  
    }
 392  
    
 393  
    /**
 394  
     * This is used to determine whether the XML element is required. 
 395  
     * This ensures that if an XML element is missing from a document
 396  
     * that deserialization can continue. Also, in the process of
 397  
     * serialization, if a value is null it does not need to be 
 398  
     * written to the resulting XML document.
 399  
     * 
 400  
     * @return true if the label represents a some required data
 401  
     */   
 402  
    public boolean isRequired() {
 403  1165
       return required;
 404  
    }
 405  
    
 406  
    /**
 407  
     * This is used to determine whether the list has been specified
 408  
     * as inline. If the list is inline then no overrides are needed
 409  
     * and the outer XML element for the list is not used.
 410  
     * 
 411  
     * @return this returns whether the annotation is inline
 412  
     */
 413  
    public boolean isInline() {
 414  45820
       return inline;
 415  
    }
 416  
    
 417  
    /**
 418  
     * This is used to describe the annotation and method or field
 419  
     * that this label represents. This is used to provide error
 420  
     * messages that can be used to debug issues that occur when
 421  
     * processing a method. This will provide enough information
 422  
     * such that the problem can be isolated correctly. 
 423  
     * 
 424  
     * @return this returns a string representation of the label
 425  
     */
 426  
    public String toString() {
 427  4
       return detail.toString();
 428  
    }   
 429  
 }