KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > freemarker > ext > beans > MethodMap


1 /*
2  * Copyright (c) 2003 The Visigoth Software Society. All rights
3  * reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowledgement:
19  * "This product includes software developed by the
20  * Visigoth Software Society (http://www.visigoths.org/)."
21  * Alternately, this acknowledgement may appear in the software itself,
22  * if and wherever such third-party acknowledgements normally appear.
23  *
24  * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25  * project contributors may be used to endorse or promote products derived
26  * from this software without prior written permission. For written
27  * permission, please contact visigoths@visigoths.org.
28  *
29  * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30  * nor may "FreeMarker" or "Visigoth" appear in their names
31  * without prior written permission of the Visigoth Software Society.
32  *
33  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36  * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  * ====================================================================
46  *
47  * This software consists of voluntary contributions made by many
48  * individuals on behalf of the Visigoth Software Society. For more
49  * information on the Visigoth Software Society, please see
50  * http://www.visigoths.org/
51  */

52
53 package freemarker.ext.beans;
54
55 import java.lang.reflect.AccessibleObject JavaDoc;
56 import java.lang.reflect.Constructor JavaDoc;
57 import java.lang.reflect.Method JavaDoc;
58 import java.util.HashMap JavaDoc;
59 import java.util.Iterator JavaDoc;
60 import java.util.LinkedList JavaDoc;
61 import java.util.List JavaDoc;
62 import java.util.Map JavaDoc;
63
64 import freemarker.template.TemplateModelException;
65
66 class MethodMap
67 {
68     private static final Class JavaDoc BIGDECIMAL_CLASS = java.math.BigDecimal JavaDoc.class;
69     private static final Class JavaDoc NUMBER_CLASS = java.lang.Number JavaDoc.class;
70     
71     private static final Object JavaDoc[] EMPTY_ARGS = new Object JavaDoc[0];
72     private static final Class JavaDoc NULL_CLASS = java.lang.Object JavaDoc.class;
73     private static final ClassString EMPTY_STRING = new ClassString(EMPTY_ARGS);
74     
75     private static final Object JavaDoc NO_SUCH_METHOD = new Object JavaDoc();
76     private static final Object JavaDoc AMBIGUOUS_METHOD = new Object JavaDoc();
77     
78     private final String JavaDoc name;
79     private final Map JavaDoc cache = new HashMap JavaDoc();
80     private final List JavaDoc methods = new LinkedList JavaDoc();
81     
82     MethodMap(String JavaDoc name)
83     {
84         this.name = name;
85     }
86     
87     void addMethod(Method JavaDoc method)
88     {
89         methods.add(method);
90     }
91     
92     void addConstructor(Constructor JavaDoc constructor)
93     {
94         methods.add(constructor);
95     }
96     String JavaDoc getName()
97     {
98         return name;
99     }
100     
101     AccessibleObject JavaDoc getMostSpecific(Object JavaDoc[] args)
102         throws TemplateModelException
103     {
104         ClassString cs = null;
105         if(args == null)
106         {
107             args = EMPTY_ARGS;
108             cs = EMPTY_STRING;
109         }
110         else
111         {
112             cs = new ClassString(args);
113         }
114         synchronized(cache)
115         {
116             Object JavaDoc obj = cache.get(cs);
117             if(obj == null)
118             {
119                 cache.put(cs, obj = cs.getMostSpecific(methods));
120             }
121             if(obj instanceof AccessibleObject JavaDoc)
122             {
123                 return (AccessibleObject JavaDoc)obj;
124             }
125             if(obj == NO_SUCH_METHOD)
126             {
127                 throw new TemplateModelException("No signature of method " + name + " matches " + cs.listArgumentTypes());
128             }
129             else
130             {
131                 // Can be only AMBIGUOUS_METHOD
132
throw new TemplateModelException("Multiple signatures of method " + name + " match " + cs.listArgumentTypes());
133             }
134         }
135     }
136     
137     private static final class ClassString
138     {
139         private final Class JavaDoc[] classes;
140         
141         ClassString(Object JavaDoc[] objects)
142         {
143             int l = objects.length;
144             classes = new Class JavaDoc[l];
145             for(int i = 0; i < l; ++i)
146             {
147                 Object JavaDoc obj = objects[i];
148                 classes[i] = obj == null ? NULL_CLASS : obj.getClass();
149             }
150         }
151         
152         public int hashCode()
153         {
154             int hash = 0;
155             for(int i = 0; i < classes.length; ++i)
156             {
157                 hash ^= classes[i].hashCode();
158             }
159             return hash;
160         }
161         
162         public boolean equals(Object JavaDoc o)
163         {
164             if(o instanceof ClassString)
165             {
166                 ClassString cs = (ClassString)o;
167                 if(cs.classes.length != classes.length)
168                 {
169                     return false;
170                 }
171                 for(int i = 0; i < classes.length; ++i)
172                 {
173                     if(cs.classes[i] != classes[i])
174                     {
175                         return false;
176                     }
177                 }
178                 return true;
179             }
180             return false;
181         }
182         
183         private static final int MORE_SPECIFIC = 0;
184         private static final int LESS_SPECIFIC = 1;
185         private static final int INDETERMINATE = 2;
186         
187         Object JavaDoc getMostSpecific(List JavaDoc methods)
188         {
189             LinkedList JavaDoc applicables = getApplicables(methods);
190             if(applicables.isEmpty())
191             {
192                 return NO_SUCH_METHOD;
193             }
194             if(applicables.size() == 1)
195             {
196                 return applicables.getFirst();
197             }
198             LinkedList JavaDoc maximals = new LinkedList JavaDoc();
199             for (Iterator JavaDoc applicable = applicables.iterator();
200                  applicable.hasNext();)
201             {
202                 Object JavaDoc objapp = applicable.next();
203                 Class JavaDoc[] appArgs = getParameterTypes(objapp);
204                 boolean lessSpecific = false;
205                 for (Iterator JavaDoc maximal = maximals.iterator();
206                      !lessSpecific && maximal.hasNext();)
207                 {
208                     Object JavaDoc max = maximal.next();
209                     switch(moreSpecific(appArgs, getParameterTypes(max)))
210                     {
211                         case MORE_SPECIFIC:
212                         {
213                             maximal.remove();
214                             break;
215                         }
216                         case LESS_SPECIFIC:
217                         {
218                             lessSpecific = true;
219                             break;
220                         }
221                     }
222                 }
223                 if(!lessSpecific)
224                 {
225                     maximals.addLast(objapp);
226                 }
227             }
228             if(maximals.size() > 1)
229             {
230                 return AMBIGUOUS_METHOD;
231             }
232             return maximals.getFirst();
233         }
234         
235         private static Class JavaDoc[] getParameterTypes(Object JavaDoc obj)
236         {
237             if(obj instanceof Method JavaDoc)
238             {
239                 return ((Method JavaDoc)obj).getParameterTypes();
240             }
241             if(obj instanceof Constructor JavaDoc)
242             {
243                 return ((Constructor JavaDoc)obj).getParameterTypes();
244             }
245             // Cannot happen
246
throw new Error JavaDoc();
247         }
248         
249         private static int moreSpecific(Class JavaDoc[] c1, Class JavaDoc[] c2)
250         {
251             boolean c1MoreSpecific = false;
252             boolean c2MoreSpecific = false;
253             for(int i = 0; i < c1.length; ++i)
254             {
255                 if(c1[i] != c2[i])
256                 {
257                     c1MoreSpecific =
258                         c1MoreSpecific ||
259                         isMoreSpecific(c1[i], c2[i]);
260                     c2MoreSpecific =
261                         c2MoreSpecific ||
262                         isMoreSpecific(c2[i], c1[i]);
263                 }
264             }
265             if(c1MoreSpecific)
266             {
267                 if(c2MoreSpecific)
268                 {
269                     return INDETERMINATE;
270                 }
271                 return MORE_SPECIFIC;
272             }
273             if(c2MoreSpecific)
274             {
275                 return LESS_SPECIFIC;
276             }
277             return INDETERMINATE;
278         }
279         
280         
281         /**
282          * Returns all methods that are applicable to actual
283          * parameter classes represented by this ClassString object.
284          */

285         LinkedList JavaDoc getApplicables(List JavaDoc methods)
286         {
287             LinkedList JavaDoc list = new LinkedList JavaDoc();
288             for (Iterator JavaDoc imethod = methods.iterator(); imethod.hasNext();)
289             {
290                 Object JavaDoc method = imethod.next();
291                 if(isApplicable(method))
292                 {
293                     list.add(method);
294                 }
295                 
296             }
297             return list;
298         }
299         
300         /**
301          * Returns true if the supplied method is applicable to actual
302          * parameter classes represented by this ClassString object.
303          *
304          */

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

339         private static boolean isMethodInvocationConvertible(Class JavaDoc formal, Class JavaDoc actual)
340         {
341             // Check for identity or widening reference conversion
342
if(formal.isAssignableFrom(actual))
343             {
344                 return true;
345             }
346             // Check for boxing with widening primitive conversion. Note that
347
// actual parameters are never primitives.
348
if(formal.isPrimitive())
349             {
350                 if(formal == Boolean.TYPE && actual == Boolean JavaDoc.class)
351                     return true;
352                 if(formal == Character.TYPE && actual == Character JavaDoc.class)
353                     return true;
354                 if(formal == Byte.TYPE && actual == Byte JavaDoc.class)
355                     return true;
356                 if(formal == Short.TYPE &&
357                    (actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
358                     return true;
359                 if(formal == Integer.TYPE &&
360                    (actual == Integer JavaDoc.class || actual == Short JavaDoc.class ||
361                     actual == Byte JavaDoc.class))
362                     return true;
363                 if(formal == Long.TYPE &&
364                    (actual == Long JavaDoc.class || actual == Integer JavaDoc.class ||
365                     actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
366                     return true;
367                 if(formal == Float.TYPE &&
368                    (actual == Float JavaDoc.class || actual == Long JavaDoc.class ||
369                     actual == Integer JavaDoc.class || actual == Short JavaDoc.class ||
370                     actual == Byte JavaDoc.class))
371                     return true;
372                 if(formal == Double.TYPE &&
373                    (actual == Double JavaDoc.class || actual == Float JavaDoc.class ||
374                     actual == Long JavaDoc.class || actual == Integer JavaDoc.class ||
375                     actual == Short JavaDoc.class || actual == Byte JavaDoc.class))
376                     return true;
377             }
378             // Special case for BigDecimals as we deem BigDecimal to be
379
// convertible to any numeric type - either object or primitive.
380
// This can actually cause us trouble as this is a narrowing
381
// conversion, not widening.
382
return isBigDecimalConvertible(formal, actual);
383         }
384         
385         /**
386          * Determines whether a type represented by a class object is
387          * convertible to another type represented by a class object using a
388          * method invocation conversion, without matching object and primitive
389          * types. This method is used to determine the more specific type when
390          * comparing signatures of methods.
391          * @param formal the formal parameter type to which the actual
392          * parameter type should be convertible
393          * @param actual the actual parameter type.
394          * @return true if either formal type is assignable from actual type,
395          * or formal and actual are both primitive types and actual can be
396          * subject to widening conversion to formal.
397          */

398         private static boolean isMoreSpecific(Class JavaDoc specific, Class JavaDoc generic)
399         {
400             // Check for identity or widening reference conversion
401
if(generic.isAssignableFrom(specific))
402             {
403                 return true;
404             }
405             // Check for widening primitive conversion.
406
if(generic.isPrimitive())
407             {
408                 if(generic == Short.TYPE && (specific == Byte.TYPE))
409                     return true;
410                 if(generic == Integer.TYPE &&
411                    (specific == Short.TYPE || specific == Byte.TYPE))
412                     return true;
413                 if(generic == Long.TYPE &&
414                    (specific == Integer.TYPE || specific == Short.TYPE ||
415                     specific == Byte.TYPE))
416                     return true;
417                 if(generic == Float.TYPE &&
418                    (specific == Long.TYPE || specific == Integer.TYPE ||
419                     specific == Short.TYPE || specific == Byte.TYPE))
420                     return true;
421                 if(generic == Double.TYPE &&
422                    (specific == Float.TYPE || specific == Long.TYPE ||
423                     specific == Integer.TYPE || specific == Short.TYPE ||
424                     specific == Byte.TYPE))
425                     return true;
426             }
427             return isBigDecimalConvertible(generic, specific);
428         }
429         
430         private static boolean isBigDecimalConvertible(Class JavaDoc formal, Class JavaDoc actual)
431         {
432             // BigDecimal
433
if(BIGDECIMAL_CLASS.isAssignableFrom(actual))
434             {
435                 if(NUMBER_CLASS.isAssignableFrom(formal))
436                 {
437                     return true;
438                 }
439                 if(formal.isPrimitive() &&
440                    formal != Boolean.TYPE && formal != Character.TYPE)
441                 {
442                    return true;
443                 }
444             }
445             return false;
446         }
447         
448         private String JavaDoc listArgumentTypes()
449         {
450             StringBuffer JavaDoc buf =
451                 new StringBuffer JavaDoc(classes.length * 32).append('(');
452             for(int i = 0; i < classes.length; ++i)
453             {
454                 buf.append(classes[i].getName()).append(',');
455             }
456             buf.setLength(buf.length() - 1);
457             return buf.append(')').toString();
458         }
459     }
460 }
461
Popular Tags