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 return true;
332 }
333 else
334 {
335 return false;
336 }
337 }
338 else if (methodArgs.length == classes.length)
339 {
340
341
342
343 for(int i = 0; i < classes.length; ++i)
344 {
345 if(!isConvertible(methodArgs[i], classes[i], false))
346 {
347
348 if (i == classes.length - 1 && methodArgs[i].isArray())
349 {
350
351
352 return isConvertible(methodArgs[i], classes[i], true);
353 }
354 return false;
355 }
356 }
357 }
358 else if (methodArgs.length > 0)
359 {
360
361 Class lastarg = methodArgs[methodArgs.length - 1];
362 if (!lastarg.isArray())
363 {
364 return false;
365 }
366
367
368 for (int i = 0; i < methodArgs.length - 1; ++i)
369 {
370 if (!isConvertible(methodArgs[i], classes[i], false))
371 {
372 return false;
373 }
374 }
375
376
377 Class vararg = lastarg.getComponentType();
378 for (int i = methodArgs.length - 1; i < classes.length; ++i)
379 {
380 if (!isConvertible(vararg, classes[i], false))
381 {
382 return false;
383 }
384 }
385 }
386
387 return true;
388 }
389
390 private static boolean isConvertible(Class formal, Class actual,
391 boolean possibleVarArg)
392 {
393 return IntrospectionUtils.
394 isMethodInvocationConvertible(formal, actual, possibleVarArg);
395 }
396
397 private static boolean isStrictConvertible(Class formal, Class actual,
398 boolean possibleVarArg)
399 {
400 return IntrospectionUtils.
401 isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
402 }
403 }