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