rdonkin 2002/12/10 11:06:49 Modified: lang/src/java/org/apache/commons/lang/reflect MethodUtils.java lang/src/test/org/apache/commons/lang/reflect MethodUtilsTestCase.java Log: Consolidated methods. Revision Changes Path 1.9 +111 -152 jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java Index: MethodUtils.java =================================================================== RCS file: /home/cvs/jakarta-commons/lang/src/java/org/apache/commons/lang/reflect/MethodUtils.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- MethodUtils.java 21 Nov 2002 19:38:51 -0000 1.8 +++ MethodUtils.java 10 Dec 2002 19:06:49 -0000 1.9 @@ -66,6 +66,7 @@ import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; + /** * <code>MethodUtils</code> contains utility methods for working for * methods by reflection. @@ -126,6 +127,21 @@ * @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. + * + * @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[] paramTypes) { return getMethod(cls, methodName, paramTypes, false); } @@ -173,7 +189,24 @@ } 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 @@ 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 @@ args = ArrayUtils.EMPTY_OBJECT_ARRAY; } - Method method = getMatchingAccessibleMethod( + Method method = getMethod( object.getClass(), methodName, parameterTypes); @@ -330,62 +407,11 @@ } } - /** - * <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) { - - try { - return getAccessibleMethod - (clazz.getMethod(methodName, parameterTypes)); - } catch (NoSuchMethodException e) { - return (null); - } - } + // -------------------------------------------------------- Private Methods - /** - * <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 @@ // 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 @@ 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 @@ */ 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 @@ } // 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 @@ 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) { 1.4 +6 -6 jakarta-commons/lang/src/test/org/apache/commons/lang/reflect/MethodUtilsTestCase.java Index: MethodUtilsTestCase.java =================================================================== RCS file: /home/cvs/jakarta-commons/lang/src/test/org/apache/commons/lang/reflect/MethodUtilsTestCase.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- MethodUtilsTestCase.java 21 Nov 2002 18:53:32 -0000 1.3 +++ MethodUtilsTestCase.java 10 Dec 2002 19:06:49 -0000 1.4 @@ -129,7 +129,7 @@ // 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 @@ // 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 @@ // 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 @@ 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 @@ 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 @@ 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",
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>