001 /*
002 * LAPIS lightweight structured text processing system
003 *
004 * Copyright (C) 1998-2002 Carnegie Mellon University,
005 * Copyright (C) 2003 Massachusetts Institute of Technology.
006 * All rights reserved.
007 *
008 * This library is free software; you can redistribute it
009 * and/or modify it under the terms of the GNU General
010 * Public License as published by the Free Software
011 * Foundation, version 2.
012 *
013 * LAPIS homepage: http://graphics.lcs.mit.edu/lapis/
014 */
015
016
017 package lapisx.util;
018
019 import java.util.*;
020
021 /**
022 * This class implements the Set interface backed by a hash table.
023 * Iterators returned by a SafeIteratorSet iterate over an
024 * unmodifiable snapshot of the set, so it's safe to call add and
025 * remove on the original set even while iterators are running.
026 * Useful for storing a collection of event listeners.
027 */
028 public class SafeIteratorSet implements Set {
029 public static Debug debug = Debug.QUIET;
030
031 HashSet set;
032 int refcount = 0; // number of iterators currently using set
033
034 /**
035 * Make an empty SafeIteratorSet.
036 */
037 public SafeIteratorSet () {
038 set = new HashSet ();
039 }
040
041 /**
042 * Make a SafeIteratorSet initialized with the elements of a collection.
043 * @param c Initial members of the set
044 */
045 public SafeIteratorSet (Collection c) {
046 set = new HashSet (c);
047 }
048
049 /**
050 * Make a SafeIteratorSet with an initial capacity.
051 * @param initialCapacity Initial capacity allocated to hash table
052 */
053 public SafeIteratorSet (int initialCapacity) {
054 set = new HashSet (initialCapacity);
055 }
056
057 /**
058 * Make a SafeIteratorSet with an initial capacity and load factor.
059 * @param initialCapacity Initial capacity allocated to hash table
060 * @param loadFactor Load factor for hash table.
061 */
062 public SafeIteratorSet (int initialCapacity, float loadFactor) {
063 set = new HashSet (initialCapacity, loadFactor);
064 }
065
066 /**
067 * Copy this set if any iterators are currently using it.
068 * @effects If any iterators are in operation on this set,
069 * make a fresh copy of the set so that mutations do not affect
070 * them.
071 */
072 private void copyOnWrite () {
073 if (refcount > 0) {
074 debug.println ("copying set, refcount=" + refcount);
075 set = new HashSet (set);
076 refcount = 0;
077 }
078 }
079
080 public boolean add (Object o) {
081 copyOnWrite ();
082 return set.add (o);
083 }
084
085 public boolean addAll (Collection c) {
086 copyOnWrite ();
087 return set.addAll (c);
088 }
089
090 public void clear () {
091 copyOnWrite ();
092 set.clear ();
093 }
094
095 public boolean contains (Object o) {
096 return set.contains (o);
097 }
098
099 public boolean containsAll (Collection c) {
100 return set.containsAll (c);
101 }
102
103 public boolean equals (Object o) {
104 return set.equals (o);
105 }
106
107 public int hashCode () {
108 return set.hashCode ();
109 }
110
111 public boolean isEmpty () {
112 return set.isEmpty ();
113 }
114
115 /**
116 * Return an iterator over the elements in this set. The iterator
117 * operates over an unmodifiable snapshot of this set's elements,
118 * so the set may be mutated without harm while the iterator is
119 * operating.
120 * @returns iterator over unmodifiable snapshot of this set
121 */
122 public Iterator iterator () {
123 return new SafeIterator ();
124 }
125
126 private class SafeIterator implements Iterator {
127 Set s;
128 Iterator g;
129 public SafeIterator () {
130 g = set.iterator ();
131 acquire ();
132 }
133
134 public boolean hasNext () {
135 if (released ())
136 return false;
137
138 boolean f = g.hasNext ();
139 if (!f)
140 release ();
141
142 return f;
143 }
144
145 public Object next () {
146 if (released ())
147 throw new NoSuchElementException ();
148 else
149 try {
150 return g.next ();
151 } catch (NoSuchElementException e) {
152 release ();
153 throw e;
154 }
155 }
156
157 public void remove () {
158 throw new UnsupportedOperationException ("Iterator.remove() not supported by SafeIteratorSet");
159 }
160
161 private void acquire () {
162 s = set;
163 ++refcount;
164 }
165
166 private void release () {
167 if (s == set) {
168 --refcount;
169 debug.assertion (refcount >= 0);
170 s = null;
171 }
172 }
173
174 private boolean released () {
175 return s == null;
176 }
177 }
178
179 public boolean remove (Object o) {
180 copyOnWrite ();
181 return set.remove (o);
182 }
183
184 public boolean removeAll (Collection c) {
185 copyOnWrite ();
186 return set.removeAll (c);
187 }
188
189 public boolean retainAll (Collection c) {
190 copyOnWrite ();
191 return set.retainAll (c);
192 }
193
194 public int size () {
195 return set.size ();
196 }
197
198 public Object[] toArray () {
199 return set.toArray ();
200 }
201
202 public Object[] toArray (Object[] a) {
203 return set.toArray (a);
204 }
205
206 }