Browse Source

Backported further GenericTypeResolver tests

Issue: SPR-11052
pull/405/head
Juergen Hoeller 12 years ago
parent
commit
3bfddc501d
  1. 18
      spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
  2. 178
      spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java

18
spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java

@ -231,7 +231,7 @@ public abstract class GenericTypeResolver { @@ -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 { @@ -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 { @@ -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 { @@ -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);

178
spring-core/src/test/java/org/springframework/core/GenericTypeResolverTests.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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 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 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 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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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> {
}
}

Loading…
Cancel
Save