import java.lang.reflect.*; public abstract class GenericListener { /** * 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 an instance of 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) { /** * The implementation of the create method uses the Dynamic Proxy API * introduced in JDK 1.3. * * Create an instance of the DefaultInvoker and override the invoke * method to handle the invoking the targetMethod on the target. */ InvocationHandler handler = new DefaultInvoker() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // Send all methods execept for the targetMethod to // the superclass for handling. if (listenerMethod.equals(method)) { return targetMethod.invoke(target, args); } else { return super.invoke(proxy, method, args); } } }; Class cls = listenerMethod.getDeclaringClass(); ClassLoader cl = cls.getClassLoader(); return Proxy.newProxyInstance(cl, new Class[]{cls}, handler); } /** * Implementation of the InvocationHandler which handles the basic * object methods. */ private static class DefaultInvoker implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { String methodName = method.getName(); if (methodName.equals("hashCode")) { return proxyHashCode(proxy); } else if (methodName.equals("equals")) { return proxyEquals(proxy, args[0]); } else if (methodName.equals("toString")) { return proxyToString(proxy); } } // Although listener methods are supposed to be void, we // allow for any return type here and produce null/0/false // as appropriate. return nullValueOf(method.getReturnType()); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); } 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() {} }