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