Autowiring of generic types
Includes revisions of MethodParameter and DependencyDescriptor (in particular towards a reference to the containing class). Also refines several ResolvableType method signatures. Issue: SPR-9965
This commit is contained in:
@@ -95,7 +95,7 @@ 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 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) {
|
||||
@@ -132,7 +132,7 @@ 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 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) {
|
||||
@@ -169,7 +169,7 @@ 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 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) {
|
||||
@@ -182,7 +182,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getCollectionParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,7 +191,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapKeyParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +200,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueParameterType(MethodParameter methodParam) {
|
||||
return forMethodParameter(methodParam).asMap().resolveGeneric(1);
|
||||
return ResolvableType.forMethodParameter(methodParam).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,7 +209,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getCollectionReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,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 ResolvableType.forMethodReturn(method).getNested(nestingLevel).asCollection().resolveGeneric();
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asCollection().resolveGeneric();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +232,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapKeyReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,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 ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(0);
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,7 +253,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueReturnType(Method method) {
|
||||
return ResolvableType.forMethodReturn(method).asMap().resolveGeneric(1);
|
||||
return ResolvableType.forMethodReturnType(method).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,14 +265,7 @@ public abstract class GenericCollectionTypeResolver {
|
||||
* @return the generic type, or {@code null} if none
|
||||
*/
|
||||
public static Class<?> getMapValueReturnType(Method method, int nestingLevel) {
|
||||
return ResolvableType.forMethodReturn(method).getNested(nestingLevel).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
private static ResolvableType forMethodParameter(MethodParameter methodParam) {
|
||||
if (methodParam.resolveClass != null) {
|
||||
return ResolvableType.forMethodParameter(methodParam, methodParam.resolveClass);
|
||||
}
|
||||
return ResolvableType.forMethodParameter(methodParam);
|
||||
return ResolvableType.forMethodReturnType(method).getNested(nestingLevel).asMap().resolveGeneric(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 2.5.2
|
||||
* @see GenericCollectionTypeResolver
|
||||
@@ -52,7 +51,7 @@ public abstract class GenericTypeResolver {
|
||||
* Determine the target type for the given parameter specification.
|
||||
* @param methodParam the method parameter specification
|
||||
* @return the corresponding generic parameter type
|
||||
* @deprecated as of Spring 4.0 use {@link MethodParameter#getGenericParameterType()}
|
||||
* @deprecated as of Spring 4.0, use {@link MethodParameter#getGenericParameterType()}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Type getTargetType(MethodParameter methodParam) {
|
||||
@@ -69,8 +68,8 @@ public abstract class GenericTypeResolver {
|
||||
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) {
|
||||
Assert.notNull(methodParam, "MethodParameter must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
methodParam.resolveClass = clazz;
|
||||
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam, clazz).resolve());
|
||||
methodParam.setContainingClass(clazz);
|
||||
methodParam.setParameterType(ResolvableType.forMethodParameter(methodParam).resolve());
|
||||
return methodParam.getParameterType();
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ public abstract class GenericTypeResolver {
|
||||
public static Class<?> resolveReturnType(Method method, Class<?> clazz) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
return ResolvableType.forMethodReturn(method, clazz).resolve(method.getReturnType());
|
||||
return ResolvableType.forMethodReturnType(method, clazz).resolve(method.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,12 +112,14 @@ public abstract class GenericTypeResolver {
|
||||
* </ul>
|
||||
* @param method the method to introspect, never {@code null}
|
||||
* @param args the arguments that will be supplied to the method when it is
|
||||
* invoked, never {@code null}
|
||||
* invoked (never {@code null})
|
||||
* @param classLoader the ClassLoader to resolve class names against, if necessary
|
||||
* (may be {@code null})
|
||||
* @return the resolved target return type, the standard return type, or {@code null}
|
||||
* @since 3.2
|
||||
* @since 3.2.5
|
||||
* @see #resolveReturnType
|
||||
*/
|
||||
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args) {
|
||||
public static Class<?> resolveReturnTypeForGenericMethod(Method method, Object[] args, ClassLoader classLoader) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(args, "Argument array must not be null");
|
||||
|
||||
@@ -158,8 +159,18 @@ public abstract class GenericTypeResolver {
|
||||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
|
||||
for (Type typeArg : actualTypeArguments) {
|
||||
if (typeArg.equals(genericReturnType)) {
|
||||
if (args[i] instanceof Class) {
|
||||
return (Class<?>) args[i];
|
||||
Object arg = args[i];
|
||||
if (arg instanceof Class) {
|
||||
return (Class<?>) arg;
|
||||
}
|
||||
else if (arg instanceof String) {
|
||||
try {
|
||||
return classLoader.loadClass((String) arg);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not resolve specific class name argument [" + arg + "]", ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Consider adding logic to determine the class of the typeArg, if possible.
|
||||
@@ -187,7 +198,7 @@ public abstract class GenericTypeResolver {
|
||||
*/
|
||||
public static Class<?> resolveReturnTypeArgument(Method method, Class<?> genericIfc) {
|
||||
Assert.notNull(method, "method must not be null");
|
||||
ResolvableType resolvableType = ResolvableType.forMethodReturn(method).as(genericIfc);
|
||||
ResolvableType resolvableType = ResolvableType.forMethodReturnType(method).as(genericIfc);
|
||||
if (!resolvableType.hasGenerics() || resolvableType.getType() instanceof WildcardType) {
|
||||
return null;
|
||||
}
|
||||
@@ -230,7 +241,7 @@ public abstract class GenericTypeResolver {
|
||||
*/
|
||||
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||
ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc);
|
||||
if(!type.hasGenerics()) {
|
||||
if (!type.hasGenerics()) {
|
||||
return null;
|
||||
}
|
||||
return type.resolveGenerics();
|
||||
@@ -261,16 +272,15 @@ public abstract class GenericTypeResolver {
|
||||
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
|
||||
if (typeVariableMap == null) {
|
||||
typeVariableMap = new HashMap<TypeVariable, Type>();
|
||||
buildTypeVaraibleMap(ResolvableType.forClass(clazz), typeVariableMap);
|
||||
buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap);
|
||||
typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap));
|
||||
}
|
||||
return typeVariableMap;
|
||||
}
|
||||
|
||||
private static void buildTypeVaraibleMap(ResolvableType type,
|
||||
Map<TypeVariable, Type> typeVariableMap) {
|
||||
if(type != ResolvableType.NONE) {
|
||||
if(type.getType() instanceof ParameterizedType) {
|
||||
private static void buildTypeVariableMap(ResolvableType type, Map<TypeVariable, Type> typeVariableMap) {
|
||||
if (type != ResolvableType.NONE) {
|
||||
if (type.getType() instanceof ParameterizedType) {
|
||||
TypeVariable<?>[] variables = type.resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
ResolvableType generic = type.getGeneric(i);
|
||||
@@ -282,94 +292,50 @@ public abstract class GenericTypeResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypeVaraibleMap(type.getSuperType(), typeVariableMap);
|
||||
buildTypeVariableMap(type.getSuperType(), typeVariableMap);
|
||||
for (ResolvableType interfaceType : type.getInterfaces()) {
|
||||
buildTypeVaraibleMap(interfaceType, typeVariableMap);
|
||||
buildTypeVariableMap(interfaceType, typeVariableMap);
|
||||
}
|
||||
if (type.resolve().isMemberClass()) {
|
||||
buildTypeVaraibleMap(ResolvableType.forClass(type.resolve().getEnclosingClass()),
|
||||
typeVariableMap);
|
||||
buildTypeVariableMap(ResolvableType.forClass(type.resolve().getEnclosingClass()), typeVariableMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the raw type for the given generic parameter type.
|
||||
* @param genericType the generic type to resolve
|
||||
* @param typeVariableMap the TypeVariable Map to resolved against
|
||||
* @return the resolved raw type
|
||||
*/
|
||||
@Deprecated
|
||||
static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
|
||||
Type resolvedType = genericType;
|
||||
if (genericType instanceof TypeVariable) {
|
||||
TypeVariable tv = (TypeVariable) genericType;
|
||||
resolvedType = typeVariableMap.get(tv);
|
||||
if (resolvedType == null) {
|
||||
resolvedType = extractBoundForTypeVariable(tv);
|
||||
}
|
||||
}
|
||||
if (resolvedType instanceof ParameterizedType) {
|
||||
return ((ParameterizedType) resolvedType).getRawType();
|
||||
}
|
||||
else {
|
||||
return resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the bound {@code Type} for a given {@link TypeVariable}.
|
||||
*/
|
||||
@Deprecated
|
||||
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
|
||||
Type[] bounds = typeVariable.getBounds();
|
||||
if (bounds.length == 0) {
|
||||
return Object.class;
|
||||
}
|
||||
Type bound = bounds[0];
|
||||
if (bound instanceof TypeVariable) {
|
||||
bound = extractBoundForTypeVariable((TypeVariable) bound);
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapts a {@code typeVariableMap} to a {@link TypeVariableResolver}.
|
||||
*/
|
||||
private static class TypeVariableMapResolver implements TypeVariableResolver {
|
||||
|
||||
private Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
public TypeVariableMapResolver(Map<TypeVariable, Type> typeVariableMap) {
|
||||
Assert.notNull("TypeVariableMap must not be null");
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type resolveVariable(TypeVariable typeVariable) {
|
||||
return typeVariableMap.get(typeVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return typeVariableMap.hashCode();
|
||||
return this.typeVariableMap.get(typeVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if(obj == this) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if(obj instanceof TypeVariableMapResolver) {
|
||||
if (obj instanceof TypeVariableMapResolver) {
|
||||
TypeVariableMapResolver other = (TypeVariableMapResolver) obj;
|
||||
return this.typeVariableMap.equals(other.typeVariableMap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.typeVariableMap.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -48,6 +47,8 @@ public class MethodParameter {
|
||||
|
||||
private final int parameterIndex;
|
||||
|
||||
private Class<?> containingClass;
|
||||
|
||||
private Class<?> parameterType;
|
||||
|
||||
private Type genericParameterType;
|
||||
@@ -63,8 +64,6 @@ public class MethodParameter {
|
||||
/** Map from Integer level to Integer type index */
|
||||
Map<Integer, Integer> typeIndexesPerLevel;
|
||||
|
||||
Class<?> resolveClass;
|
||||
|
||||
private int hash = 0;
|
||||
|
||||
|
||||
@@ -130,6 +129,7 @@ public class MethodParameter {
|
||||
this.method = original.method;
|
||||
this.constructor = original.constructor;
|
||||
this.parameterIndex = original.parameterIndex;
|
||||
this.containingClass = original.containingClass;
|
||||
this.parameterType = original.parameterType;
|
||||
this.genericParameterType = original.genericParameterType;
|
||||
this.parameterAnnotations = original.parameterAnnotations;
|
||||
@@ -137,7 +137,6 @@ public class MethodParameter {
|
||||
this.parameterName = original.parameterName;
|
||||
this.nestingLevel = original.nestingLevel;
|
||||
this.typeIndexesPerLevel = original.typeIndexesPerLevel;
|
||||
this.resolveClass = original.resolveClass;
|
||||
this.hash = original.hash;
|
||||
}
|
||||
|
||||
@@ -195,7 +194,7 @@ public class MethodParameter {
|
||||
/**
|
||||
* Return the class that declares the underlying Method or Constructor.
|
||||
*/
|
||||
public Class getDeclaringClass() {
|
||||
public Class<?> getDeclaringClass() {
|
||||
return getMember().getDeclaringClass();
|
||||
}
|
||||
|
||||
@@ -207,6 +206,17 @@ public class MethodParameter {
|
||||
return this.parameterIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a containing class to resolve the parameter type against.
|
||||
*/
|
||||
void setContainingClass(Class<?> containingClass) {
|
||||
this.containingClass = containingClass;
|
||||
}
|
||||
|
||||
public Class<?> getContainingClass() {
|
||||
return (this.containingClass != null ? this.containingClass : getDeclaringClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a resolved (generic) parameter type.
|
||||
*/
|
||||
|
||||
@@ -41,7 +41,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* <p>{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields},
|
||||
* {@link #forMethodParameter(Method, int) method parameters},
|
||||
* {@link #forMethodReturn(Method) method returns}, {@link #forClass(Class) classes}, or
|
||||
* {@link #forMethodReturnType(Method) method returns}, {@link #forClass(Class) classes}, or
|
||||
* directly from a {@link #forType(Type) java.lang.reflect.Type}. Most methods on this class
|
||||
* will themselves return {@link ResolvableType}s, allowing easy navigation. For example:
|
||||
* <pre class="code">
|
||||
@@ -59,11 +59,12 @@ import org.springframework.util.StringUtils;
|
||||
* </pre>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see TypeVariableResolver
|
||||
* @see #forField(Field)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
* @see #forMethodReturn(Method)
|
||||
* @see #forMethodReturnType(Method)
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
* @see #forClass(Class)
|
||||
* @see #forType(Type)
|
||||
@@ -85,7 +86,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
|
||||
|
||||
/**
|
||||
* The underlying java type being managed (only ever {@code null} for {@link #NONE})
|
||||
* The underlying java type being managed (only ever {@code null} for {@link #NONE}).
|
||||
*/
|
||||
private final Type type;
|
||||
|
||||
@@ -127,8 +128,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* {@link Class#isAssignableFrom(Class) assignable from} the given {@code type} as
|
||||
* well as if all {@link #getGenerics() generics} are assignable.
|
||||
* @param type the type to be checked
|
||||
* @return {@code true} if the specified {@code type} can be assigned to this
|
||||
* {@code type}.
|
||||
* @return {@code true} if the specified {@code type} can be assigned to this {@code type}
|
||||
*/
|
||||
public boolean isAssignableFrom(ResolvableType type) {
|
||||
return isAssignableFrom(type, false);
|
||||
@@ -323,15 +323,12 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* of 1 indicates this type; 2 indicates the first nested generic; 3 the second; and so
|
||||
* on. For example, given {@code List<Set<Integer>>} level 1 refers to the
|
||||
* {@code List}, level 2 the {@code Set}, and level 3 the {@code Integer}.
|
||||
*
|
||||
* <p>The {@code typeIndexesPerLevel} map can be used to reference a specific generic
|
||||
* for the given level. For example, an index of 0 would refer to a {@code Map} key;
|
||||
* whereas, 1 would refer to the value. If the map does not contain a value for a
|
||||
* specific level the last generic will be used (e.g. a {@code Map} value).
|
||||
*
|
||||
* <p>Nesting levels may also apply to array types; for example given
|
||||
* {@code String[]}, a nesting level of 2 refers to {@code String}.
|
||||
*
|
||||
* <p>If a type does not {@link #hasGenerics() contain} generics the
|
||||
* {@link #getSuperType() supertype} hierarchy will be considered.
|
||||
* @param nestingLevel the required nesting level, indexed from 1 for the current
|
||||
@@ -340,8 +337,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* level (may be {@code null})
|
||||
* @return a {@link ResolvableType} for the nested level or {@link #NONE}
|
||||
*/
|
||||
public ResolvableType getNested(int nestingLevel,
|
||||
Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
public ResolvableType getNested(int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
|
||||
ResolvableType result = this;
|
||||
for (int i = 2; i <= nestingLevel; i++) {
|
||||
if (result.isArray()) {
|
||||
@@ -369,7 +365,6 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* for example {@code getGeneric(1, 0)} will access the {@code String} from the nested
|
||||
* {@code List}. For convenience, if no indexes are specified the first generic is
|
||||
* returned.
|
||||
*
|
||||
* <p>If no generic is available at the specified indexes {@link #NONE} is returned.
|
||||
* @param indexes the indexes that refer to the generic parameter (may be omitted to
|
||||
* return the first generic)
|
||||
@@ -533,10 +528,8 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
public Type resolveVariable(TypeVariable<?> variable) {
|
||||
Assert.notNull("Variable must not be null");
|
||||
if (this.type instanceof ParameterizedType) {
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) this.type;
|
||||
Type owner = parameterizedType.getOwnerType();
|
||||
|
||||
if (parameterizedType.getRawType().equals(variable.getGenericDeclaration())) {
|
||||
TypeVariable<?>[] variables = resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
@@ -545,21 +538,18 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Type resolved = null;
|
||||
if (this.variableResolver != null) {
|
||||
resolved = this.variableResolver.resolveVariable(variable);
|
||||
resolved = this.variableResolver.resolveVariable(variable);
|
||||
}
|
||||
if (resolved == null && owner != null) {
|
||||
resolved = forType(owner, this.variableResolver).resolveVariable(variable);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
if (this.type instanceof TypeVariable<?>) {
|
||||
if (this.type instanceof TypeVariable) {
|
||||
return resolveType().resolveVariable(variable);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -576,19 +566,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(resolve() == null ? "?" : resolve().getName());
|
||||
if (hasGenerics()) {
|
||||
result.append("<");
|
||||
result.append('<');
|
||||
result.append(StringUtils.arrayToDelimitedString(getGenerics(), ", "));
|
||||
result.append(">");
|
||||
result.append('>');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.type) * 31
|
||||
+ ObjectUtils.nullSafeHashCode(this.variableResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
@@ -596,13 +580,17 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
if (obj instanceof ResolvableType) {
|
||||
ResolvableType other = (ResolvableType) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.type, other.type)
|
||||
&& ObjectUtils.nullSafeEquals(this.variableResolver,
|
||||
other.variableResolver);
|
||||
return ObjectUtils.nullSafeEquals(this.type, other.type) &&
|
||||
ObjectUtils.nullSafeEquals(this.variableResolver, other.variableResolver);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class}. For example
|
||||
@@ -628,8 +616,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forClass(Class<?> sourceClass, Class<?> implementationClass) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
ResolvableType asType = forType(implementationClass).as(sourceClass);
|
||||
ResolvableType asType = (implementationClass != null ? forType(implementationClass).as(sourceClass) : NONE);
|
||||
return (asType == NONE ? forType(sourceClass) : asType);
|
||||
}
|
||||
|
||||
@@ -646,8 +633,9 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation. Use this variant when the class that declares the field includes
|
||||
* generic parameter variables that are satisfied by the implementation class.
|
||||
* implementation.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
@@ -655,12 +643,43 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forField(Field field, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
field.getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with the
|
||||
* given nesting level.
|
||||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
return forType(field.getGenericType()).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Field} with a given
|
||||
* implementation and the given nesting level.
|
||||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Constructor} parameter.
|
||||
* @param constructor the source constructor (must not be {@code null})
|
||||
@@ -668,11 +687,9 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int, Class)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
|
||||
int parameterIndex) {
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex) {
|
||||
Assert.notNull(constructor, "Constructor must not be null");
|
||||
return forMethodParameter(MethodParameter.forMethodOrConstructor(constructor,
|
||||
parameterIndex));
|
||||
return forMethodParameter(new MethodParameter(constructor, parameterIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -686,13 +703,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor,
|
||||
int parameterIndex, Class<?> implementationClass) {
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
|
||||
Assert.notNull(constructor, "Constructor must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
return forMethodParameter(
|
||||
MethodParameter.forMethodOrConstructor(constructor, parameterIndex),
|
||||
implementationClass);
|
||||
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -705,15 +722,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forMethodParameter(MethodParameter.forMethodOrConstructor(method,
|
||||
parameterIndex));
|
||||
return forMethodParameter(new MethodParameter(method, parameterIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} parameter with a
|
||||
* given implementation. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation
|
||||
* class.
|
||||
* includes generic parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source method (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
@@ -721,74 +736,51 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
* @see #forMethodParameter(Method, int, Class)
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forMethodParameter(
|
||||
MethodParameter.forMethodOrConstructor(method, parameterIndex),
|
||||
implementationClass);
|
||||
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter}.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(MethodParameter, Class)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
return forType(methodParameter.getGenericParameterType()).getNested(
|
||||
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link MethodParameter} with a
|
||||
* given implementation. Use this variant when the class that declares the method
|
||||
* includes generic parameter variables that are satisfied by the implementation
|
||||
* class.
|
||||
* @param methodParameter the source method parameter (must not be {@code null})
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter,
|
||||
Class<?> implementationClass) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
methodParameter.getMember().getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (methodParameter.getContainingClass() != null ?
|
||||
forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()) : null);
|
||||
return forType(methodParameter.getGenericParameterType(), variableResolver).getNested(
|
||||
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return.
|
||||
* @param method the source for the method return
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* @param method the source for the method return type
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturn(Method, Class)
|
||||
* @see #forMethodReturnType(Method, Class)
|
||||
*/
|
||||
public static ResolvableType forMethodReturn(Method method) {
|
||||
public static ResolvableType forMethodReturnType(Method method) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forType(method.getGenericReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return. Use this
|
||||
* variant when the class that declares the method includes generic parameter
|
||||
* variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Use this variant when the class that declares the method includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return type
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturn(Method)
|
||||
* @see #forMethodReturnType(Method)
|
||||
*/
|
||||
public static ResolvableType forMethodReturn(Method method,
|
||||
Class<?> implementationClass) {
|
||||
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(implementationClass, "ImplementationClass must not be null");
|
||||
TypeVariableResolver variableResolver = forType(implementationClass).as(
|
||||
method.getDeclaringClass());
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(method.getDeclaringClass()) : null);
|
||||
return forType(method.getGenericReturnType(), variableResolver);
|
||||
}
|
||||
|
||||
@@ -826,12 +818,10 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
private static class WildcardBounds {
|
||||
|
||||
|
||||
private final Kind kind;
|
||||
|
||||
private final ResolvableType[] bounds;
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor to create a new {@link WildcardBounds} instance.
|
||||
* @param kind the kind of bounds
|
||||
@@ -843,7 +833,6 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return {@code true} if this bounds is the same kind as the specified bounds.
|
||||
*/
|
||||
@@ -868,18 +857,16 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
}
|
||||
|
||||
private boolean isAssignable(ResolvableType source, ResolvableType from) {
|
||||
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from)
|
||||
: from.isAssignableFrom(source));
|
||||
return (this.kind == Kind.UPPER ? source.isAssignableFrom(from) : from.isAssignableFrom(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying bounds.
|
||||
*/
|
||||
public ResolvableType[] getBounds() {
|
||||
return bounds;
|
||||
return this.bounds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a {@link WildcardBounds} instance for the specified type, returning
|
||||
* {@code null} if the specified type cannot be resolved to a {@link WildcardType}.
|
||||
@@ -888,17 +875,15 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
*/
|
||||
public static WildcardBounds get(ResolvableType type) {
|
||||
ResolvableType resolveToWildcard = type;
|
||||
while(!(resolveToWildcard.getType() instanceof WildcardType)) {
|
||||
while (!(resolveToWildcard.getType() instanceof WildcardType)) {
|
||||
if (resolveToWildcard == NONE) {
|
||||
return null;
|
||||
}
|
||||
resolveToWildcard = resolveToWildcard.resolveType();
|
||||
}
|
||||
WildcardType wildcardType = (WildcardType) resolveToWildcard.type;
|
||||
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER
|
||||
: Kind.UPPER);
|
||||
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds()
|
||||
: wildcardType.getLowerBounds();
|
||||
Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER : Kind.UPPER);
|
||||
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds();
|
||||
ResolvableType[] resolvableBounds = new ResolvableType[bounds.length];
|
||||
for (int i = 0; i < bounds.length; i++) {
|
||||
resolvableBounds[i] = forType(bounds[i], type.variableResolver);
|
||||
@@ -906,11 +891,10 @@ public final class ResolvableType implements TypeVariableResolver {
|
||||
return new WildcardBounds(boundsType, resolvableBounds);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The various kinds of bounds.
|
||||
*/
|
||||
static enum Kind { UPPER, LOWER }
|
||||
static enum Kind {UPPER, LOWER}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -24,14 +24,16 @@ import java.lang.reflect.TypeVariable;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0
|
||||
* @see ResolvableType
|
||||
* @see GenericTypeResolver
|
||||
*/
|
||||
public interface TypeVariableResolver {
|
||||
interface TypeVariableResolver {
|
||||
|
||||
/**
|
||||
* Resolve the specified type variable.
|
||||
* @param typeVariable the type variable to resolve (must not be {@code null})
|
||||
* @return the resolved {@link java.lang.reflect.Type} for the variable or
|
||||
* {@code null} if the variable cannot be resolved.
|
||||
* {@code null} if the variable cannot be resolved.
|
||||
*/
|
||||
Type resolveVariable(TypeVariable<?> typeVariable);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user