package org.emdev.utils.listeners; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.emdev.utils.LengthUtils; public class ListenerProxy { /** * All objects */ private final List<WeakReference<Object>> references = new LinkedList<WeakReference<Object>>(); /** * Real listeners. */ private final Map<Class<?>, List<WeakReference<Object>>> realListeners = new HashMap<Class<?>, List<WeakReference<Object>>>(); /** * Supported interfaces. */ private final Class<?>[] interfaces; /** * Proxy object. */ private final Object proxy; /** * Constructor. * * @param listenerInterfaces * a list of listener interfaces to implement */ public ListenerProxy(final Class<?>... listenerInterfaces) { if (LengthUtils.isEmpty(listenerInterfaces)) { throw new IllegalArgumentException("Listeners list cannot be empty"); } for (final Class<?> listener : listenerInterfaces) { if (listener == null) { throw new IllegalArgumentException("Listener class cannot be null"); } if (!listener.isInterface()) { throw new IllegalArgumentException("Listener class should be an interface"); } } interfaces = listenerInterfaces; proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), interfaces, new Handler()); } /** * Adds the target listener. * * @param listener * the listener to add */ public void addListener(final Object listener) { if (listener != null) { WeakReference<Object> ref = new WeakReference<Object>(listener); for (WeakReference<Object> r : references) { if (r.get() == listener) { return; } } references.add(ref); for (final Class<?> listenerClass : interfaces) { if (listenerClass.isInstance(listener)) { List<WeakReference<Object>> list = realListeners.get(listenerClass); if (list == null) { list = new LinkedList<WeakReference<Object>>(); realListeners.put(listenerClass, list); } list.add(ref); } } } } /** * Removes the target listener. * * @param listener * the listener to remove */ public void removeListener(final Object listener) { if (listener != null) { WeakReference<Object> ref = null; for (WeakReference<Object> r : references) { if (r.get() == listener) { ref = r; break; } } if (ref != null) { references.remove(ref); for (final Class<?> listenerClass : interfaces) { if (listenerClass.isInstance(listener)) { final List<WeakReference<Object>> list = realListeners.get(listenerClass); if (list != null) { list.remove(ref); } } } } } } /** * Removes the all target listeners. */ public void removeAllListeners() { references.clear(); for (final List<WeakReference<Object>> list : realListeners.values()) { list.clear(); } realListeners.clear(); } /** * Gets the proxy listener casted to the given listener type. * * @param <Listener> * listener type * @return an instance of the <code>Listener</code> type */ @SuppressWarnings("unchecked") public <Listener> Listener getListener() { return (Listener) proxy; } /** * This class implements invocation handler. */ private class Handler implements InvocationHandler { /** * Processes a method invocation on a proxy instance and returns the result. * * @param proxy * the proxy instance that the method was invoked on * @param method * the <code>Method</code> instance corresponding to the interface method invoked on the proxy instance. * @param args * an array of objects containing the values of the arguments passed in the method invocation on the proxy * instance. * @return the value to return from the method invocation on the proxy instance. * @throws Throwable * the exception to throw from the method invocation on the proxy instance. * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final Class<?> listenerClass = method.getDeclaringClass(); final List<WeakReference<Object>> list = realListeners.get(listenerClass); if (LengthUtils.isNotEmpty(list)) { for (final WeakReference<Object> ref : list) { Object listener = ref.get(); if (listener != null) { method.invoke(listener, args); } } } return null; } } }