Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MethodMap |
|
| 8.333333333333334;8,333 | ||||
MethodMap$AmbiguousException |
|
| 8.333333333333334;8,333 |
1 | package org.codehaus.plexus.util.introspection; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
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 | * @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 591654 2007-11-03 17:14:56Z dennisl $ | |
38 | */ | |
39 | 0 | 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 | 0 | 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 | 0 | String methodName = method.getName(); |
58 | ||
59 | 0 | List l = get( methodName ); |
60 | ||
61 | 0 | if ( l == null) |
62 | { | |
63 | 0 | l = new ArrayList(); |
64 | 0 | methodByNameMap.put(methodName, l); |
65 | } | |
66 | ||
67 | 0 | l.add(method); |
68 | ||
69 | 0 | 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 | 0 | 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 | 0 | List methodList = get(methodName); |
115 | ||
116 | 0 | if (methodList == null) |
117 | { | |
118 | 0 | return null; |
119 | } | |
120 | ||
121 | 0 | int l = args.length; |
122 | 0 | Class[] classes = new Class[l]; |
123 | ||
124 | 0 | for(int i = 0; i < l; ++i) |
125 | { | |
126 | 0 | 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 | 0 | classes[i] = |
133 | arg == null ? null : arg.getClass(); | |
134 | } | |
135 | ||
136 | 0 | return getMostSpecific(methodList, classes); |
137 | } | |
138 | ||
139 | /** | |
140 | * simple distinguishable exception, used when | |
141 | * we run across ambiguous overloading | |
142 | */ | |
143 | 0 | public static class AmbiguousException extends Exception |
144 | { | |
145 | } | |
146 | ||
147 | ||
148 | private static Method getMostSpecific(List methods, Class[] classes) | |
149 | throws AmbiguousException | |
150 | { | |
151 | 0 | LinkedList applicables = getApplicables(methods, classes); |
152 | ||
153 | 0 | if(applicables.isEmpty()) |
154 | { | |
155 | 0 | return null; |
156 | } | |
157 | ||
158 | 0 | if(applicables.size() == 1) |
159 | { | |
160 | 0 | 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 | 0 | LinkedList maximals = new LinkedList(); |
170 | ||
171 | 0 | for (Iterator applicable = applicables.iterator(); |
172 | 0 | applicable.hasNext();) |
173 | { | |
174 | 0 | Method app = (Method) applicable.next(); |
175 | 0 | Class[] appArgs = app.getParameterTypes(); |
176 | 0 | boolean lessSpecific = false; |
177 | ||
178 | 0 | for (Iterator maximal = maximals.iterator(); |
179 | 0 | !lessSpecific && maximal.hasNext();) |
180 | { | |
181 | 0 | Method max = (Method) maximal.next(); |
182 | ||
183 | 0 | 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 | 0 | maximal.remove(); |
193 | 0 | 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 | 0 | lessSpecific = true; |
206 | 0 | break; |
207 | } | |
208 | } | |
209 | } | |
210 | ||
211 | 0 | if(!lessSpecific) |
212 | { | |
213 | 0 | maximals.addLast(app); |
214 | } | |
215 | } | |
216 | ||
217 | 0 | if(maximals.size() > 1) |
218 | { | |
219 | // We have more than one maximally specific method | |
220 | 0 | throw new AmbiguousException(); |
221 | } | |
222 | ||
223 | 0 | 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 | 0 | boolean c1MoreSpecific = false; |
237 | 0 | boolean c2MoreSpecific = false; |
238 | ||
239 | 0 | for(int i = 0; i < c1.length; ++i) |
240 | { | |
241 | 0 | if(c1[i] != c2[i]) |
242 | { | |
243 | 0 | c1MoreSpecific = |
244 | c1MoreSpecific || | |
245 | isStrictMethodInvocationConvertible(c2[i], c1[i]); | |
246 | 0 | c2MoreSpecific = |
247 | c2MoreSpecific || | |
248 | isStrictMethodInvocationConvertible(c1[i], c2[i]); | |
249 | } | |
250 | } | |
251 | ||
252 | 0 | if(c1MoreSpecific) |
253 | { | |
254 | 0 | if(c2MoreSpecific) |
255 | { | |
256 | /* | |
257 | * Incomparable due to cross-assignable arguments (i.e. | |
258 | * foo(String, Object) vs. foo(Object, String)) | |
259 | */ | |
260 | ||
261 | 0 | return INCOMPARABLE; |
262 | } | |
263 | ||
264 | 0 | return MORE_SPECIFIC; |
265 | } | |
266 | ||
267 | 0 | if(c2MoreSpecific) |
268 | { | |
269 | 0 | return LESS_SPECIFIC; |
270 | } | |
271 | ||
272 | /* | |
273 | * Incomparable due to non-related arguments (i.e. | |
274 | * foo(Runnable) vs. foo(Serializable)) | |
275 | */ | |
276 | ||
277 | 0 | 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 | 0 | LinkedList list = new LinkedList(); |
291 | ||
292 | 0 | for (Iterator imethod = methods.iterator(); imethod.hasNext();) |
293 | { | |
294 | 0 | Method method = (Method) imethod.next(); |
295 | ||
296 | 0 | if(isApplicable(method, classes)) |
297 | { | |
298 | 0 | list.add(method); |
299 | } | |
300 | ||
301 | } | |
302 | 0 | 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 | 0 | Class[] methodArgs = method.getParameterTypes(); |
312 | ||
313 | 0 | if(methodArgs.length != classes.length) |
314 | { | |
315 | 0 | return false; |
316 | } | |
317 | ||
318 | 0 | for(int i = 0; i < classes.length; ++i) |
319 | { | |
320 | 0 | if(!isMethodInvocationConvertible(methodArgs[i], classes[i])) |
321 | { | |
322 | 0 | return false; |
323 | } | |
324 | } | |
325 | ||
326 | 0 | 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 | 0 | if (actual == null && !formal.isPrimitive()) |
354 | { | |
355 | 0 | return true; |
356 | } | |
357 | ||
358 | /* | |
359 | * Check for identity or widening reference conversion | |
360 | */ | |
361 | ||
362 | 0 | if (actual != null && formal.isAssignableFrom(actual)) |
363 | { | |
364 | 0 | return true; |
365 | } | |
366 | ||
367 | /* | |
368 | * Check for boxing with widening primitive conversion. Note that | |
369 | * actual parameters are never primitives. | |
370 | */ | |
371 | ||
372 | 0 | if (formal.isPrimitive()) |
373 | { | |
374 | 0 | if(formal == Boolean.TYPE && actual == Boolean.class) |
375 | 0 | return true; |
376 | 0 | if(formal == Character.TYPE && actual == Character.class) |
377 | 0 | return true; |
378 | 0 | if(formal == Byte.TYPE && actual == Byte.class) |
379 | 0 | return true; |
380 | 0 | if(formal == Short.TYPE && |
381 | (actual == Short.class || actual == Byte.class)) | |
382 | 0 | return true; |
383 | 0 | if(formal == Integer.TYPE && |
384 | (actual == Integer.class || actual == Short.class || | |
385 | actual == Byte.class)) | |
386 | 0 | return true; |
387 | 0 | if(formal == Long.TYPE && |
388 | (actual == Long.class || actual == Integer.class || | |
389 | actual == Short.class || actual == Byte.class)) | |
390 | 0 | return true; |
391 | 0 | if(formal == Float.TYPE && |
392 | (actual == Float.class || actual == Long.class || | |
393 | actual == Integer.class || actual == Short.class || | |
394 | actual == Byte.class)) | |
395 | 0 | return true; |
396 | 0 | 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 | 0 | return true; |
401 | } | |
402 | ||
403 | 0 | 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 | 0 | if (actual == null && !formal.isPrimitive()) |
427 | { | |
428 | 0 | return true; |
429 | } | |
430 | ||
431 | /* | |
432 | * Check for identity or widening reference conversion | |
433 | */ | |
434 | ||
435 | 0 | if(formal.isAssignableFrom(actual)) |
436 | { | |
437 | 0 | return true; |
438 | } | |
439 | ||
440 | /* | |
441 | * Check for widening primitive conversion. | |
442 | */ | |
443 | ||
444 | 0 | if(formal.isPrimitive()) |
445 | { | |
446 | 0 | if(formal == Short.TYPE && (actual == Byte.TYPE)) |
447 | 0 | return true; |
448 | 0 | if(formal == Integer.TYPE && |
449 | (actual == Short.TYPE || actual == Byte.TYPE)) | |
450 | 0 | return true; |
451 | 0 | if(formal == Long.TYPE && |
452 | (actual == Integer.TYPE || actual == Short.TYPE || | |
453 | actual == Byte.TYPE)) | |
454 | 0 | return true; |
455 | 0 | if(formal == Float.TYPE && |
456 | (actual == Long.TYPE || actual == Integer.TYPE || | |
457 | actual == Short.TYPE || actual == Byte.TYPE)) | |
458 | 0 | return true; |
459 | 0 | if(formal == Double.TYPE && |
460 | (actual == Float.TYPE || actual == Long.TYPE || | |
461 | actual == Integer.TYPE || actual == Short.TYPE || | |
462 | actual == Byte.TYPE)) | |
463 | 0 | return true; |
464 | } | |
465 | 0 | return false; |
466 | } | |
467 | } |