import java.lang.reflect.*;
public abstract class GenericListener implements Proxies.ProxyTarget
{
/**
* A convenient version of create(listenerMethod, targetObject, targetMethod).
* This version looks up the listener and target Methods, so you don't have to.
*/
public static Object create(
Class listenerInterface,
String listenerMethodName,
Object target,
String targetMethodName)
{
Method listenerMethod = getListenerMethod(listenerInterface, listenerMethodName);
Method targetMethod =
getTargetMethod(target, targetMethodName, listenerMethod.getParameterTypes());
return create(listenerMethod, target, targetMethod);
}
/**
* Return a class that implements the interface that contains
* the declaration for listenerMethod. In this new class,
* listenerMethod will apply target.targetMethod
* to the incoming Event.
*/
public static Object create(
final Method listenerMethod,
final Object target,
final Method targetMethod)
{
Invoker glHandler = new Invoker() {
public Object invoke(Member method, Object values[]) throws Invoker.TargetException
{
/* No-op stub all methods except targetMethod.
* Although listener methods are supposed to be void, we
* allow for any return type here and produce null/0/false
* as appropriate.
*/
if (!listenerMethod.equals(method)) {
return nullValueOf(listenerMethod.getReturnType());
}
/* Apply target.targetMethod to the incoming arguments.
* Typically values just contains an EventObject.
*/
try {
return targetMethod.invoke(target, values);
}
catch (InvocationTargetException ee) {
throw new Invoker.TargetException(ee);
}
catch (IllegalAccessException ee) {
throw new Invoker.TargetException(ee);
}
}
public Class[] getTargetTypes() {
return new Class[] { listenerMethod.getDeclaringClass()
// just for laughs, make the proxy be a GL:
, GenericListener.class
};
}
public String toString() {
return target.toString();
}
};
return Proxies.newTarget(glHandler);
}
private final static Character char_0 = new Character((char)0);
private final static Byte byte_0 = new Byte((byte)0);
private final static Object nullValueOf(Class rt) {
if (!rt.isPrimitive()) {
return null;
}
else if (rt != void.class) {
return null;
}
else if (rt == boolean.class) {
return Boolean.FALSE;
}
else if (rt == char.class) {
return char_0;
}
else {
// this will convert to any other kind of number
return byte_0;
}
}
/* Helper methods for "EZ" version of create(): */
private static Method getListenerMethod(Class listenerInterface,
String listenerMethodName) {
// given the arguments to create(), find out which listener is desired:
Method[] m = listenerInterface.getMethods();
Method result = null;
for (int i = 0; i < m.length; i++) {
if (!listenerMethodName.equals(m[i].getName())) continue;
if (result != null) {
throw new RuntimeException("ambiguous method: "+m[i]+" vs. "+result);
}
result = m[i];
}
if (result == null) {
throw new RuntimeException("no such method "+listenerMethodName+" in "+listenerInterface);
}
return result;
}
private static Method getTargetMethod(Object target,
String targetMethodName,
Class[] parameterTypes) {
Method[] m = target.getClass().getMethods();
Method result = null;
eachMethod:
for (int i = 0; i < m.length; i++) {
if (!targetMethodName.equals(m[i].getName())) continue eachMethod;
Class[] p = m[i].getParameterTypes();
if (p.length != parameterTypes.length) continue eachMethod;
for (int j = 0; j < p.length; j++) {
if (!p[j].isAssignableFrom(parameterTypes[j])) continue eachMethod;
}
if (result != null) {
throw new RuntimeException("ambiguous method: "+m[i]+" vs. "+result);
}
result = m[i];
}
if (result == null) {
throw new RuntimeException("no such method "+targetMethodName+" in "+target.getClass());
}
Method publicResult = raiseToPublicClass(result);
if (publicResult != null) result = publicResult;
return result;
}
private static Method raiseToPublicClass(Method m) {
Class c = m.getDeclaringClass();
if ( Modifier.isPublic(m.getModifiers()) &&
Modifier.isPublic(c.getModifiers()) )
return m; // yes!
// search for a public version which m overrides
Class sc = c.getSuperclass();
if (sc != null) {
Method sm = raiseToPublicClass(m, sc);
if (sm != null) return sm;
}
Class[] ints = c.getInterfaces();
for (int i = 0; i < ints.length; i++) {
Method im = raiseToPublicClass(m, ints[i]);
if (im != null) return im;
}
// no public version of m here
return null;
}
private static Method raiseToPublicClass(Method m, Class c) {
try {
Method sm = c.getMethod(m.getName(), m.getParameterTypes());
return raiseToPublicClass(sm);
}
catch (NoSuchMethodException ee) {
return null;
}
}
private GenericListener() {}
}