Coverage Report - org.simpleframework.xml.core.ObjectScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
ObjectScanner
100%
63/63
75%
9/12
1.172
 
 1  
 /*
 2  
  * ObjectScanner.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  
 import java.util.List;
 23  
 
 24  
 import org.simpleframework.xml.Order;
 25  
 import org.simpleframework.xml.Version;
 26  
 
 27  
 /**
 28  
  * The <code>ObjectScanner</code> performs the reflective inspection
 29  
  * of a class and builds a map of attributes and elements for each
 30  
  * annotated field. This acts as a cachable container for reflection
 31  
  * actions performed on a specific type. When scanning the provided
 32  
  * class this inserts the scanned field as a <code>Label</code> in to
 33  
  * a map so that it can be retrieved by name. Annotations classified
 34  
  * as attributes have the <code>Attribute</code> annotation, all other
 35  
  * annotated fields are stored as elements.
 36  
  * 
 37  
  * @author Niall Gallagher
 38  
  * 
 39  
  * @see org.simpleframework.xml.core.Schema
 40  
  */ 
 41  
 class ObjectScanner implements Scanner {
 42  
    
 43  
    /**
 44  
     * This is used to store all XML attributes and XML elements.
 45  
     */
 46  
    private StructureBuilder builder;
 47  
    
 48  
    /**
 49  
     * This method acts as a pointer to the types commit process.
 50  
     */
 51  
    private ClassScanner scanner;
 52  
    
 53  
    /**
 54  
     * This defines the structure build from the class annotations.
 55  
     */
 56  
    private Structure structure;
 57  
    
 58  
    /**
 59  
     * This object contains various support functions for the class.
 60  
     */
 61  
    private Support support;
 62  
    
 63  
    /**
 64  
     * This contains the details for the class that is being scanned.
 65  
     */
 66  
    private Detail detail;
 67  
    
 68  
    /**
 69  
     * Constructor for the <code>ObjectScanner</code> object. This is 
 70  
     * used to scan the provided class for annotations that are used 
 71  
     * to build a schema for an XML file to follow. 
 72  
     * 
 73  
     * @param detail this contains the details for the class scanned
 74  
     * @param support this contains various support functions
 75  
     */
 76  2381
    public ObjectScanner(Detail detail, Support support) throws Exception {  
 77  2381
       this.scanner = new ClassScanner(detail, support);
 78  2379
       this.builder = new StructureBuilder(this, detail, support); 
 79  2379
       this.support = support;
 80  2379
       this.detail = detail;
 81  2379
       this.scan(detail);
 82  2347
    }      
 83  
    
 84  
    /**
 85  
     * This is used to acquire the default signature for the class. 
 86  
     * The default signature is the signature for the no argument
 87  
     * constructor for the type. If there is no default constructor
 88  
     * for the type then this will return null.
 89  
     * 
 90  
     * @return this returns the default signature if it exists
 91  
     */
 92  
    public Signature getSignature() {
 93  2370
       return scanner.getSignature();
 94  
    }
 95  
    
 96  
    /**
 97  
     * This returns the signatures for the type. All constructors are
 98  
     * represented as a signature and returned. More signatures than
 99  
     * constructors will be returned if a constructor is annotated 
 100  
     * with a union annotation.
 101  
     *
 102  
     * @return this returns the list of signatures for the type
 103  
     */
 104  
    public List<Signature> getSignatures() {
 105  2370
       return scanner.getSignatures();
 106  
    }
 107  
    
 108  
    /**
 109  
     * This returns a map of all parameters that exist. This is used
 110  
     * to validate all the parameters against the field and method
 111  
     * annotations that exist within the class. 
 112  
     * 
 113  
     * @return this returns a map of all parameters within the type
 114  
     */
 115  
    public ParameterMap getParameters() {
 116  4740
       return scanner.getParameters();
 117  
    }
 118  
    
 119  
    /**
 120  
     * This is used to acquire the instantiator for the type. This is
 121  
     * used to create object instances based on the constructors that
 122  
     * have been annotated. If no constructors have been annotated
 123  
     * then this can be used to create default no argument instances.
 124  
     * 
 125  
     * @return this instantiator responsible for creating instances
 126  
     */
 127  
    public Instantiator getInstantiator() {
 128  932149
       return structure.getInstantiator();
 129  
    }
 130  
 
 131  
    /**
 132  
     * This is used to acquire the type that this scanner scans for
 133  
     * annotations to be used in a schema. Exposing the class that
 134  
     * this represents allows the schema it creates to be known.
 135  
     * 
 136  
     * @return this is the type that this creator will represent
 137  
     */
 138  
    public Class getType() {
 139  932146
       return detail.getType();
 140  
    }
 141  
    
 142  
    /**
 143  
     * This is used to acquire the <code>Decorator</code> for this.
 144  
     * A decorator is an object that adds various details to the
 145  
     * node without changing the overall structure of the node. For
 146  
     * example comments and namespaces can be added to the node with
 147  
     * a decorator as they do not affect the deserialization.
 148  
     * 
 149  
     * @return this returns the decorator associated with this
 150  
     */
 151  
    public Decorator getDecorator() {
 152  1174330
       return scanner.getDecorator();
 153  
    }
 154  
    
 155  
    /**
 156  
     * This method is used to return the <code>Caller</code> for this
 157  
     * class. The caller is a means to deliver invocations to the
 158  
     * object for the persister callback methods. It aggregates all of
 159  
     * the persister callback methods in to a single object.
 160  
     * 
 161  
     * @return this returns a caller used for delivering callbacks
 162  
     */
 163  
    public Caller getCaller(Context context) {
 164  1035935
       return new Caller(this, context);
 165  
    }
 166  
 
 167  
    /**
 168  
     * This is used to create a <code>Section</code> given the context
 169  
     * used for serialization. A section is an XML structure that 
 170  
     * contains all the elements and attributes defined for the class.
 171  
     * Each section is a tree like structure defining exactly where
 172  
     * each attribute an element is located within the source XML.
 173  
     * 
 174  
     * @return this will return a section for serialization
 175  
     */
 176  
    public Section getSection() {
 177  932171
       return structure.getSection();
 178  
    }
 179  
    
 180  
    /**
 181  
     * This is the <code>Version</code> for the scanned class. It 
 182  
     * allows the deserialization process to be configured such that
 183  
     * if the version is different from the schema class none of
 184  
     * the fields and methods are required and unmatched elements
 185  
     * and attributes will be ignored.
 186  
     * 
 187  
     * @return this returns the version of the class that is scanned
 188  
     */
 189  
    public Version getRevision() {
 190  932157
       return structure.getRevision();
 191  
    }
 192  
    
 193  
    /**
 194  
     * This is used to acquire the <code>Order</code> annotation for
 195  
     * the class schema. The order annotation defines the order that
 196  
     * the elements and attributes should appear within the document.
 197  
     * Providing order in this manner makes the resulting XML more
 198  
     * predictable. If no order is provided, appearance is random.
 199  
     * 
 200  
     * @return this returns the order, if any, defined for the class
 201  
     */
 202  
    public Order getOrder() {
 203  4743
       return scanner.getOrder();
 204  
    }
 205  
    
 206  
    /**
 207  
     * This returns the <code>Label</code> that represents the version
 208  
     * annotation for the scanned class. Only a single version can
 209  
     * exist within the class if more than one exists an exception is
 210  
     * thrown. This will read only floating point types such as double.
 211  
     * 
 212  
     * @return this returns the label used for reading the version
 213  
     */
 214  
    public Label getVersion() {
 215  932146
       return structure.getVersion();
 216  
    }
 217  
    
 218  
    /**
 219  
     * This returns the <code>Label</code> that represents the text
 220  
     * annotation for the scanned class. Only a single text annotation
 221  
     * can be used per class, so this returns only a single label
 222  
     * rather than a <code>LabelMap</code> object. Also if this is
 223  
     * not null then the elements label map must be empty.
 224  
     * 
 225  
     * @return this returns the text label for the scanned class
 226  
     */
 227  
    public Label getText() {
 228  932149
       return structure.getText();
 229  
    }
 230  
    
 231  
    /**
 232  
     * This returns the name of the class processed by this scanner.
 233  
     * The name is either the name as specified in the last found
 234  
     * <code>Root</code> annotation, or if a name was not specified
 235  
     * within the discovered root then the Java Bean class name of
 236  
     * the last class annotated with a root annotation.
 237  
     * 
 238  
     * @return this returns the name of the object being scanned
 239  
     */
 240  
    public String getName() {
 241  461856
       return detail.getName();
 242  
    }
 243  
 
 244  
    /**
 245  
     * This method is used to retrieve the schema class commit method
 246  
     * during the deserialization process. The commit method must be
 247  
     * marked with the <code>Commit</code> annotation so that when the
 248  
     * object is deserialized the persister has a chance to invoke the
 249  
     * method so that the object can build further data structures.
 250  
     * 
 251  
     * @return this returns the commit method for the schema class
 252  
     */
 253  
    public Function getCommit() {
 254  1035935
       return scanner.getCommit();           
 255  
    }
 256  
 
 257  
    /**
 258  
     * This method is used to retrieve the schema class validation
 259  
     * method during the deserialization process. The validation method
 260  
     * must be marked with the <code>Validate</code> annotation so that
 261  
     * when the object is deserialized the persister has a chance to 
 262  
     * invoke that method so that object can validate its field values.
 263  
     * 
 264  
     * @return this returns the validate method for the schema class
 265  
     */   
 266  
    public Function getValidate() {
 267  1035935
       return scanner.getValidate();       
 268  
    }
 269  
    
 270  
    /**
 271  
     * This method is used to retrieve the schema class persistence
 272  
     * method. This is invoked during the serialization process to
 273  
     * get the object a chance to perform an nessecary preparation
 274  
     * before the serialization of the object proceeds. The persist
 275  
     * method must be marked with the <code>Persist</code> annotation.
 276  
     * 
 277  
     * @return this returns the persist method for the schema class
 278  
     */
 279  
    public Function getPersist() {
 280  1035935
       return scanner.getPersist();           
 281  
    }
 282  
 
 283  
    /**
 284  
     * This method is used to retrieve the schema class completion
 285  
     * method. This is invoked after the serialization process has
 286  
     * completed and gives the object a chance to restore its state
 287  
     * if the persist method required some alteration or locking.
 288  
     * This is marked with the <code>Complete</code> annotation.
 289  
     * 
 290  
     * @return returns the complete method for the schema class
 291  
     */   
 292  
    public Function getComplete() {
 293  1035935
       return scanner.getComplete();           
 294  
    }
 295  
    
 296  
    /**
 297  
     * This method is used to retrieve the schema class replacement
 298  
     * method. The replacement method is used to substitute an object
 299  
     * that has been deserialized with another object. This allows
 300  
     * a seamless delegation mechanism to be implemented. This is
 301  
     * marked with the <code>Replace</code> annotation. 
 302  
     * 
 303  
     * @return returns the replace method for the schema class
 304  
     */
 305  
    public Function getReplace() {
 306  1035935
       return scanner.getReplace();
 307  
    }
 308  
    
 309  
    /**
 310  
     * This method is used to retrieve the schema class replacement
 311  
     * method. The replacement method is used to substitute an object
 312  
     * that has been deserialized with another object. This allows
 313  
     * a seamless delegation mechanism to be implemented. This is
 314  
     * marked with the <code>Replace</code> annotation. 
 315  
     * 
 316  
     * @return returns the replace method for the schema class
 317  
     */
 318  
    public Function getResolve() {
 319  1035935
       return scanner.getResolve();
 320  
    }
 321  
 
 322  
    /**
 323  
     * This is used to determine whether the scanned class represents
 324  
     * a primitive type. A primitive type is a type that contains no
 325  
     * XML annotations and so cannot be serialized with an XML form.
 326  
     * Instead primitives a serialized using transformations.
 327  
     * 
 328  
     * @return this returns true if no XML annotations were found
 329  
     */
 330  
    public boolean isPrimitive() {
 331  932146
       return structure.isPrimitive();
 332  
    }
 333  
    
 334  
    /**
 335  
     * This is used to determine whether the scanned class represents
 336  
     * a primitive type. A primitive type is a type that contains no
 337  
     * XML annotations and so cannot be serialized with an XML form.
 338  
     * Instead primitives a serialized using transformations.
 339  
     * 
 340  
     * @return this returns true if no XML annotations were found
 341  
     */
 342  
    public boolean isEmpty() {
 343  2236
       return scanner.getRoot() == null;
 344  
    }
 345  
    
 346  
    /**
 347  
     * This method is used to determine whether strict mappings are
 348  
     * required. Strict mapping means that all labels in the class
 349  
     * schema must match the XML elements and attributes in the
 350  
     * source XML document. When strict mapping is disabled, then
 351  
     * XML elements and attributes that do not exist in the schema
 352  
     * class will be ignored without breaking the parser.
 353  
     *
 354  
     * @return true if strict parsing is enabled, false otherwise
 355  
     */ 
 356  
    public boolean isStrict() {
 357  24
       return detail.isStrict();
 358  
    }
 359  
    
 360  
    /**
 361  
     * This is used to scan the specified object to extract the fields
 362  
     * and methods that are to be used in the serialization process.
 363  
     * This will acquire all fields and getter setter pairs that have
 364  
     * been annotated with the XML annotations.
 365  
     *
 366  
     * @param detail this contains the details for the class scanned
 367  
     */  
 368  
    private void scan(Detail detail) throws Exception {
 369  2379
       order(detail);
 370  2376
       field(detail);
 371  2375
       method(detail);
 372  2370
       validate(detail);
 373  2347
       commit(detail);
 374  2347
    }
 375  
    
 376  
    /**
 377  
     * This is used to acquire the optional order annotation to provide
 378  
     * order to the elements and attributes for the generated XML. This
 379  
     * acts as an override to the order provided by the declaration of
 380  
     * the types within the object.  
 381  
     * 
 382  
     * @param detail this contains the details for the class scanned
 383  
     */
 384  
    private void order(Detail detail) throws Exception {
 385  2379
       Class type = detail.getType();
 386  
       
 387  2379
       builder.assemble(type);
 388  2376
    }
 389  
    
 390  
    /**
 391  
     * Once the scanner has completed extracting the annotations and
 392  
     * validating the resulting structure this is called to complete 
 393  
     * the process. This will build a <code>Structure</code> object and
 394  
     * clean up any data structures no longer required by the scanner.
 395  
     * 
 396  
     * @param detail this contains the details for the class scanned
 397  
     */
 398  
    private void commit(Detail detail) throws Exception {
 399  2347
       Class type = detail.getType();
 400  
       
 401  2347
       if(structure == null) {
 402  2347
          structure = builder.build(type);
 403  
       }
 404  2347
       builder = null;
 405  2347
    }
 406  
    
 407  
    /**
 408  
     * This is used to validate the configuration of the scanned class.
 409  
     * If a <code>Text</code> annotation has been used with elements
 410  
     * then validation will fail and an exception will be thrown. 
 411  
     * 
 412  
     * @param detail this contains the details for the class scanned
 413  
     */
 414  
    private void validate(Detail detail) throws Exception {
 415  2370
       Class type = detail.getType();
 416  
       
 417  2370
       builder.commit(type);
 418  2364
       builder.validate(type);
 419  2347
    }
 420  
   
 421  
    /**
 422  
     * This is used to acquire the contacts for the annotated fields 
 423  
     * within the specified class. The field contacts are added to
 424  
     * either the attributes or elements map depending on annotation.
 425  
     * 
 426  
     * @param detail this contains the details for the class scanned
 427  
     */    
 428  
    private void field(Detail detail) throws Exception {
 429  2376
       Class type = detail.getType();
 430  2376
       ContactList list = support.getFields(type);
 431  
       
 432  2376
       for(Contact contact : list) {
 433  3459
          Annotation label = contact.getAnnotation();
 434  
          
 435  3459
          if(label != null) {
 436  3459
             builder.process(contact, label);
 437  
          }
 438  3458
       }
 439  2375
    }
 440  
    
 441  
    /**
 442  
     * This is used to acquire the contacts for the annotated fields 
 443  
     * within the specified class. The field contacts are added to
 444  
     * either the attributes or elements map depending on annotation.
 445  
     * 
 446  
     * @param detail this contains the details for the class scanned
 447  
     */ 
 448  
    private void method(Detail detail) throws Exception {
 449  2375
       Class type = detail.getType();
 450  2375
       ContactList list = support.getMethods(type);
 451  
       
 452  2371
       for(Contact contact : list) {
 453  174
          Annotation label = contact.getAnnotation();
 454  
          
 455  174
          if(label != null) {
 456  174
             builder.process(contact, label);
 457  
          }
 458  173
       }
 459  2370
    }
 460  
 }