1
2
3
4
5
6
7 package org.mule.util.generics;
8
9 import java.lang.ref.Reference;
10 import java.lang.ref.WeakReference;
11 import java.lang.reflect.GenericArrayType;
12 import java.lang.reflect.Method;
13 import java.lang.reflect.ParameterizedType;
14 import java.lang.reflect.Type;
15 import java.lang.reflect.TypeVariable;
16 import java.util.Collections;
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.WeakHashMap;
20
21
22
23
24
25
26
27
28
29 public abstract class GenericTypeResolver
30 {
31
32
33
34
35 private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache =
36 Collections.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
37
38
39 private GenericTypeResolver()
40 {
41
42 }
43
44
45
46
47
48
49
50 public static Type getTargetType(MethodParameter methodParam)
51 {
52 if (methodParam.getConstructor() != null)
53 {
54 return methodParam.getConstructor().getGenericParameterTypes()[methodParam.getParameterIndex()];
55 }
56 else
57 {
58 if (methodParam.getParameterIndex() >= 0)
59 {
60 return methodParam.getMethod().getGenericParameterTypes()[methodParam.getParameterIndex()];
61 }
62 else
63 {
64 return methodParam.getMethod().getGenericReturnType();
65 }
66 }
67 }
68
69
70
71
72
73
74
75
76 public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz)
77 {
78 Type genericType = getTargetType(methodParam);
79 Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
80 Type rawType = getRawType(genericType, typeVariableMap);
81 Class result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType());
82 methodParam.setParameterType(result);
83 methodParam.typeVariableMap = typeVariableMap;
84 return result;
85 }
86
87
88
89
90
91
92
93
94 public static Class<?> resolveReturnType(Method method, Class clazz)
95 {
96 Type genericType = method.getGenericReturnType();
97 Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
98 Type rawType = getRawType(genericType, typeVariableMap);
99 return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
100 }
101
102
103
104
105
106
107
108
109
110
111 public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc)
112 {
113 Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
114 if (typeArgs == null)
115 {
116 return null;
117 }
118 if (typeArgs.length != 1)
119 {
120 throw new IllegalArgumentException("Expected 1 type argument on generic interface [" +
121 genericIfc.getName() + "] but found " + typeArgs.length);
122 }
123 return typeArgs[0];
124 }
125
126
127
128
129
130
131
132
133
134
135
136 public static Class[] resolveTypeArguments(Class clazz, Class genericIfc)
137 {
138 return doResolveTypeArguments(clazz, clazz, genericIfc);
139 }
140
141 private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc)
142 {
143 while (classToIntrospect != null)
144 {
145 Type[] ifcs = classToIntrospect.getGenericInterfaces();
146 for (Type ifc : ifcs)
147 {
148 if (ifc instanceof ParameterizedType)
149 {
150 ParameterizedType paramIfc = (ParameterizedType) ifc;
151 Type rawType = paramIfc.getRawType();
152 if (genericIfc.equals(rawType))
153 {
154 Type[] typeArgs = paramIfc.getActualTypeArguments();
155 Class[] result = new Class[typeArgs.length];
156 for (int i = 0; i < typeArgs.length; i++)
157 {
158 Type arg = typeArgs[i];
159 if (arg instanceof TypeVariable)
160 {
161 arg = getTypeVariableMap(ownerClass).get((TypeVariable) arg);
162 }
163 result[i] = (arg instanceof Class ? (Class) arg : Object.class);
164 }
165 return result;
166 }
167 else if (genericIfc.isAssignableFrom((Class) rawType))
168 {
169 return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
170 }
171 }
172 else if (genericIfc.isAssignableFrom((Class) ifc))
173 {
174 return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
175 }
176 }
177 classToIntrospect = classToIntrospect.getSuperclass();
178 }
179 return null;
180 }
181
182
183
184
185
186
187
188
189
190 static Class resolveType(Type genericType, Map<TypeVariable, Type> typeVariableMap)
191 {
192 Type rawType = getRawType(genericType, typeVariableMap);
193 return (rawType instanceof Class ? (Class) rawType : Object.class);
194 }
195
196
197
198
199
200
201
202
203 static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap)
204 {
205 Type resolvedType = genericType;
206 if (genericType instanceof TypeVariable)
207 {
208 TypeVariable tv = (TypeVariable) genericType;
209 resolvedType = typeVariableMap.get(tv);
210 if (resolvedType == null)
211 {
212 resolvedType = extractBoundForTypeVariable(tv);
213 }
214 }
215 if (resolvedType instanceof ParameterizedType)
216 {
217 return ((ParameterizedType) resolvedType).getRawType();
218 }
219 else
220 {
221 return resolvedType;
222 }
223 }
224
225
226
227
228
229
230 static Map<TypeVariable, Type> getTypeVariableMap(Class clazz)
231 {
232 Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
233 Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
234
235 if (typeVariableMap == null)
236 {
237 typeVariableMap = new HashMap<TypeVariable, Type>();
238
239
240 extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
241
242
243 Type genericType = clazz.getGenericSuperclass();
244 Class type = clazz.getSuperclass();
245 while (type != null && !Object.class.equals(type))
246 {
247 if (genericType instanceof ParameterizedType)
248 {
249 ParameterizedType pt = (ParameterizedType) genericType;
250 populateTypeMapFromParameterizedType(pt, typeVariableMap);
251 }
252 extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
253 genericType = type.getGenericSuperclass();
254 type = type.getSuperclass();
255 }
256
257
258 type = clazz;
259 while (type.isMemberClass())
260 {
261 genericType = type.getGenericSuperclass();
262 if (genericType instanceof ParameterizedType)
263 {
264 ParameterizedType pt = (ParameterizedType) genericType;
265 populateTypeMapFromParameterizedType(pt, typeVariableMap);
266 }
267 type = type.getEnclosingClass();
268 }
269
270 typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(typeVariableMap));
271 }
272
273 return typeVariableMap;
274 }
275
276
277
278
279 static Type extractBoundForTypeVariable(TypeVariable typeVariable)
280 {
281 Type[] bounds = typeVariable.getBounds();
282 if (bounds.length == 0)
283 {
284 return Object.class;
285 }
286 Type bound = bounds[0];
287 if (bound instanceof TypeVariable)
288 {
289 bound = extractBoundForTypeVariable((TypeVariable) bound);
290 }
291 return bound;
292 }
293
294 private static void extractTypeVariablesFromGenericInterfaces(Type[] genericInterfaces, Map<TypeVariable, Type> typeVariableMap)
295 {
296 for (Type genericInterface : genericInterfaces)
297 {
298 if (genericInterface instanceof ParameterizedType)
299 {
300 ParameterizedType pt = (ParameterizedType) genericInterface;
301 populateTypeMapFromParameterizedType(pt, typeVariableMap);
302 if (pt.getRawType() instanceof Class)
303 {
304 extractTypeVariablesFromGenericInterfaces(
305 ((Class) pt.getRawType()).getGenericInterfaces(), typeVariableMap);
306 }
307 }
308 else if (genericInterface instanceof Class)
309 {
310 extractTypeVariablesFromGenericInterfaces(
311 ((Class) genericInterface).getGenericInterfaces(), typeVariableMap);
312 }
313 }
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 private static void populateTypeMapFromParameterizedType(ParameterizedType type, Map<TypeVariable, Type> typeVariableMap)
333 {
334 if (type.getRawType() instanceof Class)
335 {
336 Type[] actualTypeArguments = type.getActualTypeArguments();
337 TypeVariable[] typeVariables = ((Class) type.getRawType()).getTypeParameters();
338 for (int i = 0; i < actualTypeArguments.length; i++)
339 {
340 Type actualTypeArgument = actualTypeArguments[i];
341 TypeVariable variable = typeVariables[i];
342 if (actualTypeArgument instanceof Class)
343 {
344 typeVariableMap.put(variable, actualTypeArgument);
345 }
346 else if (actualTypeArgument instanceof GenericArrayType)
347 {
348 typeVariableMap.put(variable, actualTypeArgument);
349 }
350 else if (actualTypeArgument instanceof ParameterizedType)
351 {
352 typeVariableMap.put(variable, actualTypeArgument);
353 }
354 else if (actualTypeArgument instanceof TypeVariable)
355 {
356
357
358 TypeVariable typeVariableArgument = (TypeVariable) actualTypeArgument;
359 Type resolvedType = typeVariableMap.get(typeVariableArgument);
360 if (resolvedType == null) {
361 resolvedType = extractBoundForTypeVariable(typeVariableArgument);
362 }
363 typeVariableMap.put(variable, resolvedType);
364 }
365 }
366 }
367 }
368
369 }