/*
* This file is part of Caliph & Emir.
*
* Caliph & Emir is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Caliph & Emir is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Caliph & Emir; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright statement:
* --------------------
* (c) 2005 by Werner Klieber (werner@klieber.info)
* http://caliph-emir.sourceforge.net
*/
package at.wklieber.gui;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
public abstract class GenericListener {
/**
* A convenient version of <CODE>create(listenerMethod, targetObject,
* targetMethod)</CODE>. * 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 <CODE>listenerMethod</CODE>. In
* this new class, * <CODE>listenerMethod</CODE> will apply
* <CODE>target.targetMethod</CODE> * 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();
//TODO:if ( Modifier.isPublic(m.getModifiers()) & amp;&
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() {
}
}