jdk/src/share/classes/java/beans/ReflectionUtils.java
changeset 1851 8203c7eb8caf
parent 1844 ac2cf8242428
child 5506 202f599c92aa
equal deleted inserted replaced
1850:be2da26d2b21 1851:8203c7eb8caf
     1 /*
     1 /*
     2  * Copyright 2003-2008 Sun Microsystems, Inc.  All Rights Reserved.
     2  * Copyright 2003-2009 Sun Microsystems, Inc.  All Rights Reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Sun designates this
     7  * published by the Free Software Foundation.  Sun designates this
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
    23  * have any questions.
    23  * have any questions.
    24  */
    24  */
    25 package java.beans;
    25 package java.beans;
    26 
    26 
    27 import com.sun.beans.finder.PrimitiveWrapperMap;
       
    28 
       
    29 import java.lang.reflect.Constructor;
       
    30 import java.lang.reflect.Field;
    27 import java.lang.reflect.Field;
    31 import java.lang.reflect.Method;
       
    32 import java.lang.reflect.Modifier;
       
    33 
       
    34 import java.lang.ref.Reference;
       
    35 import java.lang.ref.SoftReference;
       
    36 
       
    37 import java.util.*;
       
    38 
       
    39 import sun.reflect.misc.MethodUtil;
       
    40 import sun.reflect.misc.ConstructorUtil;
       
    41 import sun.reflect.misc.ReflectUtil;
       
    42 
    28 
    43 /**
    29 /**
    44  * A utility class for reflectively finding methods, constuctors and fields
    30  * A utility class for reflectively finding methods, constuctors and fields
    45  * using reflection.
    31  * using reflection.
    46  */
    32  */
    47 class ReflectionUtils {
    33 class ReflectionUtils {
    48 
       
    49     private static Reference methodCacheRef;
       
    50 
    34 
    51     public static boolean isPrimitive(Class type) {
    35     public static boolean isPrimitive(Class type) {
    52         return primitiveTypeFor(type) != null;
    36         return primitiveTypeFor(type) != null;
    53     }
    37     }
    54 
    38 
    61         if (wrapper == Long.class) return Long.TYPE;
    45         if (wrapper == Long.class) return Long.TYPE;
    62         if (wrapper == Float.class) return Float.TYPE;
    46         if (wrapper == Float.class) return Float.TYPE;
    63         if (wrapper == Double.class) return Double.TYPE;
    47         if (wrapper == Double.class) return Double.TYPE;
    64         if (wrapper == Void.class) return Void.TYPE;
    48         if (wrapper == Void.class) return Void.TYPE;
    65         return null;
    49         return null;
    66     }
       
    67 
       
    68     /**
       
    69      * Tests each element on the class arrays for assignability.
       
    70      *
       
    71      * @param argClasses arguments to be tested
       
    72      * @param argTypes arguments from Method
       
    73      * @return true if each class in argTypes is assignable from the
       
    74      *         corresponding class in argClasses.
       
    75      */
       
    76     private static boolean matchArguments(Class[] argClasses, Class[] argTypes) {
       
    77         return matchArguments(argClasses, argTypes, false);
       
    78     }
       
    79 
       
    80     /**
       
    81      * Tests each element on the class arrays for equality.
       
    82      *
       
    83      * @param argClasses arguments to be tested
       
    84      * @param argTypes arguments from Method
       
    85      * @return true if each class in argTypes is equal to the
       
    86      *         corresponding class in argClasses.
       
    87      */
       
    88     private static boolean matchExplicitArguments(Class[] argClasses, Class[] argTypes) {
       
    89         return matchArguments(argClasses, argTypes, true);
       
    90     }
       
    91 
       
    92     private static boolean matchArguments(Class[] argClasses,
       
    93                                           Class[] argTypes, boolean explicit) {
       
    94 
       
    95         boolean match = (argClasses.length == argTypes.length);
       
    96         for(int j = 0; j < argClasses.length && match; j++) {
       
    97             Class argType = argTypes[j];
       
    98             if (argType.isPrimitive()) {
       
    99                 argType = PrimitiveWrapperMap.getType(argType.getName());
       
   100             }
       
   101             if (explicit) {
       
   102                 // Test each element for equality
       
   103                 if (argClasses[j] != argType) {
       
   104                     match = false;
       
   105                 }
       
   106             } else {
       
   107                 // Consider null an instance of all classes.
       
   108                 if (argClasses[j] != null &&
       
   109                     !(argType.isAssignableFrom(argClasses[j]))) {
       
   110                     match = false;
       
   111                 }
       
   112             }
       
   113         }
       
   114         return match;
       
   115     }
       
   116 
       
   117     /**
       
   118      * @return the method which best matches the signature or throw an exception
       
   119      *         if it can't be found or the method is ambiguous.
       
   120      */
       
   121     static Method getPublicMethod(Class declaringClass, String methodName,
       
   122                                            Class[] argClasses) throws NoSuchMethodException {
       
   123         Method m;
       
   124 
       
   125         m = findPublicMethod(declaringClass, methodName, argClasses);
       
   126         if (m == null)
       
   127             throw new NoSuchMethodException(declaringClass.getName() + "." + methodName);
       
   128         return m;
       
   129     }
       
   130 
       
   131     /**
       
   132      * @return the method which best matches the signature or null if it cant be found or
       
   133      *         the method is ambiguous.
       
   134      */
       
   135     public static Method findPublicMethod(Class declaringClass, String methodName,
       
   136                                            Class[] argClasses) {
       
   137         // Many methods are "getters" which take no arguments.
       
   138         // This permits the following optimisation which
       
   139         // avoids the expensive call to getMethods().
       
   140         if (argClasses.length == 0) {
       
   141             try {
       
   142                 return MethodUtil.getMethod(declaringClass, methodName, argClasses);
       
   143             }
       
   144             catch (NoSuchMethodException e) {
       
   145                   return null;
       
   146             } catch (SecurityException se) {
       
   147                 // fall through
       
   148             }
       
   149         }
       
   150         Method[] methods = MethodUtil.getPublicMethods(declaringClass);
       
   151         List list = new ArrayList();
       
   152         for(int i = 0; i < methods.length; i++) {
       
   153             // Collect all the methods which match the signature.
       
   154             Method method = methods[i];
       
   155             if (method.getName().equals(methodName)) {
       
   156                 if (matchArguments(argClasses, method.getParameterTypes())) {
       
   157                     list.add(method);
       
   158                 }
       
   159             }
       
   160         }
       
   161         if (list.size() > 0) {
       
   162             if (list.size() == 1) {
       
   163                 return (Method)list.get(0);
       
   164             }
       
   165             else {
       
   166                 ListIterator iterator = list.listIterator();
       
   167                 Method method;
       
   168                 while (iterator.hasNext()) {
       
   169                     method = (Method)iterator.next();
       
   170                     if (matchExplicitArguments(argClasses, method.getParameterTypes())) {
       
   171                         return method;
       
   172                     }
       
   173                 }
       
   174                 // There are more than one method which matches this signature.
       
   175                 // try to return the most specific method.
       
   176                 return getMostSpecificMethod(list, argClasses);
       
   177             }
       
   178         }
       
   179         return null;
       
   180     }
       
   181 
       
   182     /**
       
   183      * Return the most specific method from the list of methods which
       
   184      * matches the args. The most specific method will have the most
       
   185      * number of equal parameters or will be closest in the inheritance
       
   186      * heirarchy to the runtime execution arguments.
       
   187      * <p>
       
   188      * See the JLS section 15.12
       
   189      * http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#20448
       
   190      *
       
   191      * @param methods List of methods which already have the same param length
       
   192      *                and arg types are assignable to param types
       
   193      * @param args an array of param types to match
       
   194      * @return method or null if a specific method cannot be determined
       
   195      */
       
   196     private static Method getMostSpecificMethod(List methods, Class[] args) {
       
   197         Method method = null;
       
   198 
       
   199         int matches = 0;
       
   200         int lastMatch = matches;
       
   201 
       
   202         ListIterator iterator = methods.listIterator();
       
   203         while (iterator.hasNext()) {
       
   204             Method m = (Method)iterator.next();
       
   205             Class[] mArgs = m.getParameterTypes();
       
   206             matches = 0;
       
   207             for (int i = 0; i < args.length; i++) {
       
   208                 Class mArg = mArgs[i];
       
   209                 if (mArg.isPrimitive()) {
       
   210                     mArg = PrimitiveWrapperMap.getType(mArg.getName());
       
   211                 }
       
   212                 if (args[i] == mArg) {
       
   213                     matches++;
       
   214                 }
       
   215             }
       
   216             if (matches == 0 && lastMatch == 0) {
       
   217                 if (method == null) {
       
   218                     method = m;
       
   219                 } else {
       
   220                     // Test existing method. We already know that the args can
       
   221                     // be assigned to all the method params. However, if the
       
   222                     // current method parameters is higher in the inheritance
       
   223                     // hierarchy then replace it.
       
   224                     if (!matchArguments(method.getParameterTypes(),
       
   225                                         m.getParameterTypes())) {
       
   226                         method = m;
       
   227                     }
       
   228                 }
       
   229             } else if (matches > lastMatch) {
       
   230                 lastMatch = matches;
       
   231                 method = m;
       
   232             } else if (matches == lastMatch) {
       
   233                 // ambiguous method selection.
       
   234                 method = null;
       
   235             }
       
   236         }
       
   237         return method;
       
   238     }
       
   239 
       
   240     /**
       
   241      * @return the method or null if it can't be found or is ambiguous.
       
   242      */
       
   243     public static Method findMethod(Class targetClass, String methodName,
       
   244                                     Class[] argClasses) {
       
   245         Method m = findPublicMethod(targetClass, methodName, argClasses);
       
   246         if (m != null && Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
       
   247             return m;
       
   248         }
       
   249 
       
   250         /*
       
   251         Search the interfaces for a public version of this method.
       
   252 
       
   253         Example: the getKeymap() method of a JTextField
       
   254         returns a package private implementation of the
       
   255         of the public Keymap interface. In the Keymap
       
   256         interface there are a number of "properties" one
       
   257         being the "resolveParent" property implied by the
       
   258         getResolveParent() method. This getResolveParent()
       
   259         cannot be called reflectively because the class
       
   260         itself is not public. Instead we search the class's
       
   261         interfaces and find the getResolveParent()
       
   262         method of the Keymap interface - on which invoke
       
   263         may be applied without error.
       
   264 
       
   265         So in :-
       
   266 
       
   267             JTextField o = new JTextField("Hello, world");
       
   268             Keymap km = o.getKeymap();
       
   269             Method m1 = km.getClass().getMethod("getResolveParent", new Class[0]);
       
   270             Method m2 = Keymap.class.getMethod("getResolveParent", new Class[0]);
       
   271 
       
   272         Methods m1 and m2 are different. The invocation of method
       
   273         m1 unconditionally throws an IllegalAccessException where
       
   274         the invocation of m2 will invoke the implementation of the
       
   275         method. Note that (ignoring the overloading of arguments)
       
   276         there is only one implementation of the named method which
       
   277         may be applied to this target.
       
   278         */
       
   279         for(Class type = targetClass; type != null; type = type.getSuperclass()) {
       
   280             Class[] interfaces = type.getInterfaces();
       
   281             for(int i = 0; i < interfaces.length; i++) {
       
   282                 m = findPublicMethod(interfaces[i], methodName, argClasses);
       
   283                 if (m != null) {
       
   284                     return m;
       
   285                 }
       
   286             }
       
   287         }
       
   288         return null;
       
   289     }
       
   290 
       
   291     /**
       
   292      * A class that represents the unique elements of a method that will be a
       
   293      * key in the method cache.
       
   294      */
       
   295     private static class Signature {
       
   296         private Class targetClass;
       
   297         private String methodName;
       
   298         private Class[] argClasses;
       
   299 
       
   300         private volatile int hashCode = 0;
       
   301 
       
   302         public Signature(Class targetClass, String methodName, Class[] argClasses) {
       
   303             this.targetClass = targetClass;
       
   304             this.methodName = methodName;
       
   305             this.argClasses = argClasses;
       
   306         }
       
   307 
       
   308         public boolean equals(Object o2) {
       
   309             if (this == o2) {
       
   310                 return true;
       
   311             }
       
   312             Signature that = (Signature)o2;
       
   313             if (!(targetClass == that.targetClass)) {
       
   314                 return false;
       
   315             }
       
   316             if (!(methodName.equals(that.methodName))) {
       
   317                 return false;
       
   318             }
       
   319             if (argClasses.length != that.argClasses.length) {
       
   320                 return false;
       
   321             }
       
   322             for (int i = 0; i < argClasses.length; i++) {
       
   323                 if (!(argClasses[i] == that.argClasses[i])) {
       
   324                   return false;
       
   325                 }
       
   326             }
       
   327             return true;
       
   328         }
       
   329 
       
   330         /**
       
   331          * Hash code computed using algorithm suggested in
       
   332          * Effective Java, Item 8.
       
   333          */
       
   334         public int hashCode() {
       
   335             if (hashCode == 0) {
       
   336                 int result = 17;
       
   337                 result = 37 * result + targetClass.hashCode();
       
   338                 result = 37 * result + methodName.hashCode();
       
   339                 if (argClasses != null) {
       
   340                     for (int i = 0; i < argClasses.length; i++) {
       
   341                         result = 37 * result + ((argClasses[i] == null) ? 0 :
       
   342                             argClasses[i].hashCode());
       
   343                     }
       
   344                 }
       
   345                 hashCode = result;
       
   346             }
       
   347             return hashCode;
       
   348         }
       
   349     }
       
   350 
       
   351     /**
       
   352      * A wrapper to findMethod(), which will search or populate the method
       
   353      * in a cache.
       
   354      * @throws exception if the method is ambiguios.
       
   355      */
       
   356     public static synchronized Method getMethod(Class targetClass,
       
   357                                                 String methodName,
       
   358                                                 Class[] argClasses) {
       
   359         Object signature = new Signature(targetClass, methodName, argClasses);
       
   360 
       
   361         Method method = null;
       
   362         Map methodCache = null;
       
   363         boolean cache = false;
       
   364         if (ReflectUtil.isPackageAccessible(targetClass)) {
       
   365             cache = true;
       
   366         }
       
   367 
       
   368         if (cache && methodCacheRef != null &&
       
   369             (methodCache = (Map)methodCacheRef.get()) != null) {
       
   370             method = (Method)methodCache.get(signature);
       
   371             if (method != null) {
       
   372                 return method;
       
   373             }
       
   374         }
       
   375         method = findMethod(targetClass, methodName, argClasses);
       
   376         if (cache && method != null) {
       
   377             if (methodCache == null) {
       
   378                 methodCache = new HashMap();
       
   379                 methodCacheRef = new SoftReference(methodCache);
       
   380             }
       
   381             methodCache.put(signature, method);
       
   382         }
       
   383         return method;
       
   384     }
       
   385 
       
   386     /**
       
   387      * Return a constructor on the class with the arguments.
       
   388      *
       
   389      * @throws exception if the method is ambiguios.
       
   390      */
       
   391     public static Constructor getConstructor(Class cls, Class[] args) {
       
   392         Constructor constructor = null;
       
   393 
       
   394         // PENDING: Implement the resolutuion of ambiguities properly.
       
   395         Constructor[] ctors = ConstructorUtil.getConstructors(cls);
       
   396         for(int i = 0; i < ctors.length; i++) {
       
   397             if (matchArguments(args, ctors[i].getParameterTypes())) {
       
   398                 constructor = ctors[i];
       
   399             }
       
   400         }
       
   401         return constructor;
       
   402     }
       
   403 
       
   404     public static Object getPrivateField(Object instance, Class cls, String name) {
       
   405         return getPrivateField(instance, cls, name, null);
       
   406     }
    50     }
   407 
    51 
   408     /**
    52     /**
   409      * Returns the value of a private field.
    53      * Returns the value of a private field.
   410      *
    54      *