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