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: private static final int START_SIZE = 5;
031:
032: private Object[] _listeners;
033: private int _count;
034: private int _iteratorCount;
035: private int _uid;
036:
037: private class ListenerIterator implements Iterator {
038: private Object[] _localListeners;
039: private int _localCount;
040: private int _localUid;
041: private int _pos;
042:
043: private ListenerIterator() {
044: _localListeners = _listeners;
045: _localCount = _count;
046: _localUid = _uid;
047: }
048:
049: public boolean hasNext() {
050: if (_pos >= _localCount) {
051: // If _listeners has not been recopied during the lifespan
052: // of this iterator, then knock the count down by one.
053:
054: adjustIteratorCount(_localUid);
055:
056: _localListeners = null;
057: _localCount = 0;
058: _localUid = -1;
059: _pos = 0;
060:
061: return false;
062: }
063:
064: return true;
065: }
066:
067: public Object next() {
068: return _localListeners[_pos++];
069: }
070:
071: public void remove() {
072: throw new UnsupportedOperationException();
073: }
074:
075: }
076:
077: /**
078: * Returns an Iterator used to find all the listeners previously added.
079: * The order in which listeners are returned is not guaranteed.
080: * Currently, you may not invoke <code>remove()</code> on the Iterator.
081: *
082: * <p>
083: * Invoking this method takes a "snapshot" of the current list of listeners.
084: * You may invoke {@link #addListener(Object)} or {@link #removeListener(Object)},
085: * but that won't affect the sequence of listeners returned by the Iterator.
086: */
087: public synchronized Iterator getListeners() {
088: _iteratorCount++;
089:
090: return new ListenerIterator();
091: }
092:
093: /**
094: * Adds a new listener to the list of listeners. The same instance
095: * will may be added multiple times.
096: */
097: public synchronized void addListener(Object listener) {
098: copyOnWrite(_count + 1);
099:
100: _listeners[_count] = listener;
101:
102: _count++;
103: }
104:
105: /**
106: * Removes a listener from the list. Does nothing if the listener
107: * is not already in the list. Comparison is based on identity, not equality.
108: * If the listener is in the list multiple times, only a single
109: * instance is removed.
110: */
111: public synchronized void removeListener(Object listener) {
112: for (int i = 0; i < _count; i++) {
113: if (_listeners[i] == listener) {
114: removeListener(i);
115: return;
116: }
117: }
118: }
119:
120: private void removeListener(int index) {
121: copyOnWrite(_count);
122:
123: // Move the last listener in the list into the index to be removed.
124:
125: _listeners[index] = _listeners[_count - 1];
126:
127: // Null out the old position.
128:
129: _listeners[_count - 1] = null;
130:
131: _count--;
132: }
133:
134: /**
135: * Copies the array before an update operation if necessary (because there
136: * is a known iterator for the current array, or because the
137: * array is not large enough).
138: */
139: private void copyOnWrite(int requiredSize) {
140: int size = _listeners == null ? 0 : _listeners.length;
141:
142: if (_iteratorCount > 0 || size < requiredSize) {
143: int nominalSize = (size == 0) ? START_SIZE : 2 * size;
144:
145: // Don't grow the array if we don't need to...
146: if (size >= requiredSize) {
147: nominalSize = size;
148: }
149:
150: int newSize = Math.max(requiredSize, nominalSize);
151:
152: Object[] newListeners = new Object[newSize];
153:
154: if (_count > 0)
155: System
156: .arraycopy(_listeners, 0, newListeners, 0,
157: _count);
158:
159: _listeners = newListeners;
160:
161: // No iterators on the *new* array
162: _iteratorCount = 0;
163: _uid++;
164: }
165: }
166:
167: private synchronized void adjustIteratorCount(int iteratorUid) {
168: if (_uid == iteratorUid)
169: _iteratorCount--;
170: }
171: }
|