KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > util > introspection > MethodMap


1 package org.apache.velocity.util.introspection;
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 import java.util.Iterator JavaDoc;
20 import java.util.List JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.LinkedList JavaDoc;
23 import java.util.Set JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.Hashtable JavaDoc;
27
28 import java.lang.reflect.Method JavaDoc;
29
30 /**
31  *
32  * @author <a HREF="mailto:jvanzyl@apache.org">Jason van Zyl</a>
33  * @author <a HREF="mailto:bob@werken.com">Bob McWhirter</a>
34  * @author <a HREF="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
35  * @author <a HREF="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
36  * @author <a HREF="mailto:szegedia@freemail.hu">Attila Szegedi</a>
37  * @version $Id: MethodMap.java,v 1.15.4.1 2004/03/03 23:23:08 geirm Exp $
38  */

39 public class MethodMap
40 {
41     private static final int MORE_SPECIFIC = 0;
42     private static final int LESS_SPECIFIC = 1;
43     private static final int INCOMPARABLE = 2;
44
45     /**
46      * Keep track of all methods with the same name.
47      */

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

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

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

111     public Method JavaDoc find(String JavaDoc methodName, Object JavaDoc[] args)
112         throws AmbiguousException
113     {
114         List JavaDoc methodList = get(methodName);
115
116         if (methodList == null)
117         {
118             return null;
119         }
120
121         int l = args.length;
122         Class JavaDoc[] classes = new Class JavaDoc[l];
123
124         for(int i = 0; i < l; ++i)
125         {
126             Object JavaDoc arg = args[i];
127
128             /*
129              * if we are careful down below, a null argument goes in there
130              * so we can know that the null was passed to the method
131              */

132             classes[i] =
133                     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     public static class AmbiguousException extends Exception JavaDoc
144     {
145     }
146
147
148     private static Method JavaDoc getMostSpecific(List JavaDoc methods, Class JavaDoc[] classes)
149         throws AmbiguousException
150     {
151         LinkedList JavaDoc applicables = getApplicables(methods, classes);
152
153         if(applicables.isEmpty())
154         {
155             return null;
156         }
157
158         if(applicables.size() == 1)
159         {
160             return (Method JavaDoc)applicables.getFirst();
161         }
162
163         /*
164          * This list will contain the maximally specific methods. Hopefully at
165          * the end of the below loop, the list will contain exactly one method,
166          * (the most specific method) otherwise we have ambiguity.
167          */

168
169         LinkedList JavaDoc maximals = new LinkedList JavaDoc();
170
171         for (Iterator JavaDoc applicable = applicables.iterator();
172              applicable.hasNext();)
173         {
174             Method JavaDoc app = (Method JavaDoc) applicable.next();
175             Class JavaDoc[] appArgs = app.getParameterTypes();
176             boolean lessSpecific = false;
177
178             for (Iterator JavaDoc maximal = maximals.iterator();
179                  !lessSpecific && maximal.hasNext();)
180             {
181                 Method JavaDoc max = (Method JavaDoc) maximal.next();
182
183                 switch(moreSpecific(appArgs, max.getParameterTypes()))
184                 {
185                     case MORE_SPECIFIC:
186                     {
187                         /*
188                          * This method is more specific than the previously
189                          * known maximally specific, so remove the old maximum.
190                          */

191
192                         maximal.remove();
193                         break;
194                     }
195
196                     case LESS_SPECIFIC:
197                     {
198                         /*
199                          * This method is less specific than some of the
200                          * currently known maximally specific methods, so we
201                          * won't add it into the set of maximally specific
202                          * methods
203                          */

204
205                         lessSpecific = true;
206                         break;
207                     }
208                 }
209             }
210
211             if(!lessSpecific)
212             {
213                 maximals.addLast(app);
214             }
215         }
216
217         if(maximals.size() > 1)
218         {
219             // We have more than one maximally specific method
220
throw new AmbiguousException();
221         }
222
223         return (Method JavaDoc)maximals.getFirst();
224     }
225
226     /**
227      * Determines which method signature (represented by a class array) is more
228      * specific. This defines a partial ordering on the method signatures.
229      * @param c1 first signature to compare
230      * @param c2 second signature to compare
231      * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
232      * c1 is less specific than c2, INCOMPARABLE if they are incomparable.
233      */

234     private static int moreSpecific(Class JavaDoc[] c1, Class JavaDoc[] c2)
235     {
236         boolean c1MoreSpecific = false;
237         boolean c2MoreSpecific = false;
238
239         for(int i = 0; i < c1.length; ++i)
240         {
241             if(c1[i] != c2[i])
242             {
243                 c1MoreSpecific =
244                     c1MoreSpecific ||
245                     isStrictMethodInvocationConvertible(c2[i], c1[i]);
246                 c2MoreSpecific =
247                     c2MoreSpecific ||
248                     isStrictMethodInvocationConvertible(c1[i], c2[i]);
249             }
250         }
251
252         if(c1MoreSpecific)
253         {
254             if(c2MoreSpecific)
255             {
256                 /*
257                  * Incomparable due to cross-assignable arguments (i.e.
258                  * foo(String, Object) vs. foo(Object, String))
259                  */

260
261                 return INCOMPARABLE;
262             }
263
264             return MORE_SPECIFIC;
265         }
266
267         if(c2MoreSpecific)
268         {
269             return LESS_SPECIFIC;
270         }
271
272         /*
273          * Incomparable due to non-related arguments (i.e.
274          * foo(Runnable) vs. foo(Serializable))
275          */

276
277         return INCOMPARABLE;
278     }
279
280     /**
281      * Returns all methods that are applicable to actual argument types.
282      * @param methods list of all candidate methods
283      * @param classes the actual types of the arguments
284      * @return a list that contains only applicable methods (number of
285      * formal and actual arguments matches, and argument types are assignable
286      * to formal types through a method invocation conversion).
287      */

288     private static LinkedList JavaDoc getApplicables(List JavaDoc methods, Class JavaDoc[] classes)
289     {
290         LinkedList JavaDoc list = new LinkedList JavaDoc();
291
292         for (Iterator JavaDoc imethod = methods.iterator(); imethod.hasNext();)
293         {
294             Method JavaDoc method = (Method JavaDoc) imethod.next();
295
296             if(isApplicable(method, classes))
297             {
298                 list.add(method);
299             }
300
301         }
302         return list;
303     }
304
305     /**
306      * Returns true if the supplied method is applicable to actual
307      * argument types.
308      */

309     private static boolean isApplicable(Method JavaDoc method, Class JavaDoc[] classes)
310     {
311         Class JavaDoc[] methodArgs = method.getParameterTypes();
312
313         if(methodArgs.length != classes.length)
314         {
315             return false;
316         }
317
318         for(int i = 0; i < classes.length; ++i)
319         {
320             if(!isMethodInvocationConvertible(methodArgs[i], classes[i]))
321             {
322                 return false;
323             }
324         }
325
326         return true;
327     }
328
329     /**
330      * Determines whether a type represented by a class object is
331      * convertible to another type represented by a class object using a
332      * method invocation conversion, treating object types of primitive
333      * types as if they were primitive types (that is, a Boolean actual
334      * parameter type matches boolean primitive formal type). This behavior
335      * is because this method is used to determine applicable methods for
336      * an actual parameter list, and primitive types are represented by
337      * their object duals in reflective method calls.
338      *
339      * @param formal the formal parameter type to which the actual
340      * parameter type should be convertible
341      * @param actual the actual parameter type.
342      * @return true if either formal type is assignable from actual type,
343      * or formal is a primitive type and actual is its corresponding object
344      * type or an object type of a primitive type that can be converted to
345      * the formal type.
346      */

347     private static boolean isMethodInvocationConvertible(Class JavaDoc formal,
348                                                          Class JavaDoc actual)
349     {
350         /*
351          * if it's a null, it means the arg was null
352          */

353         if (actual == null && !formal.isPrimitive())
354         {
355             return true;
356         }
357
358         /*
359          * Check for identity or widening reference conversion
360          */

361
362         if (actual != null && formal.isAssignableFrom(actual))
363         {
364             return true;
365         }
366
367         /*
368          * Check for boxing with widening primitive conversion. Note that
369          * actual parameters are never primitives.
370          */

371
372         if (formal.isPrimitive())
373         {
374             if(formal == Boolean.TYPE && actual == Boolean JavaDoc.class)
375                 return true;
376             if(formal == Character.TYPE && actual == Character JavaDoc.class)
377                 return true;
378             if(formal == Byte.TYPE && actual == Byte JavaDoc.class)
379                 return true;
380             if(formal == Short.TYPE &&
381                (actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
382                 return true;
383             if(formal == Integer.TYPE &&
384                (actual == Integer JavaDoc.class || actual == Short JavaDoc.class ||
385                 actual == Byte JavaDoc.class))
386                 return true;
387             if(formal == Long.TYPE &&
388                (actual == Long JavaDoc.class || actual == Integer JavaDoc.class ||
389                 actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
390                 return true;
391             if(formal == Float.TYPE &&
392                (actual == Float JavaDoc.class || actual == Long JavaDoc.class ||
393                 actual == Integer JavaDoc.class || actual == Short JavaDoc.class ||
394                 actual == Byte JavaDoc.class))
395                 return true;
396             if(formal == Double.TYPE &&
397                (actual == Double JavaDoc.class || actual == Float JavaDoc.class ||
398                 actual == Long JavaDoc.class || actual == Integer JavaDoc.class ||
399                 actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
400                 return true;
401         }
402
403         return false;
404     }
405
406     /**
407      * Determines whether a type represented by a class object is
408      * convertible to another type represented by a class object using a
409      * method invocation conversion, without matching object and primitive
410      * types. This method is used to determine the more specific type when
411      * comparing signatures of methods.
412      *
413      * @param formal the formal parameter type to which the actual
414      * parameter type should be convertible
415      * @param actual the actual parameter type.
416      * @return true if either formal type is assignable from actual type,
417      * or formal and actual are both primitive types and actual can be
418      * subject to widening conversion to formal.
419      */

420     private static boolean isStrictMethodInvocationConvertible(Class JavaDoc formal,
421                                                                Class JavaDoc actual)
422     {
423         /*
424          * we shouldn't get a null into, but if so
425          */

426         if (actual == null && !formal.isPrimitive())
427         {
428             return true;
429         }
430
431         /*
432          * Check for identity or widening reference conversion
433          */

434
435         if(formal.isAssignableFrom(actual))
436         {
437             return true;
438         }
439
440         /*
441          * Check for widening primitive conversion.
442          */

443
444         if(formal.isPrimitive())
445         {
446             if(formal == Short.TYPE && (actual == Byte.TYPE))
447                 return true;
448             if(formal == Integer.TYPE &&
449                (actual == Short.TYPE || actual == Byte.TYPE))
450                 return true;
451             if(formal == Long.TYPE &&
452                (actual == Integer.TYPE || actual == Short.TYPE ||
453                 actual == Byte.TYPE))
454                 return true;
455             if(formal == Float.TYPE &&
456                (actual == Long.TYPE || actual == Integer.TYPE ||
457                 actual == Short.TYPE || actual == Byte.TYPE))
458                 return true;
459             if(formal == Double.TYPE &&
460                (actual == Float.TYPE || actual == Long.TYPE ||
461                 actual == Integer.TYPE || actual == Short.TYPE ||
462                 actual == Byte.TYPE))
463                 return true;
464         }
465         return false;
466     }
467 }
468
Popular Tags