Coverage Report - org.mule.util.generics.GenericTypeResolver
 
Classes in this File Line Coverage Branch Coverage Complexity
GenericTypeResolver
0%
0/119
0%
0/78
0
 
 1  
 /*
 2  
  * $Id: GenericTypeResolver.java 19191 2010-08-25 21:05:23Z tcarlson $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 9  
  */
 10  
 package org.mule.util.generics;
 11  
 
 12  
 import java.lang.ref.Reference;
 13  
 import java.lang.ref.WeakReference;
 14  
 import java.lang.reflect.GenericArrayType;
 15  
 import java.lang.reflect.Method;
 16  
 import java.lang.reflect.ParameterizedType;
 17  
 import java.lang.reflect.Type;
 18  
 import java.lang.reflect.TypeVariable;
 19  
 import java.util.Collections;
 20  
 import java.util.HashMap;
 21  
 import java.util.Map;
 22  
 import java.util.WeakHashMap;
 23  
 
 24  
 /**
 25  
  * Helper class for resolving generic types against type variables.
 26  
  * <p/>
 27  
  * <p>Mainly intended for usage within the framework, resolving method
 28  
  * parameter types even when they are declared generically.
 29  
  * <p/>
 30  
  * author: Spring
 31  
  */
 32  
 public abstract class GenericTypeResolver
 33  
 {
 34  
 
 35  
     /**
 36  
      * Cache from Class to TypeVariable Map
 37  
      */
 38  0
     private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache =
 39  
             Collections.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
 40  
 
 41  
 
 42  
     private GenericTypeResolver()
 43  0
     {
 44  
         // do not isntantiate
 45  0
     }
 46  
 
 47  
     /**
 48  
      * Determine the target type for the given parameter specification.
 49  
      *
 50  
      * @param methodParam the method parameter specification
 51  
      * @return the corresponding generic parameter type
 52  
      */
 53  
     public static Type getTargetType(MethodParameter methodParam)
 54  
     {
 55  0
         if (methodParam.getConstructor() != null)
 56  
         {
 57  0
             return methodParam.getConstructor().getGenericParameterTypes()[methodParam.getParameterIndex()];
 58  
         }
 59  
         else
 60  
         {
 61  0
             if (methodParam.getParameterIndex() >= 0)
 62  
             {
 63  0
                 return methodParam.getMethod().getGenericParameterTypes()[methodParam.getParameterIndex()];
 64  
             }
 65  
             else
 66  
             {
 67  0
                 return methodParam.getMethod().getGenericReturnType();
 68  
             }
 69  
         }
 70  
     }
 71  
 
 72  
     /**
 73  
      * Determine the target type for the given generic parameter type.
 74  
      *
 75  
      * @param methodParam the method parameter specification
 76  
      * @param clazz       the class to resolve type variables against
 77  
      * @return the corresponding generic parameter or return type
 78  
      */
 79  
     public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz)
 80  
     {
 81  0
         Type genericType = getTargetType(methodParam);
 82  0
         Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
 83  0
         Type rawType = getRawType(genericType, typeVariableMap);
 84  0
         Class result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType());
 85  0
         methodParam.setParameterType(result);
 86  0
         methodParam.typeVariableMap = typeVariableMap;
 87  0
         return result;
 88  
     }
 89  
 
 90  
     /**
 91  
      * Determine the target type for the generic return type of the given method.
 92  
      *
 93  
      * @param method the method to introspect
 94  
      * @param clazz  the class to resolve type variables against
 95  
      * @return the corresponding generic parameter or return type
 96  
      */
 97  
     public static Class<?> resolveReturnType(Method method, Class clazz)
 98  
     {
 99  0
         Type genericType = method.getGenericReturnType();
 100  0
         Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
 101  0
         Type rawType = getRawType(genericType, typeVariableMap);
 102  0
         return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
 103  
     }
 104  
 
 105  
     /**
 106  
      * Resolve the single type argument of the given generic interface against
 107  
      * the given target class which is assumed to implement the generic interface
 108  
      * and possibly declare a concrete type for its type variable.
 109  
      *
 110  
      * @param clazz      the target class to check against
 111  
      * @param genericIfc the generic interface to resolve the type argument from
 112  
      * @return the resolved type of the argument, or <code>null</code> if not resolvable
 113  
      */
 114  
     public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc)
 115  
     {
 116  0
         Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
 117  0
         if (typeArgs == null)
 118  
         {
 119  0
             return null;
 120  
         }
 121  0
         if (typeArgs.length != 1)
 122  
         {
 123  0
             throw new IllegalArgumentException("Expected 1 type argument on generic interface [" +
 124  
                     genericIfc.getName() + "] but found " + typeArgs.length);
 125  
         }
 126  0
         return typeArgs[0];
 127  
     }
 128  
 
 129  
     /**
 130  
      * Resolve the type arguments of the given generic interface against the given
 131  
      * target class which is assumed to implement the generic interface and possibly
 132  
      * declare concrete types for its type variables.
 133  
      *
 134  
      * @param clazz      the target class to check against
 135  
      * @param genericIfc the generic interface to resolve the type argument from
 136  
      * @return the resolved type of each argument, with the array size matching the
 137  
      *         number of actual type arguments, or <code>null</code> if not resolvable
 138  
      */
 139  
     public static Class[] resolveTypeArguments(Class clazz, Class genericIfc)
 140  
     {
 141  0
         return doResolveTypeArguments(clazz, clazz, genericIfc);
 142  
     }
 143  
 
 144  
     private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc)
 145  
     {
 146  0
         while (classToIntrospect != null)
 147  
         {
 148  0
             Type[] ifcs = classToIntrospect.getGenericInterfaces();
 149  0
             for (Type ifc : ifcs)
 150  
             {
 151  0
                 if (ifc instanceof ParameterizedType)
 152  
                 {
 153  0
                     ParameterizedType paramIfc = (ParameterizedType) ifc;
 154  0
                     Type rawType = paramIfc.getRawType();
 155  0
                     if (genericIfc.equals(rawType))
 156  
                     {
 157  0
                         Type[] typeArgs = paramIfc.getActualTypeArguments();
 158  0
                         Class[] result = new Class[typeArgs.length];
 159  0
                         for (int i = 0; i < typeArgs.length; i++)
 160  
                         {
 161  0
                             Type arg = typeArgs[i];
 162  0
                             if (arg instanceof TypeVariable)
 163  
                             {
 164  0
                                 arg = getTypeVariableMap(ownerClass).get((TypeVariable) arg);
 165  
                             }
 166  0
                             result[i] = (arg instanceof Class ? (Class) arg : Object.class);
 167  
                         }
 168  0
                         return result;
 169  
                     }
 170  0
                     else if (genericIfc.isAssignableFrom((Class) rawType))
 171  
                     {
 172  0
                         return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
 173  
                     }
 174  0
                 }
 175  0
                 else if (genericIfc.isAssignableFrom((Class) ifc))
 176  
                 {
 177  0
                     return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
 178  
                 }
 179  
             }
 180  0
             classToIntrospect = classToIntrospect.getSuperclass();
 181  0
         }
 182  0
         return null;
 183  
     }
 184  
 
 185  
 
 186  
     /**
 187  
      * Resolve the specified generic type against the given TypeVariable map.
 188  
      *
 189  
      * @param genericType     the generic type to resolve
 190  
      * @param typeVariableMap the TypeVariable Map to resolved against
 191  
      * @return the type if it resolves to a Class, or <code>Object.class</code> otherwise
 192  
      */
 193  
     static Class resolveType(Type genericType, Map<TypeVariable, Type> typeVariableMap)
 194  
     {
 195  0
         Type rawType = getRawType(genericType, typeVariableMap);
 196  0
         return (rawType instanceof Class ? (Class) rawType : Object.class);
 197  
     }
 198  
 
 199  
     /**
 200  
      * Determine the raw type for the given generic parameter type.
 201  
      *
 202  
      * @param genericType     the generic type to resolve
 203  
      * @param typeVariableMap the TypeVariable Map to resolved against
 204  
      * @return the resolved raw type
 205  
      */
 206  
     static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap)
 207  
     {
 208  0
         Type resolvedType = genericType;
 209  0
         if (genericType instanceof TypeVariable)
 210  
         {
 211  0
             TypeVariable tv = (TypeVariable) genericType;
 212  0
             resolvedType = typeVariableMap.get(tv);
 213  0
             if (resolvedType == null)
 214  
             {
 215  0
                 resolvedType = extractBoundForTypeVariable(tv);
 216  
             }
 217  
         }
 218  0
         if (resolvedType instanceof ParameterizedType)
 219  
         {
 220  0
             return ((ParameterizedType) resolvedType).getRawType();
 221  
         }
 222  
         else
 223  
         {
 224  0
             return resolvedType;
 225  
         }
 226  
     }
 227  
 
 228  
     /**
 229  
      * Build a mapping of {@link TypeVariable#getName TypeVariable names} to concrete
 230  
      * {@link Class} for the specified {@link Class}. Searches all super types,
 231  
      * enclosing types and interfaces.
 232  
      */
 233  
     static Map<TypeVariable, Type> getTypeVariableMap(Class clazz)
 234  
     {
 235  0
         Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
 236  0
         Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
 237  
 
 238  0
         if (typeVariableMap == null)
 239  
         {
 240  0
             typeVariableMap = new HashMap<TypeVariable, Type>();
 241  
 
 242  
             // interfaces
 243  0
             extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
 244  
 
 245  
             // super class
 246  0
             Type genericType = clazz.getGenericSuperclass();
 247  0
             Class type = clazz.getSuperclass();
 248  0
             while (type != null && !Object.class.equals(type))
 249  
             {
 250  0
                 if (genericType instanceof ParameterizedType)
 251  
                 {
 252  0
                     ParameterizedType pt = (ParameterizedType) genericType;
 253  0
                     populateTypeMapFromParameterizedType(pt, typeVariableMap);
 254  
                 }
 255  0
                 extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
 256  0
                 genericType = type.getGenericSuperclass();
 257  0
                 type = type.getSuperclass();
 258  
             }
 259  
 
 260  
             // enclosing class
 261  0
             type = clazz;
 262  0
             while (type.isMemberClass())
 263  
             {
 264  0
                 genericType = type.getGenericSuperclass();
 265  0
                 if (genericType instanceof ParameterizedType)
 266  
                 {
 267  0
                     ParameterizedType pt = (ParameterizedType) genericType;
 268  0
                     populateTypeMapFromParameterizedType(pt, typeVariableMap);
 269  
                 }
 270  0
                 type = type.getEnclosingClass();
 271  
             }
 272  
 
 273  0
             typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(typeVariableMap));
 274  
         }
 275  
 
 276  0
         return typeVariableMap;
 277  
     }
 278  
 
 279  
     /**
 280  
      * Extracts the bound <code>Type</code> for a given {@link TypeVariable}.
 281  
      */
 282  
     static Type extractBoundForTypeVariable(TypeVariable typeVariable)
 283  
     {
 284  0
         Type[] bounds = typeVariable.getBounds();
 285  0
         if (bounds.length == 0)
 286  
         {
 287  0
             return Object.class;
 288  
         }
 289  0
         Type bound = bounds[0];
 290  0
         if (bound instanceof TypeVariable)
 291  
         {
 292  0
             bound = extractBoundForTypeVariable((TypeVariable) bound);
 293  
         }
 294  0
         return bound;
 295  
     }
 296  
 
 297  
     private static void extractTypeVariablesFromGenericInterfaces(Type[] genericInterfaces, Map<TypeVariable, Type> typeVariableMap)
 298  
     {
 299  0
         for (Type genericInterface : genericInterfaces)
 300  
         {
 301  0
             if (genericInterface instanceof ParameterizedType)
 302  
             {
 303  0
                 ParameterizedType pt = (ParameterizedType) genericInterface;
 304  0
                 populateTypeMapFromParameterizedType(pt, typeVariableMap);
 305  0
                 if (pt.getRawType() instanceof Class)
 306  
                 {
 307  0
                     extractTypeVariablesFromGenericInterfaces(
 308  
                             ((Class) pt.getRawType()).getGenericInterfaces(), typeVariableMap);
 309  
                 }
 310  0
             }
 311  0
             else if (genericInterface instanceof Class)
 312  
             {
 313  0
                 extractTypeVariablesFromGenericInterfaces(
 314  
                         ((Class) genericInterface).getGenericInterfaces(), typeVariableMap);
 315  
             }
 316  
         }
 317  0
     }
 318  
 
 319  
     /**
 320  
      * Read the {@link TypeVariable TypeVariables} from the supplied {@link ParameterizedType}
 321  
      * and add mappings corresponding to the {@link TypeVariable#getName TypeVariable name} ->
 322  
      * concrete type to the supplied {@link Map}.
 323  
      * <p>Consider this case:
 324  
      * <pre class="code>
 325  
      * public interface Foo<S, T> {
 326  
      * ..
 327  
      * }
 328  
      * <p/>
 329  
      * public class FooImpl implements Foo<String, Integer> {
 330  
      * ..
 331  
      * }</pre>
 332  
      * For '<code>FooImpl</code>' the following mappings would be added to the {@link Map}:
 333  
      * {S=java.lang.String, T=java.lang.Integer}.
 334  
      */
 335  
     private static void populateTypeMapFromParameterizedType(ParameterizedType type, Map<TypeVariable, Type> typeVariableMap)
 336  
     {
 337  0
         if (type.getRawType() instanceof Class)
 338  
         {
 339  0
             Type[] actualTypeArguments = type.getActualTypeArguments();
 340  0
             TypeVariable[] typeVariables = ((Class) type.getRawType()).getTypeParameters();
 341  0
             for (int i = 0; i < actualTypeArguments.length; i++)
 342  
             {
 343  0
                 Type actualTypeArgument = actualTypeArguments[i];
 344  0
                 TypeVariable variable = typeVariables[i];
 345  0
                 if (actualTypeArgument instanceof Class)
 346  
                 {
 347  0
                     typeVariableMap.put(variable, actualTypeArgument);
 348  
                 }
 349  0
                 else if (actualTypeArgument instanceof GenericArrayType)
 350  
                 {
 351  0
                     typeVariableMap.put(variable, actualTypeArgument);
 352  
                 }
 353  0
                 else if (actualTypeArgument instanceof ParameterizedType)
 354  
                 {
 355  0
                     typeVariableMap.put(variable, actualTypeArgument);
 356  
                 }
 357  0
                 else if (actualTypeArgument instanceof TypeVariable)
 358  
                 {
 359  
                     // We have a type that is parameterized at instantiation time
 360  
                     // the nearest match on the bridge method will be the bounded type.
 361  0
                     TypeVariable typeVariableArgument = (TypeVariable) actualTypeArgument;
 362  0
                     Type resolvedType = typeVariableMap.get(typeVariableArgument);
 363  0
                     if (resolvedType == null) {
 364  0
                         resolvedType = extractBoundForTypeVariable(typeVariableArgument);
 365  
                     }
 366  0
                     typeVariableMap.put(variable, resolvedType);
 367  
                 }
 368  
             }
 369  
         }
 370  0
     }
 371  
 
 372  
 }