Updated method utils with latest code from the beanutils version.

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@137124 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Burrell Donkin 2002-11-14 18:51:57 +00:00
parent 15508a27a2
commit 4914d5c807
1 changed files with 217 additions and 150 deletions

View File

@ -74,12 +74,20 @@ import org.apache.commons.lang.StringUtils;
* programmer. This can break an implementation if used incorrectly. This * programmer. This can break an implementation if used incorrectly. This
* facility should be used with care. * facility should be used with care.
* *
* @author Based on code from BeanUtils
* @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a> * @author <a href="mailto:scolebourne@apache.org">Stephen Colebourne</a>
* @version $Id: MethodUtils.java,v 1.1 2002/10/24 23:12:54 scolebourne Exp $ * @author Based on code from <code>BeanUtils</code> by: Craig R. McClanahan
* @author Ralph Schaer
* @author Chris Audley
* @author Rey François
* @author Gregor Raýman
* @author Jan Sorensen
* @author Robert Burrell Donkin
* @version $Id: MethodUtils.java,v 1.2 2002/11/14 18:51:57 rdonkin Exp $
*/ */
public class MethodUtils { public class MethodUtils {
public static final boolean debug = true;
/** An empty method array */ /** An empty method array */
public static final Method[] EMPTY_METHOD_ARRAY = new Method[0]; public static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
@ -329,15 +337,15 @@ public class MethodUtils {
args = ArrayUtils.EMPTY_OBJECT_ARRAY; args = ArrayUtils.EMPTY_OBJECT_ARRAY;
} }
return null; //return null;
// Method method = getMatchingAccessibleMethod( Method method = getMatchingAccessibleMethod(
// object.getClass(), object.getClass(),
// methodName, methodName,
// parameterTypes); parameterTypes);
// if (method == null) if (method == null)
// throw new NoSuchMethodException("No such accessible method: " + throw new NoSuchMethodException("No such accessible method: " +
// methodName + "() on object: " + object.getClass().getName()); methodName + "() on object: " + object.getClass().getName());
// return method.invoke(object, args); return method.invoke(object, args);
} }
@ -608,143 +616,202 @@ 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 * <p>Find an accessible method that matches the given name and has compatible parameters.
// * the given parameters. * Compatible parameters mean that every method parameter is assignable from
// * In other words, it finds a method with the given name * the given parameters.
// * that will take the parameters given.<p> * 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 slightly undeterminstic since it loops
// * * through methods names and return the first matching method.</p>
// * <p>This method is used by *
// * {@link * <p>This method is used by
// * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. * {@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> * <p>This method can match primitive parameter by passing in wrapper classes.
// * parameter. * 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 clazz find method in this class
// * @param parameterTypes find method with compatible parameters * @param methodName find method with this name
// */ * @param parameterTypes find method with compatible parameters
// private static Method getMatchingAccessibleMethod( */
// Class clazz, private static Method getMatchingAccessibleMethod(
// String methodName, Class clazz,
// Class[] parameterTypes) { String methodName,
// // trace logging Class[] parameterTypes) {
// if (log.isTraceEnabled()) { // trace logging
// log.trace("Matching name=" + methodName + " on " + clazz); if (debug) {
// } 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 // see if we can find the method directly
// try { // most of the time this works and it's much faster
// Method method = clazz.getMethod(methodName, parameterTypes); try {
// return method; Method method = clazz.getMethod(methodName, parameterTypes);
// if (debug) {
// } catch (NoSuchMethodException e) { /* SWALLOW */ } log("Found straight match: " + method);
// log("isPublic:" + Modifier.isPublic(method.getModifiers()));
// // search through all methods }
// int paramSize = parameterTypes.length;
// Method[] methods = clazz.getMethods(); try {
// for (int i = 0, size = methods.length; i < size ; i++) { //
// if (methods[i].getName().equals(methodName)) { // XXX Default access superclass workaround
// // log some trace information //
// if (log.isTraceEnabled()) { // When a public class has a default access superclass
// log.trace("Found matching name:"); // with public methods, these methods are accessible.
// log.trace(methods[i]); // Calling them from compiled code works fine.
// } //
// // Unfortunately, using reflection to invoke these methods
// // compare parameters // seems to (wrongly) to prevent access even when the method
// Class[] methodsParams = methods[i].getParameterTypes(); // modifer is public.
// int methodParamSize = methodsParams.length; //
// if (methodParamSize == paramSize) { // The following workaround solves the problem but will only
// boolean match = true; // work from sufficiently privilages code.
// for (int n = 0 ; n < methodParamSize; n++) { //
// if (log.isTraceEnabled()) { // Better workarounds would be greatfully accepted.
// log.trace("Param=" + parameterTypes[n].getName()); //
// log.trace("Method=" + methodsParams[n].getName()); method.setAccessible(true);
// }
// if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) { } catch (SecurityException se) {
// if (log.isTraceEnabled()) { // log but continue just in case the method.invoke works anyway
// log.trace(methodsParams[n] + " is not assignable from " log(
// + parameterTypes[n]); "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
// } se);
// match = false; }
// break; return method;
// }
// } } catch (NoSuchMethodException e) { /* SWALLOW */ }
//
// if (match) { // search through all methods
// // get accessible version of method int paramSize = parameterTypes.length;
// Method method = getAccessibleMethod(methods[i]); Method[] methods = clazz.getMethods();
// if (method != null) { for (int i = 0, size = methods.length; i < size ; i++) {
// if (log.isTraceEnabled()) { if (methods[i].getName().equals(methodName)) {
// log.trace(method + " accessible version of " // log some trace information
// + methods[i]); if (debug) {
// } log("Found matching name:");
// return method; log(methods[i]);
// } }
//
// log.trace("Couldn't find accessible method."); // compare parameters
// } Class[] methodsParams = methods[i].getParameterTypes();
// } int methodParamSize = methodsParams.length;
// } if (methodParamSize == paramSize) {
// } boolean match = true;
// for (int n = 0 ; n < methodParamSize; n++) {
// // didn't find a match if (debug) {
// log.trace("No match found."); log("Param=" + parameterTypes[n].getName());
// return null; log("Method=" + methodsParams[n].getName());
// } }
// if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) {
// /** if (debug) {
// * <p>Determine whether a type can be used as a parameter in a method invocation. log(methodsParams[n] + " is not assignable from "
// * This method handles primitive conversions correctly.</p> + parameterTypes[n]);
// * }
// * <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>, match = false;
// * a <code>Long</code> to a <code>long</code>, break;
// * a <code>Float</code> to a <code>float</code>, }
// * a <code>Integer</code> to a <code>int</code>, }
// * and a <code>Double</code> to a <code>double</code>.
// * Now logic widening matches are allowed. if (match) {
// * For example, a <code>Long</code> will not match a <code>int</code>. // get accessible version of method
// * Method method = getAccessibleMethod(methods[i]);
// * @param parameterType the type of parameter accepted by the method if (method != null) {
// * @param parameterization the type of parameter being tested if (debug) {
// * log(method + " accessible version of "
// * @return true if the assignement is compatible. + methods[i]);
// */ }
// private static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) { try {
// // try plain assignment //
// if (parameterType.isAssignableFrom(parameterization)) { // XXX Default access superclass workaround
// return true; // (See above for more details.)
// } //
// method.setAccessible(true);
// if (parameterType.isPrimitive()) {
// // does anyone know a better strategy than comparing names? } catch (SecurityException se) {
// // also, this method does *not* do widening - you must specify exactly // log but continue just in case the method.invoke works anyway
// // is this the right behaviour? log(
// if (boolean.class.equals(parameterType)) { "Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.",
// return Boolean.class.equals(parameterization); se);
// } }
// if (float.class.equals(parameterType)) { return method;
// return Float.class.equals(parameterization); }
// }
// if (long.class.equals(parameterType)) { log("Couldn't find accessible method.");
// return Long.class.equals(parameterization); }
// } }
// if (int.class.equals(parameterType)) { }
// return Integer.class.equals(parameterization); }
// }
// if (double.class.equals(parameterType)) { // didn't find a match
// return Double.class.equals(parameterization); log("No match found.");
// } return null;
// } }
//
// return false;
// } /**
// * <p>Determine whether a type can be used as a parameter in a method invocation.
* This method handles primitive conversions correctly.</p>
*
* <p>In order words, it will match a <code>Boolean</code> to a <code>boolean</code>,
* a <code>Long</code> to a <code>long</code>,
* a <code>Float</code> to a <code>float</code>,
* a <code>Integer</code> to a <code>int</code>,
* and a <code>Double</code> to a <code>double</code>.
* Now logic widening matches are allowed.
* For example, a <code>Long</code> will not match a <code>int</code>.
*
* @param parameterType the type of parameter accepted by the method
* @param parameterization the type of parameter being tested
*
* @return true if the assignement is compatible.
*/
private static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) {
// try plain assignment
if (parameterType.isAssignableFrom(parameterization)) {
return true;
}
if (parameterType.isPrimitive()) {
// does anyone know a better strategy than comparing names?
// also, this method does *not* do widening - you must specify exactly
// is this the right behaviour?
if (boolean.class.equals(parameterType)) {
return Boolean.class.equals(parameterization);
}
if (float.class.equals(parameterType)) {
return Float.class.equals(parameterization);
}
if (long.class.equals(parameterType)) {
return Long.class.equals(parameterization);
}
if (int.class.equals(parameterType)) {
return Integer.class.equals(parameterization);
}
if (double.class.equals(parameterType)) {
return Double.class.equals(parameterization);
}
}
return false;
}
private static void log(Object o) {
if (debug) {
System.err.println(o);
}
}
private static void log(Object o, Throwable t) {
if (debug) {
System.err.println(o);
System.err.println(t);
}
}
} }