| 
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çois
 0051  * @author Gregor Raýman
 0052  * @author Jan Sorensen
 0053  * @author Robert Burrell Donkin
 0054  */
 0055
 0056 @SuppressWarnings({"unchecked", "rawtypes"})
 0057 public class MethodUtils {
 0058
 0059     // --------------------------------------------------------- Private Methods
 0060
 0061     /**
 0062      * Indicates whether methods should be cached for improved performance.
 0063      * <p/>
 0064      * Note that when this class is deployed via a shared classloader in
 0065      * a container, this will affect all webapps. However making this
 0066      * configurable per webapp would mean having a map keyed by context classloader
 0067      * which may introduce memory-leak problems.
 0068      */
 0069     private static boolean CACHE_METHODS = true;
 0070
 0071     /**
 0072      * An empty class array
 0073      */
 0074     private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];
 0075     /**
 0076      * An empty object array
 0077      */
 0078     private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
 0079
 0080     /**
 0081      * Stores a cache of MethodDescriptor -> Method in a WeakHashMap.
 0082      * <p/>
 0083      * The keys into this map only ever exist as temporary variables within
 0084      * methods of this class, and are never exposed to users of this class.
 0085      * This means that the WeakHashMap is used only as a mechanism for
 0086      * limiting the size of the cache, ie a way to tell the garbage collector
 0087      * that the contents of the cache can be completely garbage-collected
 0088      * whenever it needs the memory. Whether this is a good approach to
 0089      * this problem is doubtful; something like the commons-collections
 0090      * LRUMap may be more appropriate (though of course selecting an
 0091      * appropriate size is an issue).
 0092      * <p/>
 0093      * This static variable is safe even when this code is deployed via a
 0094      * shared classloader because it is keyed via a MethodDescriptor object
 0095      * which has a Class as one of its members and that member is used in
 0096      * the MethodDescriptor.equals method. So two components that load the same
 0097      * class via different classloaders will generate non-equal MethodDescriptor
 0098      * objects and hence end up with different entries in the map.
 0099      */
 0100     private static final Map cache = Collections.synchronizedMap(new WeakHashMap());
 0101
 0102     // --------------------------------------------------------- Public Methods
 0103
 0104     /**
 0105      * Set whether methods should be cached for greater performance or not,
 0106      * default is <code>true</code>.
 0107      *
 0108      * @param cacheMethods <code>true</code> if methods should be
 0109      *                     cached for greater performance, otherwise <code>false</code>
 0110      * @since 1.8.0
 0111      */
 0112     public static synchronized void setCacheMethods(boolean cacheMethods) {
 0113         CACHE_METHODS = cacheMethods;
 0114         if (!CACHE_METHODS) {
 0115             clearCache();
 0116         }
 0117     }
 0118
 0119     /**
 0120      * Clear the method cache.
 0121      *
 0122      * @return the number of cached methods cleared
 0123      * @since 1.8.0
 0124      */
 0125     public static synchronized int clearCache() {
 0126         int size = cache.size();
 0127         cache.clear();
 0128         return size;
 0129     }
 0130
 0131     /**
 0132      * <p>Invoke a named method whose parameter type matches the object type.</p>
 0133      * <p/>
 0134      * <p>The behaviour of this method is less deterministic
 0135      * than <code>invokeExactMethod()</code>.
 0136      * It loops through all methods with names that match
 0137      * and then executes the first it finds with compatable parameters.</p>
 0138      * <p/>
 0139      * <p>This method supports calls to methods taking primitive parameters
 0140      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0141      * would match a <code>boolean</code> primitive.</p>
 0142      * <p/>
 0143      * <p> This is a convenient wrapper for
 0144      * {@link #invokeMethod(Object object, String methodName, Object [] args)}.
 0145      * </p>
 0146      *
 0147      * @param object     invoke method on this object
 0148      * @param methodName get method with this name
 0149      * @param arg        use this argument
 0150      * @return The value returned by the invoked method
 0151      * @throws NoSuchMethodException     if there is no such accessible method
 0152      * @throws InvocationTargetException wraps an exception thrown by the
 0153      *                                   method invoked
 0154      * @throws IllegalAccessException    if the requested method is not accessible
 0155      *                                   via reflection
 0156      */
 0157     public static Object invokeMethod(
 0158         Object object,
 0159         String methodName,
 0160         Object arg)
 0161         throws
 0162         NoSuchMethodException,
 0163         IllegalAccessException,
 0164         InvocationTargetException {
 0165
 0166         Object[] args = {arg};
 0167         return invokeMethod(object, methodName, args);
 0168
 0169     }
 0170
 0171
 0172     /**
 0173      * <p>Invoke a named method whose parameter type matches the object type.</p>
 0174      * <p/>
 0175      * <p>The behaviour of this method is less deterministic
 0176      * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
 0177      * It loops through all methods with names that match
 0178      * and then executes the first it finds with compatable parameters.</p>
 0179      * <p/>
 0180      * <p>This method supports calls to methods taking primitive parameters
 0181      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0182      * would match a <code>boolean</code> primitive.</p>
 0183      * <p/>
 0184      * <p> This is a convenient wrapper for
 0185      * {@link #invokeMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}.
 0186      * </p>
 0187      *
 0188      * @param object     invoke method on this object
 0189      * @param methodName get method with this name
 0190      * @param args       use these arguments - treat null as empty array
 0191      * @return The value returned by the invoked method
 0192      * @throws NoSuchMethodException     if there is no such accessible method
 0193      * @throws InvocationTargetException wraps an exception thrown by the
 0194      *                                   method invoked
 0195      * @throws IllegalAccessException    if the requested method is not accessible
 0196      *                                   via reflection
 0197      */
 0198     public static Object invokeMethod(
 0199         Object object,
 0200         String methodName,
 0201         Object[] args)
 0202         throws
 0203         NoSuchMethodException,
 0204         IllegalAccessException,
 0205         InvocationTargetException {
 0206
 0207         if (args == null) {
 0208             args = EMPTY_OBJECT_ARRAY;
 0209         }
 0210         int arguments = args.length;
 0211         Class[] parameterTypes = new Class[arguments];
 0212         for (int i = 0; i < arguments; i++) {
 0213             parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
 0214         }
 0215         return invokeMethod(object, methodName, args, parameterTypes);
 0216
 0217     }
 0218
 0219
 0220     /**
 0221      * <p>Invoke a named method whose parameter type matches the object type.</p>
 0222      * <p/>
 0223      * <p>The behaviour of this method is less deterministic
 0224      * than {@link
 0225      * #invokeExactMethod(Object object, String methodName, Object [] args, Class[] parameterTypes)}.
 0226      * It loops through all methods with names that match
 0227      * and then executes the first it finds with compatable parameters.</p>
 0228      * <p/>
 0229      * <p>This method supports calls to methods taking primitive parameters
 0230      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0231      * would match a <code>boolean</code> primitive.</p>
 0232      *
 0233      * @param object         invoke method on this object
 0234      * @param methodName     get method with this name
 0235      * @param args           use these arguments - treat null as empty array
 0236      * @param parameterTypes match these parameters - treat null as empty array
 0237      * @return The value returned by the invoked method
 0238      * @throws NoSuchMethodException     if there is no such accessible method
 0239      * @throws InvocationTargetException wraps an exception thrown by the
 0240      *                                   method invoked
 0241      * @throws IllegalAccessException    if the requested method is not accessible
 0242      *                                   via reflection
 0243      */
 0244     public static Object invokeMethod(
 0245         Object object,
 0246         String methodName,
 0247         Object[] args,
 0248         Class[] parameterTypes)
 0249         throws
 0250         NoSuchMethodException,
 0251         IllegalAccessException,
 0252         InvocationTargetException {
 0253
 0254         if (parameterTypes == null) {
 0255             parameterTypes = EMPTY_CLASS_PARAMETERS;
 0256         }
 0257         if (args == null) {
 0258             args = EMPTY_OBJECT_ARRAY;
 0259         }
 0260
 0261         Method method = getMatchingAccessibleMethod(
 0262             object.getClass(),
 0263             methodName,
 0264             parameterTypes);
 0265         if (method == null) {
 0266             throw new NoSuchMethodException("No such accessible method: " +
 0267                 methodName + "() on object: " + object.getClass().getName());
 0268         }
 0269         return method.invoke(object, args);
 0270     }
 0271
 0272
 0273     /**
 0274      * <p>Invoke a method whose parameter type matches exactly the object
 0275      * type.</p>
 0276      * <p/>
 0277      * <p> This is a convenient wrapper for
 0278      * {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
 0279      * </p>
 0280      *
 0281      * @param object     invoke method on this object
 0282      * @param methodName get method with this name
 0283      * @param arg        use this argument
 0284      * @return The value returned by the invoked method
 0285      * @throws NoSuchMethodException     if there is no such accessible method
 0286      * @throws InvocationTargetException wraps an exception thrown by the
 0287      *                                   method invoked
 0288      * @throws IllegalAccessException    if the requested method is not accessible
 0289      *                                   via reflection
 0290      */
 0291     public static Object invokeExactMethod(
 0292         Object object,
 0293         String methodName,
 0294         Object arg)
 0295         throws
 0296         NoSuchMethodException,
 0297         IllegalAccessException,
 0298         InvocationTargetException {
 0299
 0300         Object[] args = {arg};
 0301         return invokeExactMethod(object, methodName, args);
 0302
 0303     }
 0304
 0305
 0306     /**
 0307      * <p>Invoke a method whose parameter types match exactly the object
 0308      * types.</p>
 0309      * <p/>
 0310      * <p> This uses reflection to invoke the method obtained from a call to
 0311      * <code>getAccessibleMethod()</code>.</p>
 0312      *
 0313      * @param object     invoke method on this object
 0314      * @param methodName get method with this name
 0315      * @param args       use these arguments - treat null as empty array
 0316      * @return The value returned by the invoked method
 0317      * @throws NoSuchMethodException     if there is no such accessible method
 0318      * @throws InvocationTargetException wraps an exception thrown by the
 0319      *                                   method invoked
 0320      * @throws IllegalAccessException    if the requested method is not accessible
 0321      *                                   via reflection
 0322      */
 0323     public static Object invokeExactMethod(
 0324         Object object,
 0325         String methodName,
 0326         Object[] args)
 0327         throws
 0328         NoSuchMethodException,
 0329         IllegalAccessException,
 0330         InvocationTargetException {
 0331         if (args == null) {
 0332             args = EMPTY_OBJECT_ARRAY;
 0333         }
 0334         int arguments = args.length;
 0335         Class[] parameterTypes = new Class[arguments];
 0336         for (int i = 0; i < arguments; i++) {
 0337             parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
 0338         }
 0339         return invokeExactMethod(object, methodName, args, parameterTypes);
 0340
 0341     }
 0342
 0343
 0344     /**
 0345      * <p>Invoke a method whose parameter types match exactly the parameter
 0346      * types given.</p>
 0347      * <p/>
 0348      * <p>This uses reflection to invoke the method obtained from a call to
 0349      * <code>getAccessibleMethod()</code>.</p>
 0350      *
 0351      * @param object         invoke method on this object
 0352      * @param methodName     get method with this name
 0353      * @param args           use these arguments - treat null as empty array
 0354      * @param parameterTypes match these parameters - treat null as empty array
 0355      * @return The value returned by the invoked method
 0356      * @throws NoSuchMethodException     if there is no such accessible method
 0357      * @throws InvocationTargetException wraps an exception thrown by the
 0358      *                                   method invoked
 0359      * @throws IllegalAccessException    if the requested method is not accessible
 0360      *                                   via reflection
 0361      */
 0362     public static Object invokeExactMethod(
 0363         Object object,
 0364         String methodName,
 0365         Object[] args,
 0366         Class[] parameterTypes)
 0367         throws
 0368         NoSuchMethodException,
 0369         IllegalAccessException,
 0370         InvocationTargetException {
 0371
 0372         if (args == null) {
 0373             args = EMPTY_OBJECT_ARRAY;
 0374         }
 0375
 0376         if (parameterTypes == null) {
 0377             parameterTypes = EMPTY_CLASS_PARAMETERS;
 0378         }
 0379
 0380         Method method = getAccessibleMethod(
 0381             object.getClass(),
 0382             methodName,
 0383             parameterTypes);
 0384         if (method == null) {
 0385             throw new NoSuchMethodException("No such accessible method: " +
 0386                 methodName + "() on object: " + object.getClass().getName());
 0387         }
 0388         return method.invoke(object, args);
 0389
 0390     }
 0391
 0392     /**
 0393      * <p>Invoke a static method whose parameter types match exactly the parameter
 0394      * types given.</p>
 0395      * <p/>
 0396      * <p>This uses reflection to invoke the method obtained from a call to
 0397      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
 0398      *
 0399      * @param objectClass    invoke static method on this class
 0400      * @param methodName     get method with this name
 0401      * @param args           use these arguments - treat null as empty array
 0402      * @param parameterTypes match these parameters - treat null as empty array
 0403      * @return The value returned by the invoked method
 0404      * @throws NoSuchMethodException     if there is no such accessible method
 0405      * @throws InvocationTargetException wraps an exception thrown by the
 0406      *                                   method invoked
 0407      * @throws IllegalAccessException    if the requested method is not accessible
 0408      *                                   via reflection
 0409      * @since 1.8.0
 0410      */
 0411     public static Object invokeExactStaticMethod(
 0412         Class objectClass,
 0413         String methodName,
 0414         Object[] args,
 0415         Class[] parameterTypes)
 0416         throws
 0417         NoSuchMethodException,
 0418         IllegalAccessException,
 0419         InvocationTargetException {
 0420
 0421         if (args == null) {
 0422             args = EMPTY_OBJECT_ARRAY;
 0423         }
 0424
 0425         if (parameterTypes == null) {
 0426             parameterTypes = EMPTY_CLASS_PARAMETERS;
 0427         }
 0428
 0429         Method method = getAccessibleMethod(
 0430             objectClass,
 0431             methodName,
 0432             parameterTypes);
 0433         if (method == null) {
 0434             throw new NoSuchMethodException("No such accessible method: " +
 0435                 methodName + "() on class: " + objectClass.getName());
 0436         }
 0437         return method.invoke(null, args);
 0438
 0439     }
 0440
 0441     /**
 0442      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 0443      * <p/>
 0444      * <p>The behaviour of this method is less deterministic
 0445      * than {@link #invokeExactMethod(Object, String, Object[], Class[])}.
 0446      * It loops through all methods with names that match
 0447      * and then executes the first it finds with compatable parameters.</p>
 0448      * <p/>
 0449      * <p>This method supports calls to methods taking primitive parameters
 0450      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0451      * would match a <code>boolean</code> primitive.</p>
 0452      * <p/>
 0453      * <p> This is a convenient wrapper for
 0454      * {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args)}.
 0455      * </p>
 0456      *
 0457      * @param objectClass invoke static method on this class
 0458      * @param methodName  get method with this name
 0459      * @param arg         use this argument
 0460      * @return The value returned by the invoked method
 0461      * @throws NoSuchMethodException     if there is no such accessible method
 0462      * @throws InvocationTargetException wraps an exception thrown by the
 0463      *                                   method invoked
 0464      * @throws IllegalAccessException    if the requested method is not accessible
 0465      *                                   via reflection
 0466      * @since 1.8.0
 0467      */
 0468     public static Object invokeStaticMethod(
 0469         Class objectClass,
 0470         String methodName,
 0471         Object arg)
 0472         throws
 0473         NoSuchMethodException,
 0474         IllegalAccessException,
 0475         InvocationTargetException {
 0476
 0477         Object[] args = {arg};
 0478         return invokeStaticMethod(objectClass, methodName, args);
 0479
 0480     }
 0481
 0482
 0483     /**
 0484      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 0485      * <p/>
 0486      * <p>The behaviour of this method is less deterministic
 0487      * than {@link #invokeExactMethod(Object object, String methodName, Object [] args)}.
 0488      * It loops through all methods with names that match
 0489      * and then executes the first it finds with compatable parameters.</p>
 0490      * <p/>
 0491      * <p>This method supports calls to methods taking primitive parameters
 0492      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0493      * would match a <code>boolean</code> primitive.</p>
 0494      * <p/>
 0495      * <p> This is a convenient wrapper for
 0496      * {@link #invokeStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}.
 0497      * </p>
 0498      *
 0499      * @param objectClass invoke static method on this class
 0500      * @param methodName  get method with this name
 0501      * @param args        use these arguments - treat null as empty array
 0502      * @return The value returned by the invoked method
 0503      * @throws NoSuchMethodException     if there is no such accessible method
 0504      * @throws InvocationTargetException wraps an exception thrown by the
 0505      *                                   method invoked
 0506      * @throws IllegalAccessException    if the requested method is not accessible
 0507      *                                   via reflection
 0508      * @since 1.8.0
 0509      */
 0510     public static Object invokeStaticMethod(
 0511         Class objectClass,
 0512         String methodName,
 0513         Object[] args)
 0514         throws
 0515         NoSuchMethodException,
 0516         IllegalAccessException,
 0517         InvocationTargetException {
 0518
 0519         if (args == null) {
 0520             args = EMPTY_OBJECT_ARRAY;
 0521         }
 0522         int arguments = args.length;
 0523         Class[] parameterTypes = new Class[arguments];
 0524         for (int i = 0; i < arguments; i++) {
 0525             parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
 0526         }
 0527         return invokeStaticMethod(objectClass, methodName, args, parameterTypes);
 0528
 0529     }
 0530
 0531
 0532     /**
 0533      * <p>Invoke a named static method whose parameter type matches the object type.</p>
 0534      * <p/>
 0535      * <p>The behaviour of this method is less deterministic
 0536      * than {@link
 0537      * #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args, Class[] parameterTypes)}.
 0538      * It loops through all methods with names that match
 0539      * and then executes the first it finds with compatable parameters.</p>
 0540      * <p/>
 0541      * <p>This method supports calls to methods taking primitive parameters
 0542      * via passing in wrapping classes. So, for example, a <code>Boolean</code> class
 0543      * would match a <code>boolean</code> primitive.</p>
 0544      *
 0545      * @param objectClass    invoke static method on this class
 0546      * @param methodName     get method with this name
 0547      * @param args           use these arguments - treat null as empty array
 0548      * @param parameterTypes match these parameters - treat null as empty array
 0549      * @return The value returned by the invoked method
 0550      * @throws NoSuchMethodException     if there is no such accessible method
 0551      * @throws InvocationTargetException wraps an exception thrown by the
 0552      *                                   method invoked
 0553      * @throws IllegalAccessException    if the requested method is not accessible
 0554      *                                   via reflection
 0555      * @since 1.8.0
 0556      */
 0557     public static Object invokeStaticMethod(
 0558         Class objectClass,
 0559         String methodName,
 0560         Object[] args,
 0561         Class[] parameterTypes)
 0562         throws
 0563         NoSuchMethodException,
 0564         IllegalAccessException,
 0565         InvocationTargetException {
 0566
 0567         if (parameterTypes == null) {
 0568             parameterTypes = EMPTY_CLASS_PARAMETERS;
 0569         }
 0570         if (args == null) {
 0571             args = EMPTY_OBJECT_ARRAY;
 0572         }
 0573
 0574         Method method = getMatchingAccessibleMethod(
 0575             objectClass,
 0576             methodName,
 0577             parameterTypes);
 0578         if (method == null) {
 0579             throw new NoSuchMethodException("No such accessible method: " +
 0580                 methodName + "() on class: " + objectClass.getName());
 0581         }
 0582         return method.invoke(null, args);
 0583     }
 0584
 0585
 0586     /**
 0587      * <p>Invoke a static method whose parameter type matches exactly the object
 0588      * type.</p>
 0589      * <p/>
 0590      * <p> This is a convenient wrapper for
 0591      * {@link #invokeExactStaticMethod(Class objectClass, String methodName, Object [] args)}.
 0592      * </p>
 0593      *
 0594      * @param objectClass invoke static method on this class
 0595      * @param methodName  get method with this name
 0596      * @param arg         use this argument
 0597      * @return The value returned by the invoked method
 0598      * @throws NoSuchMethodException     if there is no such accessible method
 0599      * @throws InvocationTargetException wraps an exception thrown by the
 0600      *                                   method invoked
 0601      * @throws IllegalAccessException    if the requested method is not accessible
 0602      *                                   via reflection
 0603      * @since 1.8.0
 0604      */
 0605     public static Object invokeExactStaticMethod(
 0606         Class objectClass,
 0607         String methodName,
 0608         Object arg)
 0609         throws
 0610         NoSuchMethodException,
 0611         IllegalAccessException,
 0612         InvocationTargetException {
 0613
 0614         Object[] args = {arg};
 0615         return invokeExactStaticMethod(objectClass, methodName, args);
 0616
 0617     }
 0618
 0619
 0620     /**
 0621      * <p>Invoke a static method whose parameter types match exactly the object
 0622      * types.</p>
 0623      * <p/>
 0624      * <p> This uses reflection to invoke the method obtained from a call to
 0625      * {@link #getAccessibleMethod(Class, String, Class[])}.</p>
 0626      *
 0627      * @param objectClass invoke static method on this class
 0628      * @param methodName  get method with this name
 0629      * @param args        use these arguments - treat null as empty array
 0630      * @return The value returned by the invoked method
 0631      * @throws NoSuchMethodException     if there is no such accessible method
 0632      * @throws InvocationTargetException wraps an exception thrown by the
 0633      *                                   method invoked
 0634      * @throws IllegalAccessException    if the requested method is not accessible
 0635      *                                   via reflection
 0636      * @since 1.8.0
 0637      */
 0638     public static Object invokeExactStaticMethod(
 0639         Class objectClass,
 0640         String methodName,
 0641         Object[] args)
 0642         throws
 0643         NoSuchMethodException,
 0644         IllegalAccessException,
 0645         InvocationTargetException {
 0646         if (args == null) {
 0647             args = EMPTY_OBJECT_ARRAY;
 0648         }
 0649         int arguments = args.length;
 0650         Class[] parameterTypes = new Class[arguments];
 0651         for (int i = 0; i < arguments; i++) {
 0652             parameterTypes[i] = args[i] != null ? args[i].getClass() : Object.class;
 0653         }
 0654         return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);
 0655
 0656     }
 0657
 0658
 0659     /**
 0660      * <p>Return an accessible method (that is, one that can be invoked via
 0661      * reflection) with given name and a single parameter.  If no such method
 0662      * can be found, return <code>null</code>.
 0663      * Basically, a convenience wrapper that constructs a <code>Class</code>
 0664      * array for you.</p>
 0665      *
 0666      * @param clazz         get method from this class
 0667      * @param methodName    get method with this name
 0668      * @param parameterType taking this type of parameter
 0669      * @return The accessible method
 0670      */
 0671     public static Method getAccessibleMethod(
 0672         Class clazz,
 0673         String methodName,
 0674         Class parameterType) {
 0675
 0676         Class[] parameterTypes = {parameterType};
 0677         return getAccessibleMethod(clazz, methodName, parameterTypes);
 0678
 0679     }
 0680
 0681
 0682     /**
 0683      * <p>Return an accessible method (that is, one that can be invoked via
 0684      * reflection) with given name and parameters.  If no such method
 0685      * can be found, return <code>null</code>.
 0686      * This is just a convenient wrapper for
 0687      * {@link #getAccessibleMethod(Method method)}.</p>
 0688      *
 0689      * @param clazz          get method from this class
 0690      * @param methodName     get method with this name
 0691      * @param parameterTypes with these parameters types
 0692      * @return The accessible method
 0693      */
 0694     public static Method getAccessibleMethod(
 0695         Class clazz,
 0696         String methodName,
 0697         Class[] parameterTypes) {
 0698
 0699         try {
 0700             MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);
 0701             // Check the cache first
 0702             Method method = getCachedMethod(md);
 0703             if (method != null) {
 0704                 return method;
 0705             }
 0706
 0707             method = getAccessibleMethod
 0708                 (clazz, clazz.getMethod(methodName, parameterTypes));
 0709             cacheMethod(md, method);
 0710             return method;
 0711         } catch (NoSuchMethodException e) {
 0712             return (null);
 0713         }
 0714
 0715     }
 0716
 0717
 0718     /**
 0719      * <p>Return an accessible method (that is, one that can be invoked via
 0720      * reflection) that implements the specified Method.  If no such method
 0721      * can be found, return <code>null</code>.</p>
 0722      *
 0723      * @param method The method that we wish to call
 0724      * @return The accessible method
 0725      */
 0726     public static Method getAccessibleMethod(Method method) {
 0727
 0728         // Make sure we have a method to check
 0729         if (method == null) {
 0730             return (null);
 0731         }
 0732
 0733         return getAccessibleMethod(method.getDeclaringClass(), method);
 0734
 0735     }
 0736
 0737
 0738     /**
 0739      * <p>Return an accessible method (that is, one that can be invoked via
 0740      * reflection) that implements the specified Method.  If no such method
 0741      * can be found, return <code>null</code>.</p>
 0742      *
 0743      * @param clazz  The class of the object
 0744      * @param method The method that we wish to call
 0745      * @return The accessible method
 0746      * @since 1.8.0
 0747      */
 0748     public static Method getAccessibleMethod(Class clazz, Method method) {
 0749
 0750         // Make sure we have a method to check
 0751         if (method == null) {
 0752             return (null);
 0753         }
 0754
 0755         // If the requested method is not public we cannot call it
 0756         if (!Modifier.isPublic(method.getModifiers())) {
 0757             return (null);
 0758         }
 0759
 0760         boolean sameClass = true;
 0761         if (clazz == null) {
 0762             clazz = method.getDeclaringClass();
 0763         } else {
 0764             sameClass = clazz.equals(method.getDeclaringClass());
 0765             if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
 0766                 throw new IllegalArgumentException(clazz.getName() +
 0767                     " is not assignable from " + method.getDeclaringClass().getName());
 0768             }
 0769         }
 0770
 0771         // If the class is public, we are done
 0772         if (Modifier.isPublic(clazz.getModifiers())) {
 0773             if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
 0774                 setMethodAccessible(method); // Default access superclass workaround
 0775             }
 0776             return (method);
 0777         }
 0778
 0779         String methodName = method.getName();
 0780         Class[] parameterTypes = method.getParameterTypes();
 0781
 0782         // Check the implemented interfaces and subinterfaces
 0783         method =
 0784             getAccessibleMethodFromInterfaceNest(clazz,
 0785                 methodName,
 0786                 parameterTypes);
 0787
 0788         // Check the superclass chain
 0789         if (method == null) {
 0790             method = getAccessibleMethodFromSuperclass(clazz,
 0791                 methodName,
 0792                 parameterTypes);
 0793         }
 0794
 0795         return (method);
 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 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 = (Reference) cache.get(md);
 1193             if (methodRef != null) {
 1194                 return (Method) methodRef.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     public static Object invokeSafe(Method method, Object instance, Object[] args) {
 1215         try {
 1216             return method.invoke(instance, args);
 1217         } catch (IllegalAccessException e) {
 1218             // ignore
 1219         } catch (InvocationTargetException e) {
 1220             // ignore
 1221         }
 1222         return null;
 1223     }
 1224
 1225     /**
 1226      * Represents the key to looking up a Method by reflection.
 1227      */
 1228     private static class MethodDescriptor {
 1229         private Class cls;
 1230         private String methodName;
 1231         private Class[] paramTypes;
 1232         private boolean exact;
 1233         private int hashCode;
 1234
 1235         /**
 1236          * The sole constructor.
 1237          *
 1238          * @param cls        the class to reflect, must not be null
 1239          * @param methodName the method name to obtain
 1240          * @param paramTypes the array of classes representing the paramater types
 1241          * @param exact      whether the match has to be exact.
 1242          */
 1243         public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {
 1244             if (cls == null) {
 1245                 throw new IllegalArgumentException("Class must not be null");
 1246             }
 1247             if (methodName == null) {
 1248                 throw new IllegalArgumentException("Method Name must not be null");
 1249             }
 1250             if (paramTypes == null) {
 1251                 paramTypes = EMPTY_CLASS_PARAMETERS;
 1252             }
 1253
 1254             this.cls = cls;
 1255             this.methodName = methodName;
 1256             this.paramTypes = paramTypes;
 1257             this.exact = exact;
 1258
 1259             this.hashCode = methodName.length();
 1260         }
 1261
 1262         /**
 1263          * Checks for equality.
 1264          *
 1265          * @param obj object to be tested for equality
 1266          * @return true, if the object describes the same Method.
 1267          */
 1268         public boolean equals(Object obj) {
 1269             if (!(obj instanceof MethodDescriptor)) {
 1270                 return false;
 1271             }
 1272             MethodDescriptor md = (MethodDescriptor) obj;
 1273
 1274             return (
 1275                 exact == md.exact &&
 1276                     methodName.equals(md.methodName) &&
 1277                     cls.equals(md.cls) &&
 1278                     java.util.Arrays.equals(paramTypes, md.paramTypes)
 1279             );
 1280         }
 1281
 1282         /**
 1283          * Returns the string length of method name. I.e. if the
 1284          * hashcodes are different, the objects are different. If the
 1285          * hashcodes are the same, need to use the equals method to
 1286          * determine equality.
 1287          *
 1288          * @return the string length of method name.
 1289          */
 1290         public int hashCode() {
 1291             return hashCode;
 1292         }
 1293     }
 1294 }
 |