cc.creativecomputing.events.CCListenerManager.java Source code

Java tutorial

Introduction

Here is the source code for cc.creativecomputing.events.CCListenerManager.java

Source

/*
 * Copyright (c) 2013 christianr.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v3
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-3.0.html
 * 
 * Contributors:
 *     christianr - initial API and implementation
 */
package cc.creativecomputing.events;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.Validate;

/**
 * An event manager can be used to manage a list of event listeners of a particular type. The class
 * provides {@link #add(Object)} and {@link #remove(Object)} methods for registering listeners, as well
 * as a {@link #proxy()} method for firing events to the listeners.
 * 
 * <p/>
 * To use this class, suppose you want to support ActionEvents. You would do: <code><pre>
 * public class MyActionEventSource
 * {
 *   private CCEventManager<ActionListener> actionListeners = 
 *       CCEventManager.create(ActionListener.class);
 * 
 *   public void someMethodThatFiresAction()
 *   {
 *     ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "somethingCool");
 *     actionListeners.proxy().actionPerformed(e);
 *   }
 * }
 * </pre></code>
 * 
 * @param <ListenerType> the type of event listener that is supported by this proxy.
 * 
 * @since 3.0
 * @version $Id: EventListenerSupport.java 978864 2010-07-24 12:49:38Z jcarman $
 */
public class CCListenerManager<ListenerType> implements Iterable<ListenerType> {

    /**
     * The list used to hold the registered listeners. This list is intentionally a thread-safe copy-on-write-array so
     * that traversals over the list of listeners will be atomic.
     */
    private final List<ListenerType> _myListeners = new ArrayList<ListenerType>();

    /**
     * The proxy representing the collection of listeners. Calls to this proxy object will sent to all registered
     * listeners.
     */
    private final ListenerType _myProxy;

    /**
     * Creates an EventListenerSupport object which supports the specified listener type.
     * 
     * @param listenerInterface the type of listener interface that will receive events posted using this class.
     * 
     * @return an EventListenerSupport object which supports the specified listener type.
     * 
     * @throws NullPointerException if <code>listenerInterface</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>listenerInterface</code> is not an interface.
     */
    public static <T> CCListenerManager<T> create(Class<T> listenerInterface) {
        return new CCListenerManager<T>(listenerInterface);
    }

    /**
     * Creates an EventListenerSupport object which supports the provided listener interface.
     * 
     * @param listenerInterface the type of listener interface that will receive events posted using this class.
     * 
     * @throws NullPointerException if <code>listenerInterface</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>listenerInterface</code> is not an interface.
     */
    public CCListenerManager(Class<ListenerType> listenerInterface) {
        this(listenerInterface, Thread.currentThread().getContextClassLoader());
    }

    /**
     * Creates an EventListenerSupport object which supports the provided listener interface using the specified class
     * loader to create the JDK dynamic proxy.
     * 
     * @param listenerInterface the listener interface.
     * @param classLoader the class loader.
     * 
     * @throws NullPointerException if <code>listenerInterface</code> or <code>classLoader</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>listenerInterface</code> is not an interface.
     */
    public CCListenerManager(Class<ListenerType> listenerInterface, ClassLoader classLoader) {
        Validate.notNull(listenerInterface, "Listener interface cannot be null.");
        Validate.notNull(classLoader, "ClassLoader cannot be null.");
        Validate.isTrue(listenerInterface.isInterface(), "Class {0} is not an interface",
                listenerInterface.getName());
        _myProxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader, new Class[] { listenerInterface },
                new ProxyInvocationHandler()));
    }

    /**
     * Returns a proxy object which can be used to call listener methods on all of the registered event listeners. All
     * calls made to this proxy will be forwarded to all registered listeners.
     * 
     * @return a proxy object which can be used to call listener methods on all of the registered event listeners
     */
    public ListenerType proxy() {
        return _myProxy;
    }

    // **********************************************************************************************************************
    // Other Methods
    // **********************************************************************************************************************

    /**
     * Registers an event listener.
     * 
     * @param theListener the event listener (may not be <code>null</code>).
     * 
     * @throws NullPointerException if <code>listener</code> is <code>null</code>.
     */
    public void add(ListenerType theListener) {
        synchronized (_myListeners) {
            Validate.notNull(theListener, "Listener object cannot be null.");
            _myListeners.add(theListener);
        }
    }

    /**
     * Returns the number of registered listeners.
     * 
     * @return the number of registered listeners.
     */
    public int getListenerCount() {
        return _myListeners.size();
    }

    /**
     * Unregisters an event listener.
     * 
     * @param theListener the event listener (may not be <code>null</code>).
     * 
     * @throws NullPointerException if <code>listener</code> is <code>null</code>.
     */
    public void remove(ListenerType theListener) {
        synchronized (_myListeners) {
            Validate.notNull(theListener, "Listener object cannot be null.");
            _myListeners.remove(theListener);
        }
    }

    /**
     * An invocation handler used to dispatch the event(s) to all the listeners.
     */
    private class ProxyInvocationHandler implements InvocationHandler {
        /**
         * Propagates the method call to all registered listeners in place of the proxy listener object.
         * 
         * @param proxy the proxy object representing a listener on which the invocation was called.
         * @param method the listener method that will be called on all of the listeners.
         * @param args event arguments to propogate to the listeners.
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            for (ListenerType listener : new ArrayList<ListenerType>(_myListeners)) {
                method.invoke(listener, args);
            }
            return null;
        }
    }

    @Override
    public Iterator<ListenerType> iterator() {
        return new ArrayList<ListenerType>(_myListeners).iterator();
    }
}