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                         /*
186                          * This method is more specific than the previously
187                          * known maximally specific, so remove the old maximum.
188                          */
189 
190                         maximal.remove();
191                         break;
192                     }
193 
194                     case LESS_SPECIFIC:
195                     {
196                         /*
197                          * This method is less specific than some of the
198                          * currently known maximally specific methods, so we
199                          * won't add it into the set of maximally specific
200                          * methods
201                          */
202 
203                         lessSpecific = true;
204                         break;
205                     }
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 }