@ -16,15 +16,8 @@
@@ -16,15 +16,8 @@
package org.springframework.core ;
import java.lang.reflect.Array ;
import java.lang.reflect.Field ;
import java.lang.reflect.GenericArrayType ;
import java.lang.reflect.MalformedParameterizedTypeException ;
import java.lang.reflect.Method ;
import java.lang.reflect.ParameterizedType ;
import java.lang.reflect.Type ;
import java.lang.reflect.TypeVariable ;
import java.lang.reflect.WildcardType ;
import java.util.Collection ;
import java.util.Map ;
@ -36,7 +29,9 @@ import java.util.Map;
@@ -36,7 +29,9 @@ import java.util.Map;
* ( to be able to attempt type conversion if appropriate ) .
*
* @author Juergen Hoeller
* @author Phillip Webb
* @since 2 . 0
* @see ResolvableType
* /
public abstract class GenericCollectionTypeResolver {
@ -47,7 +42,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -47,7 +42,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionType ( Class < ? extends Collection > collectionClass ) {
return extractTypeFromClass ( collectionClass , Collection . class , 0 ) ;
return ResolvableType . forClass ( collectionClass ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -57,7 +52,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -57,7 +52,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyType ( Class < ? extends Map > mapClass ) {
return extractTypeFromClass ( mapClass , Map . class , 0 ) ;
return ResolvableType . forClass ( mapClass ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -67,7 +62,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -67,7 +62,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueType ( Class < ? extends Map > mapClass ) {
return extractTypeFromClass ( mapClass , Map . class , 1 ) ;
return ResolvableType . forClass ( mapClass ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -76,7 +71,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -76,7 +71,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionFieldType ( Field collectionField ) {
return getGenericFieldType ( collectionField , Collection . class , 0 , null , 1 ) ;
return ResolvableType . forField ( collectionField ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -88,7 +83,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -88,7 +83,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionFieldType ( Field collectionField , int nestingLevel ) {
return getGenericFieldType ( collectionField , Collection . class , 0 , null , nestingLevel ) ;
return ResolvableType . forField ( collectionField ) . getNested ( nestingLevel ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -100,9 +95,11 @@ public abstract class GenericCollectionTypeResolver {
@@ -100,9 +95,11 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level , with each value
* expressing the type index for traversal at that level
* @return the generic type , or { @code null } if none
* @deprecated as of 4 . 0 in favor of using { @link ResolvableType } for arbitrary nesting levels
* /
@Deprecated
public static Class < ? > getCollectionFieldType ( Field collectionField , int nestingLevel , Map < Integer , Integer > typeIndexesPerLevel ) {
return getGenericFieldType ( collectionField , Collection . class , 0 , typeIndexesPerLevel , nestingLevel ) ;
return ResolvableType . forField ( collectionField ) . getNested ( nestingLevel , typeIndexesPerLevel ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -111,7 +108,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -111,7 +108,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyFieldType ( Field mapField ) {
return getGenericFieldType ( mapField , Map . class , 0 , null , 1 ) ;
return ResolvableType . forField ( mapField ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -123,7 +120,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -123,7 +120,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyFieldType ( Field mapField , int nestingLevel ) {
return getGenericFieldType ( mapField , Map . class , 0 , null , nestingLevel ) ;
return ResolvableType . forField ( mapField ) . getNested ( nestingLevel ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -135,9 +132,11 @@ public abstract class GenericCollectionTypeResolver {
@@ -135,9 +132,11 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level , with each value
* expressing the type index for traversal at that level
* @return the generic type , or { @code null } if none
* @deprecated as of 4 . 0 in favor of using { @link ResolvableType } for arbitrary nesting levels
* /
@Deprecated
public static Class < ? > getMapKeyFieldType ( Field mapField , int nestingLevel , Map < Integer , Integer > typeIndexesPerLevel ) {
return getGenericFieldType ( mapField , Map . class , 0 , typeIndexesPerLevel , nestingLevel ) ;
return ResolvableType . forField ( mapField ) . getNested ( nestingLevel , typeIndexesPerLevel ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -146,7 +145,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -146,7 +145,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueFieldType ( Field mapField ) {
return getGenericFieldType ( mapField , Map . class , 1 , null , 1 ) ;
return ResolvableType . forField ( mapField ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -158,7 +157,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -158,7 +157,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueFieldType ( Field mapField , int nestingLevel ) {
return getGenericFieldType ( mapField , Map . class , 1 , null , nestingLevel ) ;
return ResolvableType . forField ( mapField ) . getNested ( nestingLevel ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -170,9 +169,11 @@ public abstract class GenericCollectionTypeResolver {
@@ -170,9 +169,11 @@ public abstract class GenericCollectionTypeResolver {
* @param typeIndexesPerLevel Map keyed by nesting level , with each value
* expressing the type index for traversal at that level
* @return the generic type , or { @code null } if none
* @deprecated as of 4 . 0 in favor of using { @link ResolvableType } for arbitrary nesting levels
* /
@Deprecated
public static Class < ? > getMapValueFieldType ( Field mapField , int nestingLevel , Map < Integer , Integer > typeIndexesPerLevel ) {
return getGenericFieldType ( mapField , Map . class , 1 , typeIndexesPerLevel , nestingLevel ) ;
return ResolvableType . forField ( mapField ) . getNested ( nestingLevel , typeIndexesPerLevel ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -181,7 +182,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -181,7 +182,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionParameterType ( MethodParameter methodParam ) {
return getGenericParameterType ( methodParam , Collection . class , 0 ) ;
return forMethodParameter ( methodParam ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -190,7 +191,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -190,7 +191,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyParameterType ( MethodParameter methodParam ) {
return getGenericParameterType ( methodParam , Map . class , 0 ) ;
return forMethodParameter ( methodParam ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -199,7 +200,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -199,7 +200,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueParameterType ( MethodParameter methodParam ) {
return getGenericParameterType ( methodParam , Map . class , 1 ) ;
return forMethodParameter ( methodParam ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -208,7 +209,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -208,7 +209,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionReturnType ( Method method ) {
return getGenericReturnType ( method , Collection . class , 0 , 1 ) ;
return ResolvableType . forMethodReturn ( method ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -222,7 +223,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -222,7 +223,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getCollectionReturnType ( Method method , int nestingLevel ) {
return getGenericReturnType ( method , Collection . class , 0 , nestingLevel ) ;
return ResolvableType . forMethodReturn ( method ) . getNested ( nestingLevel ) . asCollection ( ) . resolveGeneric ( ) ;
}
/ * *
@ -231,7 +232,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -231,7 +232,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyReturnType ( Method method ) {
return getGenericReturnType ( method , Map . class , 0 , 1 ) ;
return ResolvableType . forMethodReturn ( method ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -243,7 +244,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -243,7 +244,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapKeyReturnType ( Method method , int nestingLevel ) {
return getGenericReturnType ( method , Map . class , 0 , nestingLevel ) ;
return ResolvableType . forMethodReturn ( method ) . getNested ( nestingLevel ) . asMap ( ) . resolveGeneric ( 0 ) ;
}
/ * *
@ -252,7 +253,7 @@ public abstract class GenericCollectionTypeResolver {
@@ -252,7 +253,7 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueReturnType ( Method method ) {
return getGenericReturnType ( method , Map . class , 1 , 1 ) ;
return ResolvableType . forMethodReturn ( method ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
@ -264,220 +265,14 @@ public abstract class GenericCollectionTypeResolver {
@@ -264,220 +265,14 @@ public abstract class GenericCollectionTypeResolver {
* @return the generic type , or { @code null } if none
* /
public static Class < ? > getMapValueReturnType ( Method method , int nestingLevel ) {
return getGenericReturnType ( method , Map . class , 1 , nestingLevel ) ;
return ResolvableType . forMethodReturn ( method ) . getNested ( nestingLevel ) . asMap ( ) . resolveGeneric ( 1 ) ;
}
/ * *
* Extract the generic parameter type from the given method or constructor .
* @param methodParam the method parameter specification
* @param source the source class / interface defining the generic parameter types
* @param typeIndex the index of the type ( e . g . 0 for Collections ,
* 0 for Map keys , 1 for Map values )
* @return the generic type , or { @code null } if none
* /
private static Class < ? > getGenericParameterType ( MethodParameter methodParam , Class < ? > source , int typeIndex ) {
return extractType ( GenericTypeResolver . getTargetType ( methodParam ) , source , typeIndex ,
methodParam . typeVariableMap , methodParam . typeIndexesPerLevel , methodParam . getNestingLevel ( ) , 1 ) ;
}
/ * *
* Extract the generic type from the given field .
* @param field the field to check the type for
* @param source the source class / interface defining the generic parameter types
* @param typeIndex the index of the type ( e . g . 0 for Collections ,
* 0 for Map keys , 1 for Map values )
* @param nestingLevel the nesting level of the target type
* @return the generic type , or { @code null } if none
* /
private static Class < ? > getGenericFieldType ( Field field , Class < ? > source , int typeIndex ,
Map < Integer , Integer > typeIndexesPerLevel , int nestingLevel ) {
return extractType ( field . getGenericType ( ) , source , typeIndex , null , typeIndexesPerLevel , nestingLevel , 1 ) ;
}
/ * *
* Extract the generic return type from the given method .
* @param method the method to check the return type for
* @param source the source class / interface defining the generic parameter types
* @param typeIndex the index of the type ( e . g . 0 for Collections ,
* 0 for Map keys , 1 for Map values )
* @param nestingLevel the nesting level of the target type
* @return the generic type , or { @code null } if none
* /
private static Class < ? > getGenericReturnType ( Method method , Class < ? > source , int typeIndex , int nestingLevel ) {
return extractType ( method . getGenericReturnType ( ) , source , typeIndex , null , null , nestingLevel , 1 ) ;
}
/ * *
* Extract the generic type from the given Type object .
* @param type the Type to check
* @param source the source collection / map Class that we check
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class , or { @code null } if none
* /
private static Class < ? > extractType ( Type type , Class < ? > source , int typeIndex ,
Map < TypeVariable , Type > typeVariableMap , Map < Integer , Integer > typeIndexesPerLevel ,
int nestingLevel , int currentLevel ) {
Type resolvedType = type ;
if ( type instanceof TypeVariable & & typeVariableMap ! = null ) {
Type mappedType = typeVariableMap . get ( type ) ;
if ( mappedType ! = null ) {
resolvedType = mappedType ;
}
private static ResolvableType forMethodParameter ( MethodParameter methodParam ) {
if ( methodParam . resolveClass ! = null ) {
return ResolvableType . forMethodParameter ( methodParam , methodParam . resolveClass ) ;
}
if ( resolvedType instanceof ParameterizedType ) {
return extractTypeFromParameterizedType ( ( ParameterizedType ) resolvedType , source , typeIndex , typeVariableMap , typeIndexesPerLevel ,
nestingLevel , currentLevel ) ;
}
else if ( resolvedType instanceof Class ) {
return extractTypeFromClass ( ( Class ) resolvedType , source , typeIndex , typeVariableMap , typeIndexesPerLevel ,
nestingLevel , currentLevel ) ;
}
else if ( resolvedType instanceof GenericArrayType ) {
Type compType = ( ( GenericArrayType ) resolvedType ) . getGenericComponentType ( ) ;
return extractType ( compType , source , typeIndex , typeVariableMap , typeIndexesPerLevel , nestingLevel , currentLevel + 1 ) ;
}
else {
return null ;
}
}
/ * *
* Extract the generic type from the given ParameterizedType object .
* @param ptype the ParameterizedType to check
* @param source the expected raw source type ( can be { @code null } )
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class , or { @code null } if none
* /
private static Class < ? > extractTypeFromParameterizedType ( ParameterizedType ptype , Class < ? > source , int typeIndex ,
Map < TypeVariable , Type > typeVariableMap , Map < Integer , Integer > typeIndexesPerLevel ,
int nestingLevel , int currentLevel ) {
if ( ! ( ptype . getRawType ( ) instanceof Class ) ) {
return null ;
}
Class rawType = ( Class ) ptype . getRawType ( ) ;
Type [ ] paramTypes = ptype . getActualTypeArguments ( ) ;
if ( nestingLevel - currentLevel > 0 ) {
int nextLevel = currentLevel + 1 ;
Integer currentTypeIndex = ( typeIndexesPerLevel ! = null ? typeIndexesPerLevel . get ( nextLevel ) : null ) ;
// Default is last parameter type: Collection element or Map value.
int indexToUse = ( currentTypeIndex ! = null ? currentTypeIndex : paramTypes . length - 1 ) ;
Type paramType = paramTypes [ indexToUse ] ;
return extractType ( paramType , source , typeIndex , typeVariableMap , typeIndexesPerLevel , nestingLevel , nextLevel ) ;
}
if ( source ! = null & & ! source . isAssignableFrom ( rawType ) ) {
return null ;
}
Class fromSuperclassOrInterface = extractTypeFromClass ( rawType , source , typeIndex , typeVariableMap , typeIndexesPerLevel ,
nestingLevel , currentLevel ) ;
if ( fromSuperclassOrInterface ! = null ) {
return fromSuperclassOrInterface ;
}
if ( paramTypes = = null | | typeIndex > = paramTypes . length ) {
return null ;
}
Type paramType = paramTypes [ typeIndex ] ;
if ( paramType instanceof TypeVariable & & typeVariableMap ! = null ) {
Type mappedType = typeVariableMap . get ( paramType ) ;
if ( mappedType ! = null ) {
paramType = mappedType ;
}
}
if ( paramType instanceof WildcardType ) {
WildcardType wildcardType = ( WildcardType ) paramType ;
Type [ ] upperBounds = wildcardType . getUpperBounds ( ) ;
if ( upperBounds ! = null & & upperBounds . length > 0 & & ! Object . class . equals ( upperBounds [ 0 ] ) ) {
paramType = upperBounds [ 0 ] ;
}
else {
Type [ ] lowerBounds = wildcardType . getLowerBounds ( ) ;
if ( lowerBounds ! = null & & lowerBounds . length > 0 & & ! Object . class . equals ( lowerBounds [ 0 ] ) ) {
paramType = lowerBounds [ 0 ] ;
}
}
}
if ( paramType instanceof ParameterizedType ) {
paramType = ( ( ParameterizedType ) paramType ) . getRawType ( ) ;
}
if ( paramType instanceof GenericArrayType ) {
// A generic array type... Let's turn it into a straight array type if possible.
Type compType = ( ( GenericArrayType ) paramType ) . getGenericComponentType ( ) ;
if ( compType instanceof Class ) {
return Array . newInstance ( ( Class ) compType , 0 ) . getClass ( ) ;
}
}
else if ( paramType instanceof Class ) {
// We finally got a straight Class...
return ( Class ) paramType ;
}
return null ;
}
/ * *
* Extract the generic type from the given Class object .
* @param clazz the Class to check
* @param source the expected raw source type ( can be { @code null } )
* @param typeIndex the index of the actual type argument
* @return the generic type as Class , or { @code null } if none
* /
private static Class < ? > extractTypeFromClass ( Class < ? > clazz , Class < ? > source , int typeIndex ) {
return extractTypeFromClass ( clazz , source , typeIndex , null , null , 1 , 1 ) ;
}
/ * *
* Extract the generic type from the given Class object .
* @param clazz the Class to check
* @param source the expected raw source type ( can be { @code null } )
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class , or { @code null } if none
* /
private static Class < ? > extractTypeFromClass ( Class < ? > clazz , Class < ? > source , int typeIndex ,
Map < TypeVariable , Type > typeVariableMap , Map < Integer , Integer > typeIndexesPerLevel ,
int nestingLevel , int currentLevel ) {
if ( clazz . getName ( ) . startsWith ( "java.util." ) ) {
return null ;
}
if ( clazz . getSuperclass ( ) ! = null & & isIntrospectionCandidate ( clazz . getSuperclass ( ) ) ) {
try {
return extractType ( clazz . getGenericSuperclass ( ) , source , typeIndex , typeVariableMap ,
typeIndexesPerLevel , nestingLevel , currentLevel ) ;
}
catch ( MalformedParameterizedTypeException ex ) {
// from getGenericSuperclass() - ignore and continue with interface introspection
}
}
Type [ ] ifcs = clazz . getGenericInterfaces ( ) ;
if ( ifcs ! = null ) {
for ( Type ifc : ifcs ) {
Type rawType = ifc ;
if ( ifc instanceof ParameterizedType ) {
rawType = ( ( ParameterizedType ) ifc ) . getRawType ( ) ;
}
if ( rawType instanceof Class & & isIntrospectionCandidate ( ( Class ) rawType ) ) {
return extractType ( ifc , source , typeIndex , typeVariableMap , typeIndexesPerLevel , nestingLevel , currentLevel ) ;
}
}
}
return null ;
}
/ * *
* Determine whether the given class is a potential candidate
* that defines generic collection or map types .
* @param clazz the class to check
* @return whether the given class is assignable to Collection or Map
* /
private static boolean isIntrospectionCandidate ( Class clazz ) {
return ( Collection . class . isAssignableFrom ( clazz ) | | Map . class . isAssignableFrom ( clazz ) ) ;
return ResolvableType . forMethodParameter ( methodParam ) ;
}
}