/* * (C) 2004 - Geotechnical Software Services * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This code 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ package no.geosoft.cc.event; import java.lang.ref.*; import java.util.*; /** * Generic event manager class. Events are string based rather than being * enumerations which whould have implied a central register of events. * When events are strings, only the negotiating classes needs to know * about the events existance. It is transparent also to the EventManager * who only delegates the events. * <p> * A potential problem with string based events are that there might be * name conflicts. In a large system, one might consider prefixing the * event names to avoid conflicts. If this seems to become a problem, the * system is probably bad designed anyway; The number of different event * types should be kept low. * <p> * The listener class must implement the EventListener interface which * implies the update() method. The listener class register itself in * the EventManager by: * <pre> * EventManager.getInstance().addListener ("EventName", this); * </pre> * The source class of the event will call: * <pre> * EventManager.getInstance().notify ("EventName", object, data); * </pre> * and the EventManager will then call the update() method of every listener. * <p> * The definition of <em>object</em> and <em>data</em> is purely up to * the involved classes. Typically <em>object</em> will be the source of * the event (the <em>created</em> object for a "Create" event, the * <em>deleted</em> object for a "Delete" event and so forth.) The additinal * <em>data</em> object is for convenience only and will often be null. * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ public class EventManager { private static EventManager instance_ = null; private HashMap listeners_; // String -> Collection of WeakRef (EventListener) /** * Return the sole instance of the class. * * @return The EventManager singelton. */ public static EventManager getInstance() { if (instance_ == null) instance_ = new EventManager(); return instance_; } /** * Create the event manager instance. */ private EventManager() { listeners_ = new HashMap(); } /** * Add a listener. * * @param eventName The event the listener will listen to. * @param eventListener The event listener object itself. */ public void addListener (String eventName, EventListener eventListener) { // Check if this is a new event name Collection eventListeners = (Collection) listeners_.get (eventName); if (eventListeners == null) { eventListeners = new ArrayList(); listeners_.put (eventName, eventListeners); } // Check to see if the listener is already there for (Iterator i = eventListeners.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); EventListener listener = (EventListener) reference.get(); if (listener == eventListener) return; } // Add the listener eventListeners.add (new WeakReference (eventListener)); } /** * Remove listener from specific event. * * @param eventName Event to remove listener from. * @param eventListener Listener to remove. */ public void removeListener (String eventName, EventListener eventListener) { if (eventName == null) { removeListener (eventListener); return; } // Find the listeners for the specified event Collection eventListeners = (Collection) listeners_.get (eventName); if (eventListeners == null) return; // Remove the listener for (Iterator i = eventListeners.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); EventListener listener = (EventListener) reference.get(); if (listener == eventListener) { i.remove(); break; } } // Remove the event as such if this was the last listener for this event if (eventListeners.size() == 0) listeners_.remove (eventName); } /** * Remove listener from all events it is registered by. Convenient * way of cleaning up an listener object being destroyed. * * @param eventListener Event listener to remove. */ public void removeListener (EventListener eventListener) { // Loop over all registered events and remove the specified listener // Loop over a copy incase the removeListener() call wants to // remove the entire event from the hash map. Collection listeners = new ArrayList (listeners_.keySet()); for (Iterator i = listeners.iterator(); i.hasNext(); ) { String eventName = (String) i.next(); removeListener (eventName, eventListener); } } /** * Call listeners. The definition of <em>object</em> and <em>data</em> * is purely up to the communicating classes. * * @param eventName Name of the event. * @param source Source of the event (or null). * @param data Additinal data of the event (or null). */ public void notify (String eventName, Object source, Object data) { // Find all listeners of this event Collection eventListeners = (Collection) listeners_.get (eventName); if (eventListeners == null) return; // Loop over a copy of the list in case it is altered by listener Collection copy = new ArrayList (eventListeners); for (Iterator i = copy.iterator(); i.hasNext(); ) { WeakReference reference = (WeakReference) i.next(); EventListener eventListener = (EventListener) reference.get(); if (eventListener == null) { i.remove(); } else eventListener.update (eventName, source, data); } } /** * Convenience front-end where the additional data paranmeter * is null. * * @param eventName Name of the event. * @param source Source of the event (or null). */ public void notify (String eventName, Object source) { notify (eventName, source, null); } }