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() {} }