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