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