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