Coverage Report - org.mule.util.generics.GenericsUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
GenericsUtils
0%
0/89
0%
0/80
0
 
 1  
 /*
 2  
  * $Id: GenericsUtils.java 20321 2010-11-24 15:21:24Z dfeist $
 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.reflect.Array;
 13  
 import java.lang.reflect.Field;
 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.lang.reflect.WildcardType;
 20  
 import java.util.Collection;
 21  
 import java.util.Map;
 22  
 
 23  
 /**
 24  
  * Helper class for determining element types of collections and maps.
 25  
  * <p/>
 26  
  * <p>Mainly intended for usage within the framework, determining the
 27  
  * target type of values to be added to a collection or map
 28  
  * (to be able to attempt type conversion if appropriate).
 29  
  * <p/>
 30  
  * author: Spring
 31  
  */
 32  0
 public class GenericsUtils
 33  
 {
 34  
 
 35  
 
 36  
     /**
 37  
      * Determine the generic element type of the given Collection class
 38  
      * (if it declares one through a generic superclass or generic interface).
 39  
      *
 40  
      * @param collectionClass the collection class to introspect
 41  
      * @return the generic type, or <code>null</code> if none
 42  
      */
 43  
     public static Class<?> getCollectionType(Class<? extends Collection<?>> collectionClass)
 44  
     {
 45  0
         return extractTypeFromClass(collectionClass, Collection.class, 0);
 46  
     }
 47  
 
 48  
     /**
 49  
      * Determine the generic key type of the given Map class
 50  
      * (if it declares one through a generic superclass or generic interface).
 51  
      *
 52  
      * @param mapClass the map class to introspect
 53  
      * @return the generic type, or <code>null</code> if none
 54  
      */
 55  
     public static Class<?> getMapKeyType(Class<? extends Map<?, ?>> mapClass)
 56  
     {
 57  0
         return extractTypeFromClass(mapClass, Map.class, 0);
 58  
     }
 59  
 
 60  
     /**
 61  
      * Determine the generic value type of the given Map class
 62  
      * (if it declares one through a generic superclass or generic interface).
 63  
      *
 64  
      * @param mapClass the map class to introspect
 65  
      * @return the generic type, or <code>null</code> if none
 66  
      */
 67  
     public static Class<?> getMapValueType(Class<? extends Map<?, ?>> mapClass)
 68  
     {
 69  0
         return extractTypeFromClass(mapClass, Map.class, 1);
 70  
     }
 71  
 
 72  
     /**
 73  
      * Determine the generic element type of the given Collection field.
 74  
      *
 75  
      * @param collectionField the collection field to introspect
 76  
      * @return the generic type, or <code>null</code> if none
 77  
      */
 78  
     public static Class<?> getCollectionFieldType(Field collectionField)
 79  
     {
 80  0
         return getGenericFieldType(collectionField, Collection.class, 0, 1);
 81  
     }
 82  
 
 83  
     /**
 84  
      * Determine the generic element type of the given Collection field.
 85  
      *
 86  
      * @param collectionField the collection field to introspect
 87  
      * @param nestingLevel    the nesting level of the target type
 88  
      *                        (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 89  
      *                        nested List, whereas 2 would indicate the element of the nested List)
 90  
      * @return the generic type, or <code>null</code> if none
 91  
      */
 92  
     public static Class<?> getCollectionFieldType(Field collectionField, int nestingLevel)
 93  
     {
 94  0
         return getGenericFieldType(collectionField, Collection.class, 0, nestingLevel);
 95  
     }
 96  
 
 97  
     /**
 98  
      * Determine the generic key type of the given Map field.
 99  
      *
 100  
      * @param mapField the map field to introspect
 101  
      * @return the generic type, or <code>null</code> if none
 102  
      */
 103  
     public static Class<?> getMapKeyFieldType(Field mapField)
 104  
     {
 105  0
         return getGenericFieldType(mapField, Map.class, 0, 1);
 106  
     }
 107  
 
 108  
     /**
 109  
      * Determine the generic key type of the given Map field.
 110  
      *
 111  
      * @param mapField     the map field to introspect
 112  
      * @param nestingLevel the nesting level of the target type
 113  
      *                     (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 114  
      *                     nested List, whereas 2 would indicate the element of the nested List)
 115  
      * @return the generic type, or <code>null</code> if none
 116  
      */
 117  
     public static Class<?> getMapKeyFieldType(Field mapField, int nestingLevel)
 118  
     {
 119  0
         return getGenericFieldType(mapField, Map.class, 0, nestingLevel);
 120  
     }
 121  
 
 122  
     /**
 123  
      * Determine the generic value type of the given Map field.
 124  
      *
 125  
      * @param mapField the map field to introspect
 126  
      * @return the generic type, or <code>null</code> if none
 127  
      */
 128  
     public static Class<?> getMapValueFieldType(Field mapField)
 129  
     {
 130  0
         return getGenericFieldType(mapField, Map.class, 1, 1);
 131  
     }
 132  
 
 133  
     /**
 134  
      * Determine the generic value type of the given Map field.
 135  
      *
 136  
      * @param mapField     the map field to introspect
 137  
      * @param nestingLevel the nesting level of the target type
 138  
      *                     (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 139  
      *                     nested List, whereas 2 would indicate the element of the nested List)
 140  
      * @return the generic type, or <code>null</code> if none
 141  
      */
 142  
     public static Class<?> getMapValueFieldType(Field mapField, int nestingLevel)
 143  
     {
 144  0
         return getGenericFieldType(mapField, Map.class, 1, nestingLevel);
 145  
     }
 146  
 
 147  
     /**
 148  
      * Determine the generic element type of the given Collection parameter.
 149  
      *
 150  
      * @param methodParam the method parameter specification
 151  
      * @return the generic type, or <code>null</code> if none
 152  
      */
 153  
     public static Class<?> getCollectionParameterType(MethodParameter methodParam)
 154  
     {
 155  0
         return getGenericParameterType(methodParam, Collection.class, 0);
 156  
     }
 157  
 
 158  
     /**
 159  
      * Determine the generic key type of the given Map parameter.
 160  
      *
 161  
      * @param methodParam the method parameter specification
 162  
      * @return the generic type, or <code>null</code> if none
 163  
      */
 164  
     public static Class<?> getMapKeyParameterType(MethodParameter methodParam)
 165  
     {
 166  0
         return getGenericParameterType(methodParam, Map.class, 0);
 167  
     }
 168  
 
 169  
     /**
 170  
      * Determine the generic value type of the given Map parameter.
 171  
      *
 172  
      * @param methodParam the method parameter specification
 173  
      * @return the generic type, or <code>null</code> if none
 174  
      */
 175  
     public static Class<?> getMapValueParameterType(MethodParameter methodParam)
 176  
     {
 177  0
         return getGenericParameterType(methodParam, Map.class, 1);
 178  
     }
 179  
 
 180  
     /**
 181  
      * Determine the generic element type of the given Collection return type.
 182  
      *
 183  
      * @param method the method to check the return type for
 184  
      * @return the generic type, or <code>null</code> if none
 185  
      */
 186  
     public static Class<?> getCollectionReturnType(Method method)
 187  
     {
 188  0
         return getGenericReturnType(method, Collection.class, 0, 1);
 189  
     }
 190  
 
 191  
     /**
 192  
      * Determine the generic element type of the given Collection return type.
 193  
      * <p>If the specified nesting level is higher than 1, the element type of
 194  
      * a nested Collection/Map will be analyzed.
 195  
      *
 196  
      * @param method       the method to check the return type for
 197  
      * @param nestingLevel the nesting level of the target type
 198  
      *                     (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 199  
      *                     nested List, whereas 2 would indicate the element of the nested List)
 200  
      * @return the generic type, or <code>null</code> if none
 201  
      */
 202  
     public static Class<?> getCollectionReturnType(Method method, int nestingLevel)
 203  
     {
 204  0
         return getGenericReturnType(method, Collection.class, 0, nestingLevel);
 205  
     }
 206  
 
 207  
     /**
 208  
      * Determine the generic key type of the given Map return type.
 209  
      *
 210  
      * @param method the method to check the return type for
 211  
      * @return the generic type, or <code>null</code> if none
 212  
      */
 213  
     public static Class<?> getMapKeyReturnType(Method method)
 214  
     {
 215  0
         return getGenericReturnType(method, Map.class, 0, 1);
 216  
     }
 217  
 
 218  
     /**
 219  
      * Determine the generic key type of the given Map return type.
 220  
      *
 221  
      * @param method       the method to check the return type for
 222  
      * @param nestingLevel the nesting level of the target type
 223  
      *                     (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 224  
      *                     nested List, whereas 2 would indicate the element of the nested List)
 225  
      * @return the generic type, or <code>null</code> if none
 226  
      */
 227  
     public static Class<?> getMapKeyReturnType(Method method, int nestingLevel)
 228  
     {
 229  0
         return getGenericReturnType(method, Map.class, 0, nestingLevel);
 230  
     }
 231  
 
 232  
     /**
 233  
      * Determine the generic value type of the given Map return type.
 234  
      *
 235  
      * @param method the method to check the return type for
 236  
      * @return the generic type, or <code>null</code> if none
 237  
      */
 238  
     public static Class<?> getMapValueReturnType(Method method)
 239  
     {
 240  0
         return getGenericReturnType(method, Map.class, 1, 1);
 241  
     }
 242  
 
 243  
     /**
 244  
      * Determine the generic value type of the given Map return type.
 245  
      *
 246  
      * @param method       the method to check the return type for
 247  
      * @param nestingLevel the nesting level of the target type
 248  
      *                     (typically 1; e.g. in case of a List of Lists, 1 would indicate the
 249  
      *                     nested List, whereas 2 would indicate the element of the nested List)
 250  
      * @return the generic type, or <code>null</code> if none
 251  
      */
 252  
     public static Class<?> getMapValueReturnType(Method method, int nestingLevel)
 253  
     {
 254  0
         return getGenericReturnType(method, Map.class, 1, nestingLevel);
 255  
     }
 256  
 
 257  
 
 258  
     /**
 259  
      * Extract the generic parameter type from the given method or constructor.
 260  
      *
 261  
      * @param methodParam the method parameter specification
 262  
      * @param source      the source class/interface defining the generic parameter types
 263  
      * @param typeIndex   the index of the type (e.g. 0 for Collections,
 264  
      *                    0 for Map keys, 1 for Map values)
 265  
      * @return the generic type, or <code>null</code> if none
 266  
      */
 267  
     private static Class<?> getGenericParameterType(MethodParameter methodParam, Class<?> source, int typeIndex)
 268  
     {
 269  0
         return extractType(methodParam, GenericTypeResolver.getTargetType(methodParam),
 270  
                 source, typeIndex, methodParam.getNestingLevel(), 1);
 271  
     }
 272  
 
 273  
     /**
 274  
      * Extract the generic type from the given field.
 275  
      *
 276  
      * @param field        the field to check the type for
 277  
      * @param source       the source class/interface defining the generic parameter types
 278  
      * @param typeIndex    the index of the type (e.g. 0 for Collections,
 279  
      *                     0 for Map keys, 1 for Map values)
 280  
      * @param nestingLevel the nesting level of the target type
 281  
      * @return the generic type, or <code>null</code> if none
 282  
      */
 283  
     private static Class<?> getGenericFieldType(Field field, Class<?> source, int typeIndex, int nestingLevel)
 284  
     {
 285  0
         return extractType(null, field.getGenericType(), source, typeIndex, nestingLevel, 1);
 286  
     }
 287  
 
 288  
     /**
 289  
      * Extract the generic return type from the given method.
 290  
      *
 291  
      * @param method       the method to check the return type for
 292  
      * @param source       the source class/interface defining the generic parameter types
 293  
      * @param typeIndex    the index of the type (e.g. 0 for Collections,
 294  
      *                     0 for Map keys, 1 for Map values)
 295  
      * @param nestingLevel the nesting level of the target type
 296  
      * @return the generic type, or <code>null</code> if none
 297  
      */
 298  
     private static Class<?> getGenericReturnType(Method method, Class<?> source, int typeIndex, int nestingLevel)
 299  
     {
 300  0
         return extractType(null, method.getGenericReturnType(), source, typeIndex, nestingLevel, 1);
 301  
     }
 302  
 
 303  
     /**
 304  
      * Extract the generic type from the given Type object.
 305  
      *
 306  
      * @param methodParam  the method parameter specification
 307  
      * @param type         the Type to check
 308  
      * @param source       the source collection/map Class<?> that we check
 309  
      * @param typeIndex    the index of the actual type argument
 310  
      * @param nestingLevel the nesting level of the target type
 311  
      * @param currentLevel the current nested level
 312  
      * @return the generic type as Class, or <code>null</code> if none
 313  
      */
 314  
     private static Class<?> extractType(MethodParameter methodParam, Type type, Class<?> source, 
 315  
         int typeIndex, int nestingLevel, int currentLevel)
 316  
     {
 317  0
         Type resolvedType = type;
 318  0
         if (type instanceof TypeVariable<?> && methodParam != null && methodParam.typeVariableMap != null)
 319  
         {
 320  0
             Type mappedType = methodParam.typeVariableMap.get(type);
 321  0
             if (mappedType != null)
 322  
             {
 323  0
                 resolvedType = mappedType;
 324  
             }
 325  
         }
 326  0
         if (resolvedType instanceof ParameterizedType)
 327  
         {
 328  0
             return extractTypeFromParameterizedType(
 329  
                     methodParam, (ParameterizedType) resolvedType, source, typeIndex, nestingLevel, currentLevel);
 330  
         }
 331  0
         else if (resolvedType instanceof Class<?>)
 332  
         {
 333  0
             Class<?> resolvedClass = (Class<?>) resolvedType;
 334  0
             return extractTypeFromClass(methodParam, resolvedClass, source, typeIndex, nestingLevel, currentLevel);
 335  
         }
 336  
         else
 337  
         {
 338  0
             return null;
 339  
         }
 340  
     }
 341  
 
 342  
     /**
 343  
      * Extract the generic type from the given ParameterizedType object.
 344  
      *
 345  
      * @param methodParam  the method parameter specification
 346  
      * @param ptype        the ParameterizedType to check
 347  
      * @param source       the expected raw source type (can be <code>null</code>)
 348  
      * @param typeIndex    the index of the actual type argument
 349  
      * @param nestingLevel the nesting level of the target type
 350  
      * @param currentLevel the current nested level
 351  
      * @return the generic type as Class, or <code>null</code> if none
 352  
      */
 353  
     private static Class<?> extractTypeFromParameterizedType(MethodParameter methodParam,
 354  
          ParameterizedType ptype, Class<?> source, int typeIndex, int nestingLevel, int currentLevel)
 355  
     {
 356  
 
 357  0
         if (!(ptype.getRawType() instanceof Class<?>))
 358  
         {
 359  0
             return null;
 360  
         }
 361  
         
 362  0
         Class<?> rawType = (Class<?>) ptype.getRawType();
 363  0
         Type[] paramTypes = ptype.getActualTypeArguments();
 364  0
         if (nestingLevel - currentLevel > 0)
 365  
         {
 366  0
             int nextLevel = currentLevel + 1;
 367  0
             Integer currentTypeIndex = (methodParam != null ? methodParam.getTypeIndexForLevel(nextLevel) : null);
 368  
             // Default is last parameter type: Collection element or Map value.
 369  0
             int indexToUse = (currentTypeIndex != null ? currentTypeIndex : paramTypes.length - 1);
 370  0
             Type paramType = paramTypes[indexToUse];
 371  0
             return extractType(methodParam, paramType, source, typeIndex, nestingLevel, nextLevel);
 372  
         }
 373  0
         if (source != null && !source.isAssignableFrom(rawType))
 374  
         {
 375  0
             return null;
 376  
         }
 377  0
         Class<?> fromSuperclassOrInterface =
 378  
                 extractTypeFromClass(methodParam, rawType, source, typeIndex, nestingLevel, currentLevel);
 379  0
         if (fromSuperclassOrInterface != null)
 380  
         {
 381  0
             return fromSuperclassOrInterface;
 382  
         }
 383  0
         if (paramTypes == null || typeIndex >= paramTypes.length)
 384  
         {
 385  0
             return null;
 386  
         }
 387  0
         Type paramType = paramTypes[typeIndex];
 388  0
         if (paramType instanceof TypeVariable<?> && methodParam != null && methodParam.typeVariableMap != null)
 389  
         {
 390  0
             Type mappedType = methodParam.typeVariableMap.get(paramType);
 391  0
             if (mappedType != null)
 392  
             {
 393  0
                 paramType = mappedType;
 394  
             }
 395  
         }
 396  0
         if (paramType instanceof WildcardType)
 397  
         {
 398  0
             WildcardType wildcardType = (WildcardType) paramType;
 399  0
             Type[] upperBounds = wildcardType.getUpperBounds();
 400  0
             if (upperBounds != null && upperBounds.length > 0 && !Object.class.equals(upperBounds[0]))
 401  
             {
 402  0
                 paramType = upperBounds[0];
 403  
             }
 404  
             else
 405  
             {
 406  0
                 Type[] lowerBounds = wildcardType.getLowerBounds();
 407  0
                 if (lowerBounds != null && lowerBounds.length > 0 && !Object.class.equals(lowerBounds[0]))
 408  
                 {
 409  0
                     paramType = lowerBounds[0];
 410  
                 }
 411  
             }
 412  
         }
 413  0
         if (paramType instanceof ParameterizedType)
 414  
         {
 415  0
             paramType = ((ParameterizedType) paramType).getRawType();
 416  
         }
 417  0
         if (paramType instanceof GenericArrayType)
 418  
         {
 419  
             // A generic array type... Let's turn it into a straight array type if possible.
 420  0
             Type compType = ((GenericArrayType) paramType).getGenericComponentType();
 421  0
             if (compType instanceof Class<?>)
 422  
             {
 423  0
                 Class<?> compClass = (Class<?>) compType;
 424  0
                 return Array.newInstance(compClass, 0).getClass();
 425  
             }
 426  0
         }
 427  0
         else if (paramType instanceof Class<?>)
 428  
         {
 429  
             // We finally got a straight Class...
 430  0
             return (Class<?>) paramType;
 431  
         }
 432  0
         return null;
 433  
     }
 434  
 
 435  
     /**
 436  
      * Extract the generic type from the given Class<?> object.
 437  
      *
 438  
      * @param clazz     the Class<?> to check
 439  
      * @param source    the expected raw source type (can be <code>null</code>)
 440  
      * @param typeIndex the index of the actual type argument
 441  
      * @return the generic type as Class, or <code>null</code> if none
 442  
      */
 443  
     private static Class<?> extractTypeFromClass(Class<?> clazz, Class<?> source, int typeIndex)
 444  
     {
 445  0
         return extractTypeFromClass(null, clazz, source, typeIndex, 1, 1);
 446  
     }
 447  
 
 448  
     /**
 449  
      * Extract the generic type from the given Class<?> object.
 450  
      *
 451  
      * @param methodParam  the method parameter specification
 452  
      * @param clazz        the Class<?> to check
 453  
      * @param source       the expected raw source type (can be <code>null</code>)
 454  
      * @param typeIndex    the index of the actual type argument
 455  
      * @param nestingLevel the nesting level of the target type
 456  
      * @param currentLevel the current nested level
 457  
      * @return the generic type as Class, or <code>null</code> if none
 458  
      */
 459  
     private static Class<?> extractTypeFromClass(
 460  
             MethodParameter methodParam, Class<?> clazz, Class<?> source, int typeIndex, int nestingLevel, int currentLevel)
 461  
     {
 462  
 
 463  0
         if (clazz.getName().startsWith("java.util."))
 464  
         {
 465  0
             return null;
 466  
         }
 467  0
         if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass()))
 468  
         {
 469  0
             return extractType(methodParam, clazz.getGenericSuperclass(), source, typeIndex, nestingLevel, currentLevel);
 470  
         }
 471  0
         Type[] ifcs = clazz.getGenericInterfaces();
 472  0
         if (ifcs != null)
 473  
         {
 474  0
             for (Type ifc : ifcs)
 475  
             {
 476  0
                 Type rawType = ifc;
 477  0
                 if (ifc instanceof ParameterizedType)
 478  
                 {
 479  0
                     rawType = ((ParameterizedType) ifc).getRawType();
 480  
                 }
 481  0
                 if (rawType instanceof Class<?> && isIntrospectionCandidate((Class<?>) rawType))
 482  
                 {
 483  0
                     return extractType(methodParam, ifc, source, typeIndex, nestingLevel, currentLevel);
 484  
                 }
 485  
             }
 486  
         }
 487  0
         return null;
 488  
     }
 489  
 
 490  
     /**
 491  
      * Determine whether the given class is a potential candidate
 492  
      * that defines generic collection or map types.
 493  
      *
 494  
      * @param clazz the class to check
 495  
      * @return whether the given class is assignable to Collection or Map
 496  
      */
 497  
     private static boolean isIntrospectionCandidate(Class<?> clazz)
 498  
     {
 499  0
         return (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz));
 500  
     }
 501  
 
 502  
 }
 503