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