Coverage Report - org.simpleframework.xml.convert.ConverterScanner
 
Classes in this File Line Coverage Branch Coverage Complexity
ConverterScanner
92%
36/39
83%
15/18
2.778
 
 1  
 /*
 2  
  * ConverterScanner.java January 2010
 3  
  *
 4  
  * Copyright (C) 2010, 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.convert;
 20  
 
 21  
 import java.lang.annotation.Annotation;
 22  
 
 23  
 import org.simpleframework.xml.Element;
 24  
 import org.simpleframework.xml.Root;
 25  
 import org.simpleframework.xml.strategy.Type;
 26  
 import org.simpleframework.xml.strategy.Value;
 27  
 
 28  
 /**
 29  
  * The <code>ConverterScanner</code> is used to create a converter 
 30  
  * given a method or field representation. Creation of the converter
 31  
  * is done using the <code>Convert</code> annotation, which may
 32  
  * be used to annotate a field, method or class. This describes the
 33  
  * implementation to use for object serialization. To account for
 34  
  * polymorphism the type scanned for annotations can be overridden
 35  
  * from type provided in the <code>Type</code> object. This ensures
 36  
  * that if a collection of objects are serialized the correct
 37  
  * implementation will be used for each type or subtype.
 38  
  * 
 39  
  * @author Niall Gallagher
 40  
  */
 41  
 class ConverterScanner {
 42  
    
 43  
    /**
 44  
     * This is used to instantiate converters given the type.
 45  
     */
 46  
    private final ConverterFactory factory;
 47  
    
 48  
    /**
 49  
     * This is used to build a scanner to scan for annotations.
 50  
     */
 51  
    private final ScannerBuilder builder;
 52  
    
 53  
    /**
 54  
     * Constructor for the <code>ConverterScanner</code> object. This
 55  
     * uses an internal factory to instantiate and cache all of the
 56  
     * converters created. This will ensure that there is reduced
 57  
     * overhead for a serialization process using converters.
 58  
     */
 59  12
    public ConverterScanner() {
 60  12
       this.factory = new ConverterFactory();
 61  12
       this.builder = new ScannerBuilder();
 62  12
    }
 63  
    
 64  
    /**
 65  
     * This method will lookup and instantiate a converter found from
 66  
     * scanning the field or method type provided. If the type has
 67  
     * been overridden then the <code>Value</code> object will provide
 68  
     * the type to scan. If no annotation is found on the class, field
 69  
     * or method then this will return null.
 70  
     * 
 71  
     * @param type this is the type to search for the annotation
 72  
     * @param value this contains the type if it was overridden
 73  
     * 
 74  
     * @return a converter scanned from the provided field or method
 75  
     */
 76  
    public Converter getConverter(Type type, Value value) throws Exception {
 77  32
       Class real = getType(type, value);
 78  32
       Convert convert = getConvert(type, real);
 79  
       
 80  32
       if(convert != null) {
 81  20
          return factory.getInstance(convert);
 82  
       }
 83  12
       return null;
 84  
    }
 85  
    
 86  
    /**
 87  
     * This method will lookup and instantiate a converter found from
 88  
     * scanning the field or method type provided. If the type has
 89  
     * been overridden then the object instance will provide the type 
 90  
     * to scan. If no annotation is found on the class, field or 
 91  
     * method then this will return null.
 92  
     * 
 93  
     * @param type this is the type to search for the annotation
 94  
     * @param value this contains the type if it was overridden
 95  
     * 
 96  
     * @return a converter scanned from the provided field or method
 97  
     */
 98  
    public Converter getConverter(Type type, Object value) throws Exception {
 99  71
       Class real = getType(type, value);
 100  71
       Convert convert = getConvert(type, real);
 101  
       
 102  71
       if(convert != null) {
 103  37
          return factory.getInstance(convert);
 104  
       }
 105  34
       return null;
 106  
    }
 107  
    
 108  
    /**
 109  
     * This method is used to scan the provided <code>Type</code> for
 110  
     * an annotation. If the <code>Type</code> represents a field or
 111  
     * method then the annotation can be taken directly from that
 112  
     * field or method. If however the type represents a class then
 113  
     * the class itself must contain the annotation. 
 114  
     * 
 115  
     * @param type the field or method containing the annotation
 116  
     * @param real the type that represents the field or method
 117  
     * 
 118  
     * @return this returns the annotation on the field or method
 119  
     */
 120  
    private Convert getConvert(Type type, Class real) throws Exception {
 121  103
       Convert convert = getConvert(type);
 122  
       
 123  103
       if(convert == null) {
 124  76
          return getConvert(real);
 125  
       }
 126  27
       return convert;
 127  
    }
 128  
    
 129  
    /**
 130  
     * This method is used to scan the provided <code>Type</code> for
 131  
     * an annotation. If the <code>Type</code> represents a field or
 132  
     * method then the annotation can be taken directly from that
 133  
     * field or method. If however the type represents a class then
 134  
     * the class itself must contain the annotation. 
 135  
     * 
 136  
     * @param type the field or method containing the annotation
 137  
     * 
 138  
     * @return this returns the annotation on the field or method
 139  
     */
 140  
    private Convert getConvert(Type type) throws Exception {
 141  103
       Convert convert = type.getAnnotation(Convert.class);
 142  
       
 143  103
       if(convert != null) {
 144  27
          Element element = type.getAnnotation(Element.class);
 145  
       
 146  27
          if(element == null) {
 147  0
             throw new ConvertException("Element annotation required for %s", type);
 148  
          }
 149  
       }
 150  103
       return convert;
 151  
    }
 152  
    
 153  
    /**
 154  
     * This method is used to scan the provided <code>Type</code> for
 155  
     * an annotation. If the <code>Type</code> represents a field or
 156  
     * method then the annotation can be taken directly from that
 157  
     * field or method. If however the type represents a class then
 158  
     * the class itself must contain the annotation. 
 159  
     * 
 160  
     * @param real the type that represents the field or method
 161  
     * 
 162  
     * @return this returns the annotation on the field or method
 163  
     */
 164  
    private Convert getConvert(Class real) throws Exception {
 165  76
       Convert convert = getAnnotation(real, Convert.class);
 166  
       
 167  76
       if(convert != null) {
 168  30
          Root root = getAnnotation(real, Root.class);
 169  
          
 170  30
          if(root == null) {
 171  0
             throw new ConvertException("Root annotation required for %s", real);
 172  
          }
 173  
       }
 174  76
       return convert;
 175  
    }
 176  
    
 177  
    /**
 178  
     * This is used to acquire the <code>Convert</code> annotation from
 179  
     * the class provided. If the type does not contain the annotation
 180  
     * then this scans all supertypes until either an annotation is
 181  
     * found or there are no further supertypes. 
 182  
     * 
 183  
     * @param type this is the type to scan for annotations
 184  
     * @param label this is the annotation type that is to be found
 185  
     * 
 186  
     * @return this returns the annotation if found otherwise null
 187  
     */
 188  
    private <T extends Annotation> T getAnnotation(Class<?> type, Class<T> label) {
 189  106
       return builder.build(type).scan(label);
 190  
    }
 191  
    
 192  
    /**
 193  
     * This is used to acquire the class that should be scanned. The
 194  
     * type is found either on the method or field, or should there
 195  
     * be a subtype then the class is taken from the provided value.
 196  
     * 
 197  
     * @param type this is the type representing the field or method
 198  
     * @param value this contains the type if it was overridden
 199  
     * 
 200  
     * @return this returns the class that has been scanned
 201  
     */
 202  
    private Class getType(Type type, Value value) {
 203  32
       Class real = type.getType();
 204  
       
 205  32
       if(value != null) {
 206  8
          return value.getType();
 207  
       }
 208  24
       return real;
 209  
    }
 210  
    
 211  
    /**
 212  
     * This is used to acquire the class that should be scanned. The
 213  
     * type is found either on the method or field, or should there
 214  
     * be a subtype then the class is taken from the provided value.
 215  
     * 
 216  
     * @param type this is the type representing the field or method
 217  
     * @param value this contains the type if it was overridden
 218  
     * 
 219  
     * @return this returns the class that has been scanned
 220  
     */
 221  
    private Class getType(Type type, Object value) {
 222  71
       Class real = type.getType();
 223  
       
 224  71
       if(value != null) {
 225  71
          return value.getClass();
 226  
       }
 227  0
       return real;
 228  
    }
 229  
 }