KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > soap > util > MethodUtils


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

57
58 package org.apache.soap.util;
59
60 import java.util.*;
61 import java.io.*;
62 import java.beans.*;
63 import java.lang.reflect.*;
64
65 /**
66  * This file is a collection of reflection utilities for dealing with
67  * methods and constructors.
68  *
69  * @author Sanjiva Weerawarana
70  * @author Joseph Kesselman
71  */

72 public class MethodUtils {
73
74   //////////////////////////////////////////////////////////////////////////
75

76   /**
77    * Search for entry point, per Java Language Spec 1.0
78    * as amended, verified by comparison against compiler behavior.
79    *
80    * @param targetClass Class object for the class to be queried.
81    * @param methodName Name of method to invoke, or null for constructor.
82    * Only Public methods will be accepted.
83    * @param argTypes Classes of intended arguments. Note that primitives
84    * must be specified via their TYPE equivalents,
85    * rather than as their wrapper classes -- Integer.TYPE
86    * rather than Integer. "null" may be passed in as an
87    * indication that you intend to invoke the method with
88    * a literal null argument and therefore can accept
89    * any object type in this position.
90    * @param isStaticReference If true, and if the target is a Class object,
91    * only static methods will be accepted as valid matches.
92    *
93    * @return a Method or Constructor of the appropriate signature
94    *
95    * @exception SecurityException if security violation
96    * @exception NoSuchMethodException if no such method
97    */

98   static private Object JavaDoc getEntryPoint(Class JavaDoc targetClass,
99                                       String JavaDoc methodName,
100                                       Class JavaDoc[] argTypes,
101                                       boolean isStaticReference)
102        throws SecurityException JavaDoc, NoSuchMethodException JavaDoc
103   {
104     // 15.11.1: OBTAIN STARTING CLASS FOR SEARCH
105
Object JavaDoc m=null;
106     
107     // 15.11.2 DETERMINE ARGUMENT SIGNATURE
108
// (Passed in as argTypes array.)
109

110     // Shortcut: If an exact match exists, return it.
111
try {
112       if(methodName!=null)
113         {
114           m=targetClass.getMethod (methodName, argTypes);
115           if(isStaticReference &&
116              !Modifier.isStatic(entryGetModifiers(m)) )
117             {
118               throw
119                 new NoSuchMethodException JavaDoc (callToString (targetClass,
120                                                          methodName,
121                                                          argTypes,
122                                                          isStaticReference)+
123                                            " resolved to instance " + m);
124             }
125           return m;
126         }
127       else
128         return targetClass.getConstructor (argTypes);
129           
130     } catch (NoSuchMethodException JavaDoc e) {
131       // no-args has no alternatives!
132
if(argTypes==null || argTypes.length==0)
133       {
134         throw
135           new NoSuchMethodException JavaDoc (callToString (targetClass,
136                                                    methodName,
137                                                    argTypes,
138                                                    isStaticReference)+
139                                      " not found.");
140       }
141       // Else fall through.
142
}
143     
144     // Well, _that_ didn't work. Time to search for the Most Specific
145
// matching function. NOTE that conflicts are possible!
146

147     // 15.11.2.1 ACCESSIBLE: We apparently need to gather from two
148
// sources to be sure we have both instance and static methods.
149
Object JavaDoc[] methods;
150     if(methodName!=null)
151       {
152         methods=targetClass.getMethods();
153       }
154     else
155       {
156         methods=targetClass.getConstructors();
157       }
158     if(0==methods.length)
159       {
160         throw new NoSuchMethodException JavaDoc("No methods!");
161       }
162
163     MoreSpecific best=new MoreSpecific();
164     for(int i=0;i<methods.length;++i)
165       {
166         Object JavaDoc mi=methods[i];
167         if (
168             // 15.11.2.1 ACCESSIBLE: Method is public.
169
Modifier.isPublic(entryGetModifiers(mi))
170             &&
171             // 15.11.2.1 APPLICABLE: Right method name (or c'tor)
172
(methodName==null || entryGetName(mi).equals(methodName) )
173             &&
174             // 15.11.2.1 APPLICABLE: Parameters match arguments
175
areMethodConvertable(entryGetParameterTypes(mi),argTypes)
176              )
177           // 15.11.2.2 MORE SPECIFIC displace less specific.
178
best.addItem(mi);
179       }
180
181     // May throw NoSuchMethodException; we pass in info needed to
182
// create a useful exception
183
m=best.getMostSpecific(targetClass,methodName,argTypes,isStaticReference);
184   
185     // 15.11.3 APPROPRIATE: Class invocation can call only static
186
// methods. Note that the defined order of evaluation permits a
187
// call to be resolved to an inappropriate method and then
188
// rejected, rather than finding the best of the appropriate
189
// methods.
190
//
191
// Constructors are never static, so we don't test them.
192
if(m==null)
193       {
194         throw new NoSuchMethodException JavaDoc (callToString(targetClass,
195                                                       methodName,
196                                                       argTypes,
197                                                       isStaticReference)+
198                                          " -- no signature match");
199       }
200
201     if( methodName!=null &&
202         isStaticReference &&
203         !Modifier.isStatic(entryGetModifiers(m)) )
204       {
205         throw new NoSuchMethodException JavaDoc (callToString(targetClass,
206                                                       methodName,
207                                                       argTypes,
208                                                       isStaticReference)+
209                                          " resolved to instance: "+m);
210       }
211
212     return m;
213   }
214
215   
216   /** Internal subroutine for getEntryPoint(): Format arguments as a
217       string describing the function being searched for. Used in
218       verbose exceptions. */

219   private static String JavaDoc callToString(Class JavaDoc targetClass,String JavaDoc methodName,
220                                     Class JavaDoc[] argTypes,boolean isStaticReference)
221   {
222     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
223     if(isStaticReference)
224       buf.append("static ");
225     buf.append(StringUtils.getClassName(targetClass));
226     if(methodName!=null)
227       buf.append(".").append(methodName);
228     buf.append("(");
229     if (argTypes != null && argTypes.length>0) {
230       if(false)
231         {
232           // ????? Sanjiva has an ArrayToString method. Using it would save
233
// a few bytes, at cost of giving up some reusability.
234
}
235       else
236         {
237           buf.append(StringUtils.getClassName(argTypes[0]));
238           for (int i = 1; i < argTypes.length; i++) {
239             buf.append(",").append(StringUtils.getClassName(argTypes[i]));
240           }
241         }
242     }
243     else
244       buf.append("[none]");
245     buf.append(")");
246     return buf.toString();
247   }
248
249   /** Determine whether a given method parameter type can accept
250     arguments of another type.
251
252     parm: The type given in the method's signature.
253     arg: The type we want to pass in.
254
255     Legal METHOD CONVERSIONS (5.3) are Identity, Widening Primitive
256     Conversion, or Widening Reference Conversion. NOTE that this is a
257     subset of the legal ASSIGNMENT CONVERSIONS (5.2) -- in particular,
258     we can't implicitly narrow int to byte, short or char.
259
260     SPECIAL CASE: In order to permit invoking methods with literal
261     "null" values, setting the arg Class to null will be taken as a
262     request to match any Class type. POSSIBLE PROBLEM: This may match
263     a primitive type, which really should not accept a null value... but
264     I'm not sure how best to distinguish those, short of enumerating them
265     */

266   static private boolean isMethodConvertable(Class JavaDoc parm, Class JavaDoc arg)
267   {
268     if (parm.equals(arg)) // If same class, short-circuit now!
269
return true;
270
271     // Accept any type EXCEPT primitives (which can't have null values).
272
if (arg == null)
273     {
274       return !parm.isPrimitive();
275     }
276
277     // Arrays are convertable if their elements are convertable
278
// ????? Does this have to be done before isAssignableFrom, or
279
// does it successfully handle arrays of primatives?
280
while(parm.isArray())
281       {
282         if(!arg.isArray())
283           return false; // Unequal array depth
284
else
285           {
286             parm=parm.getComponentType();
287             arg=arg.getComponentType();
288           }
289       }
290     if(arg.isArray())
291       return false; // Unequal array depth
292

293     // Despite its name, the 1.1.6 docs say that this function does
294
// NOT return true for all legal ASSIGNMENT CONVERSIONS
295
// (5.2):
296
// "Specifically, this method tests whether the type
297
// represented by the specified class can be converted
298
// to the type represented by this Class object via
299
// an identity conversion or via a widening reference
300
// conversion."
301
if(parm.isAssignableFrom(arg))
302       return true;
303
304     // That leaves us the Widening Primitives case. Four possibilities:
305
// void (can only convert to void), boolean (can only convert to boolean),
306
// numeric (which are sequenced) and char (which inserts itself into the
307
// numerics by promoting to int or larger)
308

309     if(parm.equals(Void.TYPE) || parm.equals(Boolean.TYPE) ||
310        arg.equals(Void.TYPE) || arg.equals(Boolean.TYPE))
311       return false;
312     
313     Class JavaDoc[] primTypes={ Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE,
314                         Long.TYPE, Float.TYPE, Double.TYPE };
315     int parmscore,argscore;
316     
317     for(parmscore=0;parmscore<primTypes.length;++parmscore)
318       if (parm.equals(primTypes[parmscore]))
319         break;
320     if(parmscore>=primTypes.length)
321       return false; // Off the end
322

323     for(argscore=0;argscore<primTypes.length;++argscore)
324       if (arg.equals(primTypes[argscore]))
325         break;
326     if(argscore>=primTypes.length)
327       return false; // Off the end
328

329     // OK if ordered AND NOT char-to-smaller-than-int
330
return (argscore<parmscore && (argscore!=0 || parmscore>2) );
331   }
332
333   
334   /** Determine whether a given type can accept assignments of another
335     type. Note that class.isAssignable() is _not_ a complete test!
336     (This method is not needed by getMethod() or getConstructor(), but
337     is provided as a convenience for other users.)
338     
339     parm: The type given in the method's signature.
340     arg: The type we want to pass in.
341
342     Legal ASSIGNMENT CONVERSIONS (5.2) are METHOD CONVERSIONS (5.3)
343     plus implicit narrowing of int to byte, short or char. */

344   static private boolean isAssignmentConvertable(Class JavaDoc parm,Class JavaDoc arg)
345   {
346     return
347       (arg.equals(Integer.TYPE) &&
348        (parm.equals(Byte.TYPE) ||
349         parm.equals(Short.TYPE) ||
350         parm.equals(Character.TYPE)
351          )
352         ) ||
353       isMethodConvertable(parm,arg);
354   }
355
356
357   /** Convenience method: Test an entire parameter-list/argument-list pair
358     for isMethodConvertable(), qv.
359     */

360   static private boolean areMethodConvertable(Class JavaDoc[] parms,Class JavaDoc[] args)
361   {
362     if(parms.length!=args.length)
363       return false;
364     
365     for(int i=0;i<parms.length;++i)
366       if(!isMethodConvertable(parms[i],args[i]))
367         return false;
368     
369     return true;
370   }
371
372   /** Internal Class for getEntryPoint(). Implements 15.11.2.2 MORE
373     SPECIFIC rules.
374
375     Retains a list of methods (already known to match the
376     arguments). As each method is added, we check against past entries
377     to determine which if any is "more specific" -- defined as having
378     _all_ its arguments (not just a preponderance) be
379     method-convertable into those of another. If such a relationship
380     is found, the more-specific method is retained and the
381     less-specific method is discarded. At the end, if this has yielded
382     a single winner it is considered the Most Specific Method and
383     hence the one that should be invoked. Otherwise, a
384     NoSuchMethodException is thrown.
385     
386     PERFORMANCE VERSUS ARCHITECTURE: Arguably, this should "have-a"
387     Vector. But the code is 6% smaller, and possibly faster, if we
388     code it as "is-a" Vector. Since it's an inner class, nobody's
389     likely to abuse the privilage.
390     
391     Note: "Static" in the case of an inner class means "Does not
392     reference instance data in the outer class", and is required since
393     our caller is a static method. */

394   private static class MoreSpecific
395   extends Vector
396   {
397     /** Submit an entry-point to the list. May be discarded if a past
398       entry is more specific, or may cause others to be discarded it
399       if is more specific.
400
401       newEntry: Method or Constructor under consideration.
402       */

403     void addItem (Object JavaDoc newEntry)
404     {
405       if(size()==0)
406         addElement(newEntry);
407       else
408         {
409           Class JavaDoc[] newargs=entryGetParameterTypes(newEntry);
410           boolean keep=true;
411           for (Enumeration e = elements();
412                keep & e.hasMoreElements() ;
413                 )
414             {
415               Object JavaDoc oldEntry=e.nextElement();
416               // CAVEAT: Implicit references to enclosing class!
417
Class JavaDoc[] oldargs=entryGetParameterTypes(oldEntry);
418               if(areMethodConvertable(oldargs,newargs))
419                 removeElement(oldEntry); // New more specific; discard old
420
else if(areMethodConvertable(newargs,oldargs))
421                 keep=false; // Old more specific; discard new
422
// Else they're tied. Keep both and hope someone beats both.
423
}
424           if(keep)
425             addElement(newEntry);
426         }
427     }
428
429     /** Obtain the single Most Specific entry-point. If there is no clear
430       winner, or if the list is empty, throw NoSuchMethodException.
431
432       Arguments describe the call we were hoping to resolve. They are
433       used to throw a nice verbose exception if something goes wrong.
434       */

435     Object JavaDoc getMostSpecific(Class JavaDoc targetClass,String JavaDoc methodName,
436                            Class JavaDoc[] argTypes,boolean isStaticReference)
437          throws NoSuchMethodException JavaDoc
438     {
439       if(size()==1)
440         return firstElement();
441       if(size()>1)
442         {
443           StringBuffer JavaDoc buf=new StringBuffer JavaDoc();
444           Enumeration e=elements();
445           buf.append(e.nextElement());
446           while(e.hasMoreElements())
447             buf.append(" and ").append(e.nextElement());
448           throw new NoSuchMethodException JavaDoc (callToString(targetClass,
449                                                         methodName,
450                                                         argTypes,
451                                                         isStaticReference)+
452                                            " is ambiguous. It matches "+
453                                            buf.toString());
454         }
455       return null;
456     }
457   }
458
459   // The common lookup code would be much easier if Method and
460
// Constructor shared an "EntryPoint" Interface. Unfortunately, even
461
// though their APIs are almost identical, they don't. These calls
462
// are a workaround... at the cost of additional runtime overhead
463
// and some extra bytecodes.
464
//
465
// (A JDK bug report has been submitted requesting that they add the
466
// Interface; it would be easy, harmless, and useful.)
467

468   /** Utility function: obtain common data from either Method or
469       Constructor. (In lieu of an EntryPoint interface.) */

470   static String JavaDoc entryGetName(Object JavaDoc entry)
471   {
472     return (entry instanceof Method)
473       ? ((Method)entry).getName()
474       : ((Constructor)entry).getName();
475   }
476   /** Utility function: obtain common data from either Method or
477       Constructor. (In lieu of an EntryPoint interface.) */

478   static int entryGetModifiers(Object JavaDoc entry)
479   {
480     return (entry instanceof Method)
481       ? ((Method)entry).getModifiers()
482       : ((Constructor)entry).getModifiers();
483   }
484   /** Utility function: obtain common data from either Method or
485       Constructor. (In lieu of an EntryPoint interface.) */

486   static Class JavaDoc[] entryGetParameterTypes(Object JavaDoc entry)
487   {
488     return (entry instanceof Method)
489       ? ((Method)entry).getParameterTypes()
490       : ((Constructor)entry).getParameterTypes();
491   }
492   /** Utility function: obtain common data from either Method or
493       Constructor. (In lieu of an EntryPoint interface.) */

494   static String JavaDoc entryToString(Object JavaDoc entry)
495   {
496     return (entry instanceof Method)
497       ? ((Method)entry).toString()
498       : ((Constructor)entry).toString();
499   }
500
501   //////////////////////////////////////////////////////////////////////////
502

503   /**
504    * Class.getMethod() finds only the entry point (if any) _exactly_
505    * matching the specified argument types. Our implmentation can
506    * decide between several imperfect matches, using the same search
507    * algorithm as the Java compiler.
508    *
509    * This version emulates the compiler behavior by allowing lookup to
510    * be performed against either a class or an instance -- classname.foo()
511    * must be a static method call, instance.foo() can invoke either static
512    * or instance methods.
513    *
514    * @param target object on which call is to be made
515    * @param methodName name of method I'm lookin' for
516    * @param argTypes array of argument types of method
517    *
518    * @return the desired method
519    *
520    * @exception SecurityException if security violation
521    * @exception NoSuchMethodException if no such method
522    */

523   static public Method getMethod(Object JavaDoc target,String JavaDoc methodName,
524                                  Class JavaDoc[] argTypes)
525        throws SecurityException JavaDoc, NoSuchMethodException JavaDoc
526   {
527     boolean staticRef=target instanceof Class JavaDoc;
528     return getMethod( staticRef ? (Class JavaDoc)target : target.getClass(),
529                       methodName,argTypes,staticRef);
530   }
531
532   //////////////////////////////////////////////////////////////////////////
533

534   /* Class.getMethod() finds only the entry point (if any) _exactly_
535     matching the specified argument types. Our implmentation can
536     decide between several imperfect matches, using the same search
537     algorithm as the Java compiler.
538
539     This version more closely resembles Class.getMethod() -- we always
540     ask the Class for the method. It differs in testing for
541     appropriateness before returning the method; if the query is
542     being made via a static reference, only static methods will be
543     found and returned. */

544   static public Method getMethod(Class JavaDoc target,String JavaDoc methodName,
545                                  Class JavaDoc[] argTypes,boolean isStaticReference)
546        throws SecurityException JavaDoc, NoSuchMethodException JavaDoc
547   {
548     return (Method)getEntryPoint(target,methodName,argTypes,isStaticReference);
549   }
550
551   //////////////////////////////////////////////////////////////////////////
552

553   /** Class.getConstructor() finds only the entry point (if any)
554     _exactly_ matching the specified argument types. Our implmentation
555     can decide between several imperfect matches, using the same
556     search algorithm as the Java compiler.
557
558     Note that all constructors are static by definition, so
559     isStaticReference is true.
560
561     @exception NoSuchMethodException if constructor not found.
562     */

563   static public Constructor getConstructor(Class JavaDoc targetClass, Class JavaDoc[] argTypes)
564        throws SecurityException JavaDoc, NoSuchMethodException JavaDoc
565   {
566     return (Constructor) getEntryPoint(targetClass,null,argTypes,true);
567   }
568 }
569
Popular Tags