diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java b/org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java index 585afa64033..2b5d8b99e9a 100644 --- a/org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java +++ b/org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java @@ -16,10 +16,6 @@ package org.springframework.context.event; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; - import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.core.GenericTypeResolver; @@ -48,13 +44,15 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen this.delegate = delegate; } + @SuppressWarnings("unchecked") public void onApplicationEvent(ApplicationEvent event) { this.delegate.onApplicationEvent(event); } public boolean supportsEventType(Class eventType) { - return getGenericEventType(this.delegate.getClass()).isAssignableFrom(eventType); + Class typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class); + return (typeArg == null || typeArg.isAssignableFrom(eventType)); } public boolean supportsSourceType(Class sourceType) { @@ -65,36 +63,4 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE); } - - @SuppressWarnings("unchecked") - private Class getGenericEventType(Class currentClass) { - Class classToIntrospect = currentClass; - while (classToIntrospect != null) { - Type[] ifcs = classToIntrospect.getGenericInterfaces(); - for (Type ifc : ifcs) { - if (ifc instanceof ParameterizedType) { - ParameterizedType paramIfc = (ParameterizedType) ifc; - Type rawType = paramIfc.getRawType(); - if (ApplicationListener.class.equals(rawType)) { - Type arg = paramIfc.getActualTypeArguments()[0]; - if (arg instanceof TypeVariable) { - arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, this.delegate.getClass()); - } - if (arg instanceof Class) { - return (Class) arg; - } - } - else if (ApplicationListener.class.isAssignableFrom((Class) rawType)) { - return getGenericEventType((Class) rawType); - } - } - else if (ApplicationListener.class.isAssignableFrom((Class) ifc)) { - return getGenericEventType((Class) ifc); - } - } - classToIntrospect = classToIntrospect.getSuperclass(); - } - return ApplicationEvent.class; - } - } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/format/support/GenericFormatterRegistry.java b/org.springframework.context/src/main/java/org/springframework/ui/format/support/GenericFormatterRegistry.java index d9a940aab92..4beb2e3668a 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/format/support/GenericFormatterRegistry.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/format/support/GenericFormatterRegistry.java @@ -17,9 +17,6 @@ package org.springframework.ui.format.support; import java.lang.annotation.Annotation; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; import java.text.ParseException; import java.util.LinkedList; import java.util.Locale; @@ -182,18 +179,20 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC // implementing FormatterRegistry public void addFormatterByType(Class type, Formatter formatter) { - Class formattedObjectType = getFormattedObjectType(formatter.getClass()); + Class formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class); if (!this.conversionService.canConvert(formattedObjectType, type)) { - throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse"); + throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + + type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse"); } if (!this.conversionService.canConvert(type, formattedObjectType)) { - throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format"); + throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + + type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format"); } this.typeFormatters.put(type, formatter); } public void addFormatterByType(Formatter formatter) { - Class formattedObjectType = getFormattedObjectType(formatter.getClass()); + Class formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class); this.typeFormatters.put(formattedObjectType, formatter); } @@ -202,7 +201,13 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC } public void addFormatterByAnnotation(AnnotationFormatterFactory factory) { - this.annotationFormatters.put(getAnnotationType(factory.getClass()), factory); + Class[] typeArgs = GenericTypeResolver.resolveTypeArguments(factory.getClass(), AnnotationFormatterFactory.class); + if (typeArgs == null) { + throw new IllegalArgumentException( + "Unable to extract Annotation type A argument from AnnotationFormatterFactory [" + + factory.getClass().getName() + "]; does the factory parameterize the generic type?"); + } + this.annotationFormatters.put(typeArgs[0], factory); } @SuppressWarnings("unchecked") @@ -218,7 +223,7 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC formatter = getTypeFormatter(type.getType()); } if (formatter != null) { - Class formattedObjectType = getFormattedObjectType(formatter.getClass()); + Class formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class); if (!type.getType().isAssignableFrom(formattedObjectType)) { return new ConvertingFormatter(type.getType(), formattedObjectType, formatter); } @@ -229,66 +234,6 @@ public class GenericFormatterRegistry implements FormatterRegistry, ApplicationC // internal helpers - private Class getFormattedObjectType(Class formatterClass) { - Class classToIntrospect = formatterClass; - while (classToIntrospect != null) { - Type[] ifcs = classToIntrospect.getGenericInterfaces(); - for (Type ifc : ifcs) { - if (ifc instanceof ParameterizedType) { - ParameterizedType paramIfc = (ParameterizedType) ifc; - Type rawType = paramIfc.getRawType(); - if (Formatter.class.equals(rawType)) { - Type arg = paramIfc.getActualTypeArguments()[0]; - if (arg instanceof TypeVariable) { - arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, formatterClass); - } - if (arg instanceof Class) { - return (Class) arg; - } - } - else if (Formatter.class.isAssignableFrom((Class) rawType)) { - return getFormattedObjectType((Class) rawType); - } - } - else if (Formatter.class.isAssignableFrom((Class) ifc)) { - return getFormattedObjectType((Class) ifc); - } - } - classToIntrospect = classToIntrospect.getSuperclass(); - } - return null; - } - - private Class getAnnotationType(Class factoryClass) { - Class classToIntrospect = factoryClass; - while (classToIntrospect != null) { - Type[] ifcs = classToIntrospect.getGenericInterfaces(); - for (Type ifc : ifcs) { - if (ifc instanceof ParameterizedType) { - ParameterizedType paramIfc = (ParameterizedType) ifc; - Type rawType = paramIfc.getRawType(); - if (AnnotationFormatterFactory.class.equals(rawType)) { - Type arg = paramIfc.getActualTypeArguments()[0]; - if (arg instanceof TypeVariable) { - arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, factoryClass); - } - if (arg instanceof Class) { - return (Class) arg; - } - } else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) rawType)) { - return getAnnotationType((Class) rawType); - } - } else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) ifc)) { - return getAnnotationType((Class) ifc); - } - } - classToIntrospect = classToIntrospect.getSuperclass(); - } - throw new IllegalArgumentException( - "Unable to extract Annotation type A argument from AnnotationFormatterFactory [" + - factoryClass.getName() + "]; does the factory parameterize the generic type?"); - } - @SuppressWarnings("unchecked") private Formatter getAnnotationFormatter(TypeDescriptor type) { Annotation[] annotations = type.getAnnotations(); 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 8d238a03f00..7017cbea97b 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 @@ -75,7 +75,7 @@ public abstract class GenericTypeResolver { * @param clazz the class to resolve type variables against * @return the corresponding generic parameter or return type */ - public static Class resolveParameterType(MethodParameter methodParam, Class clazz) { + public static Class resolveParameterType(MethodParameter methodParam, Class clazz) { Type genericType = getTargetType(methodParam); Assert.notNull(clazz, "Class must not be null"); Map typeVariableMap = getTypeVariableMap(clazz); @@ -92,7 +92,7 @@ public abstract class GenericTypeResolver { * @param clazz the class to resolve type variables against * @return the corresponding generic parameter or return type */ - public static Class resolveReturnType(Method method, Class clazz) { + public static Class resolveReturnType(Method method, Class clazz) { Assert.notNull(method, "Method must not be null"); Type genericType = method.getGenericReturnType(); Assert.notNull(clazz, "Class must not be null"); @@ -102,15 +102,68 @@ public abstract class GenericTypeResolver { } /** - * Resolve the given type variable against the given class. - * @param tv the type variable to resolve - * @param clazz the class that defines the type variable - * somewhere in its inheritance hierarchy - * @return the resolved type that the variable can get replaced with, - * or null if none found + * Resolve the single type argument of the given generic interface against + * 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 + * @return the resolved type of the argument, or null if not resolvable */ - public static Type resolveTypeVariable(TypeVariable tv, Class clazz) { - return getTypeVariableMap(clazz).get(tv); + public static Class resolveTypeArgument(Class clazz, Class genericIfc) { + Class[] typeArgs = resolveTypeArguments(clazz, genericIfc); + if (typeArgs == null) { + return null; + } + if (typeArgs.length != 1) { + throw new IllegalArgumentException("Expected 1 type argument on generic interface [" + + genericIfc.getName() + "] but found " + typeArgs.length); + } + return typeArgs[0]; + } + + /** + * 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. + * @param clazz the target class to check against + * @param genericIfc the generic interface 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 + */ + public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { + return doResolveTypeArguments(clazz, clazz, genericIfc); + } + + 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]; + if (arg instanceof TypeVariable) { + arg = getTypeVariableMap(ownerClass).get((TypeVariable) arg); + } + result[i] = (arg instanceof Class ? (Class) arg : Object.class); + } + 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); + } + } + classToIntrospect = classToIntrospect.getSuperclass(); + } + return null; } diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index bec8a250119..d90a458cec4 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -16,18 +16,11 @@ package org.springframework.core.convert.support; -import static org.springframework.core.convert.support.ConversionUtils.invokeConverter; - import java.lang.reflect.Array; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; -import java.util.List; import java.util.Map; import java.util.Set; @@ -39,6 +32,7 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; import org.springframework.core.convert.converter.ConverterInfo; import org.springframework.core.convert.converter.ConverterRegistry; +import static org.springframework.core.convert.support.ConversionUtils.*; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -109,27 +103,28 @@ public class GenericConversionService implements ConversionService, ConverterReg return this.parent; } + // implementing ConverterRegistry - public void addConverter(Converter converter) { - List typeInfo = getRequiredTypeInfo(converter); + public void addConverter(Converter converter) { + Class[] typeInfo = getRequiredTypeInfo(converter, Converter.class); if (typeInfo == null) { throw new IllegalArgumentException( "Unable to the determine sourceType and targetType your Converter converts between; declare these types or implement ConverterInfo"); } - Class sourceType = typeInfo.get(0); - Class targetType = typeInfo.get(1); + Class sourceType = typeInfo[0]; + Class targetType = typeInfo[1]; getSourceMap(sourceType).put(targetType, new ConverterAdapter(converter)); } public void addConverterFactory(ConverterFactory converterFactory) { - List typeInfo = getRequiredTypeInfo(converterFactory); + Class[] typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class); if (typeInfo == null) { throw new IllegalArgumentException( "Unable to the determine sourceType and targetRangeType R your ConverterFactory converts between; declare these types or implement ConverterInfo"); } - Class sourceType = typeInfo.get(0); - Class targetType = typeInfo.get(1); + Class sourceType = typeInfo[0]; + Class targetType = typeInfo[1]; getSourceMap(sourceType).put(targetType, new ConverterFactoryAdapter(converterFactory)); } @@ -137,6 +132,7 @@ public class GenericConversionService implements ConversionService, ConverterReg getSourceMap(sourceType).remove(targetType); } + // implementing ConversionService public boolean canConvert(Class sourceType, Class targetType) { @@ -176,6 +172,7 @@ public class GenericConversionService implements ConversionService, ConverterReg return invokeConverter(converter, source, sourceType, targetType); } + // subclassing hooks /** @@ -224,7 +221,7 @@ public class GenericConversionService implements ConversionService, ConverterReg /** * Hook method to lookup the converter for a given sourceType/targetType pair. * First queries this ConversionService's converter map. - * If no suitable Converter is found, and a {@link #setParent(GenericConversionService) parent} is set, then queries the parent. + * If no suitable Converter is found, and a {@link #setParent parent} is set, then queries the parent. * If still no suitable Converter is found, returns a NO_OP Converter if the sourceType and targetType are assignable. * Returns null if this ConversionService simply cannot convert between sourceType and targetType. * Subclasses may override. @@ -236,68 +233,34 @@ public class GenericConversionService implements ConversionService, ConverterReg GenericConverter converter = findConverterByClassPair(sourceType.getObjectType(), targetType.getObjectType()); if (converter != null) { return converter; - } else if (this.parent != null && this.parent.canConvert(sourceType, targetType)) { + } + else if (this.parent != null && this.parent.canConvert(sourceType, targetType)) { return this.parentConverterAdapter; - } else { + } + else { if (sourceType.isAssignableTo(targetType)) { return NO_OP_CONVERTER; - } else { + } + else { return null; } } } + // internal helpers - private List getRequiredTypeInfo(Object converter) { - List typeInfo = new ArrayList(2); + private Class[] getRequiredTypeInfo(Object converter, Class ifc) { + Class[] typeInfo = new Class[2]; if (converter instanceof ConverterInfo) { ConverterInfo info = (ConverterInfo) converter; - typeInfo.add(info.getSourceType()); - typeInfo.add(info.getTargetType()); + typeInfo[0] = info.getSourceType(); + typeInfo[1] = info.getTargetType(); return typeInfo; - } else { - return getConverterTypeInfo(converter.getClass()); } - } - - private List getConverterTypeInfo(Class converterClass) { - Class classToIntrospect = converterClass; - while (classToIntrospect != null) { - Type[] ifcs = classToIntrospect.getGenericInterfaces(); - for (Type ifc : ifcs) { - if (ifc instanceof ParameterizedType) { - ParameterizedType paramIfc = (ParameterizedType) ifc; - Type rawType = paramIfc.getRawType(); - if (Converter.class.equals(rawType) || ConverterFactory.class.equals(rawType)) { - List typeInfo = new ArrayList(2); - Type arg1 = paramIfc.getActualTypeArguments()[0]; - if (arg1 instanceof TypeVariable) { - arg1 = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg1, converterClass); - } - if (arg1 instanceof Class) { - typeInfo.add((Class) arg1); - } - Type arg2 = paramIfc.getActualTypeArguments()[1]; - if (arg2 instanceof TypeVariable) { - arg2 = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg2, converterClass); - } - if (arg2 instanceof Class) { - typeInfo.add((Class) arg2); - } - if (typeInfo.size() == 2) { - return typeInfo; - } - } else if (Converter.class.isAssignableFrom((Class) rawType)) { - return getConverterTypeInfo((Class) rawType); - } - } else if (Converter.class.isAssignableFrom((Class) ifc)) { - return getConverterTypeInfo((Class) ifc); - } - } - classToIntrospect = classToIntrospect.getSuperclass(); + else { + return GenericTypeResolver.resolveTypeArguments(converter.getClass(), ifc); } - return null; } private GenericConverter findConverterByClassPair(Class sourceType, Class targetType) { @@ -318,7 +281,8 @@ public class GenericConversionService implements ConversionService, ConverterReg } Map objectConverters = getConvertersForSource(Object.class); return getConverter(objectConverters, targetType); - } else { + } + else { LinkedList classQueue = new LinkedList(); classQueue.addFirst(sourceType); while (!classQueue.isEmpty()) { @@ -333,7 +297,8 @@ public class GenericConversionService implements ConversionService, ConverterReg if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } - } else { + } + else { if (currentClass.getSuperclass() != null) { classQueue.addFirst(currentClass.getSuperclass()); } @@ -380,7 +345,8 @@ public class GenericConversionService implements ConversionService, ConverterReg } } return converters.get(Object.class); - } else { + } + else { LinkedList classQueue = new LinkedList(); classQueue.addFirst(targetType); while (!classQueue.isEmpty()) { @@ -394,7 +360,8 @@ public class GenericConversionService implements ConversionService, ConverterReg if (componentType.getSuperclass() != null) { classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass()); } - } else { + } + else { if (currentClass.getSuperclass() != null) { classQueue.addFirst(currentClass.getSuperclass()); } @@ -408,6 +375,7 @@ public class GenericConversionService implements ConversionService, ConverterReg } } + private static class ConverterAdapter implements GenericConverter { private Converter converter; @@ -422,6 +390,7 @@ public class GenericConversionService implements ConversionService, ConverterReg } } + private static class ConverterFactoryAdapter implements GenericConverter { private ConverterFactory converterFactory;