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 }