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: MethodMap.html 955072 2015-06-16 23:06:34Z tibordigana $
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
148
149 private static Method getMostSpecific( List<Method> methods, Class<?>... classes )
150 throws AmbiguousException
151 {
152 LinkedList<Method> applicables = getApplicables( methods, classes );
153
154 if ( applicables.isEmpty() )
155 {
156 return null;
157 }
158
159 if ( applicables.size() == 1 )
160 {
161 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 LinkedList<Method> maximals = new LinkedList<Method>();
171
172 for ( Method app : applicables )
173 {
174 Class<?>[] appArgs = app.getParameterTypes();
175 boolean lessSpecific = false;
176
177 for ( Iterator<Method> maximal = maximals.iterator(); !lessSpecific && maximal.hasNext(); )
178 {
179 Method max = maximal.next();
180
181 switch ( moreSpecific( appArgs, max.getParameterTypes() ) )
182 {
183 case MORE_SPECIFIC:
184 /*
185 * This method is more specific than the previously
186 * known maximally specific, so remove the old maximum.
187 */
188
189 maximal.remove();
190 break;
191
192 case LESS_SPECIFIC:
193 /*
194 * This method is less specific than some of the
195 * currently known maximally specific methods, so we
196 * won't add it into the set of maximally specific
197 * methods
198 */
199
200 lessSpecific = true;
201 break;
202
203 default:
204 }
205 }
206
207 if ( !lessSpecific )
208 {
209 maximals.addLast( app );
210 }
211 }
212
213 if ( maximals.size() > 1 )
214 {
215 // We have more than one maximally specific method
216 throw new AmbiguousException();
217 }
218
219 return maximals.getFirst();
220 }
221
222 /**
223 * Determines which method signature (represented by a class array) is more
224 * specific. This defines a partial ordering on the method signatures.
225 *
226 * @param c1 first signature to compare
227 * @param c2 second signature to compare
228 * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
229 * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
230 */
231 private static int moreSpecific( Class<?>[] c1, Class<?>[] c2 )
232 {
233 boolean c1MoreSpecific = false;
234 boolean c2MoreSpecific = false;
235
236 for ( int i = 0; i < c1.length; ++i )
237 {
238 if ( c1[i] != c2[i] )
239 {
240 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible( c2[i], c1[i] );
241 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible( c1[i], c2[i] );
242 }
243 }
244
245 if ( c1MoreSpecific )
246 {
247 if ( c2MoreSpecific )
248 {
249 /*
250 * Incomparable due to cross-assignable arguments (i.e.
251 * foo(String, Object) vs. foo(Object, String))
252 */
253
254 return INCOMPARABLE;
255 }
256
257 return MORE_SPECIFIC;
258 }
259
260 if ( c2MoreSpecific )
261 {
262 return LESS_SPECIFIC;
263 }
264
265 /*
266 * Incomparable due to non-related arguments (i.e.
267 * foo(Runnable) vs. foo(Serializable))
268 */
269
270 return INCOMPARABLE;
271 }
272
273 /**
274 * Returns all methods that are applicable to actual argument types.
275 *
276 * @param methods list of all candidate methods
277 * @param classes the actual types of the arguments
278 * @return a list that contains only applicable methods (number of
279 * formal and actual arguments matches, and argument types are assignable
280 * to formal types through a method invocation conversion).
281 */
282 private static LinkedList<Method> getApplicables( List<Method> methods, Class<?>... classes )
283 {
284 LinkedList<Method> list = new LinkedList<Method>();
285
286 for ( Method method : methods )
287 {
288 if ( isApplicable( method, classes ) )
289 {
290 list.add( method );
291 }
292 }
293 return list;
294 }
295
296 /**
297 * Returns true if the supplied method is applicable to actual
298 * argument types.
299 *
300 * @param method The method to check for applicability
301 * @param classes The arguments
302 * @return true if the method applies to the parameter types
303 */
304 private static boolean isApplicable( Method method, Class<?>... classes )
305 {
306 Class<?>[] methodArgs = method.getParameterTypes();
307
308 if ( methodArgs.length != classes.length )
309 {
310 return false;
311 }
312
313 for ( int i = 0; i < classes.length; ++i )
314 {
315 if ( !isMethodInvocationConvertible( methodArgs[i], classes[i] ) )
316 {
317 return false;
318 }
319 }
320
321 return true;
322 }
323
324 /**
325 * Determines whether a type represented by a class object is
326 * convertible to another type represented by a class object using a
327 * method invocation conversion, treating object types of primitive
328 * types as if they were primitive types (that is, a Boolean actual
329 * parameter type matches boolean primitive formal type). This behavior
330 * is because this method is used to determine applicable methods for
331 * an actual parameter list, and primitive types are represented by
332 * their object duals in reflective method calls.
333 *
334 * @param formal the formal parameter type to which the actual
335 * parameter type should be convertible
336 * @param actual the actual parameter type.
337 * @return true if either formal type is assignable from actual type,
338 * or formal is a primitive type and actual is its corresponding object
339 * type or an object type of a primitive type that can be converted to
340 * the formal type.
341 */
342 private static boolean isMethodInvocationConvertible( Class<?> formal, Class<?> actual )
343 {
344 /*
345 * if it's a null, it means the arg was null
346 */
347 if ( actual == null && !formal.isPrimitive() )
348 {
349 return true;
350 }
351
352 /*
353 * Check for identity or widening reference conversion
354 */
355
356 if ( actual != null && formal.isAssignableFrom( actual ) )
357 {
358 return true;
359 }
360
361 /*
362 * Check for boxing with widening primitive conversion. Note that
363 * actual parameters are never primitives.
364 */
365
366 if ( formal.isPrimitive() )
367 {
368 if ( formal == Boolean.TYPE && actual == Boolean.class )
369 {
370 return true;
371 }
372 if ( formal == Character.TYPE && actual == Character.class )
373 {
374 return true;
375 }
376 if ( formal == Byte.TYPE && actual == Byte.class )
377 {
378 return true;
379 }
380 if ( formal == Short.TYPE && ( actual == Short.class || actual == Byte.class ) )
381 {
382 return true;
383 }
384 if ( formal == Integer.TYPE
385 && ( actual == Integer.class || actual == Short.class || actual == Byte.class ) )
386 {
387 return true;
388 }
389 if ( formal == Long.TYPE
390 && ( actual == Long.class || actual == Integer.class || actual == Short.class
391 || actual == Byte.class ) )
392 {
393 return true;
394 }
395 if ( formal == Float.TYPE
396 && ( actual == Float.class || actual == Long.class || actual == Integer.class
397 || actual == Short.class || actual == Byte.class ) )
398 {
399 return true;
400 }
401 if ( formal == Double.TYPE
402 && ( actual == Double.class || actual == Float.class || actual == Long.class || actual == Integer.class
403 || actual == Short.class || actual == Byte.class ) )
404 {
405 return true;
406 }
407 }
408
409 return false;
410 }
411
412 /**
413 * Determines whether a type represented by a class object is
414 * convertible to another type represented by a class object using a
415 * method invocation conversion, without matching object and primitive
416 * types. This method is used to determine the more specific type when
417 * comparing signatures of methods.
418 *
419 * @param formal the formal parameter type to which the actual
420 * parameter type should be convertible
421 * @param actual the actual parameter type.
422 * @return true if either formal type is assignable from actual type,
423 * or formal and actual are both primitive types and actual can be
424 * subject to widening conversion to formal.
425 */
426 private static boolean isStrictMethodInvocationConvertible( Class<?> formal, Class<?> actual )
427 {
428 /*
429 * we shouldn't get a null into, but if so
430 */
431 if ( actual == null && !formal.isPrimitive() )
432 {
433 return true;
434 }
435
436 /*
437 * Check for identity or widening reference conversion
438 */
439
440 if ( formal.isAssignableFrom( actual ) )
441 {
442 return true;
443 }
444
445 /*
446 * Check for widening primitive conversion.
447 */
448
449 if ( formal.isPrimitive() )
450 {
451 if ( formal == Short.TYPE && ( actual == Byte.TYPE ) )
452 {
453 return true;
454 }
455 if ( formal == Integer.TYPE && ( actual == Short.TYPE || actual == Byte.TYPE ) )
456 {
457 return true;
458 }
459 if ( formal == Long.TYPE && ( actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
460 {
461 return true;
462 }
463 if ( formal == Float.TYPE
464 && ( actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE ) )
465 {
466 return true;
467 }
468 if ( formal == Double.TYPE
469 && ( actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE
470 || actual == Byte.TYPE ) )
471 {
472 return true;
473 }
474 }
475 return false;
476 }
477 }