001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.hivemind.util;
016
017 import java.util.Iterator;
018
019 /**
020 * Convienience class for tracking a list of event listeners. Works efficiently
021 * (using a copy-on-write approach) to iterating through the listeners in
022 * the list even when the list of listeners may be modified.
023 *
024 * <p>
025 * EventListenerList <em>is</em> thread-safe.
026 *
027 * @author Howard Lewis Ship
028 */
029 public class EventListenerList
030 {
031 private static final int START_SIZE = 5;
032
033 private Object[] _listeners;
034 private int _count;
035 private int _iteratorCount;
036 private int _uid;
037
038 private class ListenerIterator implements Iterator
039 {
040 private Object[] _localListeners;
041 private int _localCount;
042 private int _localUid;
043 private int _pos;
044
045 private ListenerIterator()
046 {
047 _localListeners = _listeners;
048 _localCount = _count;
049 _localUid = _uid;
050 }
051
052 public boolean hasNext()
053 {
054 if (_pos >= _localCount)
055 {
056 // If _listeners has not been recopied during the lifespan
057 // of this iterator, then knock the count down by one.
058
059 adjustIteratorCount(_localUid);
060
061 _localListeners = null;
062 _localCount = 0;
063 _localUid = -1;
064 _pos = 0;
065
066 return false;
067 }
068
069 return true;
070 }
071
072 public Object next()
073 {
074 return _localListeners[_pos++];
075 }
076
077 public void remove()
078 {
079 throw new UnsupportedOperationException();
080 }
081
082 }
083
084 /**
085 * Returns an Iterator used to find all the listeners previously added.
086 * The order in which listeners are returned is not guaranteed.
087 * Currently, you may not invoke <code>remove()</code> on the Iterator.
088 *
089 * <p>
090 * Invoking this method takes a "snapshot" of the current list of listeners.
091 * You may invoke {@link #addListener(Object)} or {@link #removeListener(Object)},
092 * but that won't affect the sequence of listeners returned by the Iterator.
093 */
094 public synchronized Iterator getListeners()
095 {
096 _iteratorCount++;
097
098 return new ListenerIterator();
099 }
100
101 /**
102 * Adds a new listener to the list of listeners. The same instance
103 * will may be added multiple times.
104 */
105 public synchronized void addListener(Object listener)
106 {
107 copyOnWrite(_count + 1);
108
109 _listeners[_count] = listener;
110
111 _count++;
112 }
113
114 /**
115 * Removes a listener from the list. Does nothing if the listener
116 * is not already in the list. Comparison is based on identity, not equality.
117 * If the listener is in the list multiple times, only a single
118 * instance is removed.
119 */
120 public synchronized void removeListener(Object listener)
121 {
122 for (int i = 0; i < _count; i++)
123 {
124 if (_listeners[i] == listener)
125 {
126 removeListener(i);
127 return;
128 }
129 }
130 }
131
132 private void removeListener(int index)
133 {
134 copyOnWrite(_count);
135
136 // Move the last listener in the list into the index to be removed.
137
138 _listeners[index] = _listeners[_count - 1];
139
140 // Null out the old position.
141
142 _listeners[_count - 1] = null;
143
144 _count--;
145 }
146
147 /**
148 * Copies the array before an update operation if necessary (because there
149 * is a known iterator for the current array, or because the
150 * array is not large enough).
151 */
152 private void copyOnWrite(int requiredSize)
153 {
154 int size = _listeners == null ? 0 : _listeners.length;
155
156 if (_iteratorCount > 0 || size < requiredSize)
157 {
158 int nominalSize = (size == 0) ? START_SIZE : 2 * size;
159
160 // Don't grow the array if we don't need to...
161 if (size >= requiredSize)
162 {
163 nominalSize = size;
164 }
165
166 int newSize = Math.max(requiredSize, nominalSize);
167
168 Object[] newListeners = new Object[newSize];
169
170 if (_count > 0)
171 System.arraycopy(_listeners, 0, newListeners, 0, _count);
172
173 _listeners = newListeners;
174
175 // No iterators on the *new* array
176 _iteratorCount = 0;
177 _uid++;
178 }
179 }
180
181 private synchronized void adjustIteratorCount(int iteratorUid)
182 {
183 if (_uid == iteratorUid)
184 _iteratorCount--;
185 }
186 }