Coverage Report - org.simpleframework.xml.core.ClassScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassScanner
100%
92/92
95%
44/46
1.889
 
 1  
 /*
 2  
  * ClassScanner.java July 2008
 3  
  *
 4  
  * Copyright (C) 2008, 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.lang.reflect.Method;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 
 26  
 import org.simpleframework.xml.Namespace;
 27  
 import org.simpleframework.xml.NamespaceList;
 28  
 import org.simpleframework.xml.Order;
 29  
 import org.simpleframework.xml.Root;
 30  
 
 31  
 /**
 32  
  * The <code>ClassScanner</code> performs the reflective inspection
 33  
  * of a class and extracts all the class level annotations. This will
 34  
  * also extract the methods that are annotated. This ensures that the
 35  
  * callback methods can be invoked during the deserialization process.
 36  
  * Also, this will read the namespace annotations that are used.
 37  
  * 
 38  
  * @author Niall Gallagher
 39  
  * 
 40  
  * @see org.simpleframework.xml.core.Scanner
 41  
  */ 
 42  
 class ClassScanner  {
 43  
    
 44  
    /**
 45  
     * This is the namespace decorator associated with this scanner.
 46  
     */
 47  
    private NamespaceDecorator decorator;
 48  
    
 49  
    /**
 50  
     * This is the scanner that is used to acquire the constructors.
 51  
     */
 52  
    private ConstructorScanner scanner;
 53  
    
 54  
    /**
 55  
     * This function acts as a pointer to the types commit process.
 56  
     */
 57  
    private Function commit;
 58  
    
 59  
    /**
 60  
     * This function acts as a pointer to the types validate process.
 61  
     */
 62  
    private Function validate;
 63  
 
 64  
    /**
 65  
     * This function acts as a pointer to the types persist process.
 66  
     */
 67  
    private Function persist;
 68  
 
 69  
    /**
 70  
     * This function acts as a pointer to the types complete process.
 71  
     */
 72  
    private Function complete;   
 73  
    
 74  
    /**
 75  
     * This function is used as a pointer to the replacement method.
 76  
     */
 77  
    private Function replace;
 78  
    
 79  
    /**
 80  
     * This function is used as a pointer to the resolution method.
 81  
     */
 82  
    private Function resolve;
 83  
    
 84  
    /**
 85  
     * This object contains various support functions for the class.
 86  
     */
 87  
    private Support support;
 88  
    
 89  
    /**
 90  
     * This is the root annotation that has been scanned from the type.
 91  
     */
 92  
    private Root root;
 93  
    
 94  
    /**
 95  
     * This is the order annotation that has been scanned from the type.
 96  
     */
 97  
    private Order order;
 98  
    
 99  
    /**
 100  
     * Constructor for the <code>ClassScanner</code> object. This is 
 101  
     * used to scan the provided class for annotations that are used 
 102  
     * to build a schema for an XML file to follow. 
 103  
     * 
 104  
     * @param detail this contains the details for the class scanned
 105  
     * @param support this contains various support functions
 106  
     */
 107  2382
    public ClassScanner(Detail detail, Support support) throws Exception { 
 108  2382
       this.scanner = new ConstructorScanner(detail, support);
 109  2380
       this.decorator = new NamespaceDecorator();
 110  2380
       this.support = support;
 111  2380
       this.scan(detail);
 112  2380
    }      
 113  
 
 114  
    /**
 115  
     * This is used to acquire the default signature for the class. 
 116  
     * The default signature is the signature for the no argument
 117  
     * constructor for the type. If there is no default constructor
 118  
     * for the type then this will return null.
 119  
     * 
 120  
     * @return this returns the default signature if it exists
 121  
     */
 122  
    public Signature getSignature() {
 123  2370
       return scanner.getSignature();
 124  
    }
 125  
    
 126  
    /**
 127  
     * This returns the signatures for the type. All constructors are
 128  
     * represented as a signature and returned. More signatures than
 129  
     * constructors will be returned if a constructor is annotated 
 130  
     * with a union annotation.
 131  
     *
 132  
     * @return this returns the list of signatures for the type
 133  
     */
 134  
    public List<Signature> getSignatures(){
 135  2370
       return scanner.getSignatures();
 136  
    }
 137  
    
 138  
    /**
 139  
     * This returns a map of all parameters that exist. This is used
 140  
     * to validate all the parameters against the field and method
 141  
     * annotations that exist within the class. 
 142  
     * 
 143  
     * @return this returns a map of all parameters within the type
 144  
     */
 145  
    public ParameterMap getParameters() {
 146  4740
       return scanner.getParameters();
 147  
    }
 148  
    
 149  
    /**
 150  
     * This is used to acquire the <code>Decorator</code> for this.
 151  
     * A decorator is an object that adds various details to the
 152  
     * node without changing the overall structure of the node. For
 153  
     * example comments and namespaces can be added to the node with
 154  
     * a decorator as they do not affect the deserialization.
 155  
     * 
 156  
     * @return this returns the decorator associated with this
 157  
     */
 158  
    public Decorator getDecorator() {
 159  1174330
       return decorator;
 160  
    }
 161  
 
 162  
    /**
 163  
     * This returns the order annotation used to determine the order
 164  
     * of serialization of attributes and elements. The order is a
 165  
     * class level annotation that can be used only once per class
 166  
     * XML schema. If none exists then this will return null.
 167  
     *  of the class processed by this scanner.
 168  
     * 
 169  
     * @return this returns the name of the object being scanned
 170  
     */
 171  
    public Order getOrder() {
 172  4744
       return order;
 173  
    }
 174  
    
 175  
    /**
 176  
     * This returns the root of the class processed by this scanner.
 177  
     * The root determines the type of deserialization that is to
 178  
     * be performed and also contains the name of the root element. 
 179  
     * 
 180  
     * @return this returns the name of the object being scanned
 181  
     */
 182  
    public Root getRoot() {
 183  2237
       return root;
 184  
    }
 185  
 
 186  
    /**
 187  
     * This method is used to retrieve the schema class commit method
 188  
     * during the deserialization process. The commit method must be
 189  
     * marked with the <code>Commit</code> annotation so that when the
 190  
     * object is deserialized the persister has a chance to invoke the
 191  
     * method so that the object can build further data structures.
 192  
     * 
 193  
     * @return this returns the commit method for the schema class
 194  
     */
 195  
    public Function getCommit() {
 196  1035935
       return commit;           
 197  
    }
 198  
 
 199  
    /**
 200  
     * This method is used to retrieve the schema class validation
 201  
     * method during the deserialization process. The validation method
 202  
     * must be marked with the <code>Validate</code> annotation so that
 203  
     * when the object is deserialized the persister has a chance to 
 204  
     * invoke that method so that object can validate its field values.
 205  
     * 
 206  
     * @return this returns the validate method for the schema class
 207  
     */   
 208  
    public Function getValidate() {
 209  1035935
       return validate;       
 210  
    }
 211  
    
 212  
    /**
 213  
     * This method is used to retrieve the schema class persistence
 214  
     * method. This is invoked during the serialization process to
 215  
     * get the object a chance to perform an necessary preparation
 216  
     * before the serialization of the object proceeds. The persist
 217  
     * method must be marked with the <code>Persist</code> annotation.
 218  
     * 
 219  
     * @return this returns the persist method for the schema class
 220  
     */
 221  
    public Function getPersist() {
 222  1035935
       return persist;           
 223  
    }
 224  
 
 225  
    /**
 226  
     * This method is used to retrieve the schema class completion
 227  
     * method. This is invoked after the serialization process has
 228  
     * completed and gives the object a chance to restore its state
 229  
     * if the persist method required some alteration or locking.
 230  
     * This is marked with the <code>Complete</code> annotation.
 231  
     * 
 232  
     * @return returns the complete method for the schema class
 233  
     */   
 234  
    public Function getComplete() {
 235  1035935
       return complete;           
 236  
    }
 237  
    
 238  
    /**
 239  
     * This method is used to retrieve the schema class replacement
 240  
     * method. The replacement method is used to substitute an object
 241  
     * that has been deserialized with another object. This allows
 242  
     * a seamless delegation mechanism to be implemented. This is
 243  
     * marked with the <code>Replace</code> annotation. 
 244  
     * 
 245  
     * @return returns the replace method for the schema class
 246  
     */
 247  
    public Function getReplace() {
 248  1035935
       return replace;
 249  
    }
 250  
    
 251  
    /**
 252  
     * This method is used to retrieve the schema class replacement
 253  
     * method. The replacement method is used to substitute an object
 254  
     * that has been deserialized with another object. This allows
 255  
     * a seamless delegation mechanism to be implemented. This is
 256  
     * marked with the <code>Replace</code> annotation. 
 257  
     * 
 258  
     * @return returns the replace method for the schema class
 259  
     */
 260  
    public Function getResolve() {
 261  1035935
       return resolve;
 262  
    }
 263  
   
 264  
    /**
 265  
     * Scan the fields and methods such that the given class is scanned 
 266  
     * first then all super classes up to the root <code>Object</code>. 
 267  
     * All fields and methods from the most specialized classes override 
 268  
     * fields and methods from higher up the inheritance hierarchy. This
 269  
     * means that annotated details can be overridden.
 270  
     * 
 271  
     * @param detail contains the methods and fields to be examined
 272  
     */   
 273  
    private void scan(Detail detail) throws Exception {
 274  2380
       Class type = detail.getType();
 275  
       
 276  5772
       while(type != null) {
 277  3392
          Detail value = support.getDetail(type);
 278  
 
 279  3392
          namespace(value);
 280  3392
          method(value);
 281  3392
          definition(value);
 282  3392
          type = value.getSuper();
 283  3392
       }      
 284  2380
       commit(detail); 
 285  2380
    }
 286  
    
 287  
    /**
 288  
     * This method is used to extract the <code>Root</code> annotation
 289  
     * and the <code>Order</code> annotation from the detail provided.
 290  
     * These annotation are taken from the first definition encountered
 291  
     * from the most specialized class up through the base classes.
 292  
     * 
 293  
     * @param detail this detail object used to acquire the annotations
 294  
     */
 295  
    private void definition(Detail detail) throws Exception {
 296  3392
       if(root == null) {
 297  3335
          root = detail.getRoot();
 298  
       }
 299  3392
       if(order == null) {
 300  3389
          order = detail.getOrder();
 301  
       }
 302  3392
    }
 303  
    
 304  
    /**
 305  
     * This is used to acquire the namespace annotations that apply to 
 306  
     * the scanned class. Namespace annotations are added only if they
 307  
     * have not already been extracted from a more specialized class.
 308  
     * When scanned all the namespace definitions are used to qualify
 309  
     * the XML that is produced from serializing the class.
 310  
     * 
 311  
     * @param type this is the type to extract the annotations from
 312  
     */
 313  
    private void namespace(Detail detail) throws Exception {
 314  3392
       NamespaceList scope = detail.getNamespaceList();
 315  3392
       Namespace namespace = detail.getNamespace();
 316  
       
 317  3392
       if(namespace != null) {
 318  49
          decorator.add(namespace);
 319  
       }
 320  3392
       if(scope != null) {
 321  21
          Namespace[] list = scope.value();
 322  
          
 323  50
          for(Namespace name : list) {
 324  29
             decorator.add(name);
 325  
          }
 326  
       }
 327  3392
    }
 328  
    
 329  
    /**
 330  
     * This is used to set the primary namespace for nodes that will
 331  
     * be decorated by the namespace decorator. If no namespace is set
 332  
     * using this method then this decorator will leave the namespace
 333  
     * reference unchanged and only add namespaces for scoping.
 334  
     * 
 335  
     * @param detail the detail object that contains the namespace
 336  
     */
 337  
    private void commit(Detail detail) {
 338  2380
       Namespace namespace = detail.getNamespace();
 339  
       
 340  2380
       if(namespace != null) {
 341  49
          decorator.set(namespace);
 342  
       }
 343  2380
    }
 344  
 
 345  
    /**
 346  
     * This is used to scan the specified class for methods so that
 347  
     * the persister callback annotations can be collected. These
 348  
     * annotations help object implementations to validate the data
 349  
     * that is injected into the instance during deserialization.
 350  
     * 
 351  
     * @param detail this is a detail from within the class hierarchy
 352  
     */
 353  
    private void method(Detail detail) throws Exception {
 354  3392
       List<MethodDetail> list = detail.getMethods();
 355  
 
 356  3392
       for(MethodDetail entry : list) {
 357  33052
          method(entry);              
 358  
       }     
 359  3392
    }
 360  
    
 361  
    /**
 362  
     * Scans the provided method for a persister callback method. If 
 363  
     * the method contains an method annotated as a callback that 
 364  
     * method is stored so that it can be invoked by the persister
 365  
     * during the serialization and deserialization process.
 366  
     * 
 367  
     * @param detail the method to scan for callback annotations
 368  
     */
 369  
    private void method(MethodDetail detail) {
 370  33052
       Annotation[] list = detail.getAnnotations();
 371  33052
       Method method = detail.getMethod();
 372  
       
 373  33482
       for(Annotation label : list) {
 374  430
          if(label instanceof Commit) {           
 375  11
             commit(method);
 376  
          }
 377  430
          if(label instanceof Validate) {      
 378  11
             validate(method);
 379  
          }
 380  430
          if(label instanceof Persist) {      
 381  9
             persist(method);
 382  
          }
 383  430
          if(label instanceof Complete) {      
 384  6
             complete(method);
 385  
          }    
 386  430
          if(label instanceof Replace) {
 387  10
             replace(method);              
 388  
          }   
 389  430
          if(label instanceof Resolve) {
 390  5
             resolve(method);              
 391  
          }  
 392  
       }
 393  33052
    }
 394  
    
 395  
    /**
 396  
     * This method is used to check the provided method to determine
 397  
     * if it contains the <code>Replace</code> annotation. If the
 398  
     * method contains the required annotation it is stored so that
 399  
     * it can be invoked during the deserialization process.
 400  
     *
 401  
     * @param method this is the method checked for the annotation
 402  
     */ 
 403  
    private void replace(Method method) {
 404  10
       if(replace == null) {
 405  10
          replace = getFunction(method);
 406  
       }
 407  10
    }
 408  
    
 409  
    /**
 410  
     * This method is used to check the provided method to determine
 411  
     * if it contains the <code>Resolve</code> annotation. If the
 412  
     * method contains the required annotation it is stored so that
 413  
     * it can be invoked during the deserialization process.
 414  
     *
 415  
     * @param method this is the method checked for the annotation
 416  
     */ 
 417  
    private void resolve(Method method) {
 418  5
       if(resolve == null) {
 419  5
          resolve = getFunction(method);
 420  
       }
 421  5
    }
 422  
    
 423  
    /**
 424  
     * This method is used to check the provided method to determine
 425  
     * if it contains the <code>Commit</code> annotation. If the
 426  
     * method contains the required annotation it is stored so that
 427  
     * it can be invoked during the deserialization process.
 428  
     *
 429  
     * @param method this is the method checked for the annotation
 430  
     */ 
 431  
    private void commit(Method method) { 
 432  11
       if(commit == null) {
 433  9
          commit = getFunction(method);
 434  
       }
 435  11
    }
 436  
    
 437  
    /**
 438  
     * This method is used to check the provided method to determine
 439  
     * if it contains the <code>Validate</code> annotation. If the
 440  
     * method contains the required annotation it is stored so that
 441  
     * it can be invoked during the deserialization process.
 442  
     *
 443  
     * @param method this is the method checked for the annotation
 444  
     */ 
 445  
    private void validate(Method method) {
 446  11
       if(validate == null) {
 447  9
          validate = getFunction(method);  
 448  
       }
 449  11
    }
 450  
    
 451  
    /**
 452  
     * This method is used to check the provided method to determine
 453  
     * if it contains the <code>Persist</code> annotation. If the
 454  
     * method contains the required annotation it is stored so that
 455  
     * it can be invoked during the deserialization process.
 456  
     *
 457  
     * @param method this is the method checked for the annotation
 458  
     */    
 459  
    private void persist(Method method) {
 460  9
       if(persist == null) {
 461  7
          persist = getFunction(method);  
 462  
       }
 463  9
    }
 464  
 
 465  
    /**
 466  
     * This method is used to check the provided method to determine
 467  
     * if it contains the <code>Complete</code> annotation. If the
 468  
     * method contains the required annotation it is stored so that
 469  
     * it can be invoked during the deserialization process.
 470  
     *
 471  
     * @param method this is the method checked for the annotation
 472  
     */ 
 473  
    private void complete(Method method) {
 474  6
       if(complete == null) {
 475  4
          complete = getFunction(method);    
 476  
       }
 477  6
    } 
 478  
    
 479  
    /**
 480  
     * This is used to acquire a <code>Function</code> object for the
 481  
     * method provided. The function returned will allow the callback
 482  
     * method to be invoked when given the context and target object.
 483  
     * 
 484  
     * @param method this is the method that is to be invoked
 485  
     * 
 486  
     * @return this returns the function that is to be invoked
 487  
     */
 488  
    private Function getFunction(Method method) {
 489  44
       boolean contextual = isContextual(method);
 490  
       
 491  44
       if(!method.isAccessible()) {
 492  36
          method.setAccessible(true);
 493  
       }
 494  44
       return new Function(method, contextual);
 495  
    }
 496  
    
 497  
    /**
 498  
     * This is used to determine whether the annotated method takes a
 499  
     * contextual object. If the method takes a <code>Map</code> then
 500  
     * this returns true, otherwise it returns false.
 501  
     *
 502  
     * @param method this is the method to check the parameters of
 503  
     *
 504  
     * @return this returns true if the method takes a map object
 505  
     */ 
 506  
    private boolean isContextual(Method method)  {
 507  44
       Class[] list = method.getParameterTypes();
 508  
 
 509  44
       if(list.length == 1) {
 510  11
          return Map.class.equals(list[0]);                 
 511  
       }      
 512  33
       return false;
 513  
    }
 514  
 }