Coverage Report - org.simpleframework.xml.core.Composite
 
Classes in this File Line Coverage Branch Coverage Complexity
Composite
97%
372/381
87%
147/168
3.038
Composite$1
N/A
N/A
3.038
Composite$Builder
100%
15/15
N/A
3.038
Composite$Injector
100%
15/15
N/A
3.038
 
 1  
 /*
 2  
  * Composite.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 org.simpleframework.xml.Version;
 22  
 import org.simpleframework.xml.strategy.Type;
 23  
 import org.simpleframework.xml.stream.InputNode;
 24  
 import org.simpleframework.xml.stream.NamespaceMap;
 25  
 import org.simpleframework.xml.stream.NodeMap;
 26  
 import org.simpleframework.xml.stream.OutputNode;
 27  
 import org.simpleframework.xml.stream.Position;
 28  
 
 29  
 /**
 30  
  * The <code>Composite</code> object is used to perform serialization
 31  
  * of objects that contain XML annotations. Composite objects are objects
 32  
  * that are not primitive and contain references to serializable fields.
 33  
  * This <code>Converter</code> will visit each field within the object
 34  
  * and deserialize or serialize that field depending on the requested
 35  
  * action. If a required field is not present when deserializing from
 36  
  * an XML element this terminates and deserialization reports the error.
 37  
  * <pre>
 38  
  * 
 39  
  *    &lt;element name="test" class="some.package.Type"&gt;
 40  
  *       &lt;text&gt;string value&lt;/text&gt;
 41  
  *       &lt;integer&gt;1234&lt;/integer&gt;
 42  
  *    &lt;/element&gt;
 43  
  * 
 44  
  * </pre>
 45  
  * To deserialize the above XML source this will attempt to match the
 46  
  * attribute name with an <code>Attribute</code> annotation from the
 47  
  * XML schema class, which is specified as "some.package.Type". This
 48  
  * type must also contain <code>Element</code> annotations for the
 49  
  * "text" and "integer" elements.
 50  
  * <p>
 51  
  * Serialization requires that contacts marked as required must have
 52  
  * values that are not null. This ensures that the serialized object
 53  
  * can be deserialized at a later stage using the same class schema.
 54  
  * If a required value is null the serialization terminates and an
 55  
  * exception is thrown.
 56  
  * 
 57  
  * @author Niall Gallagher
 58  
  */
 59  2851914
 class Composite implements Converter {
 60  
   
 61  
    /**
 62  
     * This factory creates instances of the deserialized object.
 63  
     */
 64  
    private final ObjectFactory factory;
 65  
    
 66  
    /**
 67  
     * This is used to convert any primitive values that are needed.
 68  
     */
 69  
    private final Primitive primitive;
 70  
    
 71  
    /**
 72  
     * This is used to store objects so that they can be read again.
 73  
     */
 74  
    private final Criteria criteria;
 75  
    
 76  
    /**
 77  
     * This is the current revision of this composite converter.
 78  
     */
 79  
    private final Revision revision; 
 80  
    
 81  
    /**
 82  
     * This is the source object for the instance of serialization.
 83  
     */
 84  
    private final Context context;
 85  
    
 86  
    /**
 87  
     * This is the type that this composite produces instances of.
 88  
     */
 89  
    private final Type type;
 90  
    
 91  
    /**
 92  
     * Constructor for the <code>Composite</code> object. This creates 
 93  
     * a converter object capable of serializing and deserializing root
 94  
     * objects labeled with XML annotations. The XML schema class must 
 95  
     * be given to the instance in order to perform deserialization.
 96  
     *  
 97  
     * @param context the source object used to perform serialization
 98  
     * @param type this is the XML schema type to use for this
 99  
     */
 100  
    public Composite(Context context, Type type) {
 101  935338
       this(context, type, null);
 102  935338
    }
 103  
         
 104  
    /**
 105  
     * Constructor for the <code>Composite</code> object. This creates 
 106  
     * a converter object capable of serializing and deserializing root
 107  
     * objects labeled with XML annotations. The XML schema class must 
 108  
     * be given to the instance in order to perform deserialization.
 109  
     *  
 110  
     * @param context the source object used to perform serialization
 111  
     * @param type this is the XML schema type to use for this
 112  
     * @param override this is the override type declared for this
 113  
     */
 114  935502
    public Composite(Context context, Type type, Class override) {
 115  935502
       this.factory = new ObjectFactory(context, type, override);  
 116  935502
       this.primitive = new Primitive(context, type);
 117  935502
       this.criteria = new Collector();
 118  935502
       this.revision = new Revision();
 119  935502
       this.context = context;
 120  935502
       this.type = type;
 121  935502
    }
 122  
 
 123  
    /**
 124  
     * This <code>read</code> method performs deserialization of the XML
 125  
     * schema class type by traversing the contacts and instantiating them
 126  
     * using details from the provided XML element. Because this will
 127  
     * convert a non-primitive value it delegates to other converters to
 128  
     * perform deserialization of lists and primitives.
 129  
     * <p>
 130  
     * If any of the required contacts are not present within the provided
 131  
     * XML element this will terminate deserialization and throw an
 132  
     * exception. The annotation missing is reported in the exception.
 133  
     * 
 134  
     * @param node the XML element contact values are deserialized from
 135  
     * 
 136  
     * @return this returns the fully deserialized object graph
 137  
     */
 138  
    public Object read(InputNode node) throws Exception {
 139  716147
       Instance value = factory.getInstance(node); 
 140  716147
       Class type = value.getType(); 
 141  
       
 142  716147
       if(value.isReference()) {      
 143  3102
          return value.getInstance(); 
 144  
       }
 145  713045
       if(context.isPrimitive(type)) { 
 146  56
          return readPrimitive(node, value);
 147  
       }
 148  712989
       return read(node, value, type);
 149  
    }
 150  
    
 151  
    /**
 152  
     * This <code>read</code> method performs deserialization of the XML
 153  
     * schema class type by traversing the contacts and instantiating them
 154  
     * using details from the provided XML element. Because this will
 155  
     * convert a non-primitive value it delegates to other converters to
 156  
     * perform deserialization of lists and primitives.
 157  
     * <p>
 158  
     * If any of the required contacts are not present within the provided
 159  
     * XML element this will terminate deserialization and throw an
 160  
     * exception. The annotation missing is reported in the exception.
 161  
     * 
 162  
     * @param node the XML element contact values are deserialized from
 163  
     * @param source the object whose contacts are to be deserialized
 164  
     * 
 165  
     * @return this returns the fully deserialized object graph 
 166  
     */
 167  
    public Object read(InputNode node, Object source) throws Exception {
 168  2
       Class type = source.getClass();
 169  2
       Schema schema = context.getSchema(type);
 170  2
       Caller caller = schema.getCaller();
 171  
       
 172  2
       read(node, source, schema); 
 173  2
       criteria.commit(source);
 174  2
       caller.validate(source);
 175  2
       caller.commit(source);
 176  
       
 177  2
       return readResolve(node, source, caller);
 178  
    }
 179  
    
 180  
    /**
 181  
     * This <code>read</code> method performs deserialization of the XML
 182  
     * schema class type by traversing the contacts and instantiating them
 183  
     * using details from the provided XML element. Because this will
 184  
     * convert a non-primitive value it delegates to other converters to
 185  
     * perform deserialization of lists and primitives.
 186  
     * <p>
 187  
     * If any of the required contacts are not present within the provided
 188  
     * XML element this will terminate deserialization and throw an
 189  
     * exception. The annotation missing is reported in the exception.
 190  
     * 
 191  
     * @param node the XML element contact values are deserialized from
 192  
     * @param value this is the instance for the object within the graph
 193  
     * @param real this is the real type that is to be evaluated
 194  
     * 
 195  
     * @return this returns the fully deserialized object graph
 196  
     */
 197  
    private Object read(InputNode node, Instance value, Class real) throws Exception {
 198  712989
       Schema schema = context.getSchema(real);
 199  712981
       Caller caller = schema.getCaller();
 200  712981
       Builder builder = read(schema, value);
 201  712981
       Object source = builder.read(node);
 202  
       
 203  712952
       caller.validate(source); 
 204  712952
       caller.commit(source); 
 205  712952
       value.setInstance(source);
 206  
       
 207  712952
       return readResolve(node, source, caller);    
 208  
    }
 209  
    
 210  
    /**
 211  
     * This <code>read</code> method performs deserialization of the XML
 212  
     * schema class type by traversing the contacts and instantiating them
 213  
     * using details from the provided XML element. Because this will
 214  
     * convert a non-primitive value it delegates to other converters to
 215  
     * perform deserialization of lists and primitives.
 216  
     * <p>
 217  
     * If any of the required contacts are not present within the provided
 218  
     * XML element this will terminate deserialization and throw an
 219  
     * exception. The annotation missing is reported in the exception.
 220  
     * 
 221  
     * @param node the XML element contact values are deserialized from
 222  
     * @param schema this is the schema for the class to be deserialized
 223  
     * @param value this is the value used for the deserialization
 224  
     * 
 225  
     * @return this returns the fully deserialized object graph
 226  
     */
 227  
    private Builder read(Schema schema, Instance value) throws Exception {
 228  712981
       Instantiator creator = schema.getInstantiator();
 229  
       
 230  712981
       if(creator.isDefault()) {
 231  712704
          return new Builder(this, criteria, schema, value);
 232  
       } 
 233  277
       return new Injector(this, criteria, schema, value);
 234  
    }
 235  
 
 236  
    /**
 237  
     * This <code>readPrimitive</code> method will extract the text value
 238  
     * from the node and replace any template variables before converting
 239  
     * it to a primitive value. This uses a <code>Primitive</code> object
 240  
     * to convert the node text to the resulting string. This will also
 241  
     * respect all references on the node so cycle can be followed.
 242  
     *
 243  
     * @param node this is the node to be converted to a primitive
 244  
     * @param value this is the type for the object within the graph
 245  
     *
 246  
     * @return this returns the primitive that has been deserialized
 247  
     */ 
 248  
    private Object readPrimitive(InputNode node, Instance value) throws Exception {
 249  56
       Class type = value.getType();
 250  56
       Object result = primitive.read(node, type);
 251  
 
 252  56
       if(type != null) {
 253  56
          value.setInstance(result);
 254  
       }
 255  56
       return result;
 256  
    }
 257  
    
 258  
    /**
 259  
     * The <code>readResolve</code> method is used to determine if there 
 260  
     * is a resolution method which can be used to substitute the object
 261  
     * deserialized. The resolve method is used when an object wishes 
 262  
     * to provide a substitute within the deserialized object graph.
 263  
     * This acts as an equivalent to the Java Object Serialization
 264  
     * <code>readResolve</code> method for the object deserialization.
 265  
     * 
 266  
     * @param node the XML element object provided as a replacement
 267  
     * @param source the type of the object that is being deserialized
 268  
     * @param caller this is used to invoke the callback methods
 269  
     * 
 270  
     * @return this returns a replacement for the deserialized object
 271  
     */
 272  
    private Object readResolve(InputNode node, Object source, Caller caller) throws Exception {
 273  712954
       if(source != null) {
 274  712954
          Position line = node.getPosition();
 275  712954
          Object value = caller.resolve(source);
 276  712954
          Class expect = type.getType();
 277  712954
          Class real = value.getClass();
 278  
       
 279  712954
          if(!expect.isAssignableFrom(real)) {
 280  0
             throw new ElementException("Type %s does not match %s at %s", real, expect, line);              
 281  
          }
 282  712954
          return value;
 283  
       }
 284  0
       return source;
 285  
    }
 286  
    
 287  
    /**
 288  
     * This <code>read</code> method performs deserialization of the XML
 289  
     * schema class type by traversing the contacts and instantiating them
 290  
     * using details from the provided XML element. Because this will
 291  
     * convert a non-primitive value it delegates to other converters to
 292  
     * perform deserialization of lists and primitives.
 293  
     * <p>
 294  
     * If any of the required contacts are not present within the provided
 295  
     * XML element this will terminate deserialization and throw an
 296  
     * exception. The annotation missing is reported in the exception.
 297  
     * 
 298  
     * @param node the XML element contact values are deserialized from
 299  
     * @param source this type of the object that is to be deserialized
 300  
     * @param schema this object visits the objects contacts
 301  
     */
 302  
    private void read(InputNode node, Object source, Schema schema) throws Exception {
 303  2
       Section section = schema.getSection();
 304  
       
 305  2
       readVersion(node, source, schema);
 306  2
       readSection(node, source, section);
 307  2
    }   
 308  
    
 309  
    /**
 310  
     * This <code>readSection</code> method performs deserialization of a
 311  
     * schema class type by traversing the contacts and instantiating them
 312  
     * using details from the provided XML element. Because this will
 313  
     * convert a non-primitive value it delegates to other converters to
 314  
     * perform deserialization of lists and primitives.
 315  
     * <p>
 316  
     * If any of the required contacts are not present within the provided
 317  
     * XML element this will terminate deserialization and throw an
 318  
     * exception. The annotation missing is reported in the exception.
 319  
     * 
 320  
     * @param node the XML element contact values are deserialized from
 321  
     * @param source this type of the object that is to be deserialized
 322  
     * @param section this is the XML section that contains the structure
 323  
     */
 324  
    private void readSection(InputNode node, Object source, Section section) throws Exception {
 325  236
       readText(node, source, section);
 326  236
       readAttributes(node, source, section);
 327  236
       readElements(node, source, section);
 328  236
    }
 329  
    
 330  
    /**
 331  
     * This method is used to read the version from the provided input
 332  
     * node. Once the version has been read it is used to determine how
 333  
     * to deserialize the object. If the version is not the initial
 334  
     * version then it is read in a manner that ignores excessive XML
 335  
     * elements and attributes. Also none of the annotated fields or
 336  
     * methods are required if the version is not the initial version.
 337  
     * 
 338  
     * @param node the XML element contact values are deserialized from
 339  
     * @param source this object whose contacts are to be deserialized
 340  
     * @param schema this object visits the objects contacts
 341  
     */
 342  
    private void readVersion(InputNode node, Object source, Schema schema) throws Exception {
 343  712983
       Label label = schema.getVersion();
 344  712983
       Class expect = type.getType();
 345  
       
 346  712983
       if(label != null) {
 347  11
          String name = label.getName();
 348  11
          NodeMap<InputNode> map = node.getAttributes();
 349  11
          InputNode value = map.remove(name);
 350  
          
 351  11
          if(value != null) {
 352  7
             readVersion(value, source, label);
 353  
          } else {
 354  4
             Version version = context.getVersion(expect);
 355  4
             Double start = revision.getDefault();
 356  4
             Double expected = version.revision();
 357  
             
 358  4
             criteria.set(label, start);
 359  4
             revision.compare(expected, start);
 360  
          }
 361  
       }
 362  712983
    }
 363  
    
 364  
    /**
 365  
     * This method is used to read the version from the provided input
 366  
     * node. Once the version has been read it is used to determine how
 367  
     * to deserialize the object. If the version is not the initial
 368  
     * version then it is read in a manner that ignores excessive XML
 369  
     * elements and attributes. Also none of the annotated fields or
 370  
     * methods are required if the version is not the initial version.
 371  
     * 
 372  
     * @param node the XML element contact values are deserialized from
 373  
     * @param source the type of the object that is being deserialized
 374  
     * @param label this is the label used to read the version attribute
 375  
     */
 376  
    private void readVersion(InputNode node, Object source, Label label) throws Exception {
 377  7
       Object value = readInstance(node, source, label);
 378  7
       Class expect = type.getType();
 379  
      
 380  7
       if(value != null) {
 381  7
          Version version = context.getVersion(expect);
 382  7
          Double actual = version.revision();
 383  
          
 384  7
          if(!value.equals(revision)) {
 385  7
             revision.compare(actual, value);
 386  
          }
 387  
       } 
 388  7
    }
 389  
 
 390  
    /**
 391  
     * This <code>readAttributes</code> method reads the attributes from
 392  
     * the provided XML element. This will iterate over all attributes
 393  
     * within the element and convert those attributes as primitives to
 394  
     * contact values within the source object.
 395  
     * <p>
 396  
     * Once all attributes within the XML element have been evaluated
 397  
     * the <code>Schema</code> is checked to ensure that there are no
 398  
     * required contacts annotated with the <code>Attribute</code> that
 399  
     * remain. If any required attribute remains an exception is thrown. 
 400  
     * 
 401  
     * @param node this is the XML element to be evaluated
 402  
     * @param source the type of the object that is being deserialized
 403  
     * @param section this is the XML section that contains the structure
 404  
     */
 405  
    private void readAttributes(InputNode node, Object source, Section section) throws Exception {
 406  713215
       NodeMap<InputNode> list = node.getAttributes();
 407  713215
       LabelMap map = section.getAttributes();
 408  
 
 409  713215
       for(String name : list) {         
 410  706897
          InputNode value = node.getAttribute(name);
 411  
          
 412  706897
          if(value != null) {
 413  706897
             readAttribute(value, source, section, map);
 414  
          }
 415  706895
       }  
 416  713213
       validate(node, map, source);
 417  713209
    }
 418  
 
 419  
    /**
 420  
     * This <code>readElements</code> method reads the elements from 
 421  
     * the provided XML element. This will iterate over all elements
 422  
     * within the element and convert those elements to primitives or
 423  
     * composite objects depending on the contact annotation.
 424  
     * <p>
 425  
     * Once all elements within the XML element have been evaluated
 426  
     * the <code>Schema</code> is checked to ensure that there are no
 427  
     * required contacts annotated with the <code>Element</code> that
 428  
     * remain. If any required element remains an exception is thrown. 
 429  
     * 
 430  
     * @param node this is the XML element to be evaluated
 431  
     * @param source the type of the object that is being deserialized
 432  
     * @param section the XML section that contains the structure
 433  
     */
 434  
    private void readElements(InputNode node, Object source, Section section) throws Exception {
 435  713209
       LabelMap map = section.getElements();
 436  713209
       InputNode child = node.getNext();
 437  
       
 438  2074716
       while(child != null) {         
 439  1361525
          String name = child.getName();
 440  1361525
          Section block = section.getSection(name);         
 441  
          
 442  1361525
          if(block != null) {
 443  234
             readSection(child, source, block);
 444  
          } else { 
 445  1361291
             readElement(child, source, section, map);
 446  
          }
 447  1361507
          child = node.getNext();
 448  1361507
       } 
 449  713191
       validate(node, map, source);
 450  713188
    }
 451  
    
 452  
    /**
 453  
     * This <code>readText</code> method is used to read the text value
 454  
     * from the XML element node specified. This will check the class
 455  
     * schema to determine if a <code>Text</code> annotation was
 456  
     * specified. If one was specified then the text within the XML
 457  
     * element input node is used to populate the contact value.
 458  
     * 
 459  
     * @param node this is the XML element to acquire the text from
 460  
     * @param source the type of the object that is being deserialized
 461  
     * @param section this is used to visit the element contacts
 462  
     */
 463  
    private void readText(InputNode node, Object source, Section section) throws Exception {
 464  713217
       Label label = section.getText();
 465  
       
 466  713217
       if(label != null) {
 467  233
          readInstance(node, source, label);
 468  
       }
 469  713215
    }
 470  
    
 471  
    /**
 472  
     * This <code>readAttribute</code> method is used for deserialization
 473  
     * of the provided node object using a delegate converter. This is
 474  
     * typically another <code>Composite</code> converter, or if the
 475  
     * node is an attribute a <code>Primitive</code> converter. When
 476  
     * the delegate converter has completed the deserialized value is
 477  
     * assigned to the contact.
 478  
     * 
 479  
     * @param node this is the node that contains the contact value
 480  
     * @param source the type of the object that is being deserialized
 481  
     * @param section this is the section to read the attribute from
 482  
     * @param map this is the map that contains the label objects
 483  
     */
 484  
    private void readAttribute(InputNode node, Object source, Section section, LabelMap map) throws Exception {
 485  706897
       String name = node.getName();
 486  706897
       String path = section.getAttribute(name);
 487  706897
       Label label = map.getLabel(path);
 488  
       
 489  706897
       if(label == null) {
 490  16
          Position line = node.getPosition();
 491  16
          Class expect = context.getType(type, source);
 492  
 
 493  16
          if(map.isStrict(context) && revision.isEqual()) {              
 494  2
             throw new AttributeException("Attribute '%s' does not have a match in %s at %s", path, expect, line);
 495  
          }            
 496  14
       } else {
 497  706881
          readInstance(node, source, label);
 498  
       }         
 499  706895
    }
 500  
 
 501  
    /**
 502  
     * This <code>readElement</code> method is used for deserialization
 503  
     * of the provided node object using a delegate converter. This is
 504  
     * typically another <code>Composite</code> converter, or if the
 505  
     * node is an attribute a <code>Primitive</code> converter. When
 506  
     * the delegate converter has completed the deserialized value is
 507  
     * assigned to the contact.
 508  
     * 
 509  
     * @param node this is the node that contains the contact value
 510  
     * @param source the type of the object that is being deserialized
 511  
     * @param section this is the section to read the element from
 512  
     * @param map this is the map that contains the label objects
 513  
     */
 514  
    private void readElement(InputNode node, Object source, Section section, LabelMap map) throws Exception {
 515  1361291
       String name = node.getName();
 516  1361291
       String path = section.getPath(name); 
 517  1361291
       Label label = map.getLabel(path);      
 518  
 
 519  1361291
       if(label == null) {
 520  1132
          label = criteria.resolve(path);
 521  
       }
 522  1361291
       if(label == null) {
 523  14
          Position line = node.getPosition();
 524  14
          Class expect = context.getType(type, source);
 525  
          
 526  14
          if(map.isStrict(context) && revision.isEqual()) {              
 527  3
             throw new ElementException("Element '%s' does not have a match in %s at %s", path, expect, line);
 528  
          } else {
 529  11
             node.skip();                 
 530  
          }
 531  11
       } else {
 532  1361277
          readUnion(node, source, map, label);
 533  
       }         
 534  1361273
    }
 535  
    
 536  
    /**
 537  
     * The <code>readUnion</code> method is determine the unions 
 538  
     * for a particular label and set the value of that union to
 539  
     * the same value as the label. This helps the deserialization 
 540  
     * process by ensuring once a union is read it is not
 541  
     * replaced. This is also required when reading inline lists.
 542  
     * 
 543  
     * @param node this is the XML element to read the elements from
 544  
     * @param source this is the instance to read the unions from
 545  
     * @param map this is the label map associated with the label
 546  
     * @param label this is the label used to define the XML element
 547  
     */
 548  
    private void readUnion(InputNode node, Object source, LabelMap map, Label label) throws Exception {
 549  1361277
       Object value = readInstance(node, source, label);
 550  1361262
       String[] list = label.getPaths();
 551  
       
 552  2723060
       for(String key : list) {
 553  1361798
          map.getLabel(key);
 554  
       }
 555  1361262
       if(label.isInline()) {
 556  1327
          criteria.set(label, value);
 557  
       }
 558  1361262
    }
 559  
 
 560  
    /**
 561  
     * This <code>read</code> method is used to perform deserialization
 562  
     * of the provided node object using a delegate converter. This is
 563  
     * typically another <code>Composite</code> converter, or if the
 564  
     * node is an attribute a <code>Primitive</code> converter. When
 565  
     * the delegate converter has completed the deserialized value is
 566  
     * assigned to the contact.
 567  
     * 
 568  
     * @param node this is the node that contains the contact value
 569  
     * @param source the type of the object that is being deserialized
 570  
     * @param label this is the label used to create the converter
 571  
     */
 572  
    private Object readInstance(InputNode node, Object source, Label label) throws Exception {    
 573  2068398
       Object object = readVariable(node, source, label);
 574  
     
 575  2068386
       if(object == null) {     
 576  13
          Position line = node.getPosition();
 577  13
          Class expect = context.getType(type, source);
 578  
          
 579  13
          if(label.isRequired() && revision.isEqual()) {              
 580  5
             throw new ValueRequiredException("Empty value for %s in %s at %s", label, expect, line);
 581  
          }
 582  8
       } else {
 583  2068373
          if(object != label.getEmpty(context)) {  
 584  2068366
             criteria.set(label, object);
 585  
          }
 586  
       }
 587  2068381
       return object;
 588  
    }  
 589  
    
 590  
    /**
 591  
     * This <code>readObject</code> method is used to perform the
 592  
     * deserialization of the XML in to any original value. If there
 593  
     * is no original value then this will do a read and instantiate
 594  
     * a new value to deserialize in to. Reading in to the original
 595  
     * ensures that existing lists or maps can be read in to.
 596  
     * 
 597  
     * @param node this is the node that contains the contact value
 598  
     * @param source the source object to assign the contact value to
 599  
     * @param label this is the label used to create the converter
 600  
     * 
 601  
     * @return this returns the original value deserialized in to
 602  
     */
 603  
    private Object readVariable(InputNode node, Object source, Label label) throws Exception {    
 604  2068398
       Converter reader = label.getConverter(context);   
 605  
       
 606  2068397
       if(label.isCollection()) {
 607  72696
          Variable variable = criteria.get(label);
 608  72696
          Contact contact = label.getContact();
 609  
          
 610  72696
          if(variable != null) {
 611  1132
             Object value = variable.getValue();
 612  
 
 613  1132
             return reader.read(node, value);
 614  
          }
 615  71564
          if(source != null) {
 616  71525
             Object value = contact.get(source);
 617  
          
 618  71525
             if(value != null) {
 619  1149
                return reader.read(node, value);
 620  
             }
 621  
          }
 622  
       }
 623  2066116
       return reader.read(node);
 624  
    }
 625  
 
 626  
    /**
 627  
     * This method checks to see if there are any <code>Label</code>
 628  
     * objects remaining in the provided map that are required. This is
 629  
     * used when deserialization is performed to ensure the the XML
 630  
     * element deserialized contains sufficient details to satisfy the
 631  
     * XML schema class annotations. If there is a required label that
 632  
     * remains it is reported within the exception thrown.
 633  
     * 
 634  
     * @param node this is the node that contains the contact value
 635  
     * @param map this is the map to check for remaining labels
 636  
     * @param source this is the object that has been deserialized 
 637  
     */
 638  
    private void validate(InputNode node, LabelMap map, Object source) throws Exception {
 639  1426404
       Class expect = context.getType(type, source);
 640  1426404
       Position line = node.getPosition();
 641  
 
 642  1426404
       for(Label label : map) {
 643  151
          if(label.isRequired() && revision.isEqual()) {
 644  7
             throw new ValueRequiredException("Unable to satisfy %s for %s at %s", label, expect, line);
 645  
          }
 646  144
          Object value = label.getEmpty(context);
 647  
          
 648  144
          if(value != null) {
 649  3
             criteria.set(label, value);
 650  
          }
 651  144
       }      
 652  1426397
    }
 653  
    
 654  
    /**
 655  
     * This <code>validate</code> method performs validation of the XML
 656  
     * schema class type by traversing the contacts and validating them
 657  
     * using details from the provided XML element. Because this will
 658  
     * validate a non-primitive value it delegates to other converters 
 659  
     * to perform validation of lists, maps, and primitives.
 660  
     * <p>
 661  
     * If any of the required contacts are not present within the given
 662  
     * XML element this will terminate validation and throw an exception
 663  
     * The annotation missing is reported in the exception.
 664  
     * 
 665  
     * @param node the XML element contact values are validated from
 666  
     * 
 667  
     * @return true if the XML element matches the XML schema class given 
 668  
     */
 669  
    public boolean validate(InputNode node) throws Exception {      
 670  1947
       Instance value = factory.getInstance(node);
 671  
       
 672  1947
       if(!value.isReference()) {
 673  1929
          Object result = value.setInstance(null);
 674  1929
          Class type = value.getType();
 675  
             
 676  1929
          return validate(node, type);
 677  
       }
 678  18
       return true;  
 679  
    }
 680  
    
 681  
    /**
 682  
     * This <code>validate</code> method performs validation of the XML
 683  
     * schema class type by traversing the contacts and validating them
 684  
     * using details from the provided XML element. Because this will
 685  
     * validate a non-primitive value it delegates to other converters 
 686  
     * to perform validation of lists, maps, and primitives.
 687  
     * <p>
 688  
     * If any of the required contacts are not present within the given
 689  
     * XML element this will terminate validation and throw an exception
 690  
     * The annotation missing is reported in the exception.
 691  
     * 
 692  
     * @param node the XML element contact values are validated from
 693  
     * @param type this is the type to validate against the input node
 694  
     */
 695  
    private boolean validate(InputNode node, Class type) throws Exception {
 696  1929
       Schema schema = context.getSchema(type);
 697  1929
       Section section = schema.getSection();
 698  
       
 699  1929
       validateText(node, schema);
 700  1929
       validateSection(node, section);
 701  
       
 702  1921
       return node.isElement();
 703  
    }
 704  
    
 705  
    /**
 706  
     * This <code>validateSection</code> method performs validation of a
 707  
     * schema class type by traversing the contacts and validating them
 708  
     * using details from the provided XML element. Because this will
 709  
     * validate a non-primitive value it delegates to other converters 
 710  
     * to perform validation of lists, maps, and primitives.
 711  
     * <p>
 712  
     * If any of the required contacts are not present within the given
 713  
     * XML element this will terminate validation and throw an exception
 714  
     * The annotation missing is reported in the exception.
 715  
     * 
 716  
     * @param node the XML element contact values are validated from
 717  
     * @param section this is the section that defines the XML structure
 718  
     */
 719  
    private void validateSection(InputNode node, Section section) throws Exception {
 720  1997
       validateAttributes(node, section);
 721  1993
       validateElements(node, section);
 722  1989
    }
 723  
 
 724  
    /**
 725  
     * This <code>validateAttributes</code> method validates the attributes 
 726  
     * from the provided XML element. This will iterate over all attributes
 727  
     * within the element and validate those attributes as primitives to
 728  
     * contact values within the source object.
 729  
     * <p>
 730  
     * Once all attributes within the XML element have been evaluated the
 731  
     * <code>Schema</code> is checked to ensure that there are no required 
 732  
     * contacts annotated with the <code>Attribute</code> that remain. If 
 733  
     * any required attribute remains an exception is thrown. 
 734  
     * 
 735  
     * @param node this is the XML element to be validated
 736  
     * @param section this is the section that defines the XML structure
 737  
     */
 738  
    private void validateAttributes(InputNode node, Section section) throws Exception {
 739  1997
       NodeMap<InputNode> list = node.getAttributes();
 740  1997
       LabelMap map = section.getAttributes();
 741  
 
 742  1997
       for(String name : list) {         
 743  838
          InputNode value = node.getAttribute(name);
 744  
          
 745  838
          if(value != null) {
 746  838
             validateAttribute(value, section, map);
 747  
          }
 748  836
       }  
 749  1995
       validate(node, map);
 750  1993
    }
 751  
 
 752  
    /**
 753  
     * This <code>validateElements</code> method validates the elements 
 754  
     * from the provided XML element. This will iterate over all elements
 755  
     * within the element and validate those elements as primitives or
 756  
     * composite objects depending on the contact annotation.
 757  
     * <p>
 758  
     * Once all elements within the XML element have been evaluated
 759  
     * the <code>Schema</code> is checked to ensure that there are no
 760  
     * required contacts annotated with the <code>Element</code> that
 761  
     * remain. If any required element remains an exception is thrown.
 762  
     * 
 763  
     * @param node this is the XML element to be evaluated
 764  
     * @param section this is the section that defines the XML structure
 765  
     */
 766  
    private void validateElements(InputNode node, Section section) throws Exception {
 767  1993
       LabelMap map = section.getElements();
 768  1993
       InputNode next = node.getNext();
 769  
       
 770  6711
       while(next != null) {         
 771  4721
          String name = next.getName();
 772  4721
          Section child = section.getSection(name);         
 773  
          
 774  4721
          if(child != null) {
 775  68
             validateSection(next, child);
 776  
          } else {         
 777  4653
             validateElement(next, section, map);
 778  
          }
 779  4718
          next = node.getNext();
 780  4718
       } 
 781  1990
       validate(node, map);
 782  1989
    }
 783  
    
 784  
    /**
 785  
     * This <code>validateText</code> method validates the text value 
 786  
     * from the XML element node specified. This will check the class
 787  
     * schema to determine if a <code>Text</code> annotation was used. 
 788  
     * If one was specified then the text within the XML element input 
 789  
     * node is checked to determine if it is a valid entry.
 790  
     * 
 791  
     * @param node this is the XML element to acquire the text from
 792  
     * @param schema this is used to visit the element contacts
 793  
     */
 794  
    private void validateText(InputNode node, Schema schema) throws Exception {
 795  1929
       Label label = schema.getText();
 796  
       
 797  1929
       if(label != null) {
 798  0
          validate(node, label);
 799  
       }
 800  1929
    }
 801  
    
 802  
    /**
 803  
     * This <code>validateAttribute</code> method performs a validation
 804  
     * of the provided node object using a delegate converter. This is
 805  
     * typically another <code>Composite</code> converter, or if the
 806  
     * node is an attribute a <code>Primitive</code> converter. If this
 807  
     * fails validation then an exception is thrown to report the issue.
 808  
     * 
 809  
     * @param node this is the node that contains the contact value
 810  
     * @param section this is the section to validate this attribute in
 811  
     * @param map this is the map that contains the label objects
 812  
     */
 813  
    private void validateAttribute(InputNode node, Section section, LabelMap map) throws Exception {
 814  838
       Position line = node.getPosition();
 815  838
       String name = node.getName();
 816  838
       String path = section.getAttribute(name);
 817  838
       Label label = map.getLabel(path);
 818  
       
 819  838
       if(label == null) {
 820  6
          Class expect = type.getType();
 821  
          
 822  6
          if(map.isStrict(context) && revision.isEqual()) {              
 823  2
             throw new AttributeException("Attribute '%s' does not exist for %s at %s", path, expect, line);
 824  
          }            
 825  4
       } else {
 826  832
          validate(node, label);
 827  
       }         
 828  836
    }
 829  
 
 830  
    /**
 831  
     * This <code>validateElement</code> method performs a validation
 832  
     * of the provided node object using a delegate converter. This is
 833  
     * typically another <code>Composite</code> converter, or if the
 834  
     * node is an attribute a <code>Primitive</code> converter. If this
 835  
     * fails validation then an exception is thrown to report the issue.
 836  
     * 
 837  
     * @param node this is the node that contains the contact value
 838  
     * @param section this is the section to validate this element in
 839  
     * @param map this is the map that contains the label objects
 840  
     */
 841  
    private void validateElement(InputNode node, Section section, LabelMap map) throws Exception {
 842  4653
       String name = node.getName();
 843  4653
       String path = section.getPath(name);
 844  4653
       Label label = map.getLabel(path);      
 845  
 
 846  4653
       if(label == null) {
 847  42
          label = criteria.resolve(path);
 848  
       }
 849  4653
       if(label == null) {
 850  4
          Position line = node.getPosition();
 851  4
          Class expect = type.getType();
 852  
          
 853  4
          if(map.isStrict(context) && revision.isEqual()) {              
 854  0
             throw new ElementException("Element '%s' does not exist for %s at %s", path, expect, line);
 855  
          } else {
 856  4
             node.skip();                 
 857  
          }
 858  4
       } else {
 859  4649
          validateUnion(node, map, label);
 860  
       }         
 861  4650
    }
 862  
    
 863  
    /**
 864  
     * The <code>validateUnion</code> method is determine the unions 
 865  
     * for a particular label and set the value of that union to
 866  
     * the same value as the label. This helps the deserialization 
 867  
     * process by ensuring once a union is validated it is not
 868  
     * replaced. This is also required when validating inline lists.
 869  
     * 
 870  
     * @param node this is the XML element to read the elements from
 871  
     * @param map this is the label map associated with the label
 872  
     * @param label this is the label used to define the XML element
 873  
     */
 874  
    private void validateUnion(InputNode node, LabelMap map, Label label) throws Exception {
 875  4649
       String[] list = label.getPaths();
 876  
       
 877  9468
       for(String key : list) {
 878  4819
          map.getLabel(key);
 879  
       }
 880  4649
       if(label.isInline()) {
 881  103
          criteria.set(label, null);
 882  
       }
 883  4649
       validate(node, label);
 884  4646
    }
 885  
    
 886  
    /**
 887  
     * This <code>validate</code> method is used to perform validation
 888  
     * of the provided node object using a delegate converter. This is
 889  
     * typically another <code>Composite</code> converter, or if the
 890  
     * node is an attribute a <code>Primitive</code> converter. If this
 891  
     * fails validation then an exception is thrown to report the issue.
 892  
     * 
 893  
     * @param node this is the node that contains the contact value
 894  
     * @param label this is the label used to create the converter
 895  
     */
 896  
    private void validate(InputNode node, Label label) throws Exception {    
 897  5481
       Converter reader = label.getConverter(context);      
 898  5481
       Position line = node.getPosition();
 899  5481
       Class expect = type.getType();
 900  5481
       boolean valid = reader.validate(node);
 901  
     
 902  5478
       if(valid == false) {     
 903  0
         throw new PersistenceException("Invalid value for %s in %s at %s", label, expect, line);
 904  
       }
 905  5478
       criteria.set(label, null);
 906  5478
    }
 907  
 
 908  
    /**
 909  
     * This method checks to see if there are any <code>Label</code>
 910  
     * objects remaining in the provided map that are required. This is
 911  
     * used when validation is performed to ensure the the XML element 
 912  
     * validated contains sufficient details to satisfy the XML schema 
 913  
     * class annotations. If there is a required label that remains it 
 914  
     * is reported within the exception thrown.
 915  
     * 
 916  
     * @param node this is the node that contains the composite data
 917  
     * @param map this contains the converters to perform validation
 918  
     */
 919  
    private void validate(InputNode node, LabelMap map) throws Exception {     
 920  3985
       Position line = node.getPosition();
 921  
 
 922  3985
       for(Label label : map) {
 923  39
          Class expect = type.getType();
 924  
          
 925  39
          if(label.isRequired() && revision.isEqual()) {
 926  3
             throw new ValueRequiredException("Unable to satisfy %s for %s at %s", label, expect, line);
 927  
          }
 928  36
       }      
 929  3982
    }
 930  
    
 931  
    /**
 932  
     * This <code>write</code> method is used to perform serialization of
 933  
     * the given source object. Serialization is performed by appending
 934  
     * elements and attributes from the source object to the provided XML
 935  
     * element object. How the objects contacts are serialized is 
 936  
     * determined by the XML schema class that the source object is an
 937  
     * instance of. If a required contact is null an exception is thrown.
 938  
     * 
 939  
     * @param source this is the source object to be serialized
 940  
     * @param node the XML element the object is to be serialized to
 941  
     */
 942  
    public void write(OutputNode node, Object source) throws Exception {
 943  217406
       Class type = source.getClass();
 944  217406
       Schema schema = context.getSchema(type);
 945  217406
       Caller caller = schema.getCaller();
 946  
       
 947  
       try { 
 948  217406
          if(schema.isPrimitive()) {
 949  144
             primitive.write(node, source);
 950  
          } else {
 951  217262
             caller.persist(source); 
 952  217262
             write(node, source, schema);
 953  
          }
 954  
       } finally {
 955  217406
          caller.complete(source);
 956  217398
       }
 957  217398
    }
 958  
    
 959  
    /**
 960  
     * This <code>write</code> method is used to perform serialization of
 961  
     * the given source object. Serialization is performed by appending
 962  
     * elements and attributes from the source object to the provided XML
 963  
     * element object. How the objects contacts are serialized is 
 964  
     * determined by the XML schema class that the source object is an
 965  
     * instance of. If a required contact is null an exception is thrown.
 966  
     * 
 967  
     * @param source this is the source object to be serialized
 968  
     * @param node the XML element the object is to be serialized to
 969  
     * @param schema this is used to track the referenced contacts 
 970  
     */
 971  
    private void write(OutputNode node, Object source, Schema schema) throws Exception {
 972  217262
       Section section = schema.getSection();
 973  
       
 974  217262
       writeVersion(node, source, schema);
 975  217262
       writeSection(node, source, section);
 976  217254
    }
 977  
    
 978  
    /**
 979  
     * This <code>writeSection</code> method is used to perform serialization 
 980  
     * of the given source object. Serialization is performed by appending
 981  
     * elements and attributes from the source object to the provided XML
 982  
     * element object. How the objects contacts are serialized is 
 983  
     * determined by the XML schema class that the source object is an
 984  
     * instance of. If a required contact is null an exception is thrown.
 985  
     * 
 986  
     * @param source this is the source object to be serialized
 987  
     * @param node the XML element the object is to be serialized to
 988  
     * @param section this is the section that defines the XML structure
 989  
     */
 990  
    private void writeSection(OutputNode node, Object source, Section section) throws Exception {
 991  217738
       NamespaceMap scope = node.getNamespaces();
 992  217738
       String prefix = section.getPrefix();
 993  
 
 994  217738
       if(prefix != null) {
 995  59
          String reference = scope.getReference(prefix);
 996  
          
 997  59
          if(reference == null) {     
 998  1
             throw new ElementException("Namespace prefix '%s' in %s is not in scope", prefix, type);
 999  
          } else {
 1000  58
             node.setReference(reference);  
 1001  
          }
 1002  
       }
 1003  217737
       writeAttributes(node, source, section);
 1004  217734
       writeElements(node, source, section);
 1005  217727
       writeText(node, source, section);
 1006  217727
    }
 1007  
 
 1008  
    /**
 1009  
     * This method is used to write the version attribute. A version is
 1010  
     * written only if it is not the initial version or if it required.
 1011  
     * The version is used to determine how to deserialize the XML. If
 1012  
     * the version is different from the expected version then it allows
 1013  
     * the object to be deserialized in a manner that does not require
 1014  
     * any attributes or elements, and unmatched nodes are ignored. 
 1015  
     * 
 1016  
     * @param node this is the node to read the version attribute from
 1017  
     * @param source this is the source object that is to be written
 1018  
     * @param schema this is the schema that contains the version
 1019  
     */
 1020  
    private void writeVersion(OutputNode node, Object source, Schema schema) throws Exception {
 1021  217262
       Version version = schema.getRevision();
 1022  217262
       Label label = schema.getVersion();
 1023  
       
 1024  217262
       if(version != null) {
 1025  2
          Double start = revision.getDefault();
 1026  2
          Double value = version.revision();
 1027  
      
 1028  2
          if(revision.compare(value, start)) {
 1029  2
             if(label.isRequired()) {
 1030  0
                writeAttribute(node, value, label);
 1031  
             }
 1032  
          } else {
 1033  0
             writeAttribute(node, value, label);
 1034  
          }
 1035  
       }
 1036  217262
    }
 1037  
 
 1038  
    /**
 1039  
     * This write method is used to write all the attribute contacts from
 1040  
     * the provided source object to the XML element. This visits all
 1041  
     * the contacts marked with the <code>Attribute</code> annotation in
 1042  
     * the source object. All annotated contacts are written as attributes
 1043  
     * to the XML element. This will throw an exception if a required
 1044  
     * contact within the source object is null. 
 1045  
     * 
 1046  
     * @param source this is the source object to be serialized
 1047  
     * @param node this is the XML element to write attributes to
 1048  
     * @param section this is the section that defines the XML structure
 1049  
     */
 1050  
    private void writeAttributes(OutputNode node, Object source, Section section) throws Exception {
 1051  217737
       LabelMap attributes = section.getAttributes();
 1052  
 
 1053  217737
       for(Label label : attributes) {
 1054  208872
          Contact contact = label.getContact();         
 1055  208872
          Object value = contact.get(source);
 1056  208872
          Class expect = context.getType(type, source);
 1057  
          
 1058  208872
          if(value == null) {
 1059  39
             value = label.getEmpty(context);
 1060  
          }
 1061  208872
          if(value == null && label.isRequired()) {
 1062  2
             throw new AttributeException("Value for %s is null in %s", label, expect);
 1063  
          }
 1064  208870
          writeAttribute(node, value, label);              
 1065  208869
       }      
 1066  217734
    }
 1067  
 
 1068  
    /**
 1069  
     * This write method is used to write all the element contacts from
 1070  
     * the provided source object to the XML element. This visits all
 1071  
     * the contacts marked with the <code>Element</code> annotation in
 1072  
     * the source object. All annotated contacts are written as children
 1073  
     * to the XML element. This will throw an exception if a required
 1074  
     * contact within the source object is null. 
 1075  
     * 
 1076  
     * @param source this is the source object to be serialized
 1077  
     * @param node this is the XML element to write elements to
 1078  
     * @param section this is the section that defines the XML structure
 1079  
     */
 1080  
    private void writeElements(OutputNode node, Object source, Section section) throws Exception {
 1081  217734
        for(String name : section) {
 1082  421363
          Section child = section.getSection(name);
 1083  
          
 1084  421363
          if(child != null) {
 1085  476
             OutputNode next = node.getChild(name);
 1086  
 
 1087  476
             writeSection(next, source, child);
 1088  473
          } else {
 1089  420887
             String path = section.getPath(name);
 1090  420887
             Label label = section.getElement(path);
 1091  420887
             Class expect = context.getType(type, source);
 1092  420887
             Object value = criteria.get(label);
 1093  
             
 1094  420887
             if(value == null) {
 1095  420286
                if(label == null) {
 1096  0
                  throw new ElementException("Element '%s' not defined in %s", name, expect);
 1097  
                }
 1098  420286
                writeUnion(node, source, section, label);
 1099  
             }
 1100  
          }            
 1101  421356
       }
 1102  217727
    }
 1103  
    
 1104  
    /**
 1105  
     * The <code>writeUnion</code> method is determine the unions 
 1106  
     * for a particular label and set the value of that union to
 1107  
     * the same value as the label. This helps the serialization 
 1108  
     * process by ensuring once a union is written it is not
 1109  
     * replaced. This is also required when writing inline lists.
 1110  
     * 
 1111  
     * @param node this is the XML element to write elements to
 1112  
     * @param source this is the source object to be serialized
 1113  
     * @param section this is the section associated with the label
 1114  
     * @param label this is the label used to define the XML element
 1115  
     */
 1116  
    private void writeUnion(OutputNode node, Object source, Section section, Label label) throws Exception {
 1117  420286
       Contact contact = label.getContact();
 1118  420286
       Object value = contact.get(source);
 1119  420286
       Class expect = context.getType(type, source);
 1120  
       
 1121  420286
       if(value == null && label.isRequired()) {
 1122  1
          throw new ElementException("Value for %s is null in %s", label, expect);
 1123  
       }
 1124  420285
       Object replace = writeReplace(value);
 1125  
       
 1126  420285
       if(replace != null) {
 1127  420111
          writeElement(node, replace, label);            
 1128  
       }
 1129  420282
       criteria.set(label, replace);
 1130  420282
    }
 1131  
    
 1132  
    /**
 1133  
     * The <code>writeReplace</code> method is used to replace an object
 1134  
     * before it is serialized. This is used so that an object can give
 1135  
     * a substitute to be written to the XML document in the event that
 1136  
     * the actual object is not suitable or desired for serialization. 
 1137  
     * This acts as an equivalent to the Java Object Serialization
 1138  
     * <code>writeReplace</code> method for the object serialization.
 1139  
     * 
 1140  
     * @param source this is the source object that is to be replaced
 1141  
     * 
 1142  
     * @return this returns the object to use as a replacement value
 1143  
     */
 1144  
    private Object writeReplace(Object source) throws Exception {      
 1145  420285
       if(source != null) {
 1146  420111
          Class type = source.getClass();
 1147  420111
          Caller caller = context.getCaller(type);
 1148  
          
 1149  420111
          return caller.replace(source);
 1150  
       }
 1151  174
       return source;
 1152  
    }
 1153  
    
 1154  
    
 1155  
    /**
 1156  
     * This write method is used to write the text contact from the 
 1157  
     * provided source object to the XML element. This takes the text
 1158  
     * value from the source object and writes it to the single contact
 1159  
     * marked with the <code>Text</code> annotation. If the value is
 1160  
     * null and the contact value is required an exception is thrown.
 1161  
     * 
 1162  
     * @param source this is the source object to be serialized
 1163  
     * @param node this is the XML element to write text value to
 1164  
     * @param section this is used to track the referenced elements
 1165  
     */
 1166  
    private void writeText(OutputNode node, Object source, Section section) throws Exception {
 1167  217727
       Label label = section.getText();
 1168  
 
 1169  217727
       if(label != null) {
 1170  413
          Contact contact = label.getContact();
 1171  413
          Object value = contact.get(source);
 1172  413
          Class expect = context.getType(type, source);
 1173  
          
 1174  413
          if(value == null) {
 1175  11
             value = label.getEmpty(context);
 1176  
          }
 1177  413
          if(value == null && label.isRequired()) {
 1178  0
             throw new TextException("Value for %s is null in %s", label, expect);
 1179  
          }
 1180  413
          writeText(node, value, label); 
 1181  
       }         
 1182  217727
    }
 1183  
    
 1184  
    /**
 1185  
     * This write method is used to set the value of the provided object
 1186  
     * as an attribute to the XML element. This will acquire the string
 1187  
     * value of the object using <code>toString</code> only if the
 1188  
     * object provided is not an enumerated type. If the object is an
 1189  
     * enumerated type then the <code>Enum.name</code> method is used.
 1190  
     * 
 1191  
     * @param value this is the value to be set as an attribute
 1192  
     * @param node this is the XML element to write the attribute to
 1193  
     * @param label the label that contains the contact details
 1194  
     */
 1195  
    private void writeAttribute(OutputNode node, Object value, Label label) throws Exception {
 1196  208870
       if(value != null) {         
 1197  208850
          Decorator decorator = label.getDecorator();
 1198  208850
          String name = label.getName();
 1199  208850
          String text = factory.getText(value);
 1200  208849
          OutputNode done = node.setAttribute(name, text);
 1201  
          
 1202  208849
          decorator.decorate(done);
 1203  
       }
 1204  208869
    }
 1205  
    
 1206  
    /**
 1207  
     * This write method is used to append the provided object as an
 1208  
     * element to the given XML element object. This will recursively
 1209  
     * write the contacts from the provided object as elements. This is
 1210  
     * done using the <code>Converter</code> acquired from the contact
 1211  
     * label. If the type of the contact value is not of the same
 1212  
     * type as the XML schema class a "class" attribute is appended.
 1213  
     * <p>
 1214  
     * If the element being written is inline, then this will not 
 1215  
     * check to see if there is a "class" attribute specifying the
 1216  
     * name of the class. This is because inline elements do not have
 1217  
     * an outer class and thus could never have an override.
 1218  
     * 
 1219  
     * @param value this is the value to be set as an element
 1220  
     * @param node this is the XML element to write the element to
 1221  
     * @param label the label that contains the contact details
 1222  
     */
 1223  
    private void writeElement(OutputNode node, Object value, Label label) throws Exception {
 1224  420111
       if(value != null) {
 1225  420111
          Class real = value.getClass();
 1226  420111
          Label match = label.getLabel(real);
 1227  420110
          String name = match.getName();
 1228  420110
          Type type = label.getType(real); 
 1229  420110
          OutputNode next = node.getChild(name);
 1230  
 
 1231  420110
          if(!match.isInline()) {
 1232  419698
             writeNamespaces(next, type, match);
 1233  
          }
 1234  420110
          if(match.isInline() || !isOverridden(next, value, type)) {
 1235  417881
             Converter convert = match.getConverter(context);
 1236  417881
             boolean data = match.isData();
 1237  
             
 1238  417881
             next.setData(data);
 1239  417881
             writeElement(next, value, convert);
 1240  
          }
 1241  
       }
 1242  420108
    }
 1243  
    
 1244  
    /**
 1245  
     * This is used write the element specified using the specified
 1246  
     * converter. Writing the value using the specified converter will
 1247  
     * result in the node being populated with the elements, attributes,
 1248  
     * and text values to the provided node. If there is a problem
 1249  
     * writing the value using the converter an exception is thrown.
 1250  
     * 
 1251  
     * @param node this is the node that the value is to be written to
 1252  
     * @param value this is the value that is to be written
 1253  
     * @param convert this is the converter used to perform writing
 1254  
     */
 1255  
    private void writeElement(OutputNode node, Object value, Converter convert) throws Exception {
 1256  417881
       convert.write(node, value);
 1257  417879
    }
 1258  
    
 1259  
    /**
 1260  
     * This is used to apply <code>Decorator</code> objects to the
 1261  
     * provided node before it is written. Application of decorations
 1262  
     * before the node is written allows namespaces and comments to be
 1263  
     * applied to the node. Decorations such as this do not affect the
 1264  
     * overall structure of the XML that is written.
 1265  
     * 
 1266  
     * @param node this is the node that decorations are applied to
 1267  
     * @param type this is the type to acquire the decoration for
 1268  
     * @param label this contains the primary decorator to be used
 1269  
     */
 1270  
    private void writeNamespaces(OutputNode node, Type type, Label label) throws Exception {
 1271  419698
       Class expect = type.getType();
 1272  419698
       Decorator primary = context.getDecorator(expect);
 1273  419698
       Decorator decorator = label.getDecorator();
 1274  
       
 1275  419698
       decorator.decorate(node, primary);
 1276  419698
    }
 1277  
    
 1278  
    /**
 1279  
     * This write method is used to set the value of the provided object
 1280  
     * as the text for the XML element. This will acquire the string
 1281  
     * value of the object using <code>toString</code> only if the
 1282  
     * object provided is not an enumerated type. If the object is an
 1283  
     * enumerated type then the <code>Enum.name</code> method is used.
 1284  
     * 
 1285  
     * @param value this is the value to set as the XML element text
 1286  
     * @param node this is the XML element to write the text value to
 1287  
     * @param label the label that contains the contact details
 1288  
     */
 1289  
    private void writeText(OutputNode node, Object value, Label label) throws Exception {
 1290  413
       if(value != null && !label.isTextList()) {         
 1291  374
          String text = factory.getText(value); 
 1292  374
          boolean data = label.isData();
 1293  
          
 1294  374
          node.setData(data);
 1295  374
          node.setValue(text);        
 1296  
       }
 1297  413
    }   
 1298  
    
 1299  
    /**
 1300  
     * This is used to determine whether the specified value has been
 1301  
     * overridden by the strategy. If the item has been overridden
 1302  
     * then no more serialization is require for that value, this is
 1303  
     * effectively telling the serialization process to stop writing.
 1304  
     * 
 1305  
     * @param node the node that a potential override is written to
 1306  
     * @param value this is the object instance to be serialized
 1307  
     * @param type this is the type of the object to be serialized
 1308  
     * 
 1309  
     * @return returns true if the strategy overrides the object
 1310  
     */
 1311  
    private boolean isOverridden(OutputNode node, Object value, Type type) throws Exception{
 1312  419698
       return factory.setOverride(type, value, node);
 1313  
    }
 1314  
    
 1315  
    /**
 1316  
     * This takes the approach that the object is instantiated first and
 1317  
     * then the annotated fields and methods are deserialized from the 
 1318  
     * XML elements and attributes. When all the details have be read 
 1319  
     * they are set on the internal contacts of the object. This is used
 1320  
     * for places where we have a default no argument constructor.
 1321  
     *  
 1322  
     * @author Niall Gallagher
 1323  
     */
 1324  
    private static class Builder {
 1325  
       
 1326  
       /**
 1327  
        * This is the composite converter that the builder will use.
 1328  
        */
 1329  
       protected final Composite composite;
 1330  
       
 1331  
       /**
 1332  
        * This is the criteria object used to collect the values.
 1333  
        */
 1334  
       protected final Criteria criteria;
 1335  
       
 1336  
       /**
 1337  
        * This is the schema object that contains the XML definition.
 1338  
        */
 1339  
       protected final Schema schema;
 1340  
       
 1341  
       /**
 1342  
        * This is the object instance created by the strategy object.
 1343  
        */
 1344  
       protected final Instance value;
 1345  
       
 1346  
       /**
 1347  
        * Constructor for the <code>Builder</code> object. This creates
 1348  
        * a builder object capable of instantiating a object using a
 1349  
        * default no argument constructor. All fields are deserialized
 1350  
        * after the object has been instantiated.
 1351  
        * 
 1352  
        * @param composite this is the composite object used by this
 1353  
        * @param criteria this collects the objects being deserialized
 1354  
        * @param schema this is the class schema used by this
 1355  
        * @param value this is the instance created by the strategy
 1356  
        */
 1357  712981
       public Builder(Composite composite, Criteria criteria, Schema schema, Instance value) {
 1358  712981
          this.composite = composite;
 1359  712981
          this.criteria = criteria;
 1360  712981
          this.schema = schema;
 1361  712981
          this.value = value;
 1362  712981
       }
 1363  
       
 1364  
       /**
 1365  
        * This <code>read</code> method performs deserialization of the
 1366  
        * XML schema class type by traversing the contacts and using
 1367  
        * details from the provided XML element. Here an instance is
 1368  
        * created first then the contacts are traversed, this is done
 1369  
        * when a default constructor is the only option.
 1370  
        * 
 1371  
        * @param node the XML element that will be deserialized by this 
 1372  
        * 
 1373  
        * @return this returns the fully deserialized object graph
 1374  
        */
 1375  
       public Object read(InputNode node) throws Exception {
 1376  712704
          Object source = value.getInstance();
 1377  712704
          Section section = schema.getSection();
 1378  
          
 1379  712704
          value.setInstance(source);
 1380  712704
          composite.readVersion(node, source, schema);
 1381  712704
          composite.readText(node, source, section);
 1382  712702
          composite.readAttributes(node, source, section);
 1383  712696
          composite.readElements(node, source, section);
 1384  712676
          criteria.commit(source);  
 1385  
             
 1386  712676
          return source;
 1387  
       }
 1388  
    }
 1389  
    
 1390  
    /**
 1391  
     * This takes the approach that the objects are deserialized first
 1392  
     * then the instance is created using a constructor. In order for
 1393  
     * the best constructor to be found all the potential objects need
 1394  
     * to be deserialized first, then based on what has been deserialized
 1395  
     * a constructor is chosen and the parameters are injected in to it.
 1396  
     *  
 1397  
     * @author Niall Gallagher
 1398  
     */
 1399  277
    private class Injector extends Builder {
 1400  
       
 1401  
       /**
 1402  
        * Constructor for the <code>Injector</code> object. This creates
 1403  
        * a builder object capable of instantiating a object using a
 1404  
        * constructor. It injects the constructor parameters in to the
 1405  
        * constructor by using the deserialized objects.
 1406  
        * 
 1407  
        * @param composite this is the composite object used by this
 1408  
        * @param criteria this collects the objects being deserialized
 1409  
        * @param schema this is the class schema used by this
 1410  
        * @param value this is the instance created by the strategy
 1411  
        */
 1412  277
       private Injector(Composite composite, Criteria criteria, Schema schema, Instance value) {
 1413  277
          super(composite, criteria, schema, value);
 1414  277
       }
 1415  
 
 1416  
       /**
 1417  
        * This <code>read</code> method performs deserialization of the
 1418  
        * XML schema class type by traversing the contacts and using
 1419  
        * details from the provided XML element. Here an instance is
 1420  
        * instantiated only after everything has been deserialized so
 1421  
        * that the instances can be injected in to a constructor.
 1422  
        * 
 1423  
        * @param node the XML element that will be deserialized by this 
 1424  
        * 
 1425  
        * @return this returns the fully deserialized object graph
 1426  
        */
 1427  
       public Object read(InputNode node) throws Exception {
 1428  277
          Section section = schema.getSection();
 1429  
          
 1430  277
          composite.readVersion(node, null, schema);
 1431  277
          composite.readText(node, null, section);
 1432  277
          composite.readAttributes(node, null, section);
 1433  277
          composite.readElements(node, null, section);
 1434  
          
 1435  276
          return readInject(node);
 1436  
       } 
 1437  
       
 1438  
 
 1439  
       /**
 1440  
        * This <code>readInject</code> method performs deserialization
 1441  
        * of the XML schema class type by traversing the contacts and 
 1442  
        * creating them using details from the provided XML element. 
 1443  
        * Because this will convert a non-primitive value it delegates 
 1444  
        * to other converters to perform deserialization of lists and 
 1445  
        * primitives.
 1446  
        * <p>
 1447  
        * This takes the approach of reading the elements and attributes
 1448  
        * before instantiating the object. Instantiation is performed 
 1449  
        * using a declared constructor. The parameters are taken from 
 1450  
        * the deserialized objects.
 1451  
        * 
 1452  
        * @param node the XML element that will be deserialized by this 
 1453  
        * 
 1454  
        * @return this returns the fully deserialized object graph
 1455  
        */
 1456  
       private Object readInject(InputNode node) throws Exception {
 1457  276
          Instantiator creator = schema.getInstantiator();
 1458  276
          Object source = creator.getInstance(criteria);
 1459  
          
 1460  276
          value.setInstance(source);
 1461  276
          criteria.commit(source); 
 1462  
 
 1463  276
          return source;
 1464  
       }
 1465  
    }
 1466  
 }
 1467