Skip to content

Commit 3bfddc5

Browse files
committed
Backported further GenericTypeResolver tests
Issue: SPR-11052
1 parent f9081be commit 3bfddc5

File tree

2 files changed

+156
-40
lines changed

2 files changed

+156
-40
lines changed

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public static Class<?> resolveReturnTypeArgument(Method method, Class<?> generic
231231
* @return the resolved type of the argument, or {@code null} if not resolvable
232232
*/
233233
public static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc) {
234-
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
234+
Class<?>[] typeArgs = resolveTypeArguments(clazz, genericIfc);
235235
if (typeArgs == null) {
236236
return null;
237237
}
@@ -246,29 +246,33 @@ public static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc)
246246
* Resolve the type arguments of the given generic interface against the given
247247
* target class which is assumed to implement the generic interface and possibly
248248
* declare concrete types for its type variables.
249+
* <p>Note: In Spring 3.2, this method doesn't return {@code null} in all scenarios
250+
* where it should. To be fixed in Spring 4.0; for client code, this just means it
251+
* might see {@code null} in a few more cases then where it now sees an array with
252+
* a single {@link Object} type.
249253
* @param clazz the target class to check against
250254
* @param genericIfc the generic interface or superclass to resolve the type argument from
251255
* @return the resolved type of each argument, with the array size matching the
252256
* number of actual type arguments, or {@code null} if not resolvable
253257
*/
254-
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
258+
public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
255259
return doResolveTypeArguments(clazz, clazz, genericIfc);
256260
}
257261

