001 /* EnumMap.java - Map where keys are enum constants
002 Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038
039 package java.util;
040
041 import java.io.Serializable;
042
043 /**
044 * @author Tom Tromey (tromey@redhat.com)
045 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
046 * @since 1.5
047 */
048
049 public class EnumMap<K extends Enum<K>, V>
050 extends AbstractMap<K, V>
051 implements Cloneable, Serializable
052 {
053 private static final long serialVersionUID = 458661240069192865L;
054
055 V[] store;
056 int cardinality;
057 Class<K> enumClass;
058
059 /**
060 * The cache for {@link #entrySet()}.
061 */
062 transient Set<Map.Entry<K, V>> entries;
063
064 static final Object emptySlot = new Object();
065
066 public EnumMap(Class<K> keyType)
067 {
068 store = (V[]) new Object[keyType.getEnumConstants().length];
069 Arrays.fill(store, emptySlot);
070 cardinality = 0;
071 enumClass = keyType;
072 }
073
074 public EnumMap(EnumMap<K, ? extends V> map)
075 {
076 store = (V[]) map.store.clone();
077 cardinality = map.cardinality;
078 enumClass = map.enumClass;
079 }
080
081 public EnumMap(Map<K, ? extends V> map)
082 {
083 if (map instanceof EnumMap)
084 {
085 EnumMap<K, ? extends V> other = (EnumMap<K, ? extends V>) map;
086 store = (V[]) other.store.clone();
087 cardinality = other.cardinality;
088 enumClass = other.enumClass;
089 }
090 else
091 {
092 for (K key : map.keySet())
093 {
094 V value = map.get(key);
095 if (store == null)
096 {
097 enumClass = key.getDeclaringClass();
098 store = (V[]) new Object[enumClass.getEnumConstants().length];
099 }
100 int o = key.ordinal();
101 if (store[o] == emptySlot)
102 ++cardinality;
103 store[o] = value;
104 }
105 // There must be a single element.
106 if (store == null)
107 throw new IllegalArgumentException("no elements in map");
108 }
109 }
110
111 public int size()
112 {
113 return cardinality;
114 }
115
116 public boolean containsValue(Object value)
117 {
118 for (V i : store)
119 {
120 if (i != emptySlot && AbstractCollection.equals(i , value))
121 return true;
122 }
123 return false;
124 }
125
126 public boolean containsKey(Object key)
127 {
128 if (! (key instanceof Enum))
129 return false;
130 Enum<K> e = (Enum<K>) key;
131 if (e.getDeclaringClass() != enumClass)
132 return false;
133 return store[e.ordinal()] != emptySlot;
134 }
135
136 public V get(Object key)
137 {
138 if (! (key instanceof Enum))
139 return null;
140 Enum<K> e = (Enum<K>) key;
141 if (e.getDeclaringClass() != enumClass)
142 return null;
143 V o = store[e.ordinal()];
144 return o == emptySlot ? null : o;
145 }
146
147 public V put(K key, V value)
148 {
149 int o = key.ordinal();
150 V result;
151 if (store[o] == emptySlot)
152 {
153 result = null;
154 ++cardinality;
155 }
156 else
157 result = store[o];
158 store[o] = value;
159 return result;
160 }
161
162 public V remove(Object key)
163 {
164 if (! (key instanceof Enum))
165 return null;
166 Enum<K> e = (Enum<K>) key;
167 if (e.getDeclaringClass() != enumClass)
168 return null;
169 V result = store[e.ordinal()];
170 if (result == emptySlot)
171 result = null;
172 else
173 --cardinality;
174 store[e.ordinal()] = (V) emptySlot;
175 return result;
176 }
177
178 public void putAll(Map<? extends K, ? extends V> map)
179 {
180 for (K key : map.keySet())
181 {
182 V value = map.get(key);
183
184 int o = key.ordinal();
185 if (store[o] == emptySlot)
186 ++cardinality;
187 store[o] = value;
188 }
189 }
190
191 public void clear()
192 {
193 Arrays.fill(store, emptySlot);
194 cardinality = 0;
195 }
196
197 public Set<K> keySet()
198 {
199 if (keys == null)
200 {
201 keys = new AbstractSet<K>()
202 {
203 public int size()
204 {
205 return cardinality;
206 }
207
208 public Iterator<K> iterator()
209 {
210 return new Iterator<K>()
211 {
212 int count = 0;
213 int index = -1;
214
215 public boolean hasNext()
216 {
217 return count < cardinality;
218 }
219
220 public K next()
221 {
222 ++count;
223 for (++index; store[index] == emptySlot; ++index)
224 ;
225 return enumClass.getEnumConstants()[index];
226 }
227
228 public void remove()
229 {
230 --cardinality;
231 store[index] = (V) emptySlot;
232 }
233 };
234 }
235
236 public void clear()
237 {
238 EnumMap.this.clear();
239 }
240
241 public boolean contains(Object o)
242 {
243 return contains(o);
244 }
245
246 public boolean remove(Object o)
247 {
248 return EnumMap.this.remove(o) != null;
249 }
250 };
251 }
252 return keys;
253 }
254
255 public Collection<V> values()
256 {
257 if (values == null)
258 {
259 values = new AbstractCollection<V>()
260 {
261 public int size()
262 {
263 return cardinality;
264 }
265
266 public Iterator<V> iterator()
267 {
268 return new Iterator<V>()
269 {
270 int count = 0;
271 int index = -1;
272
273 public boolean hasNext()
274 {
275 return count < cardinality;
276 }
277
278 public V next()
279 {
280 ++count;
281 for (++index; store[index] == emptySlot; ++index)
282 ;
283 return store[index];
284 }
285
286 public void remove()
287 {
288 --cardinality;
289 store[index] = (V) emptySlot;
290 }
291 };
292 }
293
294 public void clear()
295 {
296 EnumMap.this.clear();
297 }
298 };
299 }
300 return values;
301 }
302
303 public Set<Map.Entry<K, V>> entrySet()
304 {
305 if (entries == null)
306 {
307 entries = new AbstractSet<Map.Entry<K, V>>()
308 {
309 public int size()
310 {
311 return cardinality;
312 }
313
314 public Iterator<Map.Entry<K, V>> iterator()
315 {
316 return new Iterator<Map.Entry<K, V>>()
317 {
318 int count = 0;
319 int index = -1;
320
321 public boolean hasNext()
322 {
323 return count < cardinality;
324 }
325
326 public Map.Entry<K,V> next()
327 {
328 ++count;
329 for (++index; store[index] == emptySlot; ++index)
330 ;
331 // FIXME: we could just return something that
332 // only knows the index. That would be cleaner.
333 return new AbstractMap.SimpleEntry<K, V>(enumClass.getEnumConstants()[index],
334 store[index])
335 {
336 public V setValue(V newVal)
337 {
338 value = newVal;
339 return put(key, newVal);
340 }
341 };
342 }
343
344 public void remove()
345 {
346 --cardinality;
347 store[index] = (V) emptySlot;
348 }
349 };
350 }
351
352 public void clear()
353 {
354 EnumMap.this.clear();
355 }
356
357 public boolean contains(Object o)
358 {
359 if (! (o instanceof Map.Entry))
360 return false;
361 Map.Entry<K, V> other = (Map.Entry<K, V>) o;
362 return (containsKey(other.getKey())
363 && AbstractCollection.equals(get(other.getKey()),
364 other.getValue()));
365 }
366
367 public boolean remove(Object o)
368 {
369 if (! (o instanceof Map.Entry))
370 return false;
371 Map.Entry<K, V> other = (Map.Entry<K, V>) o;
372 return EnumMap.this.remove(other.getKey()) != null;
373 }
374 };
375 }
376 return entries;
377 }
378
379 public boolean equals(Object o)
380 {
381 if (! (o instanceof EnumMap))
382 return false;
383 EnumMap<K, V> other = (EnumMap<K, V>) o;
384 if (other.enumClass != enumClass || other.cardinality != cardinality)
385 return false;
386 return Arrays.equals(store, other.store);
387 }
388
389 public EnumMap<K, V> clone()
390 {
391 EnumMap<K, V> result;
392 try
393 {
394 result = (EnumMap<K, V>) super.clone();
395 }
396 catch (CloneNotSupportedException ignore)
397 {
398 // Can't happen.
399 result = null;
400 }
401 result.store = (V[]) store.clone();
402 return result;
403 }
404
405 }