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    }