Added new method to allow work-arounds for Java bug 4071957; as specified in issue 34351

git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/lang/trunk@395153 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Henri Yandell 2006-04-19 06:30:14 +00:00
parent 2330a3343c
commit fc9b4cc1d8
2 changed files with 85 additions and 0 deletions

View File

@ -15,6 +15,9 @@
*/
package org.apache.commons.lang;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@ -589,6 +592,53 @@ public static Class getClass(String className, boolean initialize) throws ClassN
return getClass(loader, className, initialize );
}
/**
* <p>Returns the desired Method much like <code>Class.getMethod</code>, however
* it ensures that the returned Method is from a public class or interface and not
* from an anonymous inner class. This means that the Method is invokable and
* doesn't fall foul of Java bug
* <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).
*
* <code><pre>Set set = Collections.unmodifiableSet(...);
* Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]);
* Object result = method.invoke(set, new Object[]);</pre></code>
* </p>
*/
public static Method getPublicMethod(Class cls, String methodName, Class parameterTypes[])
throws SecurityException, NoSuchMethodException
{
Method declaredMethod = cls.getMethod(methodName, parameterTypes);
if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) {
return declaredMethod;
}
List candidateClasses = new ArrayList();
candidateClasses.addAll(getAllInterfaces(cls));
candidateClasses.addAll(getAllSuperclasses(cls));
for (Iterator iter=candidateClasses.iterator(); iter.hasNext(); ) {
Class candidateClass = (Class) iter.next();
if (!Modifier.isPublic(candidateClass.getModifiers())) {
continue;
}
Method candidateMethod;
try {
candidateMethod = candidateClass.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
continue;
}
if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) {
return candidateMethod;
}
}
String message = "Can't find an public method for " + methodName + " " + ArrayUtils.toString(parameterTypes);
throw new NoSuchMethodException(message);
}
/**
* Converts a class name to a JLS stle class name.
*

View File

@ -16,12 +16,16 @@
package org.apache.commons.lang;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import junit.framework.Test;
import junit.framework.TestCase;
@ -520,4 +524,35 @@ public static ClassLoader newSystemClassLoader() throws SecurityException, Illeg
return URLClassLoader.newInstance(urlScl.getURLs(), null);
}
// Show the Java bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957
// We may have to delete this if a JDK fixes the bug.
public void testShowJavaBug() throws Exception {
// Tests with Collections$UnmodifiableSet
Set set = Collections.unmodifiableSet(new HashSet());
Method isEmptyMethod = set.getClass().getMethod("isEmpty", new Class[0]);
try {
isEmptyMethod.invoke(set, new Object[0]);
fail("Failed to throw IllegalAccessException as expected");
} catch(IllegalAccessException iae) {
// expected
}
}
public void testGetPublicMethod() throws Exception {
// Tests with Collections$UnmodifiableSet
Set set = Collections.unmodifiableSet(new HashSet());
Method isEmptyMethod = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]);
assertTrue(Modifier.isPublic(isEmptyMethod.getDeclaringClass().getModifiers()));
try {
isEmptyMethod.invoke(set, new Object[0]);
} catch(java.lang.IllegalAccessException iae) {
fail("Should not have thrown IllegalAccessException");
}
// Tests with a public Class
Method toStringMethod = ClassUtils.getPublicMethod(Object.class, "toString", new Class[0]);
assertEquals(Object.class.getMethod("toString", new Class[0]), toStringMethod);
}
}