package org.dcache.util;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* This class contains useful static methods for working with Java
* reflection.
*/
public class ReflectionUtils
{
private static final Map<String,Method> methodCache =
new HashMap<>();
/**
* Finds a maximally specific public method called <i>name</i> in
* <i>c</i> accepting parameters of type <i>parameters</i>.
*
* In contrast to <code>Class.getMethod</code>,
* <code>resolve</code> performs type widening on the parameters,
* in effect emulating the steps performed at compile time for
* finding the a method.
*
* Notice that we return the first method found in a depth-first
* left-to-right search. This is different from what Java does at
* compile time. We do not support auto-boxing or methods with a
* variable number of arguments. Lack of auto-boxing means the
* methods with parameters of primitive types are never returned.
*
* To improve performance, a cache of resolved methods is
* maintained.
*
* @returns a matching method or null if no method is found
*/
public static Method resolve(Class<?> c, String name, Class<?> ... parameters)
{
try {
Object[] signature = { c, name, parameters };
String key = Arrays.deepToString(signature);
/* Cache lookup.
*/
Method m = methodCache.get(key);
if (m != null) {
return m;
}
/* Lookup in class c.
*/
m = c.getMethod(name, parameters);
methodCache.put(key, m);
return m;
} catch (NoSuchMethodException e) {
/* Perform type widening on parameters to find a matching
* method.
*/
for (int i = 0; i < parameters.length; i++) {
Class<?> s = parameters[i].getSuperclass();
if (s != null) {
Class<?> old = parameters[i];
parameters[i] = s;
Method m = resolve(c, name, parameters);
if (m != null) {
return m;
}
parameters[i] = old;
}
}
/* We cannot find a matching method, give up.
*/
return null;
}
}
public static boolean hasDeclaredException(Method method, Exception exception)
{
for (Class<?> clazz: method.getExceptionTypes()) {
if (clazz.isAssignableFrom(exception.getClass())) {
return true;
}
}
return false;
}
/**
* Like Class#getMethod, but also returns non-public methods. Differs from
* Class#getDeclaredMethod by also searching super classes.
*/
public static Method getAnyMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException
{
try {
// Because execute is protected, we cannot use getMethod.
return clazz.getDeclaredMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null) {
return getAnyMethod(superclass, name, parameterTypes);
}
throw e;
}
}
}