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