KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > ListenerList


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.runtime;
12
13 /**
14  * This class is a thread safe list that is designed for storing lists of listeners.
15  * The implementation is optimized for minimal memory footprint, frequent reads
16  * and infrequent writes. Modification of the list is synchronized and relatively
17  * expensive, while accessing the listeners is very fast. Readers are given access
18  * to the underlying array data structure for reading, with the trust that they will
19  * not modify the underlying array.
20  * <p>
21  * <a name="same">A listener list handles the <i>same</i> listener being added
22  * multiple times, and tolerates removal of listeners that are the same as other
23  * listeners in the list. For this purpose, listeners can be compared with each other
24  * using either equality or identity, as specified in the list constructor.
25  * </p>
26  * <p>
27  * Use the <code>getListeners</code> method when notifying listeners. The recommended
28  * code sequence for notifying all registered listeners of say,
29  * <code>FooListener.eventHappened</code>, is:
30  *
31  * <pre>
32  * Object[] listeners = myListenerList.getListeners();
33  * for (int i = 0; i &lt; listeners.length; ++i) {
34  * ((FooListener) listeners[i]).eventHappened(event);
35  * }
36  * </pre>
37  *
38  * </p><p>
39  * This class can be used without OSGi running.
40  * </p>
41  * @since org.eclipse.equinox.common 3.2
42  */

43 public class ListenerList {
44
45     /**
46      * The empty array singleton instance.
47      */

48     private static final Object JavaDoc[] EmptyArray = new Object JavaDoc[0];
49
50     /**
51      * Mode constant (value 0) indicating that listeners should be considered
52      * the <a HREF="#same">same</a> if they are equal.
53      */

54     public static final int EQUALITY = 0;
55
56     /**
57      * Mode constant (value 1) indicating that listeners should be considered
58      * the <a HREF="#same">same</a> if they are identical.
59      */

60     public static final int IDENTITY = 1;
61
62     /**
63      * Indicates the comparison mode used to determine if two
64      * listeners are equivalent
65      */

66     private final boolean identity;
67
68     /**
69      * The list of listeners. Initially empty but initialized
70      * to an array of size capacity the first time a listener is added.
71      * Maintains invariant: listeners != null
72      */

73     private volatile Object JavaDoc[] listeners = EmptyArray;
74
75     /**
76      * Creates a listener list in which listeners are compared using equality.
77      */

78     public ListenerList() {
79         this(EQUALITY);
80     }
81
82     /**
83      * Creates a listener list using the provided comparison mode.
84      *
85      * @param mode The mode used to determine if listeners are the <a HREF="#same">same</a>.
86      */

87     public ListenerList(int mode) {
88         if (mode != EQUALITY && mode != IDENTITY)
89             throw new IllegalArgumentException JavaDoc();
90         this.identity = mode == IDENTITY;
91     }
92
93     /**
94      * Adds a listener to this list. This method has no effect if the <a HREF="#same">same</a>
95      * listener is already registered.
96      *
97      * @param listener the non-<code>null</code> listener to add
98      */

99     public synchronized void add(Object JavaDoc listener) {
100         // This method is synchronized to protect against multiple threads adding
101
// or removing listeners concurrently. This does not block concurrent readers.
102
if (listener == null)
103             throw new IllegalArgumentException JavaDoc();
104         // check for duplicates
105
final int oldSize = listeners.length;
106         for (int i = 0; i < oldSize; ++i) {
107             Object JavaDoc listener2 = listeners[i];
108             if (identity ? listener == listener2 : listener.equals(listener2))
109                 return;
110         }
111         // Thread safety: create new array to avoid affecting concurrent readers
112
Object JavaDoc[] newListeners = new Object JavaDoc[oldSize + 1];
113         System.arraycopy(listeners, 0, newListeners, 0, oldSize);
114         newListeners[oldSize] = listener;
115         //atomic assignment
116
this.listeners = newListeners;
117     }
118
119     /**
120      * Returns an array containing all the registered listeners.
121      * The resulting array is unaffected by subsequent adds or removes.
122      * If there are no listeners registered, the result is an empty array.
123      * Use this method when notifying listeners, so that any modifications
124      * to the listener list during the notification will have no effect on
125      * the notification itself.
126      * <p>
127      * Note: Callers of this method <b>must not</b> modify the returned array.
128      *
129      * @return the list of registered listeners
130      */

131     public Object JavaDoc[] getListeners() {
132         return listeners;
133     }
134
135     /**
136      * Returns whether this listener list is empty.
137      *
138      * @return <code>true</code> if there are no registered listeners, and
139      * <code>false</code> otherwise
140      */

141     public boolean isEmpty() {
142         return listeners.length == 0;
143     }
144
145     /**
146      * Removes a listener from this list. Has no effect if the <a HREF="#same">same</a>
147      * listener was not already registered.
148      *
149      * @param listener the non-<code>null</code> listener to remove
150      */

151     public synchronized void remove(Object JavaDoc listener) {
152         // This method is synchronized to protect against multiple threads adding
153
// or removing listeners concurrently. This does not block concurrent readers.
154
if (listener == null)
155             throw new IllegalArgumentException JavaDoc();
156         int oldSize = listeners.length;
157         for (int i = 0; i < oldSize; ++i) {
158             Object JavaDoc listener2 = listeners[i];
159             if (identity ? listener == listener2 : listener.equals(listener2)) {
160                 if (oldSize == 1) {
161                     listeners = EmptyArray;
162                 } else {
163                     // Thread safety: create new array to avoid affecting concurrent readers
164
Object JavaDoc[] newListeners = new Object JavaDoc[oldSize - 1];
165                     System.arraycopy(listeners, 0, newListeners, 0, i);
166                     System.arraycopy(listeners, i + 1, newListeners, i, oldSize - i - 1);
167                     //atomic assignment to field
168
this.listeners = newListeners;
169                 }
170                 return;
171             }
172         }
173     }
174
175     /**
176      * Returns the number of registered listeners.
177      *
178      * @return the number of registered listeners
179      */

180     public int size() {
181         return listeners.length;
182     }
183     
184     /**
185      * Removes all listeners from this list.
186      */

187     public synchronized void clear() {
188             listeners = EmptyArray;
189     }
190 }
191
Popular Tags