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.HashMap;
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 HashMap();
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 getBestMatch(methodList, classes);
136 }
137
138 private static Method getBestMatch(List methods, Class[] args)
139 {
140 List equivalentMatches = null;
141 Method bestMatch = null;
142 Class[] bestMatchTypes = null;
143 for (Iterator i = methods.iterator(); i.hasNext(); )
144 {
145 Method method = (Method)i.next();
146 if (isApplicable(method, args))
147 {
148 if (bestMatch == null)
149 {
150 bestMatch = method;
151 bestMatchTypes = method.getParameterTypes();
152 }
153 else
154 {
155 Class[] methodTypes = method.getParameterTypes();
156 switch (compare(methodTypes, bestMatchTypes))
157 {
158 case MORE_SPECIFIC:
159 if (equivalentMatches == null)
160 {
161 bestMatch = method;
162 bestMatchTypes = methodTypes;
163 }
164 else
165 {
166
167 int ambiguities = equivalentMatches.size();
168 for (int a=0; a < ambiguities; a++)
169 {
170 Method other = (Method)equivalentMatches.get(a);
171 switch (compare(methodTypes, other.getParameterTypes()))
172 {
173 case MORE_SPECIFIC:
174
175 bestMatch = method;
176 bestMatchTypes = methodTypes;
177 equivalentMatches = null;
178 ambiguities = 0;
179 break;
180
181 case INCOMPARABLE:
182
183 equivalentMatches.add(method);
184 break;
185
186 case LESS_SPECIFIC:
187
188 break;
189 }
190 }
191 }
192 break;
193
194 case INCOMPARABLE:
195 if (equivalentMatches == null)
196 {
197 equivalentMatches = new ArrayList(bestMatchTypes.length);
198 }
199 equivalentMatches.add(method);
200 break;
201
202 case LESS_SPECIFIC:
203
204 break;
205 }
206 }
207 }
208 }
209
210 if (equivalentMatches != null)
211 {
212 throw new AmbiguousException();
213 }
214 return bestMatch;
215 }
216
217
218
219
220
221
222 public static class AmbiguousException extends RuntimeException
223 {
224
225
226
227 private static final long serialVersionUID = -2314636505414551663L;
228 }
229
230
231
232
233
234
235
236
237
238 private static int compare(Class[] c1, Class[] c2)
239 {
240 boolean c1MoreSpecific = false;
241 boolean c2MoreSpecific = false;
242
243
244
245
246 if (c1.length > c2.length)
247 {
248 return MORE_SPECIFIC;
249 }
250 if (c2.length > c1.length)
251 {
252 return LESS_SPECIFIC;
253 }
254
255
256 for(int i = 0; i < c1.length; ++i)
257 {
258 if(c1[i] != c2[i])
259 {
260 boolean last = (i == c1.length - 1);
261 c1MoreSpecific =
262 c1MoreSpecific ||
263 isStrictConvertible(c2[i], c1[i], last);
264 c2MoreSpecific =
265 c2MoreSpecific ||
266 isStrictConvertible(c1[i], c2[i], last);
267 }
268 }
269
270 if(c1MoreSpecific)
271 {
272 if(c2MoreSpecific)
273 {
274
275
276
277
278 boolean last1Array = c1[c1.length - 1].isArray();
279 boolean last2Array = c2[c2.length - 1].isArray();
280 if (last1Array && !last2Array)
281 {
282 return LESS_SPECIFIC;
283 }
284 if (!last1Array && last2Array)
285 {
286 return MORE_SPECIFIC;
287 }
288
289
290
291
292
293 return INCOMPARABLE;
294 }
295
296 return MORE_SPECIFIC;
297 }
298
299 if(c2MoreSpecific)
300 {
301 return LESS_SPECIFIC;
302 }
303
304
305
306
307
308
309 return INCOMPARABLE;
310 }
311
312
313
314
315
316
317
318
319
320 private static boolean isApplicable(Method method, Class[] classes)
321 {
322 Class[] methodArgs = method.getParameterTypes();
323
324 if (methodArgs.length > classes.length)
325 {
326
327
328 if (methodArgs.length == classes.length + 1 &&
329 methodArgs[methodArgs.length - 1].isArray())
330 {
331
332 for (int i = 0; i < classes.length; i++)
333 {
334 if (!isConvertible(methodArgs[i], classes[i], false))
335 {
336 return false;
337 }
338 }
339 return true;
340 }
341 else
342 {
343 return false;
344 }
345 }
346 else if (methodArgs.length == classes.length)
347 {
348
349
350
351 for(int i = 0; i < classes.length; ++i)
352 {
353 if(!isConvertible(methodArgs[i], classes[i], false))
354 {
355
356 if (i == classes.length - 1 && methodArgs[i].isArray())
357 {
358
359
360 return isConvertible(methodArgs[i], classes[i], true);
361 }
362 return false;
363 }
364 }
365 }
366 else if (methodArgs.length > 0)
367 {
368
369 Class lastarg = methodArgs[methodArgs.length - 1];
370 if (!lastarg.isArray())
371 {
372 return false;
373 }
374
375
376 for (int i = 0; i < methodArgs.length - 1; ++i)
377 {
378 if (!isConvertible(methodArgs[i], classes[i], false))
379 {
380 return false;
381 }
382 }
383
384
385 Class vararg = lastarg.getComponentType();
386 for (int i = methodArgs.length - 1; i < classes.length; ++i)
387 {
388 if (!isConvertible(vararg, classes[i], false))
389 {
390 return false;
391 }
392 }
393 }
394
395 return true;
396 }
397
398 private static boolean isConvertible(Class formal, Class actual,
399 boolean possibleVarArg)
400 {
401 return IntrospectionUtils.
402 isMethodInvocationConvertible(formal, actual, possibleVarArg);
403 }
404
405 private static boolean isStrictConvertible(Class formal, Class actual,
406 boolean possibleVarArg)
407 {
408 return IntrospectionUtils.
409 isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
410 }
411 }