Coverage Report - org.dozer.util.TypeResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
TypeResolver
92%
60/65
89%
34/38
3.889
 
 1  
 /**
 2  
  * Copyright 2005-2013 Dozer Project
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.dozer.util;
 17  
 
 18  
 import java.lang.reflect.Method;
 19  
 import java.lang.reflect.ParameterizedType;
 20  
 import java.lang.reflect.Type;
 21  
 import java.lang.reflect.TypeVariable;
 22  
 import java.util.HashMap;
 23  
 import java.util.Map;
 24  
 import java.util.concurrent.ConcurrentHashMap;
 25  
 
 26  
 /**
 27  
  * Utility class to map from a Java 5 Type to the Class that is represented by this Type at runtime.
 28  
  *
 29  
  * @author andor.greissl
 30  
  */
 31  
 public final class TypeResolver {
 32  
   /**
 33  
    * empty type array
 34  
    */
 35  1
   private static final Type[] EMPTY_TYPES = new Type[0];
 36  
 
 37  
   /**
 38  
    * container for the already resolved type mappings
 39  
    */
 40  1
   private static Map<Class<?>, Map<TypeVariable<?>, Type>> typeMaps =
 41  
           new ConcurrentHashMap<Class<?>, Map<TypeVariable<?>, Type>>();
 42  
 
 43  
   /**
 44  
    * utility class constructor
 45  
    */
 46  0
   private TypeResolver() {
 47  0
   }
 48  
 
 49  
   /**
 50  
    * Try to resolve the property type for the supplied methods and target class. The supplied read and write methods
 51  
    * should resemble a Java bean property (setter and getter)
 52  
    *
 53  
    * @param targetClass the target class of the property
 54  
    * @param readMethod  the read method of the property
 55  
    * @param writeMethod the write method of the property
 56  
    * @return the actual class of the property type
 57  
    */
 58  
   public static Class<?> resolvePropertyType(final Class<?> targetClass, final Method readMethod, final Method writeMethod) {
 59  56701
     Class<?> type = null;
 60  
 
 61  56701
     if (readMethod != null) {
 62  56502
       type = resolveReturnType(targetClass, readMethod);
 63  
     }
 64  
 
 65  56520
     if ((type == null) && (writeMethod != null)) {
 66  858
       type = resolveParameterType(targetClass, writeMethod);
 67  
     }
 68  56520
     return type;
 69  
   }
 70  
 
 71  
   /**
 72  
    * resolves the generic return type of the method
 73  
    */
 74  
   private static Class<?> resolveReturnType(final Class<?> targetClass, final Method method) {
 75  
     // get the generic return type
 76  56502
     Type type = method.getGenericReturnType();
 77  
 
 78  
     // get the actual type argument (expect the type to be a TypeVariable)
 79  56502
     return resolveType(targetClass, type);
 80  
   }
 81  
 
 82  
   /**
 83  
    * resolves the parameter return type of the method
 84  
    */
 85  
   private static Class<?> resolveParameterType(final Class<?> targetClass, final Method method) {
 86  
     // get the generic parametertypes of the method
 87  858
     Type[] types = method.getGenericParameterTypes();
 88  
 
 89  
     // check for correct argument list length and type
 90  858
     if ((types == null) || (types.length < 1))
 91  0
       return null;
 92  
 
 93  
     // get the actual type argument (expect the type to be a TypeVariable)
 94  858
     return resolveType(targetClass, types[0]);
 95  
   }
 96  
 
 97  
   private static Class<?> resolveType(final Class<?> targetClass, final Type type) {
 98  57360
     if (type instanceof Class)
 99  55784
       return (Class<?>) type;
 100  
 
 101  1576
     if (!(type instanceof TypeVariable<?>)) {
 102  663
       return null;
 103  
     }
 104  
 
 105  913
     Type targetType = null;
 106  913
     Map<TypeVariable<?>, Type> typeMap = getTypeMap(targetClass);
 107  732
     for (Map.Entry<TypeVariable<?>, Type> typeVariableEntry : typeMap.entrySet()) {
 108  18
       TypeVariable<?> typeVariable = typeVariableEntry.getKey();
 109  18
       if (typeVariable.getName().equals(((TypeVariable) type).getName())) {
 110  12
         targetType = typeVariableEntry.getValue();
 111  12
         break;
 112  
       }
 113  6
     }
 114  
     
 115  732
     if (targetType instanceof Class) {
 116  12
       return (Class<?>) targetType;
 117  
     }
 118  720
     return null;
 119  
   }
 120  
 
 121  
   /**
 122  
    * returns an already cached [TypeVariable -> Type] map for the targetClass or build a new one.
 123  
    */
 124  
   private static Map<TypeVariable<?>, Type> getTypeMap(final Class<?> targetClass) {
 125  
     // check if a TypeMap already exists for this class
 126  913
     if (typeMaps.containsKey(targetClass)) {
 127  726
       return typeMaps.get(targetClass);
 128  
     }
 129  
 
 130  
     // build a new TypeVariable -> Type map
 131  187
     Map<TypeVariable<?>, Type> map = new HashMap<TypeVariable<?>, Type>();
 132  187
     Class<?> clazz = targetClass;
 133  
 
 134  382
     while (!Object.class.equals(clazz)) {
 135  376
       Type genericSuperClass = clazz.getGenericSuperclass();
 136  195
       if (genericSuperClass instanceof ParameterizedType) {
 137  7
         collectActualTypeArguments((ParameterizedType) genericSuperClass, map);
 138  
       }
 139  195
       collectActualTypeArguments(clazz.getGenericInterfaces(), map);
 140  195
       clazz = clazz.getSuperclass();
 141  195
     }
 142  
 
 143  6
     typeMaps.put(targetClass, map);
 144  6
     return map;
 145  
   }
 146  
 
 147  
   /**
 148  
    * collects all TypeVariable -> Type mappings from the generic interfaces
 149  
    */
 150  
   private static void collectActualTypeArguments(final Type[] genericInterfaces,
 151  
                                                  final Map<TypeVariable<?>, Type> map) {
 152  227
     for (Type genericInterface : genericInterfaces) {
 153  16
       if (genericInterface instanceof ParameterizedType) {
 154  9
         ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
 155  9
         collectActualTypeArguments(parameterizedType, map);
 156  9
         Type rawType = parameterizedType.getRawType();
 157  9
         collectActualTypeArguments(getGenericInterfaces(rawType), map);
 158  
 
 159  9
       } else {
 160  7
         collectActualTypeArguments(getGenericInterfaces(genericInterface), map);
 161  
       }
 162  
     }
 163  211
   }
 164  
 
 165  
   /**
 166  
    * return the generic interfaces of the type if type is a class
 167  
    */
 168  
   private static Type[] getGenericInterfaces(final Type type) {
 169  16
     if (type instanceof Class) {
 170  16
       return Class.class.cast(type).getGenericInterfaces();
 171  
     }
 172  0
     return EMPTY_TYPES;
 173  
   }
 174  
 
 175  
   /**
 176  
    * collects all TypeVariable -> Type mappings from a parameterizedType
 177  
    */
 178  
   private static void collectActualTypeArguments(final ParameterizedType parameterizedType,
 179  
                                                  final Map<TypeVariable<?>, Type> map) {
 180  
     // get the raw type of the parameterized class and validate its a class
 181  16
     Type rawType = parameterizedType.getRawType();
 182  16
     if (!(rawType instanceof Class))
 183  0
       return;
 184  
 
 185  
     // get variables and types and store them into the map
 186  16
     TypeVariable<?>[] variables = ((Class<?>) rawType).getTypeParameters();
 187  16
     Type[] types = parameterizedType.getActualTypeArguments();
 188  
 
 189  42
     for (int i = 0; i < variables.length; i++) {
 190  
       // found a valid parameter class
 191  26
       if (types[i] instanceof Class) {
 192  6
         map.put(variables[i], types[i]);
 193  
       }
 194  
     }
 195  16
   }
 196  
 
 197  
 }