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 }