Backported further GenericTypeResolver tests
Issue: SPR-11052
This commit is contained in:
@@ -231,7 +231,7 @@ public abstract class GenericTypeResolver {
|
||||
* @return the resolved type of the argument, or {@code null} if not resolvable
|
||||
*/
|
||||
public static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc) {
|
||||
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||
Class<?>[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||
if (typeArgs == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -246,21 +246,25 @@ public abstract class GenericTypeResolver {
|
||||
* Resolve the type arguments of the given generic interface against the given
|
||||
* target class which is assumed to implement the generic interface and possibly
|
||||
* declare concrete types for its type variables.
|
||||
* <p>Note: In Spring 3.2, this method doesn't return {@code null} in all scenarios
|
||||
* where it should. To be fixed in Spring 4.0; for client code, this just means it
|
||||
* might see {@code null} in a few more cases then where it now sees an array with
|
||||
* a single {@link Object} type.
|
||||
* @param clazz the target class to check against
|
||||
* @param genericIfc the generic interface or superclass to resolve the type argument from
|
||||
* @return the resolved type of each argument, with the array size matching the
|
||||
* number of actual type arguments, or {@code null} if not resolvable
|
||||
*/
|
||||
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||
public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||
return doResolveTypeArguments(clazz, clazz, genericIfc);
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) {
|
||||
private static Class<?>[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) {
|
||||
while (classToIntrospect != null) {
|
||||
if (genericIfc.isInterface()) {
|
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||
for (Type ifc : ifcs) {
|
||||
Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
|
||||
Class<?>[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@@ -268,7 +272,7 @@ public abstract class GenericTypeResolver {
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
Class<?>[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
@@ -283,13 +287,13 @@ public abstract class GenericTypeResolver {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) {
|
||||
private static Class<?>[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
Type rawType = paramIfc.getRawType();
|
||||
if (genericIfc.equals(rawType)) {
|
||||
Type[] typeArgs = paramIfc.getActualTypeArguments();
|
||||
Class[] result = new Class[typeArgs.length];
|
||||
Class<?>[] result = new Class[typeArgs.length];
|
||||
for (int i = 0; i < typeArgs.length; i++) {
|
||||
Type arg = typeArgs[i];
|
||||
result[i] = extractClass(ownerClass, arg);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,10 +21,12 @@ import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.core.GenericTypeResolver.*;
|
||||
import static org.springframework.util.ReflectionUtils.*;
|
||||
@@ -63,28 +65,33 @@ public class GenericTypeResolverTests {
|
||||
|
||||
@Test
|
||||
public void methodReturnTypes() {
|
||||
assertEquals(Integer.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
|
||||
assertEquals(String.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
|
||||
assertEquals(Integer.class,
|
||||
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
|
||||
assertEquals(String.class,
|
||||
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
|
||||
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
|
||||
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
||||
assertEquals(null,
|
||||
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveType() {
|
||||
Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
|
||||
MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType.class,
|
||||
resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
|
||||
Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
|
||||
MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType.class,
|
||||
resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
|
||||
|
||||
Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage", MyInterfaceType[].class);
|
||||
MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType[].class,
|
||||
resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
|
||||
Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
|
||||
MyInterfaceType[].class);
|
||||
MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType[].class,
|
||||
resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
|
||||
|
||||
Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage", Object[].class);
|
||||
MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
|
||||
Map<TypeVariable, Type> varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
|
||||
assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
|
||||
Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
|
||||
Object[].class);
|
||||
MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
|
||||
Map<TypeVariable, Type> varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
|
||||
assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,7 +99,76 @@ public class GenericTypeResolverTests {
|
||||
assertEquals(B.class, resolveTypeArgument(TestImpl.class, ITest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTypeVariableMap() throws Exception {
|
||||
Map<TypeVariable, Type> map;
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.String}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
|
||||
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
|
||||
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
|
||||
assertThat(map.toString(), equalTo("{}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
|
||||
assertThat(map.size(), equalTo(2));
|
||||
Type t = null;
|
||||
Type x = null;
|
||||
for (Map.Entry<TypeVariable, Type> entry : map.entrySet()) {
|
||||
if(entry.getKey().toString().equals("T")) {
|
||||
t = entry.getValue();
|
||||
}
|
||||
else {
|
||||
x = entry.getValue();
|
||||
}
|
||||
}
|
||||
assertThat(t, equalTo((Type) Integer.class));
|
||||
assertThat(x, equalTo((Type) Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericsCannotBeResolved() throws Exception {
|
||||
// SPR-11030
|
||||
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
|
||||
// Note: to be changed to return null in Spring 4.0
|
||||
assertThat(resolved, equalTo(new Class[] {Object.class}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRawMapTypeCannotBeResolved() throws Exception {
|
||||
// SPR-11052
|
||||
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
|
||||
assertNull(resolved);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericsOnArrayFromParamCannotBeResolved() throws Exception {
|
||||
// SPR-11044
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(
|
||||
WithArrayBase.class.getDeclaredMethod("array", Object[].class), 0);
|
||||
Class<?> resolved = GenericTypeResolver.resolveParameterType(methodParameter, WithArray.class);
|
||||
assertThat(resolved, equalTo((Class) Object[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception {
|
||||
// SPR-11044
|
||||
Class<?> resolved = GenericTypeResolver.resolveReturnType(
|
||||
WithArrayBase.class.getDeclaredMethod("array", Object[].class),
|
||||
WithArray.class);
|
||||
assertThat(resolved, equalTo((Class) Object[].class));
|
||||
}
|
||||
|
||||
public interface MyInterfaceType<T> {
|
||||
}
|
||||
@@ -113,26 +189,44 @@ public class GenericTypeResolverTests {
|
||||
}
|
||||
|
||||
public static class MyTypeWithMethods<T> {
|
||||
public MyInterfaceType<Integer> integer() { return null; }
|
||||
public MySimpleInterfaceType string() { return null; }
|
||||
public Object object() { return null; }
|
||||
|
||||
public MyInterfaceType<Integer> integer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public MySimpleInterfaceType string() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object object() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public MyInterfaceType raw() { return null; }
|
||||
public String notParameterized() { return null; }
|
||||
public String notParameterizedWithArguments(Integer x, Boolean b) { return null; }
|
||||
public MyInterfaceType raw() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String notParameterized() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String notParameterizedWithArguments(Integer x, Boolean b) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a factory method that wraps the supplied object in a proxy
|
||||
* of the same type.
|
||||
* Simulates a factory method that wraps the supplied object in a proxy of the
|
||||
* same type.
|
||||
*/
|
||||
public static <T> T createProxy(T object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #createProxy(Object)} but adds an additional argument
|
||||
* before the argument of type {@code T}. Note that they may potentially
|
||||
* be of the same time when invoked!
|
||||
* Similar to {@link #createProxy(Object)} but adds an additional argument before
|
||||
* the argument of type {@code T}. Note that they may potentially be of the same
|
||||
* time when invoked!
|
||||
*/
|
||||
public static <T> T createNamedProxy(String name, T object) {
|
||||
return null;
|
||||
@@ -146,8 +240,8 @@ public class GenericTypeResolverTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #createMock(Class)} but adds an additional method
|
||||
* argument before the parameterized argument.
|
||||
* Similar to {@link #createMock(Class)} but adds an additional method argument
|
||||
* before the parameterized argument.
|
||||
*/
|
||||
public static <T> T createNamedMock(String name, Class<T> toMock) {
|
||||
return null;
|
||||
@@ -162,8 +256,8 @@ public class GenericTypeResolverTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract some value of the type supported by the interface (i.e., by
|
||||
* a concrete, non-generic implementation of the interface).
|
||||
* Extract some value of the type supported by the interface (i.e., by a concrete,
|
||||
* non-generic implementation of the interface).
|
||||
*/
|
||||
public static <T> T extractValueFrom(MyInterfaceType<T> myInterfaceType) {
|
||||
return null;
|
||||
@@ -201,4 +295,22 @@ public class GenericTypeResolverTests {
|
||||
class TestImpl<I extends A, T extends B<I>> extends ITest<T>{
|
||||
}
|
||||
|
||||
static class TopLevelClass<T> {
|
||||
class Nested<X> {
|
||||
}
|
||||
}
|
||||
|
||||
static class TypedTopLevelClass extends TopLevelClass<Integer> {
|
||||
class TypedNested extends Nested<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class WithArrayBase<T> {
|
||||
|
||||
public abstract T[] array(T... args);
|
||||
}
|
||||
|
||||
static abstract class WithArray<T> extends WithArrayBase<T> {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user