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