KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > ReflectionUtils


1 /*
2  * @(#)ReflectionUtils.java 1.10 05/08/26
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7 package java.beans;
8
9 import java.lang.reflect.Constructor JavaDoc;
10 import java.lang.reflect.Field JavaDoc;
11 import java.lang.reflect.Method JavaDoc;
12 import java.lang.reflect.Modifier JavaDoc;
13
14 import java.lang.ref.Reference JavaDoc;
15 import java.lang.ref.SoftReference JavaDoc;
16
17 import java.util.*;
18
19 import com.sun.beans.ObjectHandler;
20 import sun.reflect.misc.MethodUtil;
21 import sun.reflect.misc.ConstructorUtil;
22 import sun.reflect.misc.ReflectUtil;
23
24 /**
25  * A utility class for reflectively finding methods, constuctors and fields
26  * using reflection.
27  */

28 class ReflectionUtils {
29
30     private static Reference JavaDoc methodCacheRef;
31
32     public static Class JavaDoc typeToClass(Class JavaDoc type) {
33         return type.isPrimitive() ? ObjectHandler.typeNameToClass(type.getName()) : type;
34     }
35
36     public static boolean isPrimitive(Class JavaDoc type) {
37         return primitiveTypeFor(type) != null;
38     }
39
40     public static Class JavaDoc primitiveTypeFor(Class JavaDoc wrapper) {
41         if (wrapper == Boolean JavaDoc.class) return Boolean.TYPE;
42         if (wrapper == Byte JavaDoc.class) return Byte.TYPE;
43         if (wrapper == Character JavaDoc.class) return Character.TYPE;
44         if (wrapper == Short JavaDoc.class) return Short.TYPE;
45         if (wrapper == Integer JavaDoc.class) return Integer.TYPE;
46         if (wrapper == Long JavaDoc.class) return Long.TYPE;
47         if (wrapper == Float JavaDoc.class) return Float.TYPE;
48         if (wrapper == Double JavaDoc.class) return Double.TYPE;
49         if (wrapper == Void JavaDoc.class) return Void.TYPE;
50         return null;
51     }
52
53     /**
54      * Tests each element on the class arrays for assignability.
55      *
56      * @param argClasses arguments to be tested
57      * @param argTypes arguments from Method
58      * @return true if each class in argTypes is assignable from the
59      * corresponding class in argClasses.
60      */

61     private static boolean matchArguments(Class JavaDoc[] argClasses, Class JavaDoc[] argTypes) {
62     return matchArguments(argClasses, argTypes, false);
63     }
64
65     /**
66      * Tests each element on the class arrays for equality.
67      *
68      * @param argClasses arguments to be tested
69      * @param argTypes arguments from Method
70      * @return true if each class in argTypes is equal to the
71      * corresponding class in argClasses.
72      */

73     private static boolean matchExplicitArguments(Class JavaDoc[] argClasses, Class JavaDoc[] argTypes) {
74     return matchArguments(argClasses, argTypes, true);
75     }
76
77     private static boolean matchArguments(Class JavaDoc[] argClasses,
78                       Class JavaDoc[] argTypes, boolean explicit) {
79     
80         boolean match = (argClasses.length == argTypes.length);
81     for(int j = 0; j < argClasses.length && match; j++) {
82         Class JavaDoc argType = argTypes[j];
83         if (argType.isPrimitive()) {
84         argType = typeToClass(argType);
85         }
86         if (explicit) {
87         // Test each element for equality
88
if (argClasses[j] != argType) {
89             match = false;
90         }
91         } else {
92         // Consider null an instance of all classes.
93
if (argClasses[j] != null &&
94             !(argType.isAssignableFrom(argClasses[j]))) {
95             match = false;
96         }
97         }
98     }
99         return match;
100     }
101
102     /**
103      * @return the method which best matches the signature or throw an exception
104      * if it can't be found or the method is ambiguous.
105      */

106     static Method JavaDoc getPublicMethod(Class JavaDoc declaringClass, String JavaDoc methodName,
107                        Class JavaDoc[] argClasses) throws NoSuchMethodException JavaDoc {
108     Method JavaDoc m;
109
110     m = findPublicMethod(declaringClass, methodName, argClasses);
111     if (m == null)
112         throw new NoSuchMethodException JavaDoc(declaringClass.getName() + "." + methodName);
113     return m;
114     }
115
116     /**
117      * @return the method which best matches the signature or null if it cant be found or
118      * the method is ambiguous.
119      */

120     public static Method JavaDoc findPublicMethod(Class JavaDoc declaringClass, String JavaDoc methodName,
121                        Class JavaDoc[] argClasses) {
122         // Many methods are "getters" which take no arguments.
123
// This permits the following optimisation which
124
// avoids the expensive call to getMethods().
125
if (argClasses.length == 0) {
126             try {
127                 return MethodUtil.getMethod(declaringClass, methodName, argClasses);
128             }
129             catch (NoSuchMethodException JavaDoc e) {
130                   return null;
131             } catch (SecurityException JavaDoc se) {
132         // fall through
133
}
134         }
135         Method JavaDoc[] methods = MethodUtil.getPublicMethods(declaringClass);
136     List list = new ArrayList();
137         for(int i = 0; i < methods.length; i++) {
138         // Collect all the methods which match the signature.
139
Method JavaDoc method = methods[i];
140             if (method.getName().equals(methodName)) {
141                 if (matchArguments(argClasses, method.getParameterTypes())) {
142             list.add(method);
143                 }
144             }
145     }
146     if (list.size() > 0) {
147         if (list.size() == 1) {
148         return (Method JavaDoc)list.get(0);
149         }
150         else {
151         ListIterator iterator = list.listIterator();
152         Method JavaDoc method;
153         while (iterator.hasNext()) {
154             method = (Method JavaDoc)iterator.next();
155             if (matchExplicitArguments(argClasses, method.getParameterTypes())) {
156             return method;
157             }
158         }
159         // There are more than one method which matches this signature.
160
// try to return the most specific method.
161
return getMostSpecificMethod(list, argClasses);
162         }
163     }
164         return null;
165     }
166
167     /**
168      * Return the most specific method from the list of methods which
169      * matches the args. The most specific method will have the most
170      * number of equal parameters or will be closest in the inheritance
171      * heirarchy to the runtime execution arguments.
172      * <p>
173      * See the JLS section 15.12
174      * http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#20448
175      *
176      * @param methods List of methods which already have the same param length
177      * and arg types are assignable to param types
178      * @param args an array of param types to match
179      * @return method or null if a specific method cannot be determined
180      */

181     private static Method JavaDoc getMostSpecificMethod(List methods, Class JavaDoc[] args) {
182     Method JavaDoc method = null;
183     
184     int matches = 0;
185     int lastMatch = matches;
186
187     ListIterator iterator = methods.listIterator();
188     while (iterator.hasNext()) {
189         Method JavaDoc m = (Method JavaDoc)iterator.next();
190         Class JavaDoc[] mArgs = m.getParameterTypes();
191         matches = 0;
192         for (int i = 0; i < args.length; i++) {
193         Class JavaDoc mArg = mArgs[i];
194         if (mArg.isPrimitive()) {
195             mArg = typeToClass(mArg);
196         }
197         if (args[i] == mArg) {
198             matches++;
199         }
200         }
201         if (matches == 0 && lastMatch == 0) {
202         if (method == null) {
203             method = m;
204         } else {
205             // Test existing method. We already know that the args can
206
// be assigned to all the method params. However, if the
207
// current method parameters is higher in the inheritance
208
// hierarchy then replace it.
209
if (!matchArguments(method.getParameterTypes(),
210                     m.getParameterTypes())) {
211             method = m;
212             }
213         }
214         } else if (matches > lastMatch) {
215         lastMatch = matches;
216         method = m;
217         } else if (matches == lastMatch) {
218         // ambiguous method selection.
219
method = null;
220         }
221     }
222     return method;
223     }
224
225     /**
226      * @return the method or null if it can't be found or is ambiguous.
227      */

228     public static Method JavaDoc findMethod(Class JavaDoc targetClass, String JavaDoc methodName,
229                     Class JavaDoc[] argClasses) {
230         Method JavaDoc m = findPublicMethod(targetClass, methodName, argClasses);
231         if (m != null && Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
232             return m;
233         }
234
235         /*
236         Search the interfaces for a public version of this method.
237
238         Example: the getKeymap() method of a JTextField
239         returns a package private implementation of the
240         of the public Keymap interface. In the Keymap
241         interface there are a number of "properties" one
242         being the "resolveParent" property implied by the
243         getResolveParent() method. This getResolveParent()
244         cannot be called reflectively because the class
245         itself is not public. Instead we search the class's
246         interfaces and find the getResolveParent()
247         method of the Keymap interface - on which invoke
248         may be applied without error.
249
250         So in :-
251
252             JTextField o = new JTextField("Hello, world");
253             Keymap km = o.getKeymap();
254             Method m1 = km.getClass().getMethod("getResolveParent", new Class[0]);
255             Method m2 = Keymap.class.getMethod("getResolveParent", new Class[0]);
256
257         Methods m1 and m2 are different. The invocation of method
258         m1 unconditionally throws an IllegalAccessException where
259         the invocation of m2 will invoke the implementation of the
260         method. Note that (ignoring the overloading of arguments)
261         there is only one implementation of the named method which
262         may be applied to this target.
263         */

264         for(Class JavaDoc type = targetClass; type != null; type = type.getSuperclass()) {
265             Class JavaDoc[] interfaces = type.getInterfaces();
266             for(int i = 0; i < interfaces.length; i++) {
267                 m = findPublicMethod(interfaces[i], methodName, argClasses);
268                 if (m != null) {
269                     return m;
270                 }
271             }
272         }
273         return null;
274     }
275
276     /**
277      * A class that represents the unique elements of a method that will be a
278      * key in the method cache.
279      */

280     private static class Signature {
281         private Class JavaDoc targetClass;
282         private String JavaDoc methodName;
283         private Class JavaDoc[] argClasses;
284
285     private volatile int hashCode = 0;
286
287         public Signature(Class JavaDoc targetClass, String JavaDoc methodName, Class JavaDoc[] argClasses) {
288             this.targetClass = targetClass;
289             this.methodName = methodName;
290             this.argClasses = argClasses;
291         }
292
293         public boolean equals(Object JavaDoc o2) {
294         if (this == o2) {
295         return true;
296         }
297             Signature that = (Signature)o2;
298             if (!(targetClass == that.targetClass)) {
299                 return false;
300             }
301             if (!(methodName.equals(that.methodName))) {
302                 return false;
303             }
304             if (argClasses.length != that.argClasses.length) {
305                 return false;
306             }
307             for (int i = 0; i < argClasses.length; i++) {
308                 if (!(argClasses[i] == that.argClasses[i])) {
309                   return false;
310                 }
311             }
312             return true;
313         }
314
315     /**
316      * Hash code computed using algorithm suggested in
317      * Effective Java, Item 8.
318      */

319         public int hashCode() {
320         if (hashCode == 0) {
321         int result = 17;
322         result = 37 * result + targetClass.hashCode();
323         result = 37 * result + methodName.hashCode();
324         if (argClasses != null) {
325             for (int i = 0; i < argClasses.length; i++) {
326             result = 37 * result + ((argClasses[i] == null) ? 0 :
327                 argClasses[i].hashCode());
328             }
329         }
330         hashCode = result;
331         }
332             return hashCode;
333         }
334     }
335
336     /**
337      * A wrapper to findMethod(), which will search or populate the method
338      * in a cache.
339      * @throws exception if the method is ambiguios.
340      */

341     public static synchronized Method JavaDoc getMethod(Class JavaDoc targetClass,
342                         String JavaDoc methodName,
343                         Class JavaDoc[] argClasses) {
344     Object JavaDoc signature = new Signature(targetClass, methodName, argClasses);
345
346     Method JavaDoc method = null;
347     Map methodCache = null;
348     boolean cache = false;
349     if (ReflectUtil.isPackageAccessible(targetClass)) {
350         cache = true;
351     }
352
353     if (cache && methodCacheRef != null &&
354         (methodCache = (Map)methodCacheRef.get()) != null) {
355         method = (Method JavaDoc)methodCache.get(signature);
356         if (method != null) {
357         return method;
358         }
359     }
360     method = findMethod(targetClass, methodName, argClasses);
361         if (cache && method != null) {
362         if (methodCache == null) {
363         methodCache = new HashMap();
364         methodCacheRef = new SoftReference JavaDoc(methodCache);
365         }
366             methodCache.put(signature, method);
367         }
368         return method;
369     }
370
371     /**
372      * Return a constructor on the class with the arguments.
373      *
374      * @throws exception if the method is ambiguios.
375      */

376     public static Constructor JavaDoc getConstructor(Class JavaDoc cls, Class JavaDoc[] args) {
377     Constructor JavaDoc constructor = null;
378
379     // PENDING: Implement the resolutuion of ambiguities properly.
380
Constructor JavaDoc[] ctors = ConstructorUtil.getConstructors(cls);
381     for(int i = 0; i < ctors.length; i++) {
382         if (matchArguments(args, ctors[i].getParameterTypes())) {
383         constructor = ctors[i];
384         }
385     }
386     return constructor;
387     }
388
389     public static Object JavaDoc getPrivateField(Object JavaDoc instance, Class JavaDoc cls, String JavaDoc name) {
390     return getPrivateField(instance, cls, name);
391     }
392
393     /**
394      * Returns the value of a private field.
395      *
396      * @param instance object instance
397      * @param cls class
398      * @param name name of the field
399      * @param el an exception listener to handle exceptions; or null
400      * @return value of the field; null if not found or an error is encountered
401      */

402     public static Object JavaDoc getPrivateField(Object JavaDoc instance, Class JavaDoc cls,
403                      String JavaDoc name, ExceptionListener JavaDoc el) {
404         try {
405             Field JavaDoc f = cls.getDeclaredField(name);
406             f.setAccessible(true);
407             return f.get(instance);
408         }
409         catch (Exception JavaDoc e) {
410         if (el != null) {
411         el.exceptionThrown(e);
412         }
413         }
414         return null;
415     }
416 }
417
Popular Tags