258-
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) {
262+
private static Class<?>[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) {
259263
while (classToIntrospect != null) {
260264
if (genericIfc.isInterface()) {
261265
Type[] ifcs = classToIntrospect.getGenericInterfaces();
262266
for (Type ifc : ifcs) {
263-
Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
267+
Class<?>[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
264268
if (result != null) {
265269
return result;
266270
}
267271
}
268272
}
269273
else {
270274
try {
271-
Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
275+
Class<?>[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
272276
if (result != null) {
273277
return result;
274278
}
@@ -283,13 +287,13 @@ private static Class[] doResolveTypeArguments(Class<?> ownerClass, Class<?> clas
283287
return null;
284288
}
285289

286-
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) {
290+
private static Class<?>[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) {
287291
if (ifc instanceof ParameterizedType) {
288292
ParameterizedType paramIfc = (ParameterizedType) ifc;
289293
Type rawType = paramIfc.getRawType();
290294
if (genericIfc.equals(rawType)) {
291295
Type[] typeArgs = paramIfc.getActualTypeArguments();
292-
Class[] result = new Class[typeArgs.length];
296+
Class<?>[] result = new Class[typeArgs.length];
293297
for (int i = 0; i < typeArgs.length; i++) {
294298
Type arg = typeArgs[i];
295299
result[i] = extractClass(ownerClass, arg);

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

Lines changed: 145 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,10 +21,12 @@
2121
import java.lang.reflect.TypeVariable;
2222
import java.util.Collection;
2323
import java.util.HashMap;
24+
import java.util.List;
2425
import java.util.Map;
2526

2627
import org.junit.Test;
2728

29+
import static org.hamcrest.Matchers.*;
2830
import static org.junit.Assert.*;
2931
import static org.springframework.core.GenericTypeResolver.*;
3032
import static org.springframework.util.ReflectionUtils.*;
@@ -63,36 +65,110 @@ public void nullIfNotResolvable() {
6365

6466
@Test
6567
public void methodReturnTypes() {
66-
assertEquals(Integer.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
67-
assertEquals(String.class, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
68+
assertEquals(Integer.class,
69+
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "integer"), MyInterfaceType.class));
70+
assertEquals(String.class,
71+
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "string"), MyInterfaceType.class));
6872
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "raw"), MyInterfaceType.class));
69-
assertEquals(null, resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
73+
assertEquals(null,
74+
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
7075
}
7176

7277
@Test
7378
public void testResolveType() {
74-
Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
75-
MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
76-
assertEquals(MyInterfaceType.class,
77-
resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
78-
79-
Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage", MyInterfaceType[].class);
80-
MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
81-
assertEquals(MyInterfaceType[].class,
82-
resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
83-
84-
Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage", Object[].class);
85-
MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
86-
Map<TypeVariable, Type> varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
87-
assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
79+
Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
80+
MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
81+
assertEquals(MyInterfaceType.class,
82+
resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
83+
84+
Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
85+
MyInterfaceType[].class);
86+
MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
87+
assertEquals(MyInterfaceType[].class,
88+
resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap<TypeVariable, Type>()));
89+
90+
Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
91+
Object[].class);
92+
MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
93+
Map<TypeVariable, Type> varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
94+
assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
8895
}
8996

9097
@Test
9198
public void testBoundParameterizedType() {
9299
assertEquals(B.class, resolveTypeArgument(TestImpl.class, ITest.class));
93100
}
94101

102+
@Test
103+
public void testGetTypeVariableMap() throws Exception {
104+
Map<TypeVariable, Type> map;
105+
106+
map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
107+
assertThat(map.toString(), equalTo("{T=class java.lang.String}"));
108+
109+
map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
110+
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
111+
112+
map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
113+
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
114+
115+
map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
116+
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
117+
118+
map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
119+
assertThat(map.toString(), equalTo("{}"));
120+
121+
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
122+
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
123+
124+
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
125+
assertThat(map.size(), equalTo(2));
126+
Type t = null;
127+
Type x = null;
128+
for (Map.Entry<TypeVariable, Type> entry : map.entrySet()) {
129+
if(entry.getKey().toString().equals("T")) {
130+
t = entry.getValue();
131+
}
132+
else {
133+
x = entry.getValue();
134+
}
135+
}
136+
assertThat(t, equalTo((Type) Integer.class));
137+
assertThat(x, equalTo((Type) Long.class));
138+
}
139+
140+
@Test
141+
public void getGenericsCannotBeResolved() throws Exception {
142+
// SPR-11030
143+
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(List.class, Iterable.class);
144+
// Note: to be changed to return null in Spring 4.0
145+
assertThat(resolved, equalTo(new Class[] {Object.class}));
146+
}
95147

148+
@Test
149+
public void getRawMapTypeCannotBeResolved() throws Exception {
150+
// SPR-11052
151+
Class<?>[] resolved = GenericTypeResolver.resolveTypeArguments(Map.class, Map.class);
152+
assertNull(resolved);
153+
}
154+
155+
@Test
156+
public void getGenericsOnArrayFromParamCannotBeResolved() throws Exception {
157+
// SPR-11044
158+
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(
159+
WithArrayBase.class.getDeclaredMethod("array", Object[].class), 0);
160+
Class<?> resolved = GenericTypeResolver.resolveParameterType(methodParameter, WithArray.class);
161+
assertThat(resolved, equalTo((Class) Object[].class));
162+
}
163+
164+
@Test
165+
public void getGenericsOnArrayFromReturnCannotBeResolved() throws Exception {
166+
// SPR-11044
167+
Class<?> resolved = GenericTypeResolver.resolveReturnType(
168+
WithArrayBase.class.getDeclaredMethod("array", Object[].class),
169+
WithArray.class);
170+
assertThat(resolved, equalTo((Class) Object[].class));
171+
}
96172

97173
public interface MyInterfaceType<T> {
98174
}
@@ -113,26 +189,44 @@ public class MyCollectionSuperclassType extends MySuperclassType<Collection<Stri
113189
}
114190

115191
public static class MyTypeWithMethods<T> {
116-
public MyInterfaceType<Integer> integer() { return null; }
117-
public MySimpleInterfaceType string() { return null; }
118-
public Object object() { return null; }
192+
193+
public MyInterfaceType<Integer> integer() {
194+
return null;
195+
}
196+
197+
public MySimpleInterfaceType string() {
198+
return null;
199+
}
200+
201+
public Object object() {
202+
return null;
203+
}
204+
119205
@SuppressWarnings("rawtypes")
120-
public MyInterfaceType raw() { return null; }
121-
public String notParameterized() { return null; }
122-
public String notParameterizedWithArguments(Integer x, Boolean b) { return null; }
206+
public MyInterfaceType raw() {
207+
return null;
208+
}
209+
210+
public String notParameterized() {
211+
return null;
212+
}
213+
214+
public String notParameterizedWithArguments(Integer x, Boolean b) {
215+
return null;
216+
}
123217

124218
/**
125-
* Simulates a factory method that wraps the supplied object in a proxy
126-
* of the same type.
219+
* Simulates a factory method that wraps the supplied object in a proxy of the
220+
* same type.
127221
*/
128222
public static <T> T createProxy(T object) {
129223
return null;
130224
}
131225

132226
/**
133-
* Similar to {@link #createProxy(Object)} but adds an additional argument
134-
* before the argument of type {@code T}. Note that they may potentially
135-
* be of the same time when invoked!
227+
* Similar to {@link #createProxy(Object)} but adds an additional argument before
228+
* the argument of type {@code T}. Note that they may potentially be of the same
229+
* time when invoked!
136230
*/
137231
public static <T> T createNamedProxy(String name, T object) {
138232
return null;
@@ -146,8 +240,8 @@ public static <MOCK> MOCK createMock(Class<MOCK> toMock) {
146240
}
147241

148242
/**
149-
* Similar to {@link #createMock(Class)} but adds an additional method
150-
* argument before the parameterized argument.
243+
* Similar to {@link #createMock(Class)} but adds an additional method argument
244+
* before the parameterized argument.
151245
*/
152246
public static <T> T createNamedMock(String name, Class<T> toMock) {
153247
return null;
@@ -162,8 +256,8 @@ public static <V extends Object, T> T createVMock(V name, Class<T> toMock) {
162256
}
163257

164258
/**
165-
* Extract some value of the type supported by the interface (i.e., by
166-
* a concrete, non-generic implementation of the interface).
259+
* Extract some value of the type supported by the interface (i.e., by a concrete,
260+
* non-generic implementation of the interface).
167261
*/
168262
public static <T> T extractValueFrom(MyInterfaceType<T> myInterfaceType) {
169263
return null;
@@ -201,4 +295,22 @@ class ITest<T>{}
201295
class TestImpl<I extends A, T extends B<I>> extends ITest<T>{
202296
}
203297

298+
static class TopLevelClass<T> {
299+
class Nested<X> {
300+
}
301+
}
302+
303+
static class TypedTopLevelClass extends TopLevelClass<Integer> {
304+
class TypedNested extends Nested<Long> {
305+
}
306+
}
307+
308+
static abstract class WithArrayBase<T> {
309+
310+
public abstract T[] array(T... args);
311+
}
312+
313+
static abstract class WithArray<T> extends WithArrayBase<T> {
314+
}
315+
204316
}

0 commit comments

Comments
 (0)