001/* EnumMap.java - Map where keys are enum constants
002   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.util;
040
041import 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
049public 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}