/*
* (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:jacob.dreyer@geosoft.no">Jacob Dreyer</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);
}
}