1 | package org.apache.velocity.util.introspection; |
2 | |
3 | /* |
4 | * Copyright 2001,2004 The Apache Software Foundation. |
5 | * |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | */ |
18 | |
19 | import java.util.Iterator; |
20 | import java.util.List; |
21 | import java.util.ArrayList; |
22 | import java.util.LinkedList; |
23 | import java.util.Set; |
24 | import java.util.HashSet; |
25 | import java.util.Map; |
26 | import java.util.Hashtable; |
27 | |
28 | import java.lang.reflect.Method; |
29 | |
30 | /** |
31 | * |
32 | * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> |
33 | * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> |
34 | * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a> |
35 | * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
36 | * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a> |
37 | * @version $Id: MethodMap.java,v 1.15.4.1 2004/03/03 23:23:08 geirm Exp $ |
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 | * Keep track of all methods with the same name. |
47 | */ |
48 | Map methodByNameMap = new Hashtable(); |
49 | |
50 | /** |
51 | * Add a method to a list of methods by name. |
52 | * For a particular class we are keeping track |
53 | * of all the methods with the same name. |
54 | */ |
55 | public void add(Method method) |
56 | { |
57 | String methodName = method.getName(); |
58 | |
59 | List l = get( methodName ); |
60 | |
61 | if ( l == null) |
62 | { |
63 | l = new ArrayList(); |
64 | methodByNameMap.put(methodName, l); |
65 | } |
66 | |
67 | l.add(method); |
68 | |
69 | return; |
70 | } |
71 | |
72 | /** |
73 | * Return a list of methods with the same name. |
74 | * |
75 | * @param String key |
76 | * @return List list of methods |
77 | */ |
78 | public List get(String key) |
79 | { |
80 | return (List) methodByNameMap.get(key); |
81 | } |
82 | |
83 | /** |
84 | * <p> |
85 | * Find a method. Attempts to find the |
86 | * most specific applicable method using the |
87 | * algorithm described in the JLS section |
88 | * 15.12.2 (with the exception that it can't |
89 | * distinguish a primitive type argument from |
90 | * an object type argument, since in reflection |
91 | * primitive type arguments are represented by |
92 | * their object counterparts, so for an argument of |
93 | * type (say) java.lang.Integer, it will not be able |
94 | * to decide between a method that takes int and a |
95 | * method that takes java.lang.Integer as a parameter. |
96 | * </p> |
97 | * |
98 | * <p> |
99 | * This turns out to be a relatively rare case |
100 | * where this is needed - however, functionality |
101 | * like this is needed. |
102 | * </p> |
103 | * |
104 | * @param methodName name of method |
105 | * @param args the actual arguments with which the method is called |
106 | * @return the most specific applicable method, or null if no |
107 | * method is applicable. |
108 | * @throws AmbiguousException if there is more than one maximally |
109 | * specific applicable method |
110 | */ |
111 | public Method find(String methodName, Object[] args) |
112 | throws AmbiguousException |
113 | { |
114 | List methodList = get(methodName); |
115 | |
116 | if (methodList == null) |
117 | { |
118 | return null; |
119 | } |
120 | |
121 | int l = args.length; |
122 | Class[] classes = new Class[l]; |
123 | |
124 | for(int i = 0; i < l; ++i) |
125 | { |
126 | Object arg = args[i]; |
127 | |
128 | /* |
129 | * if we are careful down below, a null argument goes in there |
130 | * so we can know that the null was passed to the method |
131 | */ |
132 | classes[i] = |
133 | arg == null ? null : arg.getClass(); |
134 | } |
135 | |
136 | return getMostSpecific(methodList, classes); |
137 | } |
138 | |
139 | /** |
140 | * simple distinguishable exception, used when |
141 | * we run across ambiguous overloading |
142 | */ |
143 | public static class AmbiguousException extends Exception |
144 | { |
145 | } |
146 | |
147 | |
148 | private static Method getMostSpecific(List methods, Class[] classes) |
149 | throws AmbiguousException |
150 | { |
151 | LinkedList applicables = getApplicables(methods, classes); |
152 | |
153 | if(applicables.isEmpty()) |
154 | { |
155 | return null; |
156 | } |
157 | |
158 | if(applicables.size() == 1) |
159 | { |
160 | return (Method)applicables.getFirst(); |
161 | } |
162 | |
163 | /* |
164 | * This list will contain the maximally specific methods. Hopefully at |
165 | * the end of the below loop, the list will contain exactly one method, |
166 | * (the most specific method) otherwise we have ambiguity. |
167 | */ |
168 | |
169 | LinkedList maximals = new LinkedList(); |
170 | |
171 | for (Iterator applicable = applicables.iterator(); |
172 | applicable.hasNext();) |
173 | { |
174 | Method app = (Method) applicable.next(); |
175 | Class[] appArgs = app.getParameterTypes(); |
176 | boolean lessSpecific = false; |
177 | |
178 | for (Iterator maximal = maximals.iterator(); |
179 | !lessSpecific && maximal.hasNext();) |
180 | { |
181 | Method max = (Method) maximal.next(); |
182 | |
183 | switch(moreSpecific(appArgs, max.getParameterTypes())) |
184 | { |
185 | case MORE_SPECIFIC: |
186 | { |
187 | /* |
188 | * This method is more specific than the previously |
189 | * known maximally specific, so remove the old maximum. |
190 | */ |
191 | |
192 | maximal.remove(); |
193 | break; |
194 | } |
195 | |
196 | case LESS_SPECIFIC: |
197 | { |
198 | /* |
199 | * This method is less specific than some of the |
200 | * currently known maximally specific methods, so we |
201 | * won't add it into the set of maximally specific |
202 | * methods |
203 | */ |
204 | |
205 | lessSpecific = true; |
206 | break; |
207 | } |
208 | } |
209 | } |
210 | |
211 | if(!lessSpecific) |
212 | { |
213 | maximals.addLast(app); |
214 | } |
215 | } |
216 | |
217 | if(maximals.size() > 1) |
218 | { |
219 | // We have more than one maximally specific method |
220 | throw new AmbiguousException(); |
221 | } |
222 | |
223 | return (Method)maximals.getFirst(); |
224 | } |
225 | |
226 | /** |
227 | * Determines which method signature (represented by a class array) is more |
228 | * specific. This defines a partial ordering on the method signatures. |
229 | * @param c1 first signature to compare |
230 | * @param c2 second signature to compare |
231 | * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if |
232 | * c1 is less specific than c2, INCOMPARABLE if they are incomparable. |
233 | */ |
234 | private static int moreSpecific(Class[] c1, Class[] c2) |
235 | { |
236 | boolean c1MoreSpecific = false; |
237 | boolean c2MoreSpecific = false; |
238 | |
239 | for(int i = 0; i < c1.length; ++i) |
240 | { |
241 | if(c1[i] != c2[i]) |
242 | { |
243 | c1MoreSpecific = |
244 | c1MoreSpecific || |
245 | isStrictMethodInvocationConvertible(c2[i], c1[i]); |
246 | c2MoreSpecific = |
247 | c2MoreSpecific || |
248 | isStrictMethodInvocationConvertible(c1[i], c2[i]); |
249 | } |
250 | } |
251 | |
252 | if(c1MoreSpecific) |
253 | { |
254 | if(c2MoreSpecific) |
255 | { |
256 | /* |
257 | * Incomparable due to cross-assignable arguments (i.e. |
258 | * foo(String, Object) vs. foo(Object, String)) |
259 | */ |
260 | |
261 | return INCOMPARABLE; |
262 | } |
263 | |
264 | return MORE_SPECIFIC; |
265 | } |
266 | |
267 | if(c2MoreSpecific) |
268 | { |
269 | return LESS_SPECIFIC; |
270 | } |
271 | |
272 | /* |
273 | * Incomparable due to non-related arguments (i.e. |
274 | * foo(Runnable) vs. foo(Serializable)) |
275 | */ |
276 | |
277 | return INCOMPARABLE; |
278 | } |
279 | |
280 | /** |
281 | * Returns all methods that are applicable to actual argument types. |
282 | * @param methods list of all candidate methods |
283 | * @param classes the actual types of the arguments |
284 | * @return a list that contains only applicable methods (number of |
285 | * formal and actual arguments matches, and argument types are assignable |
286 | * to formal types through a method invocation conversion). |
287 | */ |
288 | private static LinkedList getApplicables(List methods, Class[] classes) |
289 | { |
290 | LinkedList list = new LinkedList(); |
291 | |
292 | for (Iterator imethod = methods.iterator(); imethod.hasNext();) |
293 | { |
294 | Method method = (Method) imethod.next(); |
295 | |
296 | if(isApplicable(method, classes)) |
297 | { |
298 | list.add(method); |
299 | } |
300 | |
301 | } |
302 | return list; |
303 | } |
304 | |
305 | /** |
306 | * Returns true if the supplied method is applicable to actual |
307 | * argument types. |
308 | */ |
309 | private static boolean isApplicable(Method method, Class[] classes) |
310 | { |
311 | Class[] methodArgs = method.getParameterTypes(); |
312 | |
313 | if(methodArgs.length != classes.length) |
314 | { |
315 | return false; |
316 | } |
317 | |
318 | for(int i = 0; i < classes.length; ++i) |
319 | { |
320 | if(!isMethodInvocationConvertible(methodArgs[i], classes[i])) |
321 | { |
322 | return false; |
323 | } |
324 | } |
325 | |
326 | return true; |
327 | } |
328 | |
329 | /** |
330 | * Determines whether a type represented by a class object is |
331 | * convertible to another type represented by a class object using a |
332 | * method invocation conversion, treating object types of primitive |
333 | * types as if they were primitive types (that is, a Boolean actual |
334 | * parameter type matches boolean primitive formal type). This behavior |
335 | * is because this method is used to determine applicable methods for |
336 | * an actual parameter list, and primitive types are represented by |
337 | * their object duals in reflective method calls. |
338 | * |
339 | * @param formal the formal parameter type to which the actual |
340 | * parameter type should be convertible |
341 | * @param actual the actual parameter type. |
342 | * @return true if either formal type is assignable from actual type, |
343 | * or formal is a primitive type and actual is its corresponding object |
344 | * type or an object type of a primitive type that can be converted to |
345 | * the formal type. |
346 | */ |
347 | private static boolean isMethodInvocationConvertible(Class formal, |
348 | Class actual) |
349 | { |
350 | /* |
351 | * if it's a null, it means the arg was null |
352 | */ |
353 | if (actual == null && !formal.isPrimitive()) |
354 | { |
355 | return true; |
356 | } |
357 | |
358 | /* |
359 | * Check for identity or widening reference conversion |
360 | */ |
361 | |
362 | if (actual != null && formal.isAssignableFrom(actual)) |
363 | { |
364 | return true; |
365 | } |
366 | |
367 | /* |
368 | * Check for boxing with widening primitive conversion. Note that |
369 | * actual parameters are never primitives. |
370 | */ |
371 | |
372 | if (formal.isPrimitive()) |
373 | { |
374 | if(formal == Boolean.TYPE && actual == Boolean.class) |
375 | return true; |
376 | if(formal == Character.TYPE && actual == Character.class) |
377 | return true; |
378 | if(formal == Byte.TYPE && actual == Byte.class) |
379 | return true; |
380 | if(formal == Short.TYPE && |
381 | (actual == Short.class || actual == Byte.class)) |
382 | return true; |
383 | if(formal == Integer.TYPE && |
384 | (actual == Integer.class || actual == Short.class || |
385 | actual == Byte.class)) |
386 | return true; |
387 | if(formal == Long.TYPE && |
388 | (actual == Long.class || actual == Integer.class || |
389 | actual == Short.class || actual == Byte.class)) |
390 | return true; |
391 | if(formal == Float.TYPE && |
392 | (actual == Float.class || actual == Long.class || |
393 | actual == Integer.class || actual == Short.class || |
394 | actual == Byte.class)) |
395 | return true; |
396 | if(formal == Double.TYPE && |
397 | (actual == Double.class || actual == Float.class || |
398 | actual == Long.class || actual == Integer.class || |
399 | actual == Short.class || actual == Byte.class)) |
400 | return true; |
401 | } |
402 | |
403 | return false; |
404 | } |
405 | |
406 | /** |
407 | * Determines whether a type represented by a class object is |
408 | * convertible to another type represented by a class object using a |
409 | * method invocation conversion, without matching object and primitive |
410 | * types. This method is used to determine the more specific type when |
411 | * comparing signatures of methods. |
412 | * |
413 | * @param formal the formal parameter type to which the actual |
414 | * parameter type should be convertible |
415 | * @param actual the actual parameter type. |
416 | * @return true if either formal type is assignable from actual type, |
417 | * or formal and actual are both primitive types and actual can be |
418 | * subject to widening conversion to formal. |
419 | */ |
420 | private static boolean isStrictMethodInvocationConvertible(Class formal, |
421 | Class actual) |
422 | { |
423 | /* |
424 | * we shouldn't get a null into, but if so |
425 | */ |
426 | if (actual == null && !formal.isPrimitive()) |
427 | { |
428 | return true; |
429 | } |
430 | |
431 | /* |
432 | * Check for identity or widening reference conversion |
433 | */ |
434 | |
435 | if(formal.isAssignableFrom(actual)) |
436 | { |
437 | return true; |
438 | } |
439 | |
440 | /* |
441 | * Check for widening primitive conversion. |
442 | */ |
443 | |
444 | if(formal.isPrimitive()) |
445 | { |
446 | if(formal == Short.TYPE && (actual == Byte.TYPE)) |
447 | return true; |
448 | if(formal == Integer.TYPE && |
449 | (actual == Short.TYPE || actual == Byte.TYPE)) |
450 | return true; |
451 | if(formal == Long.TYPE && |
452 | (actual == Integer.TYPE || actual == Short.TYPE || |
453 | actual == Byte.TYPE)) |
454 | return true; |
455 | if(formal == Float.TYPE && |
456 | (actual == Long.TYPE || actual == Integer.TYPE || |
457 | actual == Short.TYPE || actual == Byte.TYPE)) |
458 | return true; |
459 | if(formal == Double.TYPE && |
460 | (actual == Float.TYPE || actual == Long.TYPE || |
461 | actual == Integer.TYPE || actual == Short.TYPE || |
462 | actual == Byte.TYPE)) |
463 | return true; |
464 | } |
465 | return false; |
466 | } |
467 | } |