Consolidated methods.
git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137178 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5003e9c501
commit
b91dfd1a2c
|
@ -66,6 +66,7 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
/**
|
||||
* <code>MethodUtils</code> contains utility methods for working for
|
||||
* methods by reflection.
|
||||
|
@ -82,7 +83,7 @@ import org.apache.commons.lang.StringUtils;
|
|||
* @author Gregor Raýman
|
||||
* @author Jan Sorensen
|
||||
* @author Robert Burrell Donkin
|
||||
* @version $Id: MethodUtils.java,v 1.8 2002/11/21 19:38:51 rdonkin Exp $
|
||||
* @version $Id: MethodUtils.java,v 1.9 2002/12/10 19:06:49 rdonkin Exp $
|
||||
*/
|
||||
public class MethodUtils {
|
||||
|
||||
|
@ -116,6 +117,21 @@ public class MethodUtils {
|
|||
return getMethod(cls, methodName, ArrayUtils.EMPTY_CLASS_ARRAY, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Method by name. The method must be public.
|
||||
* Superclasses will be considered.
|
||||
*
|
||||
* @param cls the class to reflect, must not be null
|
||||
* @param methodName the field name to obtain
|
||||
* @return the Method object
|
||||
* @throws IllegalArgumentException if the class or method name is null
|
||||
* @throws ReflectionException if an error occurs during reflection
|
||||
*/
|
||||
public static Method getMethod(Class cls, String methodName, Class paramType) {
|
||||
Class[] paramTypes = {paramType};
|
||||
return getMethod(cls, methodName, paramTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a Method by name. The method must be public.
|
||||
* Superclasses will be considered.
|
||||
|
@ -173,7 +189,24 @@ public class MethodUtils {
|
|||
}
|
||||
throw new NoSuchMethodException("The method '" + methodName + "' could not be found");
|
||||
} else {
|
||||
return cls.getMethod(methodName, paramTypes);
|
||||
// apply workarounds
|
||||
Method method = null;
|
||||
try {
|
||||
|
||||
method = cls.getMethod(methodName, paramTypes);
|
||||
|
||||
} catch(NoSuchMethodException e) {
|
||||
// swallow
|
||||
}
|
||||
|
||||
if (method == null) {
|
||||
// use the same as beanutils for the moment
|
||||
Method[] compatibles = getCompatibleMethods(cls, methodName, paramTypes);
|
||||
if (compatibles.length > 0) {
|
||||
method = compatibles[0];
|
||||
}
|
||||
}
|
||||
return getMethod(method);
|
||||
}
|
||||
|
||||
} catch (ReflectionException ex) {
|
||||
|
@ -186,6 +219,50 @@ public class MethodUtils {
|
|||
ex, "getting method", cls.getName(), null, methodName), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Return an accessible method (that is, one that can be invoked via
|
||||
* reflection) that implements the specified Method. If no such method
|
||||
* can be found, return <code>null</code>.</p>
|
||||
*
|
||||
* @param method The method that we wish to call
|
||||
*/
|
||||
public static Method getMethod(Method method) {
|
||||
|
||||
Method accessibleMethod = getAccessibleMethod(method);
|
||||
if (accessibleMethod == null) {
|
||||
try {
|
||||
//
|
||||
// XXX Default access superclass workaround
|
||||
//
|
||||
// When a public class has a default access superclass
|
||||
// with public methods, these methods are accessible.
|
||||
// Calling them from compiled code works fine.
|
||||
//
|
||||
// Unfortunately, using reflection to invoke these methods
|
||||
// seems to (wrongly) to prevent access even when the method
|
||||
// modifer is public.
|
||||
//
|
||||
// The following workaround solves the problem but will only
|
||||
// work from sufficiently privilages code.
|
||||
//
|
||||
// Better workarounds would be greatfully accepted.
|
||||
//
|
||||
if (ReflectionUtils.isPublicScope(method)) {
|
||||
method.setAccessible(true);
|
||||
accessibleMethod = method;
|
||||
}
|
||||
|
||||
} catch (SecurityException se) {
|
||||
// log but continue just in case the method.invoke works anyway
|
||||
log(
|
||||
"Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
|
||||
se);
|
||||
}
|
||||
}
|
||||
return (accessibleMethod);
|
||||
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
|
@ -303,7 +380,7 @@ public class MethodUtils {
|
|||
args = ArrayUtils.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
Method method = getMatchingAccessibleMethod(
|
||||
Method method = getMethod(
|
||||
object.getClass(),
|
||||
methodName,
|
||||
parameterTypes);
|
||||
|
@ -330,62 +407,11 @@ public class MethodUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Return an accessible method (that is, one that can be invoked via
|
||||
* reflection) with given name and a single parameter. If no such method
|
||||
* can be found, return <code>null</code>.
|
||||
* Basically, a convenience wrapper that constructs a <code>Class</code>
|
||||
* array for you.</p>
|
||||
*
|
||||
* @param clazz get method from this class
|
||||
* @param methodName get method with this name
|
||||
* @param parameterType taking this type of parameter
|
||||
*/
|
||||
public static Method getAccessibleMethod(
|
||||
Class clazz,
|
||||
String methodName,
|
||||
Class parameterType) {
|
||||
|
||||
Class[] parameterTypes = {parameterType};
|
||||
return getAccessibleMethod(clazz, methodName, parameterTypes);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Return an accessible method (that is, one that can be invoked via
|
||||
* reflection) with given name and parameters. If no such method
|
||||
* can be found, return <code>null</code>.
|
||||
* This is just a convenient wrapper for
|
||||
* {@link #getAccessibleMethod(Method method)}.</p>
|
||||
*
|
||||
* @param clazz get method from this class
|
||||
* @param methodName get method with this name
|
||||
* @param parameterTypes with these parameters types
|
||||
*/
|
||||
public static Method getAccessibleMethod(
|
||||
Class clazz,
|
||||
String methodName,
|
||||
Class[] parameterTypes) {
|
||||
// -------------------------------------------------------- Private Methods
|
||||
|
||||
try {
|
||||
return getAccessibleMethod
|
||||
(clazz.getMethod(methodName, parameterTypes));
|
||||
} catch (NoSuchMethodException e) {
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Return an accessible method (that is, one that can be invoked via
|
||||
* reflection) that implements the specified Method. If no such method
|
||||
* can be found, return <code>null</code>.</p>
|
||||
*
|
||||
* @param method The method that we wish to call
|
||||
*/
|
||||
public static Method getAccessibleMethod(Method method) {
|
||||
private static Method getAccessibleMethod(Method method) {
|
||||
|
||||
// Make sure we have a method to check
|
||||
if (method == null) {
|
||||
|
@ -394,14 +420,20 @@ public class MethodUtils {
|
|||
|
||||
// If the requested method is not public we cannot call it
|
||||
if (!Modifier.isPublic(method.getModifiers())) {
|
||||
log("Method is not public");
|
||||
return (null);
|
||||
}
|
||||
|
||||
// If the declaring class is public, we are done
|
||||
Class clazz = method.getDeclaringClass();
|
||||
if (Modifier.isPublic(clazz.getModifiers())) {
|
||||
log("Class is public");
|
||||
return (method);
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
log("Method is in non-public class " + clazz);
|
||||
}
|
||||
|
||||
// Check the implemented interfaces and subinterfaces
|
||||
String methodName = method.getName();
|
||||
|
@ -410,13 +442,12 @@ public class MethodUtils {
|
|||
getAccessibleMethodFromInterfaceNest(clazz,
|
||||
method.getName(),
|
||||
method.getParameterTypes());
|
||||
|
||||
return (method);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------- Private Methods
|
||||
|
||||
/**
|
||||
* <p>Return an accessible method (that is, one that can be invoked via
|
||||
* reflection) that implements the specified method, by scanning through
|
||||
|
@ -433,7 +464,9 @@ public class MethodUtils {
|
|||
*/
|
||||
private static Method getAccessibleMethodFromInterfaceNest
|
||||
(Class clazz, String methodName, Class parameterTypes[]) {
|
||||
|
||||
if (debug) {
|
||||
log("Finding accessible method " + methodName + " from interface nest");
|
||||
}
|
||||
Method method = null;
|
||||
|
||||
// Search up the superclass chain
|
||||
|
@ -470,38 +503,18 @@ public class MethodUtils {
|
|||
}
|
||||
|
||||
// If we found a method return it
|
||||
if (method != null)
|
||||
if (method != null) {
|
||||
if (debug) {
|
||||
log("Found method in class " + method.getDeclaringClass());
|
||||
}
|
||||
return (method);
|
||||
|
||||
}
|
||||
// We did not find anything
|
||||
return (null);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Find an accessible method that matches the given name and has compatible parameters.
|
||||
* Compatible parameters mean that every method parameter is assignable from
|
||||
* the given parameters.
|
||||
* In other words, it finds a method with the given name
|
||||
* that will take the parameters given.<p>
|
||||
*
|
||||
* <p>This method is slightly undeterminstic since it loops
|
||||
* through methods names and return the first matching method.</p>
|
||||
*
|
||||
* <p>This method is used by
|
||||
* {@link
|
||||
* #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}.
|
||||
*
|
||||
* <p>This method can match primitive parameter by passing in wrapper classes.
|
||||
* For example, a <code>Boolean</code> will match a primitive <code>boolean</code>
|
||||
* parameter.
|
||||
*
|
||||
* @param clazz find method in this class
|
||||
* @param methodName find method with this name
|
||||
* @param parameterTypes find method with compatible parameters
|
||||
*/
|
||||
private static Method getMatchingAccessibleMethod(
|
||||
}
|
||||
|
||||
private static Method[] getCompatibleMethods(
|
||||
Class clazz,
|
||||
String methodName,
|
||||
Class[] parameterTypes) {
|
||||
|
@ -510,90 +523,36 @@ public class MethodUtils {
|
|||
log("Matching name=" + methodName + " on " + clazz);
|
||||
}
|
||||
|
||||
// see if we can find the method directly
|
||||
// most of the time this works and it's much faster
|
||||
try {
|
||||
Method method = clazz.getMethod(methodName, parameterTypes);
|
||||
if (debug) {
|
||||
log("Found straight match: " + method);
|
||||
log("isPublic:" + Modifier.isPublic(method.getModifiers()));
|
||||
}
|
||||
|
||||
try {
|
||||
//
|
||||
// XXX Default access superclass workaround
|
||||
//
|
||||
// When a public class has a default access superclass
|
||||
// with public methods, these methods are accessible.
|
||||
// Calling them from compiled code works fine.
|
||||
//
|
||||
// Unfortunately, using reflection to invoke these methods
|
||||
// seems to (wrongly) to prevent access even when the method
|
||||
// modifer is public.
|
||||
//
|
||||
// The following workaround solves the problem but will only
|
||||
// work from sufficiently privilages code.
|
||||
//
|
||||
// Better workarounds would be greatfully accepted.
|
||||
//
|
||||
method.setAccessible(true);
|
||||
|
||||
} catch (SecurityException se) {
|
||||
// log but continue just in case the method.invoke works anyway
|
||||
log(
|
||||
"Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
|
||||
se);
|
||||
}
|
||||
return method;
|
||||
|
||||
} catch (NoSuchMethodException e) { /* SWALLOW */ }
|
||||
|
||||
// search through all methods
|
||||
int paramSize = parameterTypes.length;
|
||||
Method[] methods = clazz.getMethods();
|
||||
ArrayList compatibles = new ArrayList(methods.length);
|
||||
for (int i = 0, size = methods.length; i < size ; i++) {
|
||||
if (debug) {
|
||||
log("Checking: " + methods[i]);
|
||||
}
|
||||
if (methods[i].getName().equals(methodName)) {
|
||||
// log some trace information
|
||||
if (debug) {
|
||||
log("Found matching name:");
|
||||
log(methods[i]);
|
||||
log("Found matching name:" + methods[i]);
|
||||
}
|
||||
|
||||
// compare parameters
|
||||
Class[] methodsParams = methods[i].getParameterTypes();
|
||||
if (ReflectionUtils.isCompatible(parameterTypes, methodsParams)) {
|
||||
// get accessible version of method
|
||||
Method method = getAccessibleMethod(methods[i]);
|
||||
Method method = getMethod(methods[i]);
|
||||
if (method != null) {
|
||||
if (debug) {
|
||||
log(method + " accessible version of "
|
||||
+ methods[i]);
|
||||
}
|
||||
try {
|
||||
//
|
||||
// XXX Default access superclass workaround
|
||||
// (See above for more details.)
|
||||
//
|
||||
method.setAccessible(true);
|
||||
|
||||
} catch (SecurityException se) {
|
||||
// log but continue just in case the method.invoke works anyway
|
||||
log(
|
||||
"Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
|
||||
se);
|
||||
}
|
||||
return method;
|
||||
compatibles.add(method);
|
||||
} else {
|
||||
log("Couldn't find accessible method for: " + methods[i]);
|
||||
}
|
||||
|
||||
log("Couldn't find accessible method.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find a match
|
||||
log("No match found.");
|
||||
return null;
|
||||
}
|
||||
return (Method[]) compatibles.toArray(new Method[compatibles.size()]);
|
||||
}
|
||||
|
||||
private static void log(Object o) {
|
||||
if (debug) {
|
||||
|
|
|
@ -129,7 +129,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
|
||||
// easy bit first - find a public method
|
||||
// METHOD ONE
|
||||
Method method = MethodUtils.getAccessibleMethod
|
||||
Method method = MethodUtils.getMethod
|
||||
(TestBean.class, "setStringProperty", String.class);
|
||||
|
||||
// check that we've found one that matches
|
||||
|
@ -141,7 +141,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
|
||||
// trickier this one - find a method in a direct interface
|
||||
// METHOD TWO
|
||||
method = MethodUtils.getAccessibleMethod
|
||||
method = MethodUtils.getMethod
|
||||
(privateBeanFactory.create().getClass(),
|
||||
"methodBar",
|
||||
String.class);
|
||||
|
@ -155,7 +155,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
|
||||
// trickier this one - find a method in a indirect interface
|
||||
// METHOD THREE
|
||||
method = MethodUtils.getAccessibleMethod
|
||||
method = MethodUtils.getMethod
|
||||
(privateBeanFactory.createSubclass().getClass(),
|
||||
"methodBaz",
|
||||
String.class);
|
||||
|
@ -478,7 +478,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
try {
|
||||
|
||||
// Acquire the methods we need
|
||||
Method currentCounterMethod = MethodUtils.getAccessibleMethod
|
||||
Method currentCounterMethod = MethodUtils.getMethod
|
||||
(TestBean.class, "currentCounter",
|
||||
new Class[0]);
|
||||
assertNotNull("currentCounterMethod exists",
|
||||
|
@ -493,7 +493,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
Modifier.isPublic(currentCounterMethod.getModifiers()));
|
||||
assertTrue("currentCounterMethod static",
|
||||
Modifier.isStatic(currentCounterMethod.getModifiers()));
|
||||
Method incrementCounterMethod1 = MethodUtils.getAccessibleMethod
|
||||
Method incrementCounterMethod1 = MethodUtils.getMethod
|
||||
(TestBean.class, "incrementCounter",
|
||||
new Class[0]);
|
||||
assertNotNull("incrementCounterMethod1 exists",
|
||||
|
@ -508,7 +508,7 @@ public class MethodUtilsTestCase extends TestCase {
|
|||
Modifier.isPublic(incrementCounterMethod1.getModifiers()));
|
||||
assertTrue("incrementCounterMethod1 static",
|
||||
Modifier.isStatic(incrementCounterMethod1.getModifiers()));
|
||||
Method incrementCounterMethod2 = MethodUtils.getAccessibleMethod
|
||||
Method incrementCounterMethod2 = MethodUtils.getMethod
|
||||
(TestBean.class, "incrementCounter",
|
||||
new Class[] { Integer.TYPE });
|
||||
assertNotNull("incrementCounterMethod2 exists",
|
||||
|
|
Loading…
Reference in New Issue