From b7b7c067c689c1e2e5dcedcdf38cc44c3bbe3c05 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 22 Jul 2010 15:57:55 +0000 Subject: [PATCH] GenericTypeResolver works for nested parameterized types and for generic superclasses as well (SPR-7389) --- .../core/GenericTypeResolver.java | 72 ++++++++++++------- .../core/GenericTypeResolverTests.java | 69 ++++++++++++++++++ 2 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 org.springframework.core/src/test/java/org/springframework/core/GenericTypeResolverTests.java diff --git a/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java b/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java index 596a925ebdf..211b7d41984 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -107,7 +107,7 @@ public abstract class GenericTypeResolver { * the given target class which is assumed to implement the generic interface * and possibly declare a concrete type for its type variable. * @param clazz the target class to check against - * @param genericIfc the generic interface to resolve the type argument from + * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of the argument, or null if not resolvable */ public static Class resolveTypeArgument(Class clazz, Class genericIfc) { @@ -127,7 +127,7 @@ public abstract class GenericTypeResolver { * target class which is assumed to implement the generic interface and possibly * declare concrete types for its type variables. * @param clazz the target class to check against - * @param genericIfc the generic interface to resolve the type argument from + * @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 null if not resolvable */ @@ -137,26 +137,20 @@ public abstract class GenericTypeResolver { private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) { while (classToIntrospect != null) { - Type[] ifcs = classToIntrospect.getGenericInterfaces(); - for (Type ifc : ifcs) { - 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]; - for (int i = 0; i < typeArgs.length; i++) { - Type arg = typeArgs[i]; - result[i] = extractClass(ownerClass, arg); - } + if (genericIfc.isInterface()) { + Type[] ifcs = classToIntrospect.getGenericInterfaces(); + for (Type ifc : ifcs) { + Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc); + if (result != null) { return result; } - else if (genericIfc.isAssignableFrom((Class) rawType)) { - return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc); - } } - else if (genericIfc.isAssignableFrom((Class) ifc)) { - return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc); + } + else { + Class[] result = doResolveTypeArguments( + ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc); + if (result != null) { + return result; } } classToIntrospect = classToIntrospect.getSuperclass(); @@ -164,11 +158,43 @@ public abstract class GenericTypeResolver { return null; } + 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]; + for (int i = 0; i < typeArgs.length; i++) { + Type arg = typeArgs[i]; + result[i] = extractClass(ownerClass, arg); + } + return result; + } + else if (genericIfc.isAssignableFrom((Class) rawType)) { + return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc); + } + } + else if (genericIfc.isAssignableFrom((Class) ifc)) { + return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc); + } + return null; + } + /** * Extract a class instance from given Type. */ private static Class extractClass(Class ownerClass, Type arg) { - if (arg instanceof TypeVariable) { + if (arg instanceof ParameterizedType) { + return extractClass(ownerClass, ((ParameterizedType) arg).getRawType()); + } + else if (arg instanceof GenericArrayType) { + GenericArrayType gat = (GenericArrayType) arg; + Type gt = gat.getGenericComponentType(); + Class componentClass = extractClass(ownerClass, gt); + return Array.newInstance(componentClass, 0).getClass(); + } + else if (arg instanceof TypeVariable) { TypeVariable tv = (TypeVariable) arg; arg = getTypeVariableMap(ownerClass).get(tv); if (arg == null) { @@ -178,12 +204,6 @@ public abstract class GenericTypeResolver { arg = extractClass(ownerClass, arg); } } - else if (arg instanceof GenericArrayType) { - GenericArrayType gat = (GenericArrayType) arg; - Type gt = gat.getGenericComponentType(); - Class componentClass = extractClass(ownerClass, gt); - arg = Array.newInstance(componentClass, 0).getClass(); - } return (arg instanceof Class ? (Class) arg : Object.class); } diff --git a/org.springframework.core/src/test/java/org/springframework/core/GenericTypeResolverTests.java b/org.springframework.core/src/test/java/org/springframework/core/GenericTypeResolverTests.java new file mode 100644 index 00000000000..df595e3f2c0 --- /dev/null +++ b/org.springframework.core/src/test/java/org/springframework/core/GenericTypeResolverTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.core; + +import java.util.Collection; + +import static org.junit.Assert.*; +import org.junit.Test; + +/** + * @author Juergen Hoeller + */ +public class GenericTypeResolverTests { + + @Test + public void testSimpleInterfaceType() { + assertEquals(String.class, GenericTypeResolver.resolveTypeArgument(MySimpleInterfaceType.class, MyInterfaceType.class)); + } + + @Test + public void testSimpleCollectionInterfaceType() { + assertEquals(Collection.class, GenericTypeResolver.resolveTypeArgument(MyCollectionInterfaceType.class, MyInterfaceType.class)); + } + + @Test + public void testSimpleSuperclassType() { + assertEquals(String.class, GenericTypeResolver.resolveTypeArgument(MySimpleSuperclassType.class, MySuperclassType.class)); + } + + @Test + public void testSimpleCollectionSuperclassType() { + assertEquals(Collection.class, GenericTypeResolver.resolveTypeArgument(MyCollectionSuperclassType.class, MySuperclassType.class)); + } + + + public interface MyInterfaceType { + } + + public class MySimpleInterfaceType implements MyInterfaceType { + } + + public class MyCollectionInterfaceType implements MyInterfaceType> { + } + + + public abstract class MySuperclassType { + } + + public class MySimpleSuperclassType extends MySuperclassType { + } + + public class MyCollectionSuperclassType extends MySuperclassType> { + } + +}