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 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 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 String methodName = method.getName();
61
62 List<Method> l = get( methodName );
63
64 if ( l == null )
65 {
66 l = new ArrayList<Method>();
67 methodByNameMap.put( methodName, l );
68 }
69
70 l.add( method );
71 }
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 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 List<Method> methodList = get( methodName );
116
117 if ( methodList == null )
118 {
119 return null;
120 }
121
122 int l = args.length;
123 Class<?>[] classes = new Class[l];
124
125 for ( int i = 0; i < l; ++i )
126 {
127 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 classes[i] = 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 static class AmbiguousException
144 extends Exception
145 {
146
147 private static final long serialVersionUID = 751688436639650618L;
148 }
149
150
151 private static Method getMostSpecific( List<Method> methods, Class<?>... classes )
152 throws AmbiguousException
153 {
154 LinkedList<Method> applicables = getApplicables( methods, classes );
155
156 if ( applicables.isEmpty() )
157 {
158 return null;
159 }
160
161 if ( applicables.size() == 1 )
162 {
163 return applicables.getFirst();
164 }
165
166 /*
167 * This list will contain the maximally specific methods. Hopefully at
168 * the end of the below loop, the list will contain exactly one method,
169 * (the most specific method) otherwise we have ambiguity.
170 */
171
172 LinkedList<Method> maximals = new LinkedList<Method>();
173
174 for ( Method app : applicables )
175 {
176 Class<?>[] appArgs = app.getParameterTypes();
177 boolean lessSpecific = false;
178
179 for ( Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); )
180 {
181 Method max = maximal.next();
182
183 switch ( moreSpecific( appArgs, max.getParameterTypes() ) )
184 {
185 case MORE_SPECIFIC:
186 /*
187 * This method is more specific than the previously
188 * known maximally specific, so remove the old maximum.
189 */
190
191 maximal.remove();
192 break;
193
194 case LESS_SPECIFIC:
195 /*
196 * This method is less specific than some of the
197 * currently known maximally specific methods, so we
198 * won't add it into the set of maximally specific
199 * methods
200 */
201
202 lessSpecific = true;
203 break;
204
205 default:
206 }
207 }
208
209 if ( !lessSpecific )
210 {
211 maximals.addLast( app );
212 }
213 }
214
215 if ( maximals.size() > 1 )
216 {
217 // We have more than one maximally specific method
218 throw new AmbiguousException();
219 }
220
221 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 boolean c1MoreSpecific = false;
236 boolean c2MoreSpecific = false;
237
238 for ( int i = 0; i < c1.length; ++i )
239 {
240 if ( c1[i] != c2[i] )
241 {
242 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] );
243 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] );
244 }
245 }
246
247 if ( c1MoreSpecific )
248 {
249 if ( c2MoreSpecific )
250 {
251 /*
252 * Incomparable due to cross-assignable arguments (i.e.
253 * foo(String, Object) vs. foo(Object, String))
254 */
255
256 return INCOMPARABLE;
257 }
258
259 return MORE_SPECIFIC;
260 }
261
262 if ( c2MoreSpecific )
263 {
264 return LESS_SPECIFIC;
265 }
266
267 /*
268 * Incomparable due to non-related arguments (i.e.
269 * foo(Runnable) vs. foo(Serializable))
270 */
271
272 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 LinkedList<Method> list = new LinkedList<Method>();
287
288 for ( Method method : methods )
289 {
290 if ( isApplicable( method, classes ) )
291 {
292 list.add( method );
293 }
294 }
295 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 Class<?>[] methodArgs = method.getParameterTypes();
309
310 if ( methodArgs.length != classes.length )
311 {
312 return false;
313 }
314
315 for ( int i = 0; i < classes.length; ++i )
316 {
317 if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) )
318 {
319 return false;
320 }
321 }
322
323 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 if ( actual == null && !formal.isPrimitive() )
350 {
351 return true;
352 }
353
354 /*
355 * Check for identity or widening reference conversion
356 */
357
358 if ( actual != null && formal.isAssignableFrom( actual ) )
359 {
360 return true;
361 }
362
363 /*
364 * Check for boxing with widening primitive conversion. Note that
365 * actual parameters are never primitives.
366 */
367
368 if ( formal.isPrimitive() )
369 {
370 if ( formal == Boolean.TYPE && actual == Boolean.class )
371 {
372 return true;
373 }
374 if ( formal == Character.TYPE && actual == Character.class )
375 {
376 return true;
377 }
378 if ( formal == Byte.TYPE && actual == Byte.class )
379 {
380 return true;
381 }
382 if ( formal == Short.TYPE && ( actual == Short.class || actual == Byte.class ) )
383 {
384 return true;
385 }
386 if ( formal == Integer.TYPE
387 && ( actual == Integer.class || actual == Short.class || actual == Byte.class ) )
388 {
389 return true;
390 }
391 if ( formal == Long.TYPE
392 && ( actual == Long.class || actual == Integer.class || actual == Short.class
393 || actual == Byte.class ) )
394 {
395 return true;
396 }
397 if ( formal == Float.TYPE
398 && ( actual == Float.class || actual == Long.class || actual == Integer.class
399 || actual == Short.class || actual == Byte.class ) )
400 {
401 return true;
402 }
403 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 return true;
408 }
409 }
410
411 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 if ( actual == null && !formal.isPrimitive() )
434 {
435 return true;
436 }
437
438 /*
439 * Check for identity or widening reference conversion
440 */
441
442 if ( formal.isAssignableFrom( actual ) )
443 {
444 return true;
445 }
446
447 /*
448 * Check for widening primitive conversion.
449 */
450
451 if ( formal.isPrimitive() )
452 {
453 if ( formal == Short.TYPE && ( actual == Byte.TYPE ) )
454 {
455 return true;
456 }
457 if ( formal == Integer.TYPE && ( actual == Short.TYPE || actual == Byte.TYPE ) )
458 {
459 return true;
460 }
461 if ( formal == Long.TYPE && ( actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
462 {
463 return true;
464 }
465 if ( formal == Float.TYPE
466 && ( actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
467 {
468 return true;
469 }
470 if ( formal == Double.TYPE
471 && ( actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE
472 || actual == Byte.TYPE ) )
473 {
474 return true;
475 }
476 }
477 return false;
478 }
479 }