1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.utils.introspection;
20
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23 import java.util.Hashtable;
24 import java.util.Map;
25
26
27
28
29
30
31
32
33
34
35
36
37 public class ClassMap {
38 private static final class CacheMiss {}
39
40 private static final CacheMiss CACHE_MISS = new CacheMiss();
41
42 private static final Object OBJECT = new Object();
43
44
45
46
47
48 private final Class<?> clazz;
49
50
51
52
53
54 private final Map<String, Object> methodCache = new Hashtable<String, Object>();
55
56 private MethodMap methodMap = new MethodMap();
57
58
59
60
61
62 public ClassMap(Class<?> clazz) {
63 this.clazz = clazz;
64 populateMethodCache();
65 }
66
67
68
69
70 Class<?> getCachedClass() {
71 return clazz;
72 }
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 public Method findMethod(String name, Object... params) throws MethodMap.AmbiguousException {
88 String methodKey = makeMethodKey(name, params);
89 Object cacheEntry = methodCache.get(methodKey);
90
91 if (cacheEntry == CACHE_MISS) {
92 return null;
93 }
94
95 if (cacheEntry == null) {
96 try {
97 cacheEntry = methodMap.find(name, params);
98 } catch (MethodMap.AmbiguousException ae) {
99
100
101
102
103 methodCache.put(methodKey, CACHE_MISS);
104
105 throw ae;
106 }
107
108 if (cacheEntry == null) {
109 methodCache.put(methodKey, CACHE_MISS);
110 } else {
111 methodCache.put(methodKey, cacheEntry);
112 }
113 }
114
115
116
117 return (Method) cacheEntry;
118 }
119
120
121
122
123
124
125 private void populateMethodCache() {
126
127
128
129
130
131 Method[] methods = getAccessibleMethods(clazz);
132
133
134
135
136
137 for (Method method : methods) {
138
139
140
141
142
143
144 Method publicMethod = getPublicMethod(method);
145
146
147
148
149
150
151
152
153 if (publicMethod != null) {
154 methodMap.add(publicMethod);
155 methodCache.put(makeMethodKey(publicMethod), publicMethod);
156 }
157 }
158 }
159
160
161
162
163
164
165 private String makeMethodKey(Method method) {
166 Class<?>[] parameterTypes = method.getParameterTypes();
167
168 StringBuilder methodKey = new StringBuilder(method.getName());
169
170 for (Class<?> parameterType : parameterTypes) {
171
172
173
174
175
176
177 if (parameterType.isPrimitive()) {
178 if (parameterType.equals(Boolean.TYPE)) {
179 methodKey.append("java.lang.Boolean");
180 } else if (parameterType.equals(Byte.TYPE)) {
181 methodKey.append("java.lang.Byte");
182 } else if (parameterType.equals(Character.TYPE)) {
183 methodKey.append("java.lang.Character");
184 } else if (parameterType.equals(Double.TYPE)) {
185 methodKey.append("java.lang.Double");
186 } else if (parameterType.equals(Float.TYPE)) {
187 methodKey.append("java.lang.Float");
188 } else if (parameterType.equals(Integer.TYPE)) {
189 methodKey.append("java.lang.Integer");
190 } else if (parameterType.equals(Long.TYPE)) {
191 methodKey.append("java.lang.Long");
192 } else if (parameterType.equals(Short.TYPE)) {
193 methodKey.append("java.lang.Short");
194 }
195 } else {
196 methodKey.append(parameterType.getName());
197 }
198 }
199
200 return methodKey.toString();
201 }
202
203 private static String makeMethodKey(String method, Object... params) {
204 StringBuilder methodKey = new StringBuilder().append(method);
205
206 for (Object param : params) {
207 Object arg = param;
208
209 if (arg == null) {
210 arg = OBJECT;
211 }
212
213 methodKey.append(arg.getClass().getName());
214 }
215
216 return methodKey.toString();
217 }
218
219
220
221
222
223
224
225 private static Method[] getAccessibleMethods(Class<?> clazz) {
226 Method[] methods = clazz.getMethods();
227
228
229
230
231
232
233 if (Modifier.isPublic(clazz.getModifiers())) {
234 return methods;
235 }
236
237
238
239
240
241 MethodInfo[] methodInfos = new MethodInfo[methods.length];
242
243 for (int i = methods.length; i-- > 0; ) {
244 methodInfos[i] = new MethodInfo(methods[i]);
245 }
246
247 int upcastCount = getAccessibleMethods(clazz, methodInfos, 0);
248
249
250
251
252
253 if (upcastCount < methods.length) {
254 methods = new Method[upcastCount];
255 }
256
257 int j = 0;
258 for (MethodInfo methodInfo : methodInfos) {
259 if (methodInfo.upcast) {
260 methods[j++] = methodInfo.method;
261 }
262 }
263 return methods;
264 }
265
266
267
268
269
270
271
272
273
274
275 private static int getAccessibleMethods(Class<?> clazz, MethodInfo[] methodInfos, int upcastCount) {
276 int l = methodInfos.length;
277
278
279
280
281
282
283 if (Modifier.isPublic(clazz.getModifiers())) {
284 for (int i = 0; i < l && upcastCount < l; ++i) {
285 try {
286 MethodInfo methodInfo = methodInfos[i];
287
288 if (!methodInfo.upcast) {
289 methodInfo.tryUpcasting(clazz);
290 upcastCount++;
291 }
292 } catch (NoSuchMethodException e) {
293
294
295
296
297 }
298 }
299
300
301
302
303
304 if (upcastCount == l) {
305 return upcastCount;
306 }
307 }
308
309
310
311
312
313 Class<?> superclazz = clazz.getSuperclass();
314
315 if (superclazz != null) {
316 upcastCount = getAccessibleMethods(superclazz, methodInfos, upcastCount);
317
318
319
320
321
322 if (upcastCount == l) {
323 return upcastCount;
324 }
325 }
326
327
328
329
330
331
332
333 Class<?>[] interfaces = clazz.getInterfaces();
334
335 for (int i = interfaces.length; i-- > 0; ) {
336 upcastCount = getAccessibleMethods(interfaces[i], methodInfos, upcastCount);
337
338
339
340
341
342 if (upcastCount == l) {
343 return upcastCount;
344 }
345 }
346
347 return upcastCount;
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361 private static Method getPublicMethod(Method method) {
362 Class<?> clazz = method.getDeclaringClass();
363
364
365
366
367
368
369 if ((clazz.getModifiers() & Modifier.PUBLIC) != 0) {
370 return method;
371 }
372
373 return getPublicMethod(clazz, method.getName(), method.getParameterTypes());
374 }
375
376
377
378
379
380
381
382
383
384 private static Method getPublicMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
385
386
387
388
389 if ((clazz.getModifiers() & Modifier.PUBLIC) != 0) {
390 try {
391 return clazz.getMethod(name, paramTypes);
392 } catch (NoSuchMethodException e) {
393
394
395
396
397
398 return null;
399 }
400 }
401
402
403
404
405
406 Class<?> superclazz = clazz.getSuperclass();
407
408 if (superclazz != null) {
409 Method superclazzMethod = getPublicMethod(superclazz, name, paramTypes);
410
411 if (superclazzMethod != null) {
412 return superclazzMethod;
413 }
414 }
415
416
417
418
419
420 Class<?>[] interfaces = clazz.getInterfaces();
421
422 for (Class<?> anInterface : interfaces) {
423 Method interfaceMethod = getPublicMethod(anInterface, name, paramTypes);
424
425 if (interfaceMethod != null) {
426 return interfaceMethod;
427 }
428 }
429
430 return null;
431 }
432
433
434
435
436 private static final class MethodInfo {
437 Method method;
438
439 String name;
440
441 Class<?>[] parameterTypes;
442
443 boolean upcast;
444
445 MethodInfo(Method method) {
446 this.method = null;
447 name = method.getName();
448 parameterTypes = method.getParameterTypes();
449 upcast = false;
450 }
451
452 void tryUpcasting(Class<?> clazz) throws NoSuchMethodException {
453 method = clazz.getMethod(name, parameterTypes);
454 name = null;
455 parameterTypes = null;
456 upcast = true;
457 }
458 }
459 }