KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > jexl > util > introspection > MethodMap


1 /*
2  * Copyright 2001,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.apache.commons.jexl.util.introspection;
18
19 import java.lang.reflect.Method JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Hashtable JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Map JavaDoc;
26
27 /**
28  *
29  * @author <a HREF="mailto:jvanzyl@apache.org">Jason van Zyl</a>
30  * @author <a HREF="mailto:bob@werken.com">Bob McWhirter</a>
31  * @author <a HREF="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
32  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
33  * @author <a HREF="mailto:szegedia@freemail.hu">Attila Szegedi</a>
34  * @since 1.0
35  * @version $Id: MethodMap.java 398495 2006-05-01 01:33:43Z dion $
36  */

37 public class MethodMap {
38     /** whether a method is more specific than a previously compared one. */
39     private static final int MORE_SPECIFIC = 0;
40     /** whether a method is less specific than a previously compared one. */
41     private static final int LESS_SPECIFIC = 1;
42     /** A method doesn't match a previously compared one. */
43     private static final int INCOMPARABLE = 2;
44
45     /**
46      * Keep track of all methods with the same name.
47      */

48     protected Map JavaDoc methodByNameMap = new Hashtable JavaDoc();
49
50     /**
51      * Add a method to a list of methods by name. For a particular class we are
52      * keeping track of all the methods with the same name.
53      * @param method the method.
54      */

55     public void add(Method JavaDoc method) {
56         String JavaDoc methodName = method.getName();
57
58         List JavaDoc l = get(methodName);
59
60         if (l == null) {
61             l = new ArrayList JavaDoc();
62             methodByNameMap.put(methodName, l);
63         }
64
65         l.add(method);
66     }
67
68     /**
69      * Return a list of methods with the same name.
70      *
71      * @param key The method name.
72      * @return List list of methods
73      */

74     public List JavaDoc get(String JavaDoc key) {
75         return (List JavaDoc) methodByNameMap.get(key);
76     }
77
78     /**
79      * <p>
80      * Find a method. Attempts to find the most specific applicable method using
81      * the algorithm described in the JLS section 15.12.2 (with the exception
82      * that it can't distinguish a primitive type argument from an object type
83      * argument, since in reflection primitive type arguments are represented by
84      * their object counterparts, so for an argument of type (say)
85      * java.lang.Integer, it will not be able to decide between a method that
86      * takes int and a method that takes java.lang.Integer as a parameter.
87      * </p>
88      *
89      * <p>
90      * This turns out to be a relatively rare case where this is needed -
91      * however, functionality like this is needed.
92      * </p>
93      *
94      * @param methodName name of method
95      * @param args the actual arguments with which the method is called
96      * @return the most specific applicable method, or null if no method is
97      * applicable.
98      * @throws AmbiguousException if there is more than one maximally specific
99      * applicable method
100      */

101     public Method JavaDoc find(String JavaDoc methodName, Object JavaDoc[] args) throws AmbiguousException {
102         List JavaDoc methodList = get(methodName);
103
104         if (methodList == null) {
105             return null;
106         }
107
108         int l = args.length;
109         Class JavaDoc[] classes = new Class JavaDoc[l];
110
111         for (int i = 0; i < l; ++i) {
112             Object JavaDoc arg = args[i];
113
114             /*
115              * if we are careful down below, a null argument goes in there so we
116              * can know that the null was passed to the method
117              */

118             classes[i] = arg == null ? null : arg.getClass();
119         }
120
121         return getMostSpecific(methodList, classes);
122     }
123
124     /**
125      * simple distinguishable exception, used when we run across ambiguous
126      * overloading.
127      */

128     public static class AmbiguousException extends Exception JavaDoc {
129         /** serialization version id jdk13 generated. */
130         static final long serialVersionUID = 8758118091728717367L;
131     }
132
133     /**
134      * Gets the most specific method from a list.
135      * @param methods list of {@link Method methods}
136      * @param classes argument types
137      * @return the most specific method, or null
138      * @throws AmbiguousException if there is more than one specific method
139      */

140     private static Method JavaDoc getMostSpecific(List JavaDoc methods, Class JavaDoc[] classes) throws AmbiguousException {
141         LinkedList JavaDoc applicables = getApplicables(methods, classes);
142
143         if (applicables.isEmpty()) {
144             return null;
145         }
146
147         if (applicables.size() == 1) {
148             return (Method JavaDoc) applicables.getFirst();
149         }
150
151         /*
152          * This list will contain the maximally specific methods. Hopefully at
153          * the end of the below loop, the list will contain exactly one method,
154          * (the most specific method) otherwise we have ambiguity.
155          */

156
157         LinkedList JavaDoc maximals = new LinkedList JavaDoc();
158
159         for (Iterator JavaDoc applicable = applicables.iterator(); applicable.hasNext();) {
160             Method JavaDoc app = (Method JavaDoc) applicable.next();
161             Class JavaDoc[] appArgs = app.getParameterTypes();
162             boolean lessSpecific = false;
163
164             for (Iterator JavaDoc maximal = maximals.iterator(); !lessSpecific && maximal.hasNext();) {
165                 Method JavaDoc max = (Method JavaDoc) maximal.next();
166
167                 switch (moreSpecific(appArgs, max.getParameterTypes())) {
168                     case MORE_SPECIFIC:
169                         /*
170                          * This method is more specific than the previously
171                          * known maximally specific, so remove the old maximum.
172                          */

173                         maximal.remove();
174                         break;
175
176                     case LESS_SPECIFIC:
177                         /*
178                          * This method is less specific than some of the
179                          * currently known maximally specific methods, so we
180                          * won't add it into the set of maximally specific
181                          * methods
182                          */

183                         lessSpecific = true;
184                         break;
185                 }
186             }
187
188             if (!lessSpecific) {
189                 maximals.addLast(app);
190             }
191         }
192
193         if (maximals.size() > 1) {
194             // We have more than one maximally specific method
195
throw new AmbiguousException();
196         }
197
198         return (Method JavaDoc) maximals.getFirst();
199     }
200
201     /**
202      * Determines which method signature (represented by a class array) is more
203      * specific. This defines a partial ordering on the method signatures.
204      *
205      * @param c1 first signature to compare
206      * @param c2 second signature to compare
207      * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if c1
208      * is less specific than c2, INCOMPARABLE if they are incomparable.
209      */

210     private static int moreSpecific(Class JavaDoc[] c1, Class JavaDoc[] c2) {
211         boolean c1MoreSpecific = false;
212         boolean c2MoreSpecific = false;
213
214         for (int i = 0; i < c1.length; ++i) {
215             if (c1[i] != c2[i]) {
216                 c1MoreSpecific = c1MoreSpecific || isStrictMethodInvocationConvertible(c2[i], c1[i]);
217                 c2MoreSpecific = c2MoreSpecific || isStrictMethodInvocationConvertible(c1[i], c2[i]);
218             }
219         }
220
221         if (c1MoreSpecific) {
222             if (c2MoreSpecific) {
223                 /*
224                  * Incomparable due to cross-assignable arguments (i.e.
225                  * foo(String, Object) vs. foo(Object, String))
226                  */

227
228                 return INCOMPARABLE;
229             }
230
231             return MORE_SPECIFIC;
232         }
233
234         if (c2MoreSpecific) {
235             return LESS_SPECIFIC;
236         }
237
238         /*
239          * Incomparable due to non-related arguments (i.e. foo(Runnable) vs.
240          * foo(Serializable))
241          */

242
243         return INCOMPARABLE;
244     }
245
246     /**
247      * Returns all methods that are applicable to actual argument types.
248      *
249      * @param methods list of all candidate methods
250      * @param classes the actual types of the arguments
251      * @return a list that contains only applicable methods (number of formal
252      * and actual arguments matches, and argument types are assignable
253      * to formal types through a method invocation conversion).
254      */

255     private static LinkedList JavaDoc getApplicables(List JavaDoc methods, Class JavaDoc[] classes) {
256         LinkedList JavaDoc list = new LinkedList JavaDoc();
257
258         for (Iterator JavaDoc imethod = methods.iterator(); imethod.hasNext();) {
259             Method JavaDoc method = (Method JavaDoc) imethod.next();
260
261             if (isApplicable(method, classes)) {
262                 list.add(method);
263             }
264
265         }
266         return list;
267     }
268
269     /**
270      * Returns true if the supplied method is applicable to actual argument
271      * types.
272      * @param method the method to check
273      * @param classes possible argument types
274      * @return true if the arguments are applicable to the method.
275      */

276     private static boolean isApplicable(Method JavaDoc method, Class JavaDoc[] classes) {
277         Class JavaDoc[] 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 convertible to
294      * another type represented by a class object using a method invocation
295      * conversion, treating object types of primitive types as if they were
296      * primitive types (that is, a Boolean actual parameter type matches boolean
297      * primitive formal type). This behavior is because this method is used to
298      * determine applicable methods for an actual parameter list, and primitive
299      * types are represented by their object duals in reflective method calls.
300      *
301      * @param formal the formal parameter type to which the actual parameter
302      * type should be convertible
303      * @param actual the actual parameter type.
304      * @return true if either formal type is assignable from actual type, or
305      * formal is a primitive type and actual is its corresponding object
306      * type or an object type of a primitive type that can be converted
307      * to the formal type.
308      */

309     private static boolean isMethodInvocationConvertible(Class JavaDoc formal, Class JavaDoc actual) {
310         /*
311          * if it's a null, it means the arg was null
312          */

313         if (actual == null && !formal.isPrimitive()) {
314             return true;
315         }
316
317         /*
318          * Check for identity or widening reference conversion
319          */

320
321         if (actual != null && formal.isAssignableFrom(actual)) {
322             return true;
323         }
324
325         /*
326          * Check for boxing with widening primitive conversion. Note that actual
327          * parameters are never primitives.
328          */

329
330         if (formal.isPrimitive()) {
331             if (formal == Boolean.TYPE && actual == Boolean JavaDoc.class) {
332                 return true;
333             }
334             if (formal == Character.TYPE && actual == Character JavaDoc.class) {
335                 return true;
336             }
337             if (formal == Byte.TYPE && actual == Byte JavaDoc.class) {
338                 return true;
339             }
340             if (formal == Short.TYPE && (actual == Short JavaDoc.class || actual == Byte JavaDoc.class)) {
341                 return true;
342             }
343             if (formal == Integer.TYPE && (actual == Integer JavaDoc.class || actual == Short JavaDoc.class || actual == Byte JavaDoc.class)) {
344                 return true;
345             }
346             if (formal == Long.TYPE
347                 && (actual == Long JavaDoc.class || actual == Integer JavaDoc.class || actual == Short JavaDoc.class || actual == Byte JavaDoc.class)) {
348                 return true;
349             }
350             if (formal == Float.TYPE
351                 && (actual == Float JavaDoc.class || actual == Long JavaDoc.class || actual == Integer JavaDoc.class
352                     || actual == Short JavaDoc.class || actual == Byte JavaDoc.class)) {
353                 return true;
354             }
355             if (formal == Double.TYPE
356                 && (actual == Double JavaDoc.class || actual == Float JavaDoc.class || actual == Long JavaDoc.class || actual == Integer JavaDoc.class
357                     || actual == Short JavaDoc.class || actual == Byte JavaDoc.class)) {
358                 return true;
359             }
360         }
361
362         return false;
363     }
364
365     /**
366      * Determines whether a type represented by a class object is convertible to
367      * another type represented by a class object using a method invocation
368      * conversion, without matching object and primitive types. This method is
369      * used to determine the more specific type when comparing signatures of
370      * methods.
371      *
372      * @param formal the formal parameter type to which the actual parameter
373      * type should be convertible
374      * @param actual the actual parameter type.
375      * @return true if either formal type is assignable from actual type, or
376      * formal and actual are both primitive types and actual can be
377      * subject to widening conversion to formal.
378      */

379     private static boolean isStrictMethodInvocationConvertible(Class JavaDoc formal, Class JavaDoc actual) {
380         /*
381          * we shouldn't get a null into, but if so
382          */

383         if (actual == null && !formal.isPrimitive()) {
384             return true;
385         }
386
387         /*
388          * Check for identity or widening reference conversion
389          */

390
391         if (formal.isAssignableFrom(actual)) {
392             return true;
393         }
394
395         /*
396          * Check for widening primitive conversion.
397          */

398
399         if (formal.isPrimitive()) {
400             if (formal == Short.TYPE && (actual == Byte.TYPE)) {
401                 return true;
402             }
403             if (formal == Integer.TYPE && (actual == Short.TYPE || actual == Byte.TYPE)) {
404                 return true;
405             }
406             if (formal == Long.TYPE && (actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
407                 return true;
408             }
409             if (formal == Float.TYPE
410                 && (actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE || actual == Byte.TYPE)) {
411                 return true;
412             }
413             if (formal == Double.TYPE
414                 && (actual == Float.TYPE || actual == Long.TYPE || actual == Integer.TYPE || actual == Short.TYPE
415                     || actual == Byte.TYPE)) {
416                 return true;
417             }
418         }
419         return false;
420     }
421 }
422
Popular Tags