View Javadoc
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 
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 }