package org.emdev.ui.actions; import android.app.Activity; 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.common.log.LogContext; import org.emdev.common.log.LogManager; import org.emdev.utils.LengthUtils; public class EventDispatcher { private static final LogContext LCTX = LogManager.root().lctx("Events"); private final Activity m_base; private final InvokationType m_type; /** * Supported interfaces. */ private final Class<?>[] m_interfaces; /** * Real listeners. */ private final Map<Class<?>, List<Object>> m_listeners = new HashMap<Class<?>, List<Object>>(); private final Object m_proxy; private final InvocationHandler m_handler; /** * Constructor * * @param type * invocation type * @param target * target object * @param listeners * a list of listener interfaces */ public EventDispatcher(final Activity base, final InvokationType type, final Class<?>... listeners) { if (LengthUtils.isEmpty(listeners)) { throw new IllegalArgumentException("Listeners list cannot be empty"); } for (final Class<?> listener : listeners) { if (listener == null) { throw new IllegalArgumentException("Listener class cannot be null"); } if (!listener.isInterface()) { throw new IllegalArgumentException("Listener class should be an interface"); } } m_base = base; m_type = type; m_handler = new Handler(); m_interfaces = listeners; m_proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), listeners, m_handler); } /** * Adds the target listener. * * @param listener * the listener to add */ public void addListener(final Object listener) { if (listener != null) { for (final Class<?> listenerClass : m_interfaces) { if (listenerClass.isInstance(listener)) { List<Object> list = m_listeners.get(listenerClass); if (list == null) { list = new LinkedList<Object>(); m_listeners.put(listenerClass, list); } if (!list.contains(listener)) { list.add(listener); } } } } } /** * Removes the target listener. * * @param listener * the listener to remove */ public void removeListener(final Object listener) { if (listener != null) { for (final Class<?> listenerClass : m_interfaces) { if (listenerClass.isInstance(listener)) { final List<Object> list = m_listeners.get(listenerClass); if (list != null) { list.remove(listener); } } } } } /** * Gets a listener of the given type. * * @param <Listener> * listener type * @return listener proxy object casted to the given type */ @SuppressWarnings("unchecked") public <Listener> Listener getListener() { return (Listener) m_proxy; } /** * This class implements invocation handler for event listeners. */ 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 InvocationHandler#invoke(Object, Method, Object[]) */ @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { final Class<?> listenerClass = method.getDeclaringClass(); final List<Object> targets = m_listeners.get(listenerClass); if (LengthUtils.isNotEmpty(targets)) { final Task task = new Task(targets, method, args); switch (m_type) { case AsyncUI: m_base.runOnUiThread(task); break; case SeparatedThread: new Thread(task).start(); break; case Direct: default: task.run(); break; } } return null; } } /** * This class implements thread task for listener invocation. */ private class Task implements Runnable { private final List<Object> m_targets; private final Method m_method; private final Object[] m_args; /** * Constructor * * @param method * called method * @param args * method parameters */ public Task(final List<Object> targets, final Method method, final Object[] args) { m_targets = targets; m_method = method; m_args = args; } /** * * @see java.lang.Runnable#run() */ @Override public synchronized void run() { directInvoke(); } /** * Direct invoke of the action. * * @param method * called method * @param args * method parameters */ protected void directInvoke() { for (final Object target : m_targets) { try { m_method.invoke(target, m_args); } catch (final Throwable ex) { LCTX.e("Invokation error: " + m_method.getName(), ex); } } } } }