+++ /dev/null
-/* ====================================================================\r
- Licensed to the Apache Software Foundation (ASF) under one or more\r
- contributor license agreements. See the NOTICE file distributed with\r
- this work for additional information regarding copyright ownership.\r
- The ASF licenses this file to You under the Apache License, Version 2.0\r
- (the "License"); you may not use this file except in compliance with\r
- the License. You may obtain a copy of the License at\r
-\r
- http://www.apache.org/licenses/LICENSE-2.0\r
-\r
- Unless required by applicable law or agreed to in writing, software\r
- distributed under the License is distributed on an "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- See the License for the specific language governing permissions and\r
- limitations under the License.\r
-==================================================================== */\r
-\r
-package org.apache.poi.util;\r
-\r
-\r
-import java.lang.reflect.Constructor;\r
-import java.lang.reflect.InvocationTargetException;\r
-import java.lang.reflect.Method;\r
-import java.lang.reflect.Modifier;\r
-\r
-import org.apache.commons.logging.Log;\r
-import org.apache.commons.logging.LogFactory;\r
-\r
-\r
-/**\r
- * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p>\r
- *\r
- * <h3>Known Limitations</h3>\r
- * <h4>Accessing Public Methods In A Default Access Superclass</h4>\r
- * <p>There is an issue when invoking public methods contained in a default access superclass.\r
- * Reflection locates these methods fine and correctly assigns them as public.\r
- * However, an <code>IllegalAccessException</code> is thrown if the method is invoked.</p>\r
- *\r
- * <p><code>MethodUtils</code> contains a workaround for this situation. \r
- * It will attempt to call <code>setAccessible</code> on this method.\r
- * If this call succeeds, then the method can be invoked as normal.\r
- * This call will only succeed when the application has sufficient security privilages. \r
- * If this call fails then a warning will be logged and the method may fail.</p>\r
- *\r
- * @author Craig R. McClanahan\r
- * @author Ralph Schaer\r
- * @author Chris Audley\r
- * @author Rey François\r
- * @author Gregor Raýman\r
- * @author Jan Sorensen\r
- * @author Robert Burrell Donkin\r
- */\r
-\r
-public class MethodUtils {\r
-\r
- // --------------------------------------------------------- Private Methods\r
- \r
- /** \r
- * Only log warning about accessibility work around once.\r
- * <p>\r
- * Note that this is broken when this class is deployed via a shared\r
- * classloader in a container, as the warning message will be emitted\r
- * only once, not once per webapp. However making the warning appear\r
- * once per webapp means having a map keyed by context classloader\r
- * which introduces nasty memory-leak problems. As this warning is\r
- * really optional we can ignore this problem; only one of the webapps\r
- * will get the warning in its logs but that should be good enough.\r
- */\r
- private static boolean loggedAccessibleWarning = false;\r
- \r
- /** \r
- * Indicates whether methods should be cached for improved performance.\r
- * <p>\r
- * Note that when this class is deployed via a shared classloader in\r
- * a container, this will affect all webapps. However making this\r
- * configurable per webapp would mean having a map keyed by context classloader\r
- * which may introduce memory-leak problems.\r
- */\r
- private static boolean CACHE_METHODS = true;\r
-\r
- /** An empty class array */\r
- private static final Class[] EMPTY_CLASS_PARAMETERS = new Class[0];\r
- /** An empty object array */\r
- private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];\r
-\r
- // --------------------------------------------------------- Public Methods\r
-\r
- /**\r
- * <p>Invoke a named method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than <code>invokeExactMethod()</code>.\r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeMethod(Object object,String methodName,Object [] args)}.\r
- * </p>\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param arg use this argument\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeMethod(\r
- Object object,\r
- String methodName,\r
- Object arg)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
-\r
- Object[] args = {arg};\r
- return invokeMethod(object, methodName, args);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a named method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. \r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.\r
- * </p>\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeMethod(\r
- Object object,\r
- String methodName,\r
- Object[] args)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- int arguments = args.length;\r
- Class[] parameterTypes = new Class[arguments];\r
- for (int i = 0; i < arguments; i++) {\r
- parameterTypes[i] = args[i].getClass();\r
- }\r
- return invokeMethod(object, methodName, args, parameterTypes);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a named method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than {@link \r
- * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. \r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @param parameterTypes match these parameters - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeMethod(\r
- Object object,\r
- String methodName,\r
- Object[] args,\r
- Class[] parameterTypes)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (parameterTypes == null) {\r
- parameterTypes = EMPTY_CLASS_PARAMETERS;\r
- } \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
-\r
- Method method = getMatchingAccessibleMethod(\r
- object.getClass(),\r
- methodName,\r
- parameterTypes);\r
- if (method == null) {\r
- throw new NoSuchMethodException("No such accessible method: " +\r
- methodName + "() on object: " + object.getClass().getName());\r
- }\r
- return method.invoke(object, args);\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a method whose parameter type matches exactly the object\r
- * type.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}.\r
- * </p>\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param arg use this argument\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactMethod(\r
- Object object,\r
- String methodName,\r
- Object arg)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
-\r
- Object[] args = {arg};\r
- return invokeExactMethod(object, methodName, args);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a method whose parameter types match exactly the object\r
- * types.</p>\r
- *\r
- * <p> This uses reflection to invoke the method obtained from a call to\r
- * <code>getAccessibleMethod()</code>.</p>\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactMethod(\r
- Object object,\r
- String methodName,\r
- Object[] args)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- int arguments = args.length;\r
- Class[] parameterTypes = new Class[arguments];\r
- for (int i = 0; i < arguments; i++) {\r
- parameterTypes[i] = args[i].getClass();\r
- }\r
- return invokeExactMethod(object, methodName, args, parameterTypes);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a method whose parameter types match exactly the parameter\r
- * types given.</p>\r
- *\r
- * <p>This uses reflection to invoke the method obtained from a call to\r
- * <code>getAccessibleMethod()</code>.</p>\r
- *\r
- * @param object invoke method on this object\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @param parameterTypes match these parameters - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactMethod(\r
- Object object,\r
- String methodName,\r
- Object[] args,\r
- Class[] parameterTypes)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- \r
- if (parameterTypes == null) {\r
- parameterTypes = EMPTY_CLASS_PARAMETERS;\r
- }\r
-\r
- Method method = getAccessibleMethod(\r
- object.getClass(),\r
- methodName,\r
- parameterTypes);\r
- if (method == null) {\r
- throw new NoSuchMethodException("No such accessible method: " +\r
- methodName + "() on object: " + object.getClass().getName());\r
- }\r
- return method.invoke(object, args);\r
-\r
- }\r
-\r
- /**\r
- * <p>Invoke a static method whose parameter types match exactly the parameter\r
- * types given.</p>\r
- *\r
- * <p>This uses reflection to invoke the method obtained from a call to\r
- * {@link #getAccessibleMethod(Class, String, Class[])}.</p>\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @param parameterTypes match these parameters - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object[] args,\r
- Class[] parameterTypes)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- \r
- if (parameterTypes == null) {\r
- parameterTypes = EMPTY_CLASS_PARAMETERS;\r
- }\r
-\r
- Method method = getAccessibleMethod(\r
- objectClass,\r
- methodName,\r
- parameterTypes);\r
- if (method == null) {\r
- throw new NoSuchMethodException("No such accessible method: " +\r
- methodName + "() on class: " + objectClass.getName());\r
- }\r
- return method.invoke(null, args);\r
-\r
- }\r
-\r
- /**\r
- * <p>Invoke a named static method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than {@link #invokeExactMethod(Object, String, Object[], Class[])}. \r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args)}.\r
- * </p>\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param arg use this argument\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object arg)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
-\r
- Object[] args = {arg};\r
- return invokeStaticMethod (objectClass, methodName, args);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a named static method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. \r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}.\r
- * </p>\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object[] args)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- int arguments = args.length;\r
- Class[] parameterTypes = new Class[arguments];\r
- for (int i = 0; i < arguments; i++) {\r
- parameterTypes[i] = args[i].getClass();\r
- }\r
- return invokeStaticMethod (objectClass, methodName, args, parameterTypes);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a named static method whose parameter type matches the object type.</p>\r
- *\r
- * <p>The behaviour of this method is less deterministic \r
- * than {@link \r
- * #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}. \r
- * It loops through all methods with names that match\r
- * and then executes the first it finds with compatable parameters.</p>\r
- *\r
- * <p>This method supports calls to methods taking primitive parameters \r
- * via passing in wrapping classes. So, for example, a <code>Boolean</code> class\r
- * would match a <code>boolean</code> primitive.</p>\r
- *\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @param parameterTypes match these parameters - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object[] args,\r
- Class[] parameterTypes)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- \r
- if (parameterTypes == null) {\r
- parameterTypes = EMPTY_CLASS_PARAMETERS;\r
- } \r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
-\r
- Method method = getMatchingAccessibleMethod(\r
- objectClass,\r
- methodName,\r
- parameterTypes);\r
- if (method == null) {\r
- throw new NoSuchMethodException("No such accessible method: " +\r
- methodName + "() on class: " + objectClass.getName());\r
- }\r
- return method.invoke(null, args);\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a static method whose parameter type matches exactly the object\r
- * type.</p>\r
- *\r
- * <p> This is a convenient wrapper for\r
- * {@link #invokeExactStaticMethod(Class objectClass,String methodName,Object [] args)}.\r
- * </p>\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param arg use this argument\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object arg)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
-\r
- Object[] args = {arg};\r
- return invokeExactStaticMethod (objectClass, methodName, args);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Invoke a static method whose parameter types match exactly the object\r
- * types.</p>\r
- *\r
- * <p> This uses reflection to invoke the method obtained from a call to\r
- * {@link #getAccessibleMethod(Class, String, Class[])}.</p>\r
- *\r
- * @param objectClass invoke static method on this class\r
- * @param methodName get method with this name\r
- * @param args use these arguments - treat null as empty array\r
- * @return The value returned by the invoked method\r
- *\r
- * @throws NoSuchMethodException if there is no such accessible method\r
- * @throws InvocationTargetException wraps an exception thrown by the\r
- * method invoked\r
- * @throws IllegalAccessException if the requested method is not accessible\r
- * via reflection\r
- */\r
- public static Object invokeExactStaticMethod(\r
- Class objectClass,\r
- String methodName,\r
- Object[] args)\r
- throws\r
- NoSuchMethodException,\r
- IllegalAccessException,\r
- InvocationTargetException {\r
- if (args == null) {\r
- args = EMPTY_OBJECT_ARRAY;\r
- } \r
- int arguments = args.length;\r
- Class[] parameterTypes = new Class[arguments];\r
- for (int i = 0; i < arguments; i++) {\r
- parameterTypes[i] = args[i].getClass();\r
- }\r
- return invokeExactStaticMethod(objectClass, methodName, args, parameterTypes);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) with given name and a single parameter. If no such method\r
- * can be found, return <code>null</code>.\r
- * Basically, a convenience wrapper that constructs a <code>Class</code>\r
- * array for you.</p>\r
- *\r
- * @param clazz get method from this class\r
- * @param methodName get method with this name\r
- * @param parameterType taking this type of parameter\r
- * @return The accessible method\r
- */\r
- public static Method getAccessibleMethod(\r
- Class clazz,\r
- String methodName,\r
- Class parameterType) {\r
-\r
- Class[] parameterTypes = {parameterType};\r
- return getAccessibleMethod(clazz, methodName, parameterTypes);\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) with given name and parameters. If no such method\r
- * can be found, return <code>null</code>.\r
- * This is just a convenient wrapper for\r
- * {@link #getAccessibleMethod(Method method)}.</p>\r
- *\r
- * @param clazz get method from this class\r
- * @param methodName get method with this name\r
- * @param parameterTypes with these parameters types\r
- * @return The accessible method\r
- */\r
- public static Method getAccessibleMethod(\r
- Class clazz,\r
- String methodName,\r
- Class[] parameterTypes) {\r
-\r
- try {\r
- MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, true);\r
- Method method = getAccessibleMethod\r
- (clazz, clazz.getMethod(methodName, parameterTypes));\r
- return method;\r
- } catch (NoSuchMethodException e) {\r
- return (null);\r
- }\r
-\r
- }\r
-\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) that implements the specified Method. If no such method\r
- * can be found, return <code>null</code>.</p>\r
- *\r
- * @param method The method that we wish to call\r
- * @return The accessible method\r
- */\r
- public static Method getAccessibleMethod(Method method) {\r
-\r
- // Make sure we have a method to check\r
- if (method == null) {\r
- return (null);\r
- }\r
-\r
- return getAccessibleMethod(method.getDeclaringClass(), method);\r
-\r
- }\r
-\r
-\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) that implements the specified Method. If no such method\r
- * can be found, return <code>null</code>.</p>\r
- *\r
- * @param clazz The class of the object\r
- * @param method The method that we wish to call\r
- * @return The accessible method\r
- */\r
- public static Method getAccessibleMethod(Class clazz, Method method) {\r
-\r
- // Make sure we have a method to check\r
- if (method == null) {\r
- return (null);\r
- }\r
-\r
- // If the requested method is not public we cannot call it\r
- if (!Modifier.isPublic(method.getModifiers())) {\r
- return (null);\r
- }\r
-\r
- boolean sameClass = true;\r
- if (clazz == null) {\r
- clazz = method.getDeclaringClass();\r
- } else {\r
- sameClass = clazz.equals(method.getDeclaringClass());\r
- if (!method.getDeclaringClass().isAssignableFrom(clazz)) {\r
- throw new IllegalArgumentException(clazz.getName() +\r
- " is not assignable from " + method.getDeclaringClass().getName());\r
- }\r
- }\r
-\r
- // If the class is public, we are done\r
- if (Modifier.isPublic(clazz.getModifiers())) {\r
- if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {\r
- setMethodAccessible(method); // Default access superclass workaround\r
- }\r
- return (method);\r
- }\r
-\r
- String methodName = method.getName();\r
- Class[] parameterTypes = method.getParameterTypes();\r
-\r
- // Check the implemented interfaces and subinterfaces\r
- method =\r
- getAccessibleMethodFromInterfaceNest(clazz,\r
- methodName,\r
- parameterTypes);\r
-\r
- // Check the superclass chain\r
- if (method == null) {\r
- method = getAccessibleMethodFromSuperclass(clazz,\r
- methodName,\r
- parameterTypes);\r
- }\r
-\r
- return (method);\r
-\r
- }\r
-\r
-\r
- // -------------------------------------------------------- Private Methods\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) by scanning through the superclasses. If no such method\r
- * can be found, return <code>null</code>.</p>\r
- *\r
- * @param clazz Class to be checked\r
- * @param methodName Method name of the method we wish to call\r
- * @param parameterTypes The parameter type signatures\r
- */\r
- private static Method getAccessibleMethodFromSuperclass\r
- (Class clazz, String methodName, Class[] parameterTypes) {\r
-\r
- Class parentClazz = clazz.getSuperclass();\r
- while (parentClazz != null) {\r
- if (Modifier.isPublic(parentClazz.getModifiers())) {\r
- try {\r
- return parentClazz.getMethod(methodName, parameterTypes);\r
- } catch (NoSuchMethodException e) {\r
- return null;\r
- }\r
- }\r
- parentClazz = parentClazz.getSuperclass();\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * <p>Return an accessible method (that is, one that can be invoked via\r
- * reflection) that implements the specified method, by scanning through\r
- * all implemented interfaces and subinterfaces. If no such method\r
- * can be found, return <code>null</code>.</p>\r
- *\r
- * <p> There isn't any good reason why this method must be private.\r
- * It is because there doesn't seem any reason why other classes should\r
- * call this rather than the higher level methods.</p>\r
- *\r
- * @param clazz Parent class for the interfaces to be checked\r
- * @param methodName Method name of the method we wish to call\r
- * @param parameterTypes The parameter type signatures\r
- */\r
- private static Method getAccessibleMethodFromInterfaceNest\r
- (Class clazz, String methodName, Class[] parameterTypes) {\r
-\r
- Method method = null;\r
-\r
- // Search up the superclass chain\r
- for (; clazz != null; clazz = clazz.getSuperclass()) {\r
-\r
- // Check the implemented interfaces of the parent class\r
- Class[] interfaces = clazz.getInterfaces();\r
- for (int i = 0; i < interfaces.length; i++) {\r
-\r
- // Is this interface public?\r
- if (!Modifier.isPublic(interfaces[i].getModifiers())) {\r
- continue;\r
- }\r
-\r
- // Does the method exist on this interface?\r
- try {\r
- method = interfaces[i].getDeclaredMethod(methodName,\r
- parameterTypes);\r
- } catch (NoSuchMethodException e) {\r
- /* Swallow, if no method is found after the loop then this\r
- * method returns null.\r
- */\r
- }\r
- if (method != null) {\r
- return method;\r
- }\r
-\r
- // Recursively check our parent interfaces\r
- method =\r
- getAccessibleMethodFromInterfaceNest(interfaces[i],\r
- methodName,\r
- parameterTypes);\r
- if (method != null) {\r
- return method;\r
- }\r
-\r
- }\r
-\r
- }\r
-\r
- // If we found a method return it\r
- if (method != null) {\r
- return (method);\r
- }\r
-\r
- // We did not find anything\r
- return (null);\r
-\r
- }\r
-\r
- /**\r
- * <p>Find an accessible method that matches the given name and has compatible parameters.\r
- * Compatible parameters mean that every method parameter is assignable from \r
- * the given parameters.\r
- * In other words, it finds a method with the given name \r
- * that will take the parameters given.<p>\r
- *\r
- * <p>This method is slightly undeterminstic since it loops \r
- * through methods names and return the first matching method.</p>\r
- * \r
- * <p>This method is used by \r
- * {@link \r
- * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.\r
- *\r
- * <p>This method can match primitive parameter by passing in wrapper classes.\r
- * For example, a <code>Boolean</code> will match a primitive <code>boolean</code>\r
- * parameter.\r
- *\r
- * @param clazz find method in this class\r
- * @param methodName find method with this name\r
- * @param parameterTypes find method with compatible parameters \r
- * @return The accessible method\r
- */\r
- public static Method getMatchingAccessibleMethod(\r
- Class clazz,\r
- String methodName,\r
- Class[] parameterTypes) {\r
- // trace logging\r
- Log log = LogFactory.getLog(MethodUtils.class);\r
- if (log.isTraceEnabled()) {\r
- log.trace("Matching name=" + methodName + " on " + clazz);\r
- }\r
- MethodDescriptor md = new MethodDescriptor(clazz, methodName, parameterTypes, false);\r
- \r
- // see if we can find the method directly\r
- // most of the time this works and it's much faster\r
- try {\r
- Method method = clazz.getMethod(methodName, parameterTypes);\r
- if (log.isTraceEnabled()) {\r
- log.trace("Found straight match: " + method);\r
- log.trace("isPublic:" + Modifier.isPublic(method.getModifiers()));\r
- }\r
- \r
- setMethodAccessible(method); // Default access superclass workaround\r
-\r
- return method;\r
- \r
- } catch (NoSuchMethodException e) { /* SWALLOW */ }\r
- \r
- // search through all methods \r
- int paramSize = parameterTypes.length;\r
- Method bestMatch = null;\r
- Method[] methods = clazz.getMethods();\r
- float bestMatchCost = Float.MAX_VALUE;\r
- float myCost = Float.MAX_VALUE;\r
- for (int i = 0, size = methods.length; i < size ; i++) {\r
- if (methods[i].getName().equals(methodName)) {\r
- // log some trace information\r
- if (log.isTraceEnabled()) {\r
- log.trace("Found matching name:");\r
- log.trace(methods[i]);\r
- } \r
- \r
- // compare parameters\r
- Class[] methodsParams = methods[i].getParameterTypes();\r
- int methodParamSize = methodsParams.length;\r
- if (methodParamSize == paramSize) { \r
- boolean match = true;\r
- for (int n = 0 ; n < methodParamSize; n++) {\r
- if (log.isTraceEnabled()) {\r
- log.trace("Param=" + parameterTypes[n].getName());\r
- log.trace("Method=" + methodsParams[n].getName());\r
- }\r
- if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {\r
- if (log.isTraceEnabled()) {\r
- log.trace(methodsParams[n] + " is not assignable from " \r
- + parameterTypes[n]);\r
- } \r
- match = false;\r
- break;\r
- }\r
- }\r
- \r
- if (match) {\r
- // get accessible version of method\r
- Method method = getAccessibleMethod(clazz, methods[i]);\r
- if (method != null) {\r
- if (log.isTraceEnabled()) {\r
- log.trace(method + " accessible version of " \r
- + methods[i]);\r
- }\r
- setMethodAccessible(method); // Default access superclass workaround\r
- myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes());\r
- if ( myCost < bestMatchCost ) {\r
- bestMatch = method;\r
- bestMatchCost = myCost;\r
- }\r
- }\r
- \r
- log.trace("Couldn't find accessible method.");\r
- }\r
- }\r
- }\r
- }\r
- if ( bestMatch == null ){\r
- // didn't find a match\r
- log.trace("No match found.");\r
- }\r
- \r
- return bestMatch; \r
- }\r
-\r
- public static <T> Constructor<T> getMatchingAccessibleConstructor(\r
- Class<T> clazz,\r
- Class[] parameterTypes) {\r
- // trace logging\r
- Log log = LogFactory.getLog(MethodUtils.class);\r
- MethodDescriptor md = new MethodDescriptor(clazz, "dummy", parameterTypes, false);\r
- \r
- // see if we can find the method directly\r
- // most of the time this works and it's much faster\r
- try {\r
- Constructor<T> constructor = clazz.getConstructor(parameterTypes);\r
- if (log.isTraceEnabled()) {\r
- log.trace("Found straight match: " + constructor);\r
- log.trace("isPublic:" + Modifier.isPublic(constructor.getModifiers()));\r
- }\r
- \r
- setMethodAccessible(constructor); // Default access superclass workaround\r
-\r
- return constructor;\r
- \r
- } catch (NoSuchMethodException e) { /* SWALLOW */ }\r
- \r
- // search through all methods \r
- int paramSize = parameterTypes.length;\r
- Constructor<T> bestMatch = null;\r
- Constructor<?>[] constructors = clazz.getConstructors();\r
- float bestMatchCost = Float.MAX_VALUE;\r
- float myCost = Float.MAX_VALUE;\r
- for (int i = 0, size = constructors.length; i < size ; i++) {\r
- // compare parameters\r
- Class[] methodsParams = constructors[i].getParameterTypes();\r
- int methodParamSize = methodsParams.length;\r
- if (methodParamSize == paramSize) { \r
- boolean match = true;\r
- for (int n = 0 ; n < methodParamSize; n++) {\r
- if (log.isTraceEnabled()) {\r
- log.trace("Param=" + parameterTypes[n].getName());\r
- log.trace("Method=" + methodsParams[n].getName());\r
- }\r
- if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {\r
- if (log.isTraceEnabled()) {\r
- log.trace(methodsParams[n] + " is not assignable from " \r
- + parameterTypes[n]);\r
- } \r
- match = false;\r
- break;\r
- }\r
- }\r
- \r
- if (match) {\r
- // get accessible version of method\r
- Constructor<T> cons = (Constructor<T>)constructors[i];\r
- myCost = getTotalTransformationCost(parameterTypes,cons.getParameterTypes());\r
- if ( myCost < bestMatchCost ) {\r
- bestMatch = cons;\r
- bestMatchCost = myCost;\r
- }\r
- }\r
- }\r
- }\r
- if ( bestMatch == null ){\r
- // didn't find a match\r
- log.trace("No match found.");\r
- }\r
- \r
- return bestMatch; \r
- }\r
-\r
- /**\r
- * Try to make the method accessible\r
- * @param method The source arguments\r
- */\r
- private static void setMethodAccessible(Object method) {\r
- try {\r
- //\r
- // XXX Default access superclass workaround\r
- //\r
- // When a public class has a default access superclass\r
- // with public methods, these methods are accessible.\r
- // Calling them from compiled code works fine.\r
- //\r
- // Unfortunately, using reflection to invoke these methods\r
- // seems to (wrongly) to prevent access even when the method\r
- // modifer is public.\r
- //\r
- // The following workaround solves the problem but will only\r
- // work from sufficiently privilages code. \r
- //\r
- // Better workarounds would be greatfully accepted.\r
- //\r
- if (method instanceof Method) {\r
- ((Method)method).setAccessible(true);\r
- } else if (method instanceof Constructor) {\r
- ((Constructor)method).setAccessible(true);\r
- } else {\r
- throw new RuntimeException("invalid parameter");\r
- }\r
- \r
- } catch (SecurityException se) {\r
- // log but continue just in case the method.invoke works anyway\r
- Log log = LogFactory.getLog(MethodUtils.class);\r
- if (!loggedAccessibleWarning) {\r
- boolean vulnerableJVM = false;\r
- try {\r
- String specVersion = System.getProperty("java.specification.version");\r
- if (specVersion.charAt(0) == '1' && \r
- (specVersion.charAt(2) == '0' ||\r
- specVersion.charAt(2) == '1' ||\r
- specVersion.charAt(2) == '2' ||\r
- specVersion.charAt(2) == '3')) {\r
- \r
- vulnerableJVM = true;\r
- }\r
- } catch (SecurityException e) {\r
- // don't know - so display warning\r
- vulnerableJVM = true;\r
- }\r
- if (vulnerableJVM) {\r
- log.warn(\r
- "Current Security Manager restricts use of workarounds for reflection bugs "\r
- + " in pre-1.4 JVMs.");\r
- }\r
- loggedAccessibleWarning = true;\r
- }\r
- log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se);\r
- }\r
- }\r
-\r
- /**\r
- * Returns the sum of the object transformation cost for each class in the source\r
- * argument list.\r
- * @param srcArgs The source arguments\r
- * @param destArgs The destination arguments\r
- * @return The total transformation cost\r
- */\r
- private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) {\r
-\r
- float totalCost = 0.0f;\r
- for (int i = 0; i < srcArgs.length; i++) {\r
- Class srcClass, destClass;\r
- srcClass = srcArgs[i];\r
- destClass = destArgs[i];\r
- totalCost += getObjectTransformationCost(srcClass, destClass);\r
- }\r
-\r
- return totalCost;\r
- }\r
- \r
- /**\r
- * Gets the number of steps required needed to turn the source class into the \r
- * destination class. This represents the number of steps in the object hierarchy \r
- * graph.\r
- * @param srcClass The source class\r
- * @param destClass The destination class\r
- * @return The cost of transforming an object\r
- */\r
- private static float getObjectTransformationCost(Class srcClass, Class destClass) {\r
- float cost = 0.0f;\r
- while (destClass != null && !destClass.equals(srcClass)) {\r
- if (destClass.isInterface() && isAssignmentCompatible(destClass,srcClass)) {\r
- // slight penalty for interface match. \r
- // we still want an exact match to override an interface match, but \r
- // an interface match should override anything where we have to get a \r
- // superclass.\r
- cost += 0.25f;\r
- break;\r
- }\r
- cost++;\r
- destClass = destClass.getSuperclass();\r
- }\r
-\r
- /*\r
- * If the destination class is null, we've travelled all the way up to \r
- * an Object match. We'll penalize this by adding 1.5 to the cost.\r
- */\r
- if (destClass == null) {\r
- cost += 1.5f;\r
- }\r
-\r
- return cost;\r
- }\r
- \r
- \r
- /**\r
- * <p>Determine whether a type can be used as a parameter in a method invocation.\r
- * This method handles primitive conversions correctly.</p>\r
- *\r
- * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,\r
- * a <code>Long</code> to a <code>long</code>,\r
- * a <code>Float</code> to a <code>float</code>,\r
- * a <code>Integer</code> to a <code>int</code>,\r
- * and a <code>Double</code> to a <code>double</code>.\r
- * Now logic widening matches are allowed.\r
- * For example, a <code>Long</code> will not match a <code>int</code>.\r
- *\r
- * @param parameterType the type of parameter accepted by the method\r
- * @param parameterization the type of parameter being tested \r
- *\r
- * @return true if the assignement is compatible.\r
- */\r
- public static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {\r
- // try plain assignment\r
- if (parameterType.isAssignableFrom(parameterization)) {\r
- return true;\r
- }\r
- \r
- if (parameterType.isPrimitive()) {\r
- // this method does *not* do widening - you must specify exactly\r
- // is this the right behaviour?\r
- Class parameterWrapperClazz = getPrimitiveWrapper(parameterType);\r
- if (parameterWrapperClazz != null) {\r
- return parameterWrapperClazz.equals(parameterization);\r
- }\r
- }\r
- \r
- return false;\r
- }\r
- \r
- /**\r
- * Gets the wrapper object class for the given primitive type class.\r
- * For example, passing <code>boolean.class</code> returns <code>Boolean.class</code>\r
- * @param primitiveType the primitive type class for which a match is to be found\r
- * @return the wrapper type associated with the given primitive \r
- * or null if no match is found\r
- */\r
- public static Class getPrimitiveWrapper(Class primitiveType) {\r
- // does anyone know a better strategy than comparing names?\r
- if (boolean.class.equals(primitiveType)) {\r
- return Boolean.class;\r
- } else if (float.class.equals(primitiveType)) {\r
- return Float.class;\r
- } else if (long.class.equals(primitiveType)) {\r
- return Long.class;\r
- } else if (int.class.equals(primitiveType)) {\r
- return Integer.class;\r
- } else if (short.class.equals(primitiveType)) {\r
- return Short.class;\r
- } else if (byte.class.equals(primitiveType)) {\r
- return Byte.class;\r
- } else if (double.class.equals(primitiveType)) {\r
- return Double.class;\r
- } else if (char.class.equals(primitiveType)) {\r
- return Character.class;\r
- } else {\r
- \r
- return null;\r
- }\r
- }\r
-\r
- /**\r
- * Gets the class for the primitive type corresponding to the primitive wrapper class given.\r
- * For example, an instance of <code>Boolean.class</code> returns a <code>boolean.class</code>. \r
- * @param wrapperType the \r
- * @return the primitive type class corresponding to the given wrapper class,\r
- * null if no match is found\r
- */\r
- public static Class getPrimitiveType(Class wrapperType) {\r
- // does anyone know a better strategy than comparing names?\r
- if (Boolean.class.equals(wrapperType)) {\r
- return boolean.class;\r
- } else if (Float.class.equals(wrapperType)) {\r
- return float.class;\r
- } else if (Long.class.equals(wrapperType)) {\r
- return long.class;\r
- } else if (Integer.class.equals(wrapperType)) {\r
- return int.class;\r
- } else if (Short.class.equals(wrapperType)) {\r
- return short.class;\r
- } else if (Byte.class.equals(wrapperType)) {\r
- return byte.class;\r
- } else if (Double.class.equals(wrapperType)) {\r
- return double.class;\r
- } else if (Character.class.equals(wrapperType)) {\r
- return char.class;\r
- } else {\r
- Log log = LogFactory.getLog(MethodUtils.class);\r
- if (log.isDebugEnabled()) {\r
- log.debug("Not a known primitive wrapper class: " + wrapperType);\r
- }\r
- return null;\r
- }\r
- }\r
- \r
- /**\r
- * Find a non primitive representation for given primitive class.\r
- *\r
- * @param clazz the class to find a representation for, not null\r
- * @return the original class if it not a primitive. Otherwise the wrapper class. Not null\r
- */\r
- public static Class toNonPrimitiveClass(Class clazz) {\r
- if (clazz.isPrimitive()) {\r
- Class primitiveClazz = MethodUtils.getPrimitiveWrapper(clazz);\r
- // the above method returns \r
- if (primitiveClazz != null) {\r
- return primitiveClazz;\r
- } else {\r
- return clazz;\r
- }\r
- } else {\r
- return clazz;\r
- }\r
- }\r
- \r
-\r
- /**\r
- * Represents the key to looking up a Method by reflection.\r
- */\r
- private static class MethodDescriptor {\r
- private Class cls;\r
- private String methodName;\r
- private Class[] paramTypes;\r
- private boolean exact;\r
- private int hashCode;\r
-\r
- /**\r
- * The sole constructor.\r
- *\r
- * @param cls the class to reflect, must not be null\r
- * @param methodName the method name to obtain\r
- * @param paramTypes the array of classes representing the paramater types\r
- * @param exact whether the match has to be exact.\r
- */\r
- public MethodDescriptor(Class cls, String methodName, Class[] paramTypes, boolean exact) {\r
- if (cls == null) {\r
- throw new IllegalArgumentException("Class cannot be null");\r
- }\r
- if (methodName == null) {\r
- throw new IllegalArgumentException("Method Name cannot be null");\r
- }\r
- if (paramTypes == null) {\r
- paramTypes = EMPTY_CLASS_PARAMETERS;\r
- }\r
-\r
- this.cls = cls;\r
- this.methodName = methodName;\r
- this.paramTypes = paramTypes;\r
- this.exact= exact;\r
-\r
- this.hashCode = methodName.length();\r
- }\r
- /**\r
- * Checks for equality.\r
- * @param obj object to be tested for equality\r
- * @return true, if the object describes the same Method.\r
- */\r
- public boolean equals(Object obj) {\r
- if (!(obj instanceof MethodDescriptor)) {\r
- return false;\r
- }\r
- MethodDescriptor md = (MethodDescriptor)obj;\r
-\r
- return (\r
- exact == md.exact &&\r
- methodName.equals(md.methodName) &&\r
- cls.equals(md.cls) &&\r
- java.util.Arrays.equals(paramTypes, md.paramTypes)\r
- );\r
- }\r
- /**\r
- * Returns the string length of method name. I.e. if the\r
- * hashcodes are different, the objects are different. If the\r
- * hashcodes are the same, need to use the equals method to\r
- * determine equality.\r
- * @return the string length of method name.\r
- */\r
- public int hashCode() {\r
- return hashCode;\r
- }\r
- }\r
-}\r