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.util.ArrayList;
24 import java.util.Hashtable;
25 import java.util.Iterator;
26 import java.util.LinkedList;
27 import java.util.List;
28 import java.util.Map;
29
30
31
32
33
34
35
36
37
38 class MethodMap
39 {
40 private static final int MORE_SPECIFIC = 0;
41
42 private static final int LESS_SPECIFIC = 1;
43
44 private static final int INCOMPARABLE = 2;
45
46
47
48
49 private final Map<String, List<Method>> methodByNameMap = new Hashtable<String, List<Method>>();
50
51
52
53
54
55
56
57
58 void add( Method method )
59 {
60 String methodName = method.getName();
61
62 List<Method> l = get( methodName );
63
64 if ( l == null )
65 {
66 l = new ArrayList<Method>();
67 methodByNameMap.put( methodName, l );
68 }
69
70 l.add( method );
71 }
72
73
74
75
76
77
78
79 List<Method> get( String key )
80 {
81 return methodByNameMap.get( key );
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112 Method find( String methodName, Object... args )
113 throws AmbiguousException
114 {
115 List<Method> methodList = get( methodName );
116
117 if ( methodList == null )
118 {
119 return null;
120 }
121
122 int l = args.length;
123 Class<?>[] classes = new Class[l];
124
125 for ( int i = 0; i < l; ++i )
126 {
127 Object arg = args[i];
128
129
130
131
132
133 classes[i] = arg == null ? null : arg.getClass();
134 }
135
136 return getMostSpecific( methodList, classes );
137 }
138
139
140
141
142
143 static class AmbiguousException
144 extends Exception
145 {
146
147 private static final long serialVersionUID = 751688436639650618L;
148 }
149
150
151 private static Method getMostSpecific( List<Method> methods, Class<?>... classes )
152 throws AmbiguousException
153 {
154 LinkedList<Method> applicables = getApplicables( methods, classes );
155
156 if ( applicables.isEmpty() )
157 {
158 return null;
159 }
160
161 if ( applicables.size() == 1 )
162 {
163 return applicables.getFirst();
164 }
165
166
167
168
169
170
171
172 LinkedList<Method> maximals = new LinkedList<Method>();
173
174 for ( Method app : applicables )
175 {
176 Class<?>[] appArgs = app.getParameterTypes();
177 boolean lessSpecific = false;
178
179 for ( Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); )
180 {
181 Method max = maximal.next();
182
183 switch ( moreSpecific( appArgs, max.getParameterTypes() ) )
184 {
185 case MORE_SPECIFIC:
186
187
188
189
190
191 maximal.remove();
192 break;
193
194 case LESS_SPECIFIC:
195
196
197
198
199
200
201
202 lessSpecific = true;
203 break;
204
205 default:
206 }
207 }
208
209 if ( !lessSpecific )
210 {
211 maximals.addLast( app );
212 }
213 }
214
215 if ( maximals.size() > 1 )
216 {
217
218 throw new AmbiguousException();
219 }
220
221 return maximals.getFirst();
222 }
223
224
225
226
227
228
229
230
231
232
233 private static int moreSpecific( Class<?>[] c1, Class<?>[] c2 )
234 {
235 boolean c1MoreSpecific = false;
236 boolean c2MoreSpecific = false;
237
238 for ( int i = 0; i < c1.length; ++i )
239 {
240 if ( c1[i] != c2[i] )
241 {
242 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] );
243 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] );
244 }
245 }
246
247 if ( c1MoreSpecific )
248 {
249 if ( c2MoreSpecific )
250 {
251
252
253
254
255
256 return INCOMPARABLE;
257 }
258
259 return MORE_SPECIFIC;
260 }
261
262 if ( c2MoreSpecific )
263 {
264 return LESS_SPECIFIC;
265 }
266
267
268
269
270
271
272 return INCOMPARABLE;
273 }
274
275
276
277
278
279
280
281
282
283
284 private static LinkedList<Method> getApplicables( List<Method> methods, Class<?>... classes )
285 {
286 LinkedList<Method> list = new LinkedList<Method>();
287
288 for ( Method method : methods )
289 {
290 if ( isApplicable( method, classes ) )
291 {
292 list.add( method );
293 }
294 }
295 return list;
296 }
297
298
299
300
301
302
303
304
305
306 private static boolean isApplicable( Method method, Class<?>... classes )
307 {
308 Class<?>[] methodArgs = method.getParameterTypes();
309
310 if ( methodArgs.length != classes.length )
311 {
312 return false;
313 }
314
315 for ( int i = 0; i < classes.length; ++i )
316 {
317 if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) )
318 {
319 return false;
320 }
321 }
322
323 return true;
324 }
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 private static boolean isMethodInvocationConvertible( Class<?> formal, Class<?> actual )
345 {
346
347
348
349 if ( actual == null && !formal.isPrimitive() )
350 {
351 return true;
352 }
353
354
355
356
357
358 if ( actual != null && formal.isAssignableFrom( actual ) )
359 {
360 return true;
361 }
362
363
364
365
366
367
368 if ( formal.isPrimitive() )
369 {
370 if ( formal == Boolean.TYPE && actual == Boolean.class )
371 {
372 return true;
373 }
374 if ( formal == Character.TYPE && actual == Character.class )
375 {
376 return true;
377 }
378 if ( formal == Byte.TYPE && actual == Byte.class )
379 {
380 return true;
381 }
382 if ( formal == Short.TYPE && ( actual == Short.class || actual == Byte.class ) )
383 {
384 return true;
385 }
386 if ( formal == Integer.TYPE
387 && ( actual == Integer.class || actual == Short.class || actual == Byte.class ) )
388 {
389 return true;
390 }
391 if ( formal == Long.TYPE
392 && ( actual == Long.class || actual == Integer.class || actual == Short.class
393 || actual == Byte.class ) )
394 {
395 return true;
396 }
397 if ( formal == Float.TYPE
398 && ( actual == Float.class || actual == Long.class || actual == Integer.class
399 || actual == Short.class || actual == Byte.class ) )
400 {
401 return true;
402 }
403 if ( formal == Double.TYPE
404 && ( actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class
405 || actual == Short.class || actual == Byte.class ) )
406 {
407 return true;
408 }
409 }
410
411 return false;
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428 private static boolean isStrictMethodInvocationConvertible( Class<?> formal, Class<?> actual )
429 {
430
431
432
433 if ( actual == null && !formal.isPrimitive() )
434 {
435 return true;
436 }
437
438
439
440
441
442 if ( formal.isAssignableFrom( actual ) )
443 {
444 return true;
445 }
446
447
448
449
450
451 if ( formal.isPrimitive() )
452 {
453 if ( formal == Short.TYPE && ( actual == Byte.TYPE ) )
454 {
455 return true;
456 }
457 if ( formal == Integer.TYPE && ( actual == Short.TYPE || actual == Byte.TYPE ) )
458 {
459 return true;
460 }
461 if ( formal == Long.TYPE && ( actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
462 {
463 return true;
464 }
465 if ( formal == Float.TYPE
466 && ( actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
467 {
468 return true;
469 }
470 if ( formal == Double.TYPE
471 && ( actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE
472 || actual == Byte.TYPE ) )
473 {
474 return true;
475 }
476 }
477 return false;
478 }
479 }