Skip to content

Commit 49bf85b

Browse files
committed
factored out common resolveTypeArgument(s) method to GenericTypeResolver
1 parent b8c1130 commit 49bf85b

File tree

4 files changed

+115
-182
lines changed

4 files changed

+115
-182
lines changed

org.springframework.context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@
1616

1717
package org.springframework.context.event;
1818

19-
import java.lang.reflect.ParameterizedType;
20-
import java.lang.reflect.Type;
21-
import java.lang.reflect.TypeVariable;
22-
2319
import org.springframework.context.ApplicationEvent;
2420
import org.springframework.context.ApplicationListener;
2521
import org.springframework.core.GenericTypeResolver;
@@ -48,13 +44,15 @@ public GenericApplicationListenerAdapter(ApplicationListener delegate) {
4844
this.delegate = delegate;
4945
}
5046

47+
5148
@SuppressWarnings("unchecked")
5249
public void onApplicationEvent(ApplicationEvent event) {
5350
this.delegate.onApplicationEvent(event);
5451
}
5552

5653
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
57-
return getGenericEventType(this.delegate.getClass()).isAssignableFrom(eventType);
54+
Class typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
55+
return (typeArg == null || typeArg.isAssignableFrom(eventType));
5856
}
5957

6058
public boolean supportsSourceType(Class<?> sourceType) {
@@ -65,36 +63,4 @@ public int getOrder() {
6563
return (this.delegate instanceof Ordered ? ((Ordered) this.delegate).getOrder() : Ordered.LOWEST_PRECEDENCE);
6664
}
6765

68-
69-
@SuppressWarnings("unchecked")
70-
private Class<? extends ApplicationEvent> getGenericEventType(Class<? extends ApplicationListener> currentClass) {
71-
Class classToIntrospect = currentClass;
72-
while (classToIntrospect != null) {
73-
Type[] ifcs = classToIntrospect.getGenericInterfaces();
74-
for (Type ifc : ifcs) {
75-
if (ifc instanceof ParameterizedType) {
76-
ParameterizedType paramIfc = (ParameterizedType) ifc;
77-
Type rawType = paramIfc.getRawType();
78-
if (ApplicationListener.class.equals(rawType)) {
79-
Type arg = paramIfc.getActualTypeArguments()[0];
80-
if (arg instanceof TypeVariable) {
81-
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, this.delegate.getClass());
82-
}
83-
if (arg instanceof Class) {
84-
return (Class) arg;
85-
}
86-
}
87-
else if (ApplicationListener.class.isAssignableFrom((Class) rawType)) {
88-
return getGenericEventType((Class) rawType);
89-
}
90-
}
91-
else if (ApplicationListener.class.isAssignableFrom((Class) ifc)) {
92-
return getGenericEventType((Class) ifc);
93-
}
94-
}
95-
classToIntrospect = classToIntrospect.getSuperclass();
96-
}
97-
return ApplicationEvent.class;
98-
}
99-
10066
}

org.springframework.context/src/main/java/org/springframework/ui/format/support/GenericFormatterRegistry.java

Lines changed: 14 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
package org.springframework.ui.format.support;
1818

1919
import java.lang.annotation.Annotation;
20-
import java.lang.reflect.ParameterizedType;
21-
import java.lang.reflect.Type;
22-
import java.lang.reflect.TypeVariable;
2320
import java.text.ParseException;
2421
import java.util.LinkedList;
2522
import java.util.Locale;
@@ -182,18 +179,20 @@ public GenericFormatterRegistry clone() {
182179
// implementing FormatterRegistry
183180

184181
public void addFormatterByType(Class<?> type, Formatter<?> formatter) {
185-
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
182+
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
186183
if (!this.conversionService.canConvert(formattedObjectType, type)) {
187-
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse");
184+
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" +
185+
type.getName() + "]; not able to convert from [" + formattedObjectType.getName() + "] to parse");
188186
}
189187
if (!this.conversionService.canConvert(type, formattedObjectType)) {
190-
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" + type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format");
188+
throw new IllegalArgumentException("Unable to register Formatter " + formatter + " for type [" +
189+
type.getName() + "]; not able to convert to [" + formattedObjectType.getName() + "] to format");
191190
}
192191
this.typeFormatters.put(type, formatter);
193192
}
194193

195194
public <T> void addFormatterByType(Formatter<T> formatter) {
196-
Class formattedObjectType = getFormattedObjectType(formatter.getClass());
195+
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
197196
this.typeFormatters.put(formattedObjectType, formatter);
198197
}
199198

@@ -202,7 +201,13 @@ public void addFormatterByAnnotation(Class<? extends Annotation> annotationType,
202201
}
203202

204203
public <A extends Annotation, T> void addFormatterByAnnotation(AnnotationFormatterFactory<A, T> factory) {
205-
this.annotationFormatters.put(getAnnotationType(factory.getClass()), factory);
204+
Class[] typeArgs = GenericTypeResolver.resolveTypeArguments(factory.getClass(), AnnotationFormatterFactory.class);
205+
if (typeArgs == null) {
206+
throw new IllegalArgumentException(
207+
"Unable to extract Annotation type A argument from AnnotationFormatterFactory [" +
208+
factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?");
209+
}
210+
this.annotationFormatters.put(typeArgs[0], factory);
206211
}
207212

208213
@SuppressWarnings("unchecked")
@@ -218,7 +223,7 @@ public Formatter<Object> getFormatter(TypeDescriptor type) {
218223
formatter = getTypeFormatter(type.getType());
219224
}
220225
if (formatter != null) {
221-
Class<?> formattedObjectType = getFormattedObjectType(formatter.getClass());
226+
Class<?> formattedObjectType = GenericTypeResolver.resolveTypeArgument(formatter.getClass(), Formatter.class);
222227
if (!type.getType().isAssignableFrom(formattedObjectType)) {
223228
return new ConvertingFormatter(type.getType(), formattedObjectType, formatter);
224229
}
@@ -229,66 +234,6 @@ public Formatter<Object> getFormatter(TypeDescriptor type) {
229234

230235
// internal helpers
231236

232-
private Class getFormattedObjectType(Class formatterClass) {
233-
Class classToIntrospect = formatterClass;
234-
while (classToIntrospect != null) {
235-
Type[] ifcs = classToIntrospect.getGenericInterfaces();
236-
for (Type ifc : ifcs) {
237-
if (ifc instanceof ParameterizedType) {
238-
ParameterizedType paramIfc = (ParameterizedType) ifc;
239-
Type rawType = paramIfc.getRawType();
240-
if (Formatter.class.equals(rawType)) {
241-
Type arg = paramIfc.getActualTypeArguments()[0];
242-
if (arg instanceof TypeVariable) {
243-
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, formatterClass);
244-
}
245-
if (arg instanceof Class) {
246-
return (Class) arg;
247-
}
248-
}
249-
else if (Formatter.class.isAssignableFrom((Class) rawType)) {
250-
return getFormattedObjectType((Class) rawType);
251-
}
252-
}
253-
else if (Formatter.class.isAssignableFrom((Class) ifc)) {
254-
return getFormattedObjectType((Class) ifc);
255-
}
256-
}
257-
classToIntrospect = classToIntrospect.getSuperclass();
258-
}
259-
return null;
260-
}
261-
262-
private Class getAnnotationType(Class factoryClass) {
263-
Class classToIntrospect = factoryClass;
264-
while (classToIntrospect != null) {
265-
Type[] ifcs = classToIntrospect.getGenericInterfaces();
266-
for (Type ifc : ifcs) {
267-
if (ifc instanceof ParameterizedType) {
268-
ParameterizedType paramIfc = (ParameterizedType) ifc;
269-
Type rawType = paramIfc.getRawType();
270-
if (AnnotationFormatterFactory.class.equals(rawType)) {
271-
Type arg = paramIfc.getActualTypeArguments()[0];
272-
if (arg instanceof TypeVariable) {
273-
arg = GenericTypeResolver.resolveTypeVariable((TypeVariable) arg, factoryClass);
274-
}
275-
if (arg instanceof Class) {
276-
return (Class) arg;
277-
}
278-
} else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) rawType)) {
279-
return getAnnotationType((Class) rawType);
280-
}
281-
} else if (AnnotationFormatterFactory.class.isAssignableFrom((Class) ifc)) {
282-
return getAnnotationType((Class) ifc);
283-
}
284-
}
285-
classToIntrospect = classToIntrospect.getSuperclass();
286-
}
287-
throw new IllegalArgumentException(
288-
"Unable to extract Annotation type A argument from AnnotationFormatterFactory [" +
289-
factoryClass.getName() + "]; does the factory parameterize the <A> generic type?");
290-
}
291-
292237
@SuppressWarnings("unchecked")
293238
private Formatter getAnnotationFormatter(TypeDescriptor type) {
294239
Annotation[] annotations = type.getAnnotations();

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

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static Type getTargetType(MethodParameter methodParam) {
7575
* @param clazz the class to resolve type variables against
7676
* @return the corresponding generic parameter or return type
7777
*/
78-
public static Class resolveParameterType(MethodParameter methodParam, Class clazz) {
78+
public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz) {
7979
Type genericType = getTargetType(methodParam);
8080
Assert.notNull(clazz, "Class must not be null");
8181
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
@@ -92,7 +92,7 @@ public static Class resolveParameterType(MethodParameter methodParam, Class claz
9292
* @param clazz the class to resolve type variables against
9393
* @return the corresponding generic parameter or return type
9494
*/
95-
public static Class resolveReturnType(Method method, Class clazz) {
95+
public static Class<?> resolveReturnType(Method method, Class clazz) {
9696
Assert.notNull(method, "Method must not be null");
9797
Type genericType = method.getGenericReturnType();
9898
Assert.notNull(clazz, "Class must not be null");
@@ -102,15 +102,68 @@ public static Class resolveReturnType(Method method, Class clazz) {
102102
}
103103

104104
/**
105-
* Resolve the given type variable against the given class.
106-
* @param tv the type variable to resolve
107-
* @param clazz the class that defines the type variable
108-
* somewhere in its inheritance hierarchy
109-
* @return the resolved type that the variable can get replaced with,
110-
* or <code>null</code> if none found
105+
* Resolve the single type argument of the given generic interface against
106+
* the given target class which is assumed to implement the generic interface
107+
* and possibly declare a concrete type for its type variable.
108+
* @param clazz the target class to check against
109+
* @param genericIfc the generic interface to resolve the type argument from
110+
* @return the resolved type of the argument, or <code>null</code> if not resolvable
111111
*/
112-
public static Type resolveTypeVariable(TypeVariable tv, Class clazz) {
113-
return getTypeVariableMap(clazz).get(tv);
112+
public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc) {
113+
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
114+
if (typeArgs == null) {
115+
return null;
116+
}
117+
if (typeArgs.length != 1) {
118+
throw new IllegalArgumentException("Expected 1 type argument on generic interface [" +
119+
genericIfc.getName() + "] but found " + typeArgs.length);
120+
}
121+
return typeArgs[0];
122+
}
123+
124+
/**
125+
* Resolve the type arguments of the given generic interface against the given
126+
* target class which is assumed to implement the generic interface and possibly
127+
* declare concrete types for its type variables.
128+
* @param clazz the target class to check against
129+
* @param genericIfc the generic interface to resolve the type argument from
130+
* @return the resolved type of each argument, with the array size matching the
131+
* number of actual type arguments, or <code>null</code> if not resolvable
132+
*/
133+
public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) {
134+
return doResolveTypeArguments(clazz, clazz, genericIfc);
135+
}
136+
137+
private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) {
138+
while (classToIntrospect != null) {
139+
Type[] ifcs = classToIntrospect.getGenericInterfaces();
140+
for (Type ifc : ifcs) {
141+
if (ifc instanceof ParameterizedType) {
142+
ParameterizedType paramIfc = (ParameterizedType) ifc;
143+
Type rawType = paramIfc.getRawType();
144+
if (genericIfc.equals(rawType)) {
145+
Type[] typeArgs = paramIfc.getActualTypeArguments();
146+
Class[] result = new Class[typeArgs.length];
147+
for (int i = 0; i < typeArgs.length; i++) {
148+
Type arg = typeArgs[i];
149+
if (arg instanceof TypeVariable) {
150+
arg = getTypeVariableMap(ownerClass).get((TypeVariable) arg);
151+
}
152+
result[i] = (arg instanceof Class ? (Class) arg : Object.class);
153+
}
154+
return result;
155+
}
156+
else if (genericIfc.isAssignableFrom((Class) rawType)) {
157+
return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
158+
}
159+
}
160+
else if (genericIfc.isAssignableFrom((Class) ifc)) {
161+
return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
162+
}
163+
}
164+
classToIntrospect = classToIntrospect.getSuperclass();
165+
}
166+
return null;
114167
}
115168

116169

0 commit comments

Comments
 (0)