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