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