here's the new MethodUtils class. also included is a test case for this new class and associated testing patches.
two methods were moved into MethodUtils from PropertyUtils and one made public (the other one could be public but i don't really think anyone will ever want to call it directly) together with a number of convenience wrapper methods. you'll probably wonder why the method is called invokeExactMethod (rather than invokeMethod, say). that's because i plan to add another method that searches for a correctly named method with compatible parameters (rather than exact matching) but i though that it'd be better to submit the patches required to fix Donnie's problem (and then add more functionality later). i've included the digester patch in a separate email. - robert
/* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999-2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.beanutils; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.InvocationTargetException; /** * <p> Utility reflection methods focussed on methods in general rather than properties in particular. </p> * * @author Craig R. McClanahan * @author Ralph Schaer * @author Chris Audley * @author Rey François * @author Gregor Raıman * @author Jan Sorensen */ public class MethodUtils { // --------------------------------------------------------- Public Methods /** * <p> Invoke a method whose parameter type matches exactly the object type. </p> * * <p> This is a convenient wrapper for * {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. * * @param object invoke method on this object * @param methodName get method with this name * @param arg use this argument * * @throw NoSuchMethodException if there is no such accessible method * @throw InvocationTargetException wraps an exception thrown by the method invoked * @throw IllegalAccessException can think of any reason why you should get this one */ public static Object invokeExactMethod( Object object, String methodName, Object arg) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Object [] args = {arg}; return invokeExactMethod(object, methodName, args); } /** * <p> Invoke a method whose parameter types match exactly the object types. </p> * * <p> This uses reflection to invoke the method obtained from a call to * {@link #getAccessibleMethod} * * @param object invoke method on this object * @param methodName get method with this name * @param args use these arguments * * @throw NoSuchMethodException if there is no such accessible method * @throw InvocationTargetException wraps an exception thrown by the method invoked * @throw IllegalAccessException can think of any reason why you should get this one */ public static Object invokeExactMethod( Object object, String methodName, Object [] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 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); } /** * <p> Invoke a method whose parameter types match exactly the parameter types givem. </p> * * <p> This uses reflection to invoke the method obtained from a call to * {@link #getAccessibleMethod} * * @param object invoke method on this object * @param methodName get method with this name * @param args use these arguments * @param parameterTypes match these parameters * * @throw NoSuchMethodException if there is no such accessible method * @throw InvocationTargetException wraps an exception thrown by the method invoked * @throw IllegalAccessException can think of any reason why you should get this one */ public static Object invokeExactMethod( Object object, String methodName, Object [] args, Class [] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = getAccessibleMethod( object.getClass(), methodName, parameterTypes); if (method == null) throw new NoSuchMethodException("No such accessible method."); return method.invoke(object, args); } /** * <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. * * @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) throws NoSuchMethodException { 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)}. * * @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) throws NoSuchMethodException { return getAccessibleMethod( clazz.getMethod(methodName, parameterTypes)); } /** * 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>. * * @param method The method that we wish to call */ public static Method getAccessibleMethod(Method method) { // Make sure we have a method to check if (method == null) { return (null); } // If the requested method is not public we cannot call it if (!Modifier.isPublic(method.getModifiers())) { return (null); } // If the declaring class is public, we are done Class clazz = method.getDeclaringClass(); if (Modifier.isPublic(clazz.getModifiers())) { return (method); } // Check the implemented interfaces and subinterfaces String methodName = method.getName(); Class[] parameterTypes = method.getParameterTypes(); method = getAccessibleMethodFromInterfaceNest(clazz, method.getName(), method.getParameterTypes()); return (method); /* Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { // Is this interface public? if (!Modifier.isPublic(interfaces[i].getModifiers())) { continue; } // Does the method exist on this interface? try { method = interfaces[i].getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { continue; } // We have found what we are looking for return (method); } // We are out of luck return (null); */ } // --------------------------------------------------------- Private Methods /** * <p> Return an accessible method (that is, one that can be invoked via * reflection) that implements the specified method, by scanning through * all implemented interfaces and subinterfaces. If no such method * can be found, return <code>null</code>.</p> * * <p> There isn't any good reason why this method must be private. * It is because there doesn't seem any reason why other classes should * call this rather than the higher level methods.</p> * * @param clazz Parent class for the interfaces to be checked * @param methodName Method name of the method we wish to call * @param parameterTypes The parameter type signatures */ private static Method getAccessibleMethodFromInterfaceNest (Class clazz, String methodName, Class parameterTypes[]) { Method method = null; // Search up the superclass chain for ( ; clazz != null; clazz = clazz.getSuperclass()) { // Check the implemented interfaces of the parent class Class interfaces[] = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { // Is this interface public? if (!Modifier.isPublic(interfaces[i].getModifiers())) continue; // Does the method exist on this interface? try { method = interfaces[i].getDeclaredMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { ; } if (method != null) break; // Recursively check our parent interfaces method = getAccessibleMethodFromInterfaceNest(interfaces[i], methodName, parameterTypes); if (method != null) break; } } // If we found a method return it if (method != null) return (method); // We did not find anything return (null); } }
/* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999-2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Commons", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */ package org.apache.commons.beanutils; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import org.apache.commons.beanutils.priv.PrivateBeanFactory; /** * <p> Test case for <code>MethodUtils</code> </p> * */ public class MethodUtilsTestCase extends TestCase { // ---------------------------------------------------- Instance Variables protected PrivateBeanFactory privateBeanFactory; // ---------------------------------------------------------- Constructors /** * Construct a new instance of this test case. * * @param name Name of the test case */ public MethodUtilsTestCase(String name) { super(name); } // -------------------------------------------------- Overall Test Methods /** * Set up instance variables required by this test case. */ public void setUp() { privateBeanFactory = new PrivateBeanFactory(); } /** * Return the tests included in this test suite. */ public static Test suite() { return (new TestSuite(MethodUtilsTestCase.class)); } /** * Tear down instance variables required by this test case. */ public void tearDown() { privateBeanFactory = null; } // ------------------------------------------------ Individual Test Methods /** * <p> Test <code>getAccessibleMethod</code>. */ public void testGetAccessibleMethod() { // test MethodUtils.getAccessibleMethod // we'll make things easier by using the convenience methods // easy bit first - find a public method // METHOD ONE try { Method method = MethodUtils.getAccessibleMethod(TestBean.class, "setStringProperty",String.class); // check that we've found one that matches assertNotNull(method); assertEquals("method ONE is named correctly","setStringProperty",method.getName()); assertTrue("Method ONE is public", Modifier.isPublic(method.getModifiers())); } catch (NoSuchMethodException e) { // ONE fail("Cannot find method ONE"); } // trickier this one - find a method in a direct interface // METHOD TWO try { Method method = MethodUtils.getAccessibleMethod( privateBeanFactory.create().getClass(), "methodBar", String.class); // check that we've found one that matches assertNotNull(method); assertEquals("Method TWO is named correctly","methodBar",method.getName()); assertTrue("Method TWO is public", Modifier.isPublic(method.getModifiers())); } catch (NoSuchMethodException e) { // ONE fail("Cannot find method TWO"); } // trickier this one - find a method in a indirect interface // METHOD THREE try { Method method = MethodUtils.getAccessibleMethod( privateBeanFactory.createSubclass().getClass(), "methodBaz", String.class); // check that we've found one that matches assertNotNull(method); assertEquals("Method THREE is named correctly","methodBaz",method.getName()); assertTrue("Method THREE is public", Modifier.isPublic(method.getModifiers())); } catch (NoSuchMethodException e) { // ONE fail("Cannot find method TWO"); } } /** * <p> Test <code>invokeExactMethod</code>. */ public void testInvokeExactMethod() { // test MethodUtils.invokeExactMethod // easy bit first - invoke a public method // METHOD ONE try { TestBean bean = new TestBean(); Object ret = MethodUtils.invokeExactMethod(bean, "setStringProperty","TEST"); // check that the return's right and that the properties been set assertNull(ret); assertEquals("Method ONE was invoked","TEST",bean.getStringProperty()); } catch (Throwable t) { // ONE fail("Exception in method ONE prevented invokation: " + t.toString()); } // trickier this one - find a method in a direct interface // METHOD TWO FAILURE try { Object ret = MethodUtils.invokeExactMethod( privateBeanFactory.create(), "methodBar", "ANOTHER TEST"); // check that we've found one that matches assertEquals("Method TWO was invoked correctly","ANOTHER TEST",ret); } catch (Throwable t) { // METHOD TWO FAILURE fail("Exception in method TWO prevented invokation: " + t.toString()); } // trickier this one - find a method in a indirect interface // METHOD THREE try { Object ret = MethodUtils.invokeExactMethod( privateBeanFactory.createSubclass(), "methodBaz", "YET ANOTHER TEST"); // check that we've found one that matches assertEquals("Method TWO was invoked correctly","YET ANOTHER TEST",ret); } catch (Throwable t) { // METHOD THREE FAILURE fail("Exception in method THREE prevented invokation: " + t.toString()); } } }
build.xml
Description: Binary data
Index: beanutils/src/test/org/apache/commons/beanutils/priv/PrivateBean.java =================================================================== RCS file: /home/cvs/jakarta-commons/beanutils/src/test/org/apache/commons/beanutils/priv/PrivateBean.java,v retrieving revision 1.1 diff -u -r1.1 PrivateBean.java --- beanutils/src/test/org/apache/commons/beanutils/priv/PrivateBean.java 2001/05/07 02:09:02 1.1 +++ beanutils/src/test/org/apache/commons/beanutils/priv/PrivateBean.java +2001/12/11 22:18:08 @@ -119,6 +119,13 @@ } + /** + * A method accessible via an directly implemented interface. + */ + public String methodBar(String in) + { + return in; + } /** * A property accessible via an indirectly implemented interface. @@ -130,7 +137,13 @@ return (this.baz); } - + /** + * A method accessible via an indirectly implemented interface. + */ + public String methodBaz(String in) + { + return in; + } }
Index: beanutils/src/test/org/apache/commons/beanutils/priv/PrivateDirect.java =================================================================== RCS file: /home/cvs/jakarta-commons/beanutils/src/test/org/apache/commons/beanutils/priv/PrivateDirect.java,v retrieving revision 1.1 diff -u -r1.1 PrivateDirect.java --- beanutils/src/test/org/apache/commons/beanutils/priv/PrivateDirect.java 2001/05/07 02:09:02 1.1 +++ beanutils/src/test/org/apache/commons/beanutils/priv/PrivateDirect.java +2001/12/11 22:18:39 @@ -81,5 +81,8 @@ */ String getBar(); - + /** + * A method accessible via a directly implemented interface. + */ + String methodBar(String in); }
Index: beanutils/src/test/org/apache/commons/beanutils/priv/PrivateIndirect.java =================================================================== RCS file: /home/cvs/jakarta-commons/beanutils/src/test/org/apache/commons/beanutils/priv/PrivateIndirect.java,v retrieving revision 1.1 diff -u -r1.1 PrivateIndirect.java --- beanutils/src/test/org/apache/commons/beanutils/priv/PrivateIndirect.java 2001/05/07 02:09:02 1.1 +++ beanutils/src/test/org/apache/commons/beanutils/priv/PrivateIndirect.java +2001/12/11 22:19:01 @@ -81,5 +81,8 @@ */ public String getBaz(); - + /** + * A method accessible via an indirectly implemented interface. + */ + public String methodBaz(String in); }
Index: beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java =================================================================== RCS file: /home/cvs/jakarta-commons/beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java,v retrieving revision 1.14 diff -u -r1.14 PropertyUtils.java --- beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java 2001/10/14 01:15:07 1.14 +++ beanutils/src/java/org/apache/commons/beanutils/PropertyUtils.java 2001/12/11 +22:17:27 @@ -908,7 +908,7 @@ */ public static Method getReadMethod(PropertyDescriptor descriptor) { - return (getAccessibleMethod(descriptor.getReadMethod())); + return (MethodUtils.getAccessibleMethod(descriptor.getReadMethod())); } @@ -977,7 +977,7 @@ */ public static Method getWriteMethod(PropertyDescriptor descriptor) { - return (getAccessibleMethod(descriptor.getWriteMethod())); + return (MethodUtils.getAccessibleMethod(descriptor.getWriteMethod())); } @@ -1387,126 +1387,4 @@ writeMethod.invoke(bean, values); } - - - // -------------------------------------------------------- Private Methods - - - /** - * 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>. - * - * @param method The method that we wish to call - */ - private static Method getAccessibleMethod(Method method) { - - // Make sure we have a method to check - if (method == null) { - return (null); - } - - // If the requested method is not public we cannot call it - if (!Modifier.isPublic(method.getModifiers())) { - return (null); - } - - // If the declaring class is public, we are done - Class clazz = method.getDeclaringClass(); - if (Modifier.isPublic(clazz.getModifiers())) { - return (method); - } - - // Check the implemented interfaces and subinterfaces - String methodName = method.getName(); - Class[] parameterTypes = method.getParameterTypes(); - method = - getAccessibleMethodFromInterfaceNest(clazz, - method.getName(), - method.getParameterTypes()); - return (method); - - /* - Class[] interfaces = clazz.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - // Is this interface public? - if (!Modifier.isPublic(interfaces[i].getModifiers())) { - continue; - } - // Does the method exist on this interface? - try { - method = interfaces[i].getDeclaredMethod(methodName, - parameterTypes); - } catch (NoSuchMethodException e) { - continue; - } - // We have found what we are looking for - return (method); - } - - // We are out of luck - return (null); - */ - - } - - - /** - * Return an accessible method (that is, one that can be invoked via - * reflection) that implements the specified method, by scanning through - * all implemented interfaces and subinterfaces. If no such Method - * can be found, return <code>null</code>. - * - * @param clazz Parent class for the interfaces to be checked - * @param methodName Method name of the method we wish to call - * @param parameterTypes The parameter type signatures - */ - private static Method getAccessibleMethodFromInterfaceNest - (Class clazz, String methodName, Class parameterTypes[]) { - - Method method = null; - - // Search up the superclass chain - for ( ; clazz != null; clazz = clazz.getSuperclass()) { - - // Check the implemented interfaces of the parent class - Class interfaces[] = clazz.getInterfaces(); - for (int i = 0; i < interfaces.length; i++) { - - // Is this interface public? - if (!Modifier.isPublic(interfaces[i].getModifiers())) - continue; - - // Does the method exist on this interface? - try { - method = interfaces[i].getDeclaredMethod(methodName, - parameterTypes); - } catch (NoSuchMethodException e) { - ; - } - if (method != null) - break; - - // Recursively check our parent interfaces - method = - getAccessibleMethodFromInterfaceNest(interfaces[i], - methodName, - parameterTypes); - if (method != null) - break; - - } - - } - - // If we found a method return it - if (method != null) - return (method); - - // We did not find anything - return (null); - - } - - }
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>