package org.apache.commons.beanutils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class MethodUtils { private static Log log = LogFactory.getLog(MethodUtils.class); private static boolean loggedAccessibleWarning = false; private static final Class[] emptyClassArray = new Class[0]; private static final Object[] emptyObjectArray = new Object[0]; public static Object invokeMethod(Object object, String methodName, Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Object[] args = { arg }; return invokeMethod(object, methodName, args); } public static Object invokeMethod(Object object, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (args == null) args = emptyObjectArray; int arguments = args.length; Class[] parameterTypes = new Class[arguments]; for (int i = 0; i < arguments; i++) parameterTypes[i] = args[i].getClass(); return invokeMethod(object, methodName, args, parameterTypes); } public static Object invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (parameterTypes == null) parameterTypes = emptyClassArray; if (args == null) args = emptyObjectArray; Method method = getMatchingAccessibleMethod(object.getClass(), methodName, parameterTypes); if (method == null) throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName()); return method.invoke(object, args); } public static Object invokeExactMethod(Object object, String methodName, Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Object[] args = { arg }; return invokeExactMethod(object, methodName, args); } public static Object invokeExactMethod(Object object, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (args == null) args = emptyObjectArray; int arguments = args.length; Class[] parameterTypes = new Class[arguments]; for (int i = 0; i < arguments; i++) parameterTypes[i] = args[i].getClass(); return invokeExactMethod(object, methodName, args, parameterTypes); } public static Object invokeExactMethod(Object object, String methodName, Object[] args, Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (args == null) args = emptyObjectArray; if (parameterTypes == null) parameterTypes = emptyClassArray; Method method = getAccessibleMethod(object.getClass(), methodName, parameterTypes); if (method == null) throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName()); return method.invoke(object, args); } public static Method getAccessibleMethod(Class clazz, String methodName, Class parameterType) { Class[] parameterTypes = { parameterType }; return getAccessibleMethod(clazz, methodName, parameterTypes); } public static Method getAccessibleMethod(Class clazz, String methodName, Class[] parameterTypes) { try { return getAccessibleMethod(clazz.getMethod(methodName, parameterTypes)); } catch (NoSuchMethodException e) { return null; } } public static Method getAccessibleMethod(Method method) { if (method == null) return null; if (!Modifier.isPublic(method.getModifiers())) return null; Class clazz = method.getDeclaringClass(); if (Modifier.isPublic(clazz.getModifiers())) return method; method = getAccessibleMethodFromInterfaceNest(clazz, method.getName(), method.getParameterTypes()); return method; } private static Method getAccessibleMethodFromInterfaceNest(Class clazz, String methodName, Class[] parameterTypes) { Method method = null; for (; clazz != null; clazz = clazz.getSuperclass()) { Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (Modifier.isPublic(interfaces[i].getModifiers())) { try { method = interfaces[i].getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) {} if (method != null) break; method = getAccessibleMethodFromInterfaceNest(interfaces[i], methodName, parameterTypes); if (method != null) break; } } } if (method != null) return method; return null; } public static Method getMatchingAccessibleMethod(Class clazz, String methodName, Class[] parameterTypes) { if (log.isTraceEnabled()) log.trace("Matching name=" + methodName + " on " + clazz); try { Method method = clazz.getMethod(methodName, parameterTypes); if (log.isTraceEnabled()) { log.trace("Found straight match: " + method); log.trace("isPublic:" + Modifier.isPublic(method.getModifiers())); } try { method.setAccessible(true); } catch (SecurityException se) { if (!loggedAccessibleWarning) { log.warn("Cannot use JVM pre-1.4 access bug workaround die to restrictive security manager."); loggedAccessibleWarning = true; } log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se); } return method; } catch (NoSuchMethodException e) { int paramSize = parameterTypes.length; Method[] methods = clazz.getMethods(); for (int i = 0, size = methods.length; i < size; i++) { if (methods[i].getName().equals(methodName)) { if (log.isTraceEnabled()) { log.trace("Found matching name:"); log.trace(methods[i]); } Class[] methodsParams = methods[i].getParameterTypes(); int methodParamSize = methodsParams.length; if (methodParamSize == paramSize) { boolean match = true; for (int n = 0; n < methodParamSize; n++) { if (log.isTraceEnabled()) { log.trace("Param=" + parameterTypes[n].getName()); log.trace("Method=" + methodsParams[n].getName()); } if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) { if (log.isTraceEnabled()) log.trace(methodsParams[n] + " is not assignable from " + parameterTypes[n]); match = false; break; } } if (match) { Method method = getAccessibleMethod(methods[i]); if (method != null) { if (log.isTraceEnabled()) log.trace(method + " accessible version of " + methods[i]); try { method.setAccessible(true); } catch (SecurityException se) { if (!loggedAccessibleWarning) { log.warn("Cannot use JVM pre-1.4 access bug workaround die to restrictive security manager."); loggedAccessibleWarning = true; } log.debug("Cannot setAccessible on method. Therefore cannot use jvm access bug workaround.", se); } return method; } log.trace("Couldn't find accessible method."); } } } } log.trace("No match found."); return null; } } protected static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) { if (parameterType.isAssignableFrom(parameterization)) return true; if (parameterType.isPrimitive()) { 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; } }