Coverage Report - org.simpleframework.xml.core.Reflector
 
Classes in this File Line Coverage Branch Coverage Complexity
Reflector
86%
77/89
76%
35/46
3
 
 1  
 /*
 2  
  * Reflector.java April 2007
 3  
  *
 4  
  * Copyright (C) 2007, 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.reflect.Array;
 22  
 import java.lang.reflect.Constructor;
 23  
 import java.lang.reflect.Field;
 24  
 import java.lang.reflect.GenericArrayType;
 25  
 import java.lang.reflect.Method;
 26  
 import java.lang.reflect.ParameterizedType;
 27  
 import java.lang.reflect.Type;
 28  
 
 29  
 /**
 30  
  * The <code>Reflector</code> object is used to determine the type
 31  
  * of a generic type. This is used when the type of an XML annotation
 32  
  * is not explicitly and the schema scanner needs to determine via
 33  
  * reflection what the generic parameters are of a specific type. In
 34  
  * particular this is used to determine the parameters within a list
 35  
  * annotated with the <code>ElementList</code> annotation. This also
 36  
  * has special handling for arrays within generic collections.
 37  
  * 
 38  
  * @author Niall Gallagher
 39  
  */
 40  0
 final class Reflector {
 41  
    
 42  
    /**
 43  
     * This method is used to acquire a generic parameter dependent 
 44  
     * from the specified field. This will acquire the field class and
 45  
     * attempt to extract the first generic parameter type from that  
 46  
     * field. If there is a generic parameter then the class of that 
 47  
     * parameter is returned from this method.
 48  
     * 
 49  
     * @param field this is the field to acquire the dependent class
 50  
     * 
 51  
     * @return this returns the generic parameter class declared
 52  
     */
 53  
    public static Class getDependent(Field field) {
 54  209
       ParameterizedType type = getType(field);
 55  
       
 56  209
       if(type != null) {
 57  209
          return getClass(type);
 58  
       }
 59  0
       return null;
 60  
    }
 61  
    
 62  
    /**
 63  
     * This method is used to acquire generic parameter dependents 
 64  
     * from the specified field. This will acquire the field class and
 65  
     * attempt to extract all of the generic parameter types from that  
 66  
     * field. If there is a generic parameter then the class of that 
 67  
     * parameter is returned from this method.
 68  
     * 
 69  
     * @param field this is the field to acquire the dependent types
 70  
     * 
 71  
     * @return this returns the generic parameter classes declared
 72  
     */
 73  
    public static Class[] getDependents(Field field) {
 74  751
       ParameterizedType type = getType(field);
 75  
       
 76  751
       if(type != null) {
 77  748
          return getClasses(type);
 78  
       }
 79  3
       return new Class[]{};
 80  
    }
 81  
    
 82  
    /**
 83  
     * This is used to acquire the parameterized types from the given
 84  
     * field. If the field class has been parameterized then this will
 85  
     * return the parameters that have been declared on that class.
 86  
     * 
 87  
     * @param field this is the field to acquire the parameters from
 88  
     * 
 89  
     * @return this will return the parameterized types for the field
 90  
     */
 91  
    private static ParameterizedType getType(Field field) {
 92  960
       Type type = field.getGenericType();
 93  
          
 94  960
       if(type instanceof ParameterizedType) {
 95  957
          return (ParameterizedType) type;
 96  
       }
 97  3
       return null;      
 98  
    }
 99  
    
 100  
    /**
 101  
     * This method is used to acquire a generic parameter dependent 
 102  
     * from the method return type. This will acquire the return type
 103  
     * and attempt to extract the first generic parameter type from 
 104  
     * that type. If there is a generic parameter then the class of 
 105  
     * that parameter is returned from this method.
 106  
     * 
 107  
     * @param method this is the method to acquire the dependent of   
 108  
     * 
 109  
     * @return this returns the generic parameter class declared
 110  
     */   
 111  
    public static Class getReturnDependent(Method method) {
 112  335
       ParameterizedType type = getReturnType(method);
 113  
       
 114  335
       if(type != null) {
 115  31
          return getClass(type);
 116  
       }
 117  304
       return null;
 118  
    }
 119  
    
 120  
    /**
 121  
     * This method is used to acquire a generic parameter dependent 
 122  
     * from the method return type. This will acquire the return type
 123  
     * and attempt to extract the first generic parameter type from 
 124  
     * that type. If there is a generic parameter then the class of 
 125  
     * that parameter is returned from this method.
 126  
     * 
 127  
     * @param method this is the method to acquire the dependent of   
 128  
     * 
 129  
     * @return this returns the generic parameter class declared
 130  
     */   
 131  
    public static Class[] getReturnDependents(Method method) {
 132  335
       ParameterizedType type = getReturnType(method);
 133  
       
 134  335
       if(type != null) {
 135  35
          return getClasses(type);
 136  
       }
 137  300
       return new Class[]{};
 138  
    }
 139  
    
 140  
    /**
 141  
     * This is used to acquire the parameterized types from the given
 142  
     * methods return class. If the return type class is parameterized
 143  
     * then this will return the parameters that have been declared on
 144  
     * that class, otherwise null is returned.
 145  
     * 
 146  
     * @param method this is the method to acquire the parameters from
 147  
     * 
 148  
     * @return this  returns the parameterized types for the method
 149  
     */
 150  
    private static ParameterizedType getReturnType(Method method) {
 151  670
       Type type = method.getGenericReturnType();
 152  
       
 153  670
       if(type instanceof ParameterizedType) {
 154  66
          return (ParameterizedType) type;
 155  
       }
 156  604
       return null;
 157  
    }
 158  
    
 159  
    /**
 160  
     * This method is used to acquire a generic parameter dependent 
 161  
     * from the specified parameter type. This will acquire the type
 162  
     * for the parameter at the specified index and attempt to extract
 163  
     * the first generic parameter type from that type. If there is a
 164  
     * generic parameter then the class of that parameter is returned
 165  
     * from this method, otherwise null is returned.
 166  
     * 
 167  
     * @param method this is the method to acquire the dependent of
 168  
     * @param index this is the index to acquire the parameter from    
 169  
     * 
 170  
     * @return this returns the generic parameter class declared
 171  
     */
 172  
    public static Class getParameterDependent(Method method, int index) {
 173  0
       ParameterizedType type = getParameterType(method, index);
 174  
       
 175  0
       if(type != null) {
 176  0
          return getClass(type);
 177  
       }
 178  0
       return null;
 179  
    }
 180  
    
 181  
    /**
 182  
     * This method is used to acquire a generic parameter dependent 
 183  
     * from the specified parameter type. This will acquire the type
 184  
     * for the parameter at the specified index and attempt to extract
 185  
     * the first generic parameter type from that type. If there is a
 186  
     * generic parameter then the class of that parameter is returned
 187  
     * from this method, otherwise null is returned.
 188  
     * 
 189  
     * @param method this is the method to acquire the dependent of
 190  
     * @param index this is the index to acquire the parameter from    
 191  
     * 
 192  
     * @return this returns the generic parameter class declared
 193  
     */
 194  
    public static Class[] getParameterDependents(Method method, int index) {
 195  4
       ParameterizedType type = getParameterType(method, index);
 196  
       
 197  4
       if(type != null) {
 198  2
          return getClasses(type);
 199  
       }
 200  2
       return new Class[]{};
 201  
    }
 202  
    
 203  
    /**
 204  
     * This method is used to acquire a generic parameter dependent 
 205  
     * from the specified parameter type. This will acquire the type
 206  
     * for the parameter at the specified index and attempt to extract
 207  
     * the first generic parameter type from that type. If there is a
 208  
     * generic parameter then the class of that parameter is returned
 209  
     * from this method, otherwise null is returned.
 210  
     * 
 211  
     * @param factory this is the constructor to acquire the dependent
 212  
     * @param index this is the index to acquire the parameter from    
 213  
     * 
 214  
     * @return this returns the generic parameter class declared
 215  
     */
 216  
    public static Class getParameterDependent(Constructor factory, int index) {
 217  37
       ParameterizedType type = getParameterType(factory, index);
 218  
       
 219  37
       if(type != null) {
 220  37
          return getClass(type);
 221  
       }
 222  0
       return null;
 223  
    }
 224  
    
 225  
    /**
 226  
     * This method is used to acquire a generic parameter dependent 
 227  
     * from the specified parameter type. This will acquire the type
 228  
     * for the parameter at the specified index and attempt to extract
 229  
     * the first generic parameter type from that type. If there is a
 230  
     * generic parameter then the class of that parameter is returned
 231  
     * from this method, otherwise null is returned.
 232  
     * 
 233  
     * @param factory this is the constructor to acquire the dependent 
 234  
     * @param index this is the index to acquire the parameter from    
 235  
     * 
 236  
     * @return this returns the generic parameter class declared
 237  
     */
 238  
    public static Class[] getParameterDependents(Constructor factory, int index) {
 239  16
       ParameterizedType type = getParameterType(factory, index);
 240  
       
 241  16
       if(type != null) {
 242  16
          return getClasses(type);
 243  
       }
 244  0
       return new Class[]{};
 245  
    }
 246  
    
 247  
    /**
 248  
     * This is used to acquire the parameterized types from the given
 249  
     * methods parameter class at the specified index position. If the
 250  
     * parameter class is parameterized this returns the parameters 
 251  
     * that have been declared on that class.
 252  
     * 
 253  
     * @param method this is the method to acquire the parameters from
 254  
     * @param index this is the index to acquire the parameter from     
 255  
     * 
 256  
     * @return this  returns the parameterized types for the method
 257  
     */
 258  
    private static ParameterizedType getParameterType(Method method, int index) {
 259  4
       Type[] list = method.getGenericParameterTypes();
 260  
          
 261  4
       if(list.length > index) {         
 262  4
          Type type = list[index];
 263  
          
 264  4
          if(type instanceof ParameterizedType) {
 265  2
             return (ParameterizedType) type;
 266  
          }
 267  
       }
 268  2
       return null;
 269  
    }
 270  
    
 271  
    /**
 272  
     * This is used to acquire the parameterized types from the given
 273  
     * constructors parameter class at the specified index position. If 
 274  
     * the parameter class is parameterized this returns the parameters 
 275  
     * that have been declared on that class.
 276  
     * 
 277  
     * @param factory this is constructor method to acquire the parameters 
 278  
     * @param index this is the index to acquire the parameter from     
 279  
     * 
 280  
     * @return this  returns the parameterized types for the method
 281  
     */
 282  
    private static ParameterizedType getParameterType(Constructor factory, int index) {
 283  53
       Type[] list = factory.getGenericParameterTypes();
 284  
          
 285  53
       if(list.length > index) {         
 286  53
          Type type = list[index];
 287  
          
 288  53
          if(type instanceof ParameterizedType) {
 289  53
             return (ParameterizedType) type;
 290  
          }
 291  
       }
 292  0
       return null;
 293  
    }
 294  
    
 295  
    /**
 296  
     * This is used to extract the class from the specified type. If
 297  
     * there are no actual generic type arguments to the specified
 298  
     * type then this will return null. Otherwise this will return 
 299  
     * the actual class, regardless of whether the class is an array.
 300  
     *  
 301  
     * @param type this is the type to extract the class from
 302  
     *  
 303  
     * @return this returns the class type from the first parameter
 304  
     */
 305  
    private static Class getClass(ParameterizedType type) {
 306  277
       Type[] list = type.getActualTypeArguments();
 307  
       
 308  277
       if(list.length > 0) {
 309  277
          return getClass(list[0]);
 310  
       }      
 311  0
       return null;      
 312  
    }
 313  
    
 314  
    /**
 315  
     * This is used to extract the class from the specified type. If
 316  
     * there are no actual generic type arguments to the specified
 317  
     * type then this will return null. Otherwise this will return 
 318  
     * the actual class, regardless of whether the class is an array.
 319  
     *  
 320  
     * @param type this is the type to extract the class from
 321  
     *  
 322  
     * @return this returns the class type from the first parameter
 323  
     */
 324  
    private static Class[] getClasses(ParameterizedType type) {
 325  801
       Type[] list = type.getActualTypeArguments();
 326  801
       Class[] types = new Class[list.length]; 
 327  
             
 328  2379
       for(int i = 0; i < list.length; i++) {
 329  1578
          types[i] = getClass(list[i]);
 330  
       }  
 331  801
       return types;     
 332  
    }
 333  
    
 334  
    /**
 335  
     * This is used to extract the class from the specified type. If
 336  
     * there are no actual generic type arguments to the specified
 337  
     * type then this will return null. Otherwise this will return 
 338  
     * the actual class, regardless of whether the class is an array.
 339  
     *  
 340  
     * @param type this is the type to extract the class from
 341  
     *  
 342  
     * @return this returns the class type from the first parameter
 343  
     */
 344  
    private static Class getClass(Type type) {
 345  1868
       if(type instanceof Class) {
 346  1739
          return (Class) type;
 347  
       }
 348  129
       return getGenericClass(type);
 349  
    }
 350  
    
 351  
    /**
 352  
     * This is used to extract the class from the specified type. If
 353  
     * there are no actual generic type arguments to the specified
 354  
     * type then this will return null. Otherwise this will return 
 355  
     * the actual class, regardless of whether the class is an array.
 356  
     *  
 357  
     * @param type this is the type to extract the class from
 358  
     *  
 359  
     * @return this returns the class type from the first parameter
 360  
     */
 361  
    private static Class getGenericClass(Type type) {
 362  129
       if(type instanceof GenericArrayType) {
 363  13
          return getArrayClass(type);
 364  
       }      
 365  116
       return Object.class;
 366  
    }
 367  
    
 368  
    /**
 369  
     * This is used to extract an array class from the specified. If
 370  
     * a class can be extracted from the type then the array class 
 371  
     * is created by reflective creating a zero length array with 
 372  
     * the component type of the array and returning the array class.
 373  
     *  
 374  
     * @param type this is the type to extract the class from
 375  
     *  
 376  
     * @return this returns the class type from the array type
 377  
     */
 378  
    private static Class getArrayClass(Type type) {
 379  13
       GenericArrayType generic = (GenericArrayType) type;
 380  13
       Type array = generic.getGenericComponentType();
 381  13
       Class entry = getClass(array);
 382  
       
 383  13
       if(entry != null) {
 384  13
          return Array.newInstance(entry, 0).getClass();
 385  
       }
 386  0
       return null;
 387  
    }
 388  
    
 389  
    /**
 390  
     * This is used to acquire a bean name for a method or field name.
 391  
     * A bean name is the name of a method or field with the first
 392  
     * character decapitalized. An exception to this is when a method
 393  
     * or field starts with an acronym, in such a case the name will
 394  
     * remain unchanged from the original name.
 395  
     * 
 396  
     * @param name this is the name to convert to a bean name
 397  
     * 
 398  
     * @return this returns the bean value for the given name
 399  
     */
 400  
    public static String getName(String name) {
 401  8111
       int length = name.length();
 402  
       
 403  8111
       if(length > 0) {
 404  8111
          char[] array = name.toCharArray();
 405  8111
          char first = array[0];
 406  
       
 407  8111
          if(!isAcronym(array)) { 
 408  8109
             array[0] = toLowerCase(first);
 409  
          }
 410  8111
          return new String(array);
 411  
       }
 412  0
       return name;
 413  
    }
 414  
    
 415  
    /**
 416  
     * This is used to determine if the provided array of characters
 417  
     * represents an acronym. The array of characters is considered
 418  
     * an acronym if the first and second characters are upper case.
 419  
     * 
 420  
     * @param array the array to evaluate whether it is an acronym
 421  
     * 
 422  
     * @return this returns true if the provided array is an acronym
 423  
     */
 424  
    private static boolean isAcronym(char[] array) {
 425  8111
       if(array.length < 2) {
 426  370
          return false;
 427  
       }
 428  7741
       if(!isUpperCase(array[0])) {
 429  23
          return false;
 430  
       }
 431  7718
       return isUpperCase(array[1]);
 432  
    }
 433  
    
 434  
    /**
 435  
     * This is used to convert the provided character to lower case.
 436  
     * The character conversion is done for all unicode characters. 
 437  
     * 
 438  
     * @param value this is the value that is to be converted
 439  
     * 
 440  
     * @return this returns the provided character in lower case
 441  
     */
 442  
    private static char toLowerCase(char value) {
 443  8109
       return Character.toLowerCase(value);
 444  
    }
 445  
    
 446  
    /**
 447  
     * This is used to determine if the provided character is an
 448  
     * upper case character. This can deal with unicode characters.
 449  
     * 
 450  
     * @param value this is the value that is to be evaluated
 451  
     * 
 452  
     * @return this returns true if the character is upper case
 453  
     */
 454  
    private static boolean isUpperCase(char value) {
 455  15459
       return Character.isUpperCase(value);
 456  
    }
 457  
 }