/**
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 2. Redistributions in binary form must reproduce the
* above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. The name "Exolab" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Intalio, Inc. For written permission,
* please contact info@exolab.org.
*
* 4. Products derived from this Software may not be called "Exolab"
* nor may "Exolab" appear in their names without prior written
* permission of Intalio, Inc. Exolab is a registered
* trademark of Intalio, Inc.
*
* 5. Due credit should be given to the Exolab Project
* (http://www.exolab.org/).
*
* THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Contribution(s):
*
* - Jeff Norris, Jeff.Norris@jpl.nasa.gov
* - Original Author
*
* $Id$
*/
package org.exolab.castor.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.EventListener;
/**
* <p>This class is an efficient repository for EventListeners based
* on javax.swing.EventListenerList.</p>
*
* <p>This modification of javax.swing.EventListenerList retains the
* core functionality of that class but changes the basic API and adds
* a few more features, as summarized below:<p>
*
* <ol>
* <li>javax.swing.EventListenerList requires all listeners to be
* added in conjunction with a class object that identified the type
* of the listener. This implementation's add methods simply take the
* listener object.
*
* <li>The listener list returned from javax.swing.EventListenerList
* had to be iterated over in a cumbersome manner because the
* listeners were stored along with their Class objects in the array.
* Since listener classes are not stored in this listener list, the
* returned listener array can be iterated over normally (1 element at
* a time).
*
* <li>The remove method in javax.swing.EventListenerList had return
* type "void". This implementation's remove method returns true if
* the specified listener was found in the listener array and false
* otherwise.
*
* <li>This implementation adds {@link #add(EventListener, int)},
* which allows the addition of a listener at a specific position in
* the array.
*
* <li>The add and remove methods in this implementation throw
* IllegalArgumentExceptions when their arguments are null.
* </ol>
*
* <p>As is the case with javax.swing.EventListenerList, this class
* provides multi-threaded safety through a copy-on-modify strategy.
* It is optimized to provide high performance when events are being
* fired, with slightly slower performance than the Collection API
* when listeners are being added and removed. Like its predecessor,
* this class will never return a null array from getListenerList.</p>
*
* <p>The most important thing to keep in mind when using this class
* is that the array returned by getListenerList is the actual
* internal array of this class and MUST NOT BE MODIFIED UNDER ANY
* CIRCUMSTANCES. Below is an example of how to use this class,
* borrowed (and slightly modified) from the
* javax.swing.EventListenerList documentation:</p>
*
* <p>Usage example:
* Say one is defining a class that sends out FooEvents, and one wants
* to allow users of the class to register FooListeners and receive
* notification when FooEvents occur. The following should be added
* to the class definition:</p>
* <pre>
* EventListenerList listenerList = new EventListenerList();
* FooEvent fooEvent = null;
*
* public void addFooListener(FooListener l) {
* listenerList.add(l);
* }
*
* public void removeFooListener(FooListener l) {
* listenerList.remove(l);
* }
*
* // Notify all listeners that have registered interest for
* // notification on this event type. The event instance
* // is lazily created using the parameters passed into
* // the fire method.
*
* protected void fireFooXXX() {
* // Guaranteed to return a non-null array
* EventListener[] listeners = listenerList.getListenerList();
* // Process the listeners last to first, notifying
* // those that are interested in this event
* for (int i = 0 ; i < listeners.length ; i++) {
* // Lazily create the event:
* if (fooEvent == null)
* fooEvent = new FooEvent(this);
* ((FooListener)listeners[i]).fooXXX(fooEvent);
* }
* }
* </pre>
*
* <p>foo should be changed to the appropriate name, and fireFooXxx to the
* appropriate method name. One fire method should exist for each
* notification method in the FooListener interface.</p>
*
* <p>The authors of javax.swing.EventListenerList are Georges Saab,
* Hans Muller, and James Gosling.</p>
*
* @author <a href="mailto:Jeff.Norris@jpl.nasa.gov">Jeff Norris</a>
* @version $Revision$ $Date: 2005-12-13 14:58:48 -0700 (Tue, 13 Dec 2005) $
*/
public class EventListenerList implements Serializable {
/** SerialVersionUID */
private static final long serialVersionUID = 4472874989562384564L;
/**
* A null array to be shared by all empty listener lists
*/
private final static EventListener[] NULL_ARRAY = new EventListener[0];
/**
* The internal list of listeners that is returned from
* getListenerList
*/
protected transient EventListener[] listenerList = NULL_ARRAY;
/**
* <p>Passes back the event listener list as an array of
* EventListeners.</p>
*
* <p>Note that for performance reasons, this implementation passes
* back the actual data structure in which the listener data is
* stored internally! This method is guaranteed to pass back a
* non-null array, so that no null-checking is required in fire
* methods. A zero-length array of Object will be returned if
* there are currently no listeners.</p>
*
* WARNING!!! Absolutely NO modification of
* the data contained in this array should be made -- if
* any such manipulation is necessary, it should be done
* on a copy of the array returned rather than the array
* itself.
*/
public EventListener[] getListenerList() {
return listenerList;
}
/**
* <p>Returns the total number of listeners in this listener
* list.</p>
*
*/
public int getListenerCount() {
return listenerList.length;
}
/**
* <p>Adds the listener to the end of the listener list.</p>
*
* @param newListener the listener to be added
* @exception IllegalArgumentException if the specified newListener
* is null
*/
public synchronized void add(EventListener newListener)
{
if (newListener==null)
throw new IllegalArgumentException("Listener to add must not be null.");
if (listenerList == NULL_ARRAY) {
// if this is the first listener added,
// initialize the lists
listenerList = new EventListener[] { newListener };
} else {
// Otherwise copy the array and add the new listener
int oldLength = listenerList.length;
EventListener[] tmp = new EventListener[oldLength+1];
System.arraycopy(listenerList, 0, tmp, 0, oldLength);
tmp[oldLength] = newListener;
listenerList = tmp;
}
}
/**
* <p>Adds the listener at the specified index in the listener
* list.</p>
*
* @param newListener the listener to be added
* @exception IllegalArgumentException if the specified newListener
* is null, or the specified index is less than zero or greater than
* the length of the listener list array.
*/
public synchronized void add(EventListener newListener, int index)
{
if (newListener==null)
throw new IllegalArgumentException("Listener to add must not be null.");
if ((index < 0) || (index > listenerList.length))
throw new IllegalArgumentException("Index to add listener (" + index +
") is out of bounds. List length is " +
listenerList.length);
if (listenerList == NULL_ARRAY) {
// if this is the first listener added, initialize the lists
listenerList = new EventListener[] { newListener };
} else {
// Otherwise copy the array and add the new listener
int oldLength = listenerList.length;
EventListener[] tmp = new EventListener[oldLength+1];
// Copy up to the index where the new listener should go
System.arraycopy(listenerList, 0, tmp, 0, index);
// Skip a cell and copy the rest of the list
System.arraycopy(listenerList, index, tmp, index+1, oldLength-index);
// Insert the new listener
tmp[index] = newListener;
listenerList = tmp;
}
}
/**
* Removes the listener as a listener of the specified type.
*
* @param listenerToRemove the listener to be removed
* @exception IllegalArgumentException if the specified listener is
* null
*/
public synchronized boolean remove(EventListener listenerToRemove) {
if (listenerToRemove ==null)
throw new IllegalArgumentException("Listener to remove must " +
"not be null.");
// Is listenerToRemove on the list?
int index = -1;
for (int i = listenerList.length-1; i>=0; i--) {
if (listenerList[i].equals(listenerToRemove) == true) {
index = i;
break;
}
}
// If so, remove it
if (index != -1) {
EventListener[] tmp = new EventListener[listenerList.length-1];
// Copy the list up to index
System.arraycopy(listenerList, 0, tmp, 0, index);
// Copy from one past the index, up to the end of tmp (which is
// one element shorter than the old list)
if (index < tmp.length)
System.arraycopy(listenerList, index+1, tmp, index,
tmp.length - index);
// set the listener array to the new array or null
listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
return true;
}
return false;
}
// Serialization support.
private void writeObject(ObjectOutputStream s) throws IOException {
Object[] lList = listenerList;
s.defaultWriteObject();
// Save the non-null event listeners:
for (int i = 0; i < lList.length; i+=1) {
EventListener l = (EventListener)lList[i];
if ((l!=null) && (l instanceof Serializable)) {
s.writeObject(l);
}
}
s.writeObject(null);
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
listenerList = NULL_ARRAY;
s.defaultReadObject();
EventListener listenerOrNull;
while (null != (listenerOrNull = (EventListener)s.readObject())) {
add(listenerOrNull);
}
}
/**
* Returns a string representation of the EventListenerList.
*/
public String toString() {
Object[] lList = listenerList;
String s = "EventListenerList: ";
s += lList.length + " listeners: ";
for (int i = 0 ; i < lList.length ; i++) {
s += " listener " + lList[i+1];
}
return s;
}
} //-- class: EventListenerList