MethodUtils.java
0001 /*
0002  * Licensed to the Apache Software Foundation (ASF) under one or more
0003  * contributor license agreements.  See the NOTICE file distributed with
0004  * this work for additional information regarding copyright ownership.
0005  * The ASF licenses this file to You under the Apache License, Version 2.0
0006  * (the "License"); you may not use this file except in compliance with
0007  * the License.  You may obtain a copy of the License at
0008  *
0009  *      http://www.apache.org/licenses/LICENSE-2.0
0010  *
0011  * Unless required by applicable law or agreed to in writing, software
0012  * distributed under the License is distributed on an "AS IS" BASIS,
0013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014  * See the License for the specific language governing permissions and
0015  * limitations under the License.
0016  */
0017 
0018 package griffon.util;
0019 
0020 
0021 import java.lang.ref.Reference;
0022 import java.lang.ref.WeakReference;
0023 import java.lang.reflect.InvocationTargetException;
0024 import java.lang.reflect.Method;
0025 import java.lang.reflect.Modifier;
0026 import java.util.Collections;
0027 import java.util.Map;
0028 import java.util.WeakHashMap;
0029 
0030 
0031 /**
0032  <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p>
0033  <p/>
0034  <p><b>Copied from commons-beanutils, removed dependencies to commons-logging</b></p>
0035  <p/>
0036  <h3>Known Limitations</h3>
0037  <h4>Accessing Public Methods In A Default Access Superclass</h4>
0038  <p>There is an issue when invoking public methods contained in a default access superclass.
0039  * Reflection locates these methods fine and correctly assigns them as public.
0040  * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>
0041  <p/>
0042  <p><code>MethodUtils</code> contains a workaround for this situation.
0043  * It will attempt to call <code>setAccessible</code> on this method.
0044  * If this call succeeds, then the method can be invoked as normal.
0045  * This call will only succeed when the application has sufficient security privilages.
0046  *
0047  @author Craig R. McClanahan
0048  @author Ralph Schaer
0049  @author Chris Audley
0050  @author Rey Fran&#231;ois
0051  @author Gregor Ra&#253;man
0052  @author Jan Sorensen
0053  @author Robert Burrell Donkin
0054  */
0055 
0056 public class MethodUtils {
0057 
0058     // --------------------------------------------------------- Private Methods
0059 
0060     /**
0061      * Indicates whether methods should be cached for improved performance.
0062      <p/>
0063      * Note that when this class is deployed via a shared classloader in
0064      * a container, this will affect all webapps. However making this
0065      * configurable per webapp would mean having a map keyed by context classloader
0066      * which may introduce memory-leak problems.
0067      */
0068     private static boolean CACHE_METHODS = true;
0069 
0070     /**
0071      * An empty class array
0072      */
0073     private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
0074     /**
0075      * An empty object array
0076      */
0077     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
0078 
0079     /**
0080      * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
0081      <p/>
0082      * The keys into this map only ever exist as temporary variables within
0083      * methods of this class, and are never exposed to users of this class.
0084      * This means that the WeakHashMap is used only as a mechanism for
0085      * limiting the size of the cache, ie a way to tell the garbage collector
0086      * that the contents of the cache can be completely garbage-collected
0087      * whenever it needs the memory. Whether this is a good approach to
0088      * this problem is doubtful; something like the commons-collections
0089      * LRUMap may be more appropriate (though of course selecting an
0090      * appropriate size is an issue).
0091      <p/>
0092      * This static variable is safe even when this code is deployed via a
0093      * shared classloader because it is keyed via a MethodDescriptor object
0094      * which has a Class as one of its members and that member is used in
0095      * the MethodDescriptor.equals method. So two components that load the same
0096      * class via different classloaders will generate non-equal MethodDescriptor
0097      * objects and hence end up with different entries in the map.
0098      */
0099     private static final Map cache = Collections.synchronizedMap(new WeakHashMap());
0100 
0101     // --------------------------------------------------------- Public Methods
0102 
0103     /**
0104      * Set whether methods should be cached for greater performance or not,
0105      * default is <code>true</code>.
0106      *
0107      @param cacheMethods <code>true</code> if methods should be
0108      *                     cached for greater performance, otherwise <code>false</code>
0109      @since 1.8.0
0110      */
0111     public static synchronized void setCacheMethods(boolean cacheMethods) {
0112         CACHE_METHODS = cacheMethods;
0113         if (!CACHE_METHODS) {
0114             clearCache();
0115         }
0116     }
0117 
0118     /**
0119      * Clear the method cache.
0120      *
0121      @return the number of cached methods cleared
0122      @since 1.8.0
0123      */
0124     public static synchronized int clearCache() {
0125         int size = cache.size();
0126         cache.clear();
0127         return size;
0128     }
0129 
0130     /**
0131      <p>Invoke a named method whose parameter type matches the object type.</p>
0132      <p/>
0133      <p>The behaviour of this method is less deterministic
0134      * than <code>invokeExactMethod()</code>.
0135      * It loops through all methods with names that match
0136      * and then executes the first it finds with compatable parameters.</p>
0137      <p/>
0138      <p>This method supports calls to methods taking primitive parameters
0139      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0140      * would match a <code>boolean</code> primitive.</p>
0141      <p/>
0142      <p> This is a convenient wrapper for
0143      {@link #invokeMethod(Object object, String methodName, Object [] args)}.
0144      </p>
0145      *
0146      @param object     invoke method on this object
0147      @param methodName get method with this name
0148      @param arg        use this argument
0149      @return The value returned by the invoked method
0150      @throws NoSuchMethodException     if there is no such accessible method
0151      @throws InvocationTargetException wraps an exception thrown by the
0152      *                                   method invoked
0153      @throws IllegalAccessException    if the requested method is not accessible
0154      *                                   via reflection
0155      */
0156     public static Object invokeMethod(
0157             Object object,
0158             String methodName,
0159             Object arg)
0160             throws
0161             NoSuchMethodException,
0162             IllegalAccessException,
0163             InvocationTargetException {
0164 
0165         Object[] args = {arg};
0166         return invokeMethod(object, methodName, args);
0167 
0168     }
0169 
0170 
0171     /**
0172      <p>Invoke a named method whose parameter type matches the object type.</p>
0173      <p/>
0174      <p>The behaviour of this method is less deterministic
0175      * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
0176      * It loops through all methods with names that match
0177      * and then executes the first it finds with compatable parameters.</p>
0178      <p/>
0179      <p>This method supports calls to methods taking primitive parameters
0180      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0181      * would match a <code>boolean</code> primitive.</p>
0182      <p/>
0183      <p> This is a convenient wrapper for
0184      {@link #invokeMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}.
0185      </p>
0186      *
0187      @param object     invoke method on this object
0188      @param methodName get method with this name
0189      @param args       use these arguments - treat null as empty array
0190      @return The value returned by the invoked method
0191      @throws NoSuchMethodException     if there is no such accessible method
0192      @throws InvocationTargetException wraps an exception thrown by the
0193      *                                   method invoked
0194      @throws IllegalAccessException    if the requested method is not accessible
0195      *                                   via reflection
0196      */
0197     public static Object invokeMethod(
0198             Object object,
0199             String methodName,
0200             Object[] args)
0201             throws
0202             NoSuchMethodException,
0203             IllegalAccessException,
0204             InvocationTargetException {
0205 
0206         if (args == null) {
0207             args = EMPTY_OBJECT_ARRAY;
0208         }
0209         int arguments = args.length;
0210         Class[] parameterTypes = new Class[arguments];
0211         for (int i = 0; i < arguments; i++) {
0212             parameterTypes[i= args[i].getClass();
0213         }
0214         return invokeMethod(object, methodName, args, parameterTypes);
0215 
0216     }
0217 
0218 
0219     /**
0220      <p>Invoke a named method whose parameter type matches the object type.</p>
0221      <p/>
0222      <p>The behaviour of this method is less deterministic
0223      * than {@link
0224      * #invokeExactMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}.
0225      * It loops through all methods with names that match
0226      * and then executes the first it finds with compatable parameters.</p>
0227      <p/>
0228      <p>This method supports calls to methods taking primitive parameters
0229      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0230      * would match a <code>boolean</code> primitive.</p>
0231      *
0232      @param object         invoke method on this object
0233      @param methodName     get method with this name
0234      @param args           use these arguments - treat null as empty array
0235      @param parameterTypes match these parameters - treat null as empty array
0236      @return The value returned by the invoked method
0237      @throws NoSuchMethodException     if there is no such accessible method
0238      @throws InvocationTargetException wraps an exception thrown by the
0239      *                                   method invoked
0240      @throws IllegalAccessException    if the requested method is not accessible
0241      *                                   via reflection
0242      */
0243     public static Object invokeMethod(
0244             Object object,
0245             String methodName,
0246             Object[] args,
0247             Class[] parameterTypes)
0248             throws
0249             NoSuchMethodException,
0250             IllegalAccessException,
0251             InvocationTargetException {
0252 
0253         if (parameterTypes == null) {
0254             parameterTypes = EMPTY_CLASS_PARAMETERS;
0255         }
0256         if (args == null) {
0257             args = EMPTY_OBJECT_ARRAY;
0258         }
0259 
0260         Method method = getMatchingAccessibleMethod(
0261                 object.getClass(),
0262                 methodName,
0263                 parameterTypes);
0264         if (method == null) {
0265             throw new NoSuchMethodException("No such accessible method: " +
0266                     methodName + "() on object: " + object.getClass().getName());
0267         }
0268         return method.invoke(object, args);
0269     }
0270 
0271 
0272     /**
0273      <p>Invoke a method whose parameter type matches exactly the object
0274      * type.</p>
0275      <p/>
0276      <p> This is a convenient wrapper for
0277      {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
0278      </p>
0279      *
0280      @param object     invoke method on this object
0281      @param methodName get method with this name
0282      @param arg        use this argument
0283      @return The value returned by the invoked method
0284      @throws NoSuchMethodException     if there is no such accessible method
0285      @throws InvocationTargetException wraps an exception thrown by the
0286      *                                   method invoked
0287      @throws IllegalAccessException    if the requested method is not accessible
0288      *                                   via reflection
0289      */
0290     public static Object invokeExactMethod(
0291             Object object,
0292             String methodName,
0293             Object arg)
0294             throws
0295             NoSuchMethodException,
0296             IllegalAccessException,
0297             InvocationTargetException {
0298 
0299         Object[] args = {arg};
0300         return invokeExactMethod(object, methodName, args);
0301 
0302     }
0303 
0304 
0305     /**
0306      <p>Invoke a method whose parameter types match exactly the object
0307      * types.</p>
0308      <p/>
0309      <p> This uses reflection to invoke the method obtained from a call to
0310      <code>getAccessibleMethod()</code>.</p>
0311      *
0312      @param object     invoke method on this object
0313      @param methodName get method with this name
0314      @param args       use these arguments - treat null as empty array
0315      @return The value returned by the invoked method
0316      @throws NoSuchMethodException     if there is no such accessible method
0317      @throws InvocationTargetException wraps an exception thrown by the
0318      *                                   method invoked
0319      @throws IllegalAccessException    if the requested method is not accessible
0320      *                                   via reflection
0321      */
0322     public static Object invokeExactMethod(
0323             Object object,
0324             String methodName,
0325             Object[] args)
0326             throws
0327             NoSuchMethodException,
0328             IllegalAccessException,
0329             InvocationTargetException {
0330         if (args == null) {
0331             args = EMPTY_OBJECT_ARRAY;
0332         }
0333         int arguments = args.length;
0334         Class[] parameterTypes = new Class[arguments];
0335         for (int i = 0; i < arguments; i++) {
0336             parameterTypes[i= args[i].getClass();
0337         }
0338         return invokeExactMethod(object, methodName, args, parameterTypes);
0339 
0340     }
0341 
0342 
0343     /**
0344      <p>Invoke a method whose parameter types match exactly the parameter
0345      * types given.</p>
0346      <p/>
0347      <p>This uses reflection to invoke the method obtained from a call to
0348      <code>getAccessibleMethod()</code>.</p>
0349      *
0350      @param object         invoke method on this object
0351      @param methodName     get method with this name
0352      @param args           use these arguments - treat null as empty array
0353      @param parameterTypes match these parameters - treat null as empty array
0354      @return The value returned by the invoked method
0355      @throws NoSuchMethodException     if there is no such accessible method
0356      @throws InvocationTargetException wraps an exception thrown by the
0357      *                                   method invoked
0358      @throws IllegalAccessException    if the requested method is not accessible
0359      *                                   via reflection
0360      */
0361     public static Object invokeExactMethod(
0362             Object object,
0363             String methodName,
0364             Object[] args,
0365             Class[] parameterTypes)
0366             throws
0367             NoSuchMethodException,
0368             IllegalAccessException,
0369             InvocationTargetException {
0370 
0371         if (args == null) {
0372             args = EMPTY_OBJECT_ARRAY;
0373         }
0374 
0375         if (parameterTypes == null) {
0376             parameterTypes = EMPTY_CLASS_PARAMETERS;
0377         }
0378 
0379         Method method = getAccessibleMethod(
0380                 object.getClass(),
0381                 methodName,
0382                 parameterTypes);
0383         if (method == null) {
0384             throw new NoSuchMethodException("No such accessible method: " +
0385                     methodName + "() on object: " + object.getClass().getName());
0386         }
0387         return method.invoke(object, args);
0388 
0389     }
0390 
0391     /**
0392      <p>Invoke a static method whose parameter types match exactly the parameter
0393      * types given.</p>
0394      <p/>
0395      <p>This uses reflection to invoke the method obtained from a call to
0396      {@link #getAccessibleMethod(Class, String, Class[])}.</p>
0397      *
0398      @param objectClass    invoke static method on this class
0399      @param methodName     get method with this name
0400      @param args           use these arguments - treat null as empty array
0401      @param parameterTypes match these parameters - treat null as empty array
0402      @return The value returned by the invoked method
0403      @throws NoSuchMethodException     if there is no such accessible method
0404      @throws InvocationTargetException wraps an exception thrown by the
0405      *                                   method invoked
0406      @throws IllegalAccessException    if the requested method is not accessible
0407      *                                   via reflection
0408      @since 1.8.0
0409      */
0410     public static Object invokeExactStaticMethod(
0411             Class objectClass,
0412             String methodName,
0413             Object[] args,
0414             Class[] parameterTypes)
0415             throws
0416             NoSuchMethodException,
0417             IllegalAccessException,
0418             InvocationTargetException {
0419 
0420         if (args == null) {
0421             args = EMPTY_OBJECT_ARRAY;
0422         }
0423 
0424         if (parameterTypes == null) {
0425             parameterTypes = EMPTY_CLASS_PARAMETERS;
0426         }
0427 
0428         Method method = getAccessibleMethod(
0429                 objectClass,
0430                 methodName,
0431                 parameterTypes);
0432         if (method == null) {
0433             throw new NoSuchMethodException("No such accessible method: " +
0434                     methodName + "() on class: " + objectClass.getName());
0435         }
0436         return method.invoke(null, args);
0437 
0438     }
0439 
0440     /**
0441      <p>Invoke a named static method whose parameter type matches the object type.</p>
0442      <p/>
0443      <p>The behaviour of this method is less deterministic
0444      * than {@link #invokeExactMethod(Object, String, Object[], Class[])}.
0445      * It loops through all methods with names that match
0446      * and then executes the first it finds with compatable parameters.</p>
0447      <p/>
0448      <p>This method supports calls to methods taking primitive parameters
0449      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0450      * would match a <code>boolean</code> primitive.</p>
0451      <p/>
0452      <p> This is a convenient wrapper for
0453      {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args)}.
0454      </p>
0455      *
0456      @param objectClass invoke static method on this class
0457      @param methodName  get method with this name
0458      @param arg         use this argument
0459      @return The value returned by the invoked method
0460      @throws NoSuchMethodException     if there is no such accessible method
0461      @throws InvocationTargetException wraps an exception thrown by the
0462      *                                   method invoked
0463      @throws IllegalAccessException    if the requested method is not accessible
0464      *                                   via reflection
0465      @since 1.8.0
0466      */
0467     public static Object invokeStaticMethod(
0468             Class objectClass,
0469             String methodName,
0470             Object arg)
0471             throws
0472             NoSuchMethodException,
0473             IllegalAccessException,
0474             InvocationTargetException {
0475 
0476         Object[] args = {arg};
0477         return invokeStaticMethod(objectClass, methodName, args);
0478 
0479     }
0480 
0481 
0482     /**
0483      <p>Invoke a named static method whose parameter type matches the object type.</p>
0484      <p/>
0485      <p>The behaviour of this method is less deterministic
0486      * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
0487      * It loops through all methods with names that match
0488      * and then executes the first it finds with compatable parameters.</p>
0489      <p/>
0490      <p>This method supports calls to methods taking primitive parameters
0491      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0492      * would match a <code>boolean</code> primitive.</p>
0493      <p/>
0494      <p> This is a convenient wrapper for
0495      {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}.
0496      </p>
0497      *
0498      @param objectClass invoke static method on this class
0499      @param methodName  get method with this name
0500      @param args        use these arguments - treat null as empty array
0501      @return The value returned by the invoked method
0502      @throws NoSuchMethodException     if there is no such accessible method
0503      @throws InvocationTargetException wraps an exception thrown by the
0504      *                                   method invoked
0505      @throws IllegalAccessException    if the requested method is not accessible
0506      *                                   via reflection
0507      @since 1.8.0
0508      */
0509     public static Object invokeStaticMethod(
0510             Class objectClass,
0511             String methodName,
0512             Object[] args)
0513             throws
0514             NoSuchMethodException,
0515             IllegalAccessException,
0516             InvocationTargetException {
0517 
0518         if (args == null) {
0519             args = EMPTY_OBJECT_ARRAY;
0520         }
0521         int arguments = args.length;
0522         Class[] parameterTypes = new Class[arguments];
0523         for (int i = 0; i < arguments; i++) {
0524             parameterTypes[i= args[i].getClass();
0525         }
0526         return invokeStaticMethod(objectClass, methodName, args, parameterTypes);
0527 
0528     }
0529 
0530 
0531     /**
0532      <p>Invoke a named static method whose parameter type matches the object type.</p>
0533      <p/>
0534      <p>The behaviour of this method is less deterministic
0535      * than {@link
0536      * #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}.
0537      * It loops through all methods with names that match
0538      * and then executes the first it finds with compatable parameters.</p>
0539      <p/>
0540      <p>This method supports calls to methods taking primitive parameters
0541      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
0542      * would match a <code>boolean</code> primitive.</p>
0543      *
0544      @param objectClass    invoke static method on this class
0545      @param methodName     get method with this name
0546      @param args           use these arguments - treat null as empty array
0547      @param parameterTypes match these parameters - treat null as empty array
0548      @return The value returned by the invoked method
0549      @throws NoSuchMethodException     if there is no such accessible method
0550      @throws InvocationTargetException wraps an exception thrown by the
0551      *                                   method invoked
0552      @throws IllegalAccessException    if the requested method is not accessible
0553      *                                   via reflection
0554      @since 1.8.0
0555      */
0556     public static Object invokeStaticMethod(
0557             Class objectClass,
0558             String methodName,
0559             Object[] args,
0560             Class[] parameterTypes)
0561             throws
0562             NoSuchMethodException,
0563             IllegalAccessException,
0564             InvocationTargetException {
0565 
0566         if (parameterTypes == null) {
0567             parameterTypes = EMPTY_CLASS_PARAMETERS;
0568         }
0569         if (args == null) {
0570             args = EMPTY_OBJECT_ARRAY;
0571         }
0572 
0573         Method method = getMatchingAccessibleMethod(
0574                 objectClass,
0575                 methodName,
0576                 parameterTypes);
0577         if (method == null) {
0578             throw new NoSuchMethodException("No such accessible method: " +
0579                     methodName + "() on class: " + objectClass.getName());
0580         }
0581         return method.invoke(null, args);
0582     }
0583 
0584 
0585     /**
0586      <p>Invoke a static method whose parameter type matches exactly the object
0587      * type.</p>
0588      <p/>
0589      <p> This is a convenient wrapper for
0590      {@link #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args)}.
0591      </p>
0592      *
0593      @param objectClass invoke static method on this class
0594      @param methodName  get method with this name
0595      @param arg         use this argument
0596      @return The value returned by the invoked method
0597      @throws NoSuchMethodException     if there is no such accessible method
0598      @throws InvocationTargetException wraps an exception thrown by the
0599      *                                   method invoked
0600      @throws IllegalAccessException    if the requested method is not accessible
0601      *                                   via reflection
0602      @since 1.8.0
0603      */
0604     public static Object invokeExactStaticMethod(
0605             Class objectClass,
0606             String methodName,
0607             Object arg)
0608             throws
0609             NoSuchMethodException,
0610             IllegalAccessException,
0611             InvocationTargetException {
0612 
0613         Object[] args = {arg};
0614         return invokeExactStaticMethod(objectClass, methodName, args);
0615 
0616     }
0617 
0618 
0619     /**
0620      <p>Invoke a static method whose parameter types match exactly the object
0621      * types.</p>
0622      <p/>
0623      <p> This uses reflection to invoke the method obtained from a call to
0624      {@link #getAccessibleMethod(Class, String, Class[])}.</p>
0625      *
0626      @param objectClass invoke static method on this class
0627      @param methodName  get method with this name
0628      @param args        use these arguments - treat null as empty array
0629      @return The value returned by the invoked method
0630      @throws NoSuchMethodException     if there is no such accessible method
0631      @throws InvocationTargetException wraps an exception thrown by the
0632      *                                   method invoked
0633      @throws IllegalAccessException    if the requested method is not accessible
0634      *                                   via reflection
0635      @since 1.8.0
0636      */
0637     public static Object invokeExactStaticMethod(
0638             Class objectClass,
0639             String methodName,
0640             Object[] args)
0641             throws
0642             NoSuchMethodException,
0643             IllegalAccessException,
0644             InvocationTargetException {
0645         if (args == null) {
0646             args = EMPTY_OBJECT_ARRAY;
0647         }
0648         int arguments = args.length;
0649         Class[] parameterTypes = new Class[arguments];
0650         for (int i = 0; i < arguments; i++) {
0651             parameterTypes[i= args[i].getClass();
0652         }
0653         return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
0654 
0655     }
0656 
0657 
0658     /**
0659      <p>Return an accessible method (that is, one that can be invoked via
0660      * reflection) with given name and a single parameter.  If no such method
0661      * can be found, return <code>null</code>.
0662      * Basically, a convenience wrapper that constructs a <code>Class</code>
0663      * array for you.</p>
0664      *
0665      @param clazz         get method from this class
0666      @param methodName    get method with this name
0667      @param parameterType taking this type of parameter
0668      @return The accessible method
0669      */
0670     public static Method getAccessibleMethod(
0671             Class clazz,
0672             String methodName,
0673             Class parameterType) {
0674 
0675         Class[] parameterTypes = {parameterType};
0676         return getAccessibleMethod(clazz, methodName, parameterTypes);
0677 
0678     }
0679 
0680 
0681     /**
0682      <p>Return an accessible method (that is, one that can be invoked via
0683      * reflection) with given name and parameters.  If no such method
0684      * can be found, return <code>null</code>.
0685      * This is just a convenient wrapper for
0686      {@link #getAccessibleMethod(Method method)}.</p>
0687      *
0688      @param clazz          get method from this class
0689      @param methodName     get method with this name
0690      @param parameterTypes with these parameters types
0691      @return The accessible method
0692      */
0693     public static Method getAccessibleMethod(
0694             Class clazz,
0695             String methodName,
0696             Class[] parameterTypes) {
0697 
0698         try {
0699             MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
0700             // Check the cache first
0701             Method method = getCachedMethod(md);
0702             if (method != null) {
0703                 return method;
0704             }
0705 
0706             method = getAccessibleMethod
0707                     (clazz, clazz.getMethod(methodName, parameterTypes));
0708             cacheMethod(md, method);
0709             return method;
0710         catch (NoSuchMethodException e) {
0711             return (null);
0712         }
0713 
0714     }
0715 
0716 
0717     /**
0718      <p>Return an accessible method (that is, one that can be invoked via
0719      * reflection) that implements the specified Method.  If no such method
0720      * can be found, return <code>null</code>.</p>
0721      *
0722      @param method The method that we wish to call
0723      @return The accessible method
0724      */
0725     public static Method getAccessibleMethod(Method method) {
0726 
0727         // Make sure we have a method to check
0728         if (method == null) {
0729             return (null);
0730         }
0731 
0732         return getAccessibleMethod(method.getDeclaringClass(), method);
0733 
0734     }
0735 
0736 
0737     /**
0738      <p>Return an accessible method (that is, one that can be invoked via
0739      * reflection) that implements the specified Method.  If no such method
0740      * can be found, return <code>null</code>.</p>
0741      *
0742      @param clazz  The class of the object
0743      @param method The method that we wish to call
0744      @return The accessible method
0745      @since 1.8.0
0746      */
0747     public static Method getAccessibleMethod(Class clazz, Method method) {
0748 
0749         // Make sure we have a method to check
0750         if (method == null) {
0751             return (null);
0752         }
0753 
0754         // If the requested method is not public we cannot call it
0755         if (!Modifier.isPublic(method.getModifiers())) {
0756             return (null);
0757         }
0758 
0759         boolean sameClass = true;
0760         if (clazz == null) {
0761             clazz = method.getDeclaringClass();
0762         else {
0763             sameClass = clazz.equals(method.getDeclaringClass());
0764             if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
0765                 throw new IllegalArgumentException(clazz.getName() +
0766                         " is not assignable from " + method.getDeclaringClass().getName());
0767             }
0768         }
0769 
0770         // If the class is public, we are done
0771         if (Modifier.isPublic(clazz.getModifiers())) {
0772             if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
0773                 setMethodAccessible(method)// Default access superclass workaround
0774             }
0775             return (method);
0776         }
0777 
0778         String methodName = method.getName();
0779         Class[] parameterTypes = method.getParameterTypes();
0780 
0781         // Check the implemented interfaces and subinterfaces
0782         method =
0783                 getAccessibleMethodFromInterfaceNest(clazz,
0784                         methodName,
0785                         parameterTypes);
0786 
0787         // Check the superclass chain
0788         if (method == null) {
0789             method = getAccessibleMethodFromSuperclass(clazz,
0790                     methodName,
0791                     parameterTypes);
0792         }
0793 
0794         return (method);
0795 
0796     }
0797 
0798 
0799     // -------------------------------------------------------- Private Methods
0800 
0801     /**
0802      <p>Return an accessible method (that is, one that can be invoked via
0803      * reflection) by scanning through the superclasses. If no such method
0804      * can be found, return <code>null</code>.</p>
0805      *
0806      @param clazz          Class to be checked
0807      @param methodName     Method name of the method we wish to call
0808      @param parameterTypes The parameter type signatures
0809      */
0810     private static Method getAccessibleMethodFromSuperclass
0811     (Class clazz, String methodName, Class[] parameterTypes) {
0812 
0813         Class parentClazz = clazz.getSuperclass();
0814         while (parentClazz != null) {
0815             if (Modifier.isPublic(parentClazz.getModifiers())) {
0816                 try {
0817                     return parentClazz.getMethod(methodName, parameterTypes);
0818                 catch (NoSuchMethodException e) {
0819                     return null;
0820                 }
0821             }
0822             parentClazz = parentClazz.getSuperclass();
0823         }
0824         return null;
0825     }
0826 
0827     /**
0828      <p>Return an accessible method (that is, one that can be invoked via
0829      * reflection) that implements the specified method, by scanning through
0830      * all implemented interfaces and subinterfaces.  If no such method
0831      * can be found, return <code>null</code>.</p>
0832      <p/>
0833      <p> There isn't any good reason why this method must be private.
0834      * It is because there doesn't seem any reason why other classes should
0835      * call this rather than the higher level methods.</p>
0836      *
0837      @param clazz          Parent class for the interfaces to be checked
0838      @param methodName     Method name of the method we wish to call
0839      @param parameterTypes The parameter type signatures
0840      */
0841     private static Method getAccessibleMethodFromInterfaceNest
0842     (Class clazz, String methodName, Class[] parameterTypes) {
0843 
0844         Method method = null;
0845 
0846         // Search up the superclass chain
0847         for (; clazz != null; clazz = clazz.getSuperclass()) {
0848 
0849             // Check the implemented interfaces of the parent class
0850             Class[] interfaces = clazz.getInterfaces();
0851             for (int i = 0; i < interfaces.length; i++) {
0852 
0853                 // Is this interface public?
0854                 if (!Modifier.isPublic(interfaces[i].getModifiers())) {
0855                     continue;
0856                 }
0857 
0858                 // Does the method exist on this interface?
0859                 try {
0860                     method = interfaces[i].getDeclaredMethod(methodName,
0861                             parameterTypes);
0862                 catch (NoSuchMethodException e) {
0863                     /* Swallow, if no method is found after the loop then this
0864                      * method returns null.
0865                      */
0866                 }
0867                 if (method != null) {
0868                     return method;
0869                 }
0870 
0871                 // Recursively check our parent interfaces
0872                 method =
0873                         getAccessibleMethodFromInterfaceNest(interfaces[i],
0874                                 methodName,
0875                                 parameterTypes);
0876                 if (method != null) {
0877                     return method;
0878                 }
0879 
0880             }
0881 
0882         }
0883 
0884         // We did not find anything
0885         return (null);
0886 
0887     }
0888 
0889     /**
0890      <p>Find an accessible method that matches the given name and has compatible parameters.
0891      * Compatible parameters mean that every method parameter is assignable from
0892      * the given parameters.
0893      * In other words, it finds a method with the given name
0894      * that will take the parameters given.<p>
0895      <p/>
0896      <p>This method is slightly undeterminstic since it loops
0897      * through methods names and return the first matching method.</p>
0898      <p/>
0899      <p>This method is used by
0900      {@link
0901      * #invokeMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}.
0902      <p/>
0903      <p>This method can match primitive parameter by passing in wrapper classes.
0904      * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
0905      * parameter.
0906      *
0907      @param clazz          find method in this class
0908      @param methodName     find method with this name
0909      @param parameterTypes find method with compatible parameters
0910      @return The accessible method
0911      */
0912     public static Method getMatchingAccessibleMethod(
0913             Class clazz,
0914             String methodName,
0915             Class[] parameterTypes) {
0916         MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);
0917 
0918         // see if we can find the method directly
0919         // most of the time this works and it's much faster
0920         try {
0921             // Check the cache first
0922             Method method = getCachedMethod(md);
0923             if (method != null) {
0924                 return method;
0925             }
0926 
0927             method = clazz.getMethod(methodName, parameterTypes);
0928 
0929             setMethodAccessible(method)// Default access superclass workaround
0930 
0931             cacheMethod(md, method);
0932             return method;
0933 
0934         catch (NoSuchMethodException e) { /* SWALLOW */ }
0935 
0936         // search through all methods 
0937         int paramSize = parameterTypes.length;
0938         Method bestMatch = null;
0939         Method[] methods = clazz.getMethods();
0940         float bestMatchCost = Float.MAX_VALUE;
0941         float myCost = Float.MAX_VALUE;
0942         for (int i = 0, size = methods.length; i < size; i++) {
0943             if (methods[i].getName().equals(methodName)) {
0944                 // compare parameters
0945                 Class[] methodsParams = methods[i].getParameterTypes();
0946                 int methodParamSize = methodsParams.length;
0947                 if (methodParamSize == paramSize) {
0948                     boolean match = true;
0949                     for (int n = 0; n < methodParamSize; n++) {
0950                         if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
0951                             match = false;
0952                             break;
0953                         }
0954                     }
0955 
0956                     if (match) {
0957                         // get accessible version of method
0958                         Method method = getAccessibleMethod(clazz, methods[i]);
0959                         if (method != null) {
0960                             setMethodAccessible(method)// Default access superclass workaround
0961                             myCost = getTotalTransformationCost(parameterTypes, method.getParameterTypes());
0962                             if (myCost < bestMatchCost) {
0963                                 bestMatch = method;
0964                                 bestMatchCost = myCost;
0965                             }
0966                         }
0967                     }
0968                 }
0969             }
0970         }
0971         if (bestMatch != null) { //find a match
0972             cacheMethod(md, bestMatch);
0973         }
0974         return bestMatch;
0975     }
0976 
0977     /**
0978      * Try to make the method accessible
0979      *
0980      @param method The source arguments
0981      */
0982     private static void setMethodAccessible(Method method) {
0983         try {
0984             //
0985             // XXX Default access superclass workaround
0986             //
0987             // When a public class has a default access superclass
0988             // with public methods, these methods are accessible.
0989             // Calling them from compiled code works fine.
0990             //
0991             // Unfortunately, using reflection to invoke these methods
0992             // seems to (wrongly) to prevent access even when the method
0993             // modifer is public.
0994             //
0995             // The following workaround solves the problem but will only
0996             // work from sufficiently privilages code. 
0997             //
0998             // Better workarounds would be greatfully accepted.
0999             //
1000             if (!method.isAccessible()) {
1001                 method.setAccessible(true);
1002             }
1003 
1004         catch (SecurityException se) {
1005             // ignore
1006         }
1007     }
1008 
1009     /**
1010      * Returns the sum of the object transformation cost for each class in the source
1011      * argument list.
1012      *
1013      @param srcArgs  The source arguments
1014      @param destArgs The destination arguments
1015      @return The total transformation cost
1016      */
1017     private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) {
1018 
1019         float totalCost = 0.0f;
1020         for (int i = 0; i < srcArgs.length; i++) {
1021             Class srcClass, destClass;
1022             srcClass = srcArgs[i];
1023             destClass = destArgs[i];
1024             totalCost += getObjectTransformationCost(srcClass, destClass);
1025         }
1026 
1027         return totalCost;
1028     }
1029 
1030     /**
1031      * Gets the number of steps required needed to turn the source class into the
1032      * destination class. This represents the number of steps in the object hierarchy
1033      * graph.
1034      *
1035      @param srcClass  The source class
1036      @param destClass The destination class
1037      @return The cost of transforming an object
1038      */
1039     private static float getObjectTransformationCost(Class srcClass, Class destClass) {
1040         float cost = 0.0f;
1041         while (destClass != null && !destClass.equals(srcClass)) {
1042             if (destClass.isInterface() && isAssignmentCompatible(destClass, srcClass)) {
1043                 // slight penalty for interface match. 
1044                 // we still want an exact match to override an interface match, but  
1045                 // an interface match should override anything where we have to get a 
1046                 // superclass.
1047                 cost += 0.25f;
1048                 break;
1049             }
1050             cost++;
1051             destClass = destClass.getSuperclass();
1052         }
1053 
1054         /*
1055          * If the destination class is null, we've travelled all the way up to 
1056          * an Object match. We'll penalize this by adding 1.5 to the cost.
1057          */
1058         if (destClass == null) {
1059             cost += 1.5f;
1060         }
1061 
1062         return cost;
1063     }
1064 
1065 
1066     /**
1067      <p>Determine whether a type can be used as a parameter in a method invocation.
1068      * This method handles primitive conversions correctly.</p>
1069      <p/>
1070      <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
1071      * a <code>Long</code> to a <code>long</code>,
1072      * a <code>Float</code> to a <code>float</code>,
1073      * a <code>Integer</code> to a <code>int</code>,
1074      * and a <code>Double</code> to a <code>double</code>.
1075      * Now logic widening matches are allowed.
1076      * For example, a <code>Long</code> will not match a <code>int</code>.
1077      *
1078      @param parameterType    the type of parameter accepted by the method
1079      @param parameterization the type of parameter being tested
1080      @return true if the assignement is compatible.
1081      */
1082     public static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
1083         // try plain assignment
1084         if (parameterType.isAssignableFrom(parameterization)) {
1085             return true;
1086         }
1087 
1088         if (parameterType.isPrimitive()) {
1089             // this method does *not* do widening - you must specify exactly
1090             // is this the right behaviour?
1091             Class parameterWrapperClazz = getPrimitiveWrapper(parameterType);
1092             if (parameterWrapperClazz != null) {
1093                 return parameterWrapperClazz.equals(parameterization);
1094             }
1095         }
1096 
1097         return false;
1098     }
1099 
1100     /**
1101      * Gets the wrapper object class for the given primitive type class.
1102      * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code>
1103      *
1104      @param primitiveType the primitive type class for which a match is to be found
1105      @return the wrapper type associated with the given primitive
1106      *         or null if no match is found
1107      */
1108     public static Class getPrimitiveWrapper(Class primitiveType) {
1109         // does anyone know a better strategy than comparing names?
1110         if (boolean.class.equals(primitiveType)) {
1111             return Boolean.class;
1112         else if (float.class.equals(primitiveType)) {
1113             return Float.class;
1114         else if (long.class.equals(primitiveType)) {
1115             return Long.class;
1116         else if (int.class.equals(primitiveType)) {
1117             return Integer.class;
1118         else if (short.class.equals(primitiveType)) {
1119             return Short.class;
1120         else if (byte.class.equals(primitiveType)) {
1121             return Byte.class;
1122         else if (double.class.equals(primitiveType)) {
1123             return Double.class;
1124         else if (char.class.equals(primitiveType)) {
1125             return Character.class;
1126         else {
1127 
1128             return null;
1129         }
1130     }
1131 
1132     /**
1133      * Gets the class for the primitive type corresponding to the primitive wrapper class given.
1134      * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>.
1135      *
1136      @param wrapperType the
1137      @return the primitive type class corresponding to the given wrapper class,
1138      *         null if no match is found
1139      */
1140     public static Class getPrimitiveType(Class wrapperType) {
1141         // does anyone know a better strategy than comparing names?
1142         if (Boolean.class.equals(wrapperType)) {
1143             return boolean.class;
1144         else if (Float.class.equals(wrapperType)) {
1145             return float.class;
1146         else if (Long.class.equals(wrapperType)) {
1147             return long.class;
1148         else if (Integer.class.equals(wrapperType)) {
1149             return int.class;
1150         else if (Short.class.equals(wrapperType)) {
1151             return short.class;
1152         else if (Byte.class.equals(wrapperType)) {
1153             return byte.class;
1154         else if (Double.class.equals(wrapperType)) {
1155             return double.class;
1156         else if (Character.class.equals(wrapperType)) {
1157             return char.class;
1158         else {
1159             return null;
1160         }
1161     }
1162 
1163     /**
1164      * Find a non primitive representation for given primitive class.
1165      *
1166      @param clazz the class to find a representation for, not null
1167      @return the original class if it not a primitive. Otherwise the wrapper class. Not null
1168      */
1169     public static Class toNonPrimitiveClass(Class clazz) {
1170         if (clazz.isPrimitive()) {
1171             Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);
1172             // the above method returns 
1173             if (primitiveClazz != null) {
1174                 return primitiveClazz;
1175             else {
1176                 return clazz;
1177             }
1178         else {
1179             return clazz;
1180         }
1181     }
1182 
1183 
1184     /**
1185      * Return the method from the cache, if present.
1186      *
1187      @param md The method descriptor
1188      @return The cached method
1189      */
1190     private static Method getCachedMethod(MethodDescriptor md) {
1191         if (CACHE_METHODS) {
1192             Reference methodRef = (Referencecache.get(md);
1193             if (methodRef != null) {
1194                 return (MethodmethodRef.get();
1195             }
1196         }
1197         return null;
1198     }
1199 
1200     /**
1201      * Add a method to the cache.
1202      *
1203      @param md     The method descriptor
1204      @param method The method to cache
1205      */
1206     private static void cacheMethod(MethodDescriptor md, Method method) {
1207         if (CACHE_METHODS) {
1208             if (method != null) {
1209                 cache.put(md, new WeakReference(method));
1210             }
1211         }
1212     }
1213 
1214     /**
1215      * Represents the key to looking up a Method by reflection.
1216      */
1217     private static class MethodDescriptor {
1218         private Class cls;
1219         private String methodName;
1220         private Class[] paramTypes;
1221         private boolean exact;
1222         private int hashCode;
1223 
1224         /**
1225          * The sole constructor.
1226          *
1227          @param cls        the class to reflect, must not be null
1228          @param methodName the method name to obtain
1229          @param paramTypes the array of classes representing the paramater types
1230          @param exact      whether the match has to be exact.
1231          */
1232         public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {
1233             if (cls == null) {
1234                 throw new IllegalArgumentException("Class cannot be null");
1235             }
1236             if (methodName == null) {
1237                 throw new IllegalArgumentException("Method Name cannot be null");
1238             }
1239             if (paramTypes == null) {
1240                 paramTypes = EMPTY_CLASS_PARAMETERS;
1241             }
1242 
1243             this.cls = cls;
1244             this.methodName = methodName;
1245             this.paramTypes = paramTypes;
1246             this.exact = exact;
1247 
1248             this.hashCode = methodName.length();
1249         }
1250 
1251         /**
1252          * Checks for equality.
1253          *
1254          @param obj object to be tested for equality
1255          @return true, if the object describes the same Method.
1256          */
1257         public boolean equals(Object obj) {
1258             if (!(obj instanceof MethodDescriptor)) {
1259                 return false;
1260             }
1261             MethodDescriptor md = (MethodDescriptorobj;
1262 
1263             return (
1264                     exact == md.exact &&
1265                             methodName.equals(md.methodName&&
1266                             cls.equals(md.cls&&
1267                             java.util.Arrays.equals(paramTypes, md.paramTypes)
1268             );
1269         }
1270 
1271         /**
1272          * Returns the string length of method name. I.e. if the
1273          * hashcodes are different, the objects are different. If the
1274          * hashcodes are the same, need to use the equals method to
1275          * determine equality.
1276          *
1277          @return the string length of method name.
1278          */
1279         public int hashCode() {
1280             return hashCode;
1281         }
1282     }
1283 }