| /* EnumMap.java - Map where keys are enum constants |
| Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package java.util; |
| |
| import java.io.Serializable; |
| |
| /** |
| * @author Tom Tromey (tromey@redhat.com) |
| * @author Andrew John Hughes (gnu_andrew@member.fsf.org) |
| * @since 1.5 |
| */ |
| |
| public class EnumMap<K extends Enum<K>, V> |
| extends AbstractMap<K, V> |
| implements Cloneable, Serializable |
| { |
| private static final long serialVersionUID = 458661240069192865L; |
| |
| V[] store; |
| int cardinality; |
| Class<K> enumClass; |
| |
| /** |
| * The cache for {@link #entrySet()}. |
| */ |
| transient Set<Map.Entry<K, V>> entries; |
| |
| static final Object emptySlot = new Object(); |
| |
| public EnumMap(Class<K> keyType) |
| { |
| store = (V[]) new Object[keyType.getEnumConstants().length]; |
| Arrays.fill(store, emptySlot); |
| cardinality = 0; |
| enumClass = keyType; |
| } |
| |
| public EnumMap(EnumMap<K, ? extends V> map) |
| { |
| store = (V[]) map.store.clone(); |
| cardinality = map.cardinality; |
| enumClass = map.enumClass; |
| } |
| |
| public EnumMap(Map<K, ? extends V> map) |
| { |
| if (map instanceof EnumMap) |
| { |
| EnumMap<K, ? extends V> other = (EnumMap<K, ? extends V>) map; |
| store = (V[]) other.store.clone(); |
| cardinality = other.cardinality; |
| enumClass = other.enumClass; |
| } |
| else |
| { |
| for (K key : map.keySet()) |
| { |
| V value = map.get(key); |
| if (store == null) |
| { |
| enumClass = key.getDeclaringClass(); |
| store = (V[]) new Object[enumClass.getEnumConstants().length]; |
| } |
| int o = key.ordinal(); |
| if (store[o] == emptySlot) |
| ++cardinality; |
| store[o] = value; |
| } |
| // There must be a single element. |
| if (store == null) |
| throw new IllegalArgumentException("no elements in map"); |
| } |
| } |
| |
| public int size() |
| { |
| return cardinality; |
| } |
| |
| public boolean containsValue(Object value) |
| { |
| for (V i : store) |
| { |
| if (i != emptySlot && AbstractCollection.equals(i , value)) |
| return true; |
| } |
| return false; |
| } |
| |
| public boolean containsKey(Object key) |
| { |
| if (! (key instanceof Enum)) |
| return false; |
| Enum<K> e = (Enum<K>) key; |
| if (e.getDeclaringClass() != enumClass) |
| return false; |
| return store[e.ordinal()] != emptySlot; |
| } |
| |
| public V get(Object key) |
| { |
| if (! (key instanceof Enum)) |
| return null; |
| Enum<K> e = (Enum<K>) key; |
| if (e.getDeclaringClass() != enumClass) |
| return null; |
| V o = store[e.ordinal()]; |
| return o == emptySlot ? null : o; |
| } |
| |
| public V put(K key, V value) |
| { |
| int o = key.ordinal(); |
| V result; |
| if (store[o] == emptySlot) |
| { |
| result = null; |
| ++cardinality; |
| } |
| else |
| result = store[o]; |
| store[o] = value; |
| return result; |
| } |
| |
| public V remove(Object key) |
| { |
| if (! (key instanceof Enum)) |
| return null; |
| Enum<K> e = (Enum<K>) key; |
| if (e.getDeclaringClass() != enumClass) |
| return null; |
| V result = store[e.ordinal()]; |
| if (result == emptySlot) |
| result = null; |
| else |
| --cardinality; |
| store[e.ordinal()] = (V) emptySlot; |
| return result; |
| } |
| |
| public void putAll(Map<? extends K, ? extends V> map) |
| { |
| for (K key : map.keySet()) |
| { |
| V value = map.get(key); |
| |
| int o = key.ordinal(); |
| if (store[o] == emptySlot) |
| ++cardinality; |
| store[o] = value; |
| } |
| } |
| |
| public void clear() |
| { |
| Arrays.fill(store, emptySlot); |
| cardinality = 0; |
| } |
| |
| public Set<K> keySet() |
| { |
| if (keys == null) |
| { |
| keys = new AbstractSet<K>() |
| { |
| public int size() |
| { |
| return cardinality; |
| } |
| |
| public Iterator<K> iterator() |
| { |
| return new Iterator<K>() |
| { |
| int count = 0; |
| int index = -1; |
| |
| public boolean hasNext() |
| { |
| return count < cardinality; |
| } |
| |
| public K next() |
| { |
| ++count; |
| for (++index; store[index] == emptySlot; ++index) |
| ; |
| return enumClass.getEnumConstants()[index]; |
| } |
| |
| public void remove() |
| { |
| --cardinality; |
| store[index] = (V) emptySlot; |
| } |
| }; |
| } |
| |
| public void clear() |
| { |
| EnumMap.this.clear(); |
| } |
| |
| public boolean contains(Object o) |
| { |
| return contains(o); |
| } |
| |
| public boolean remove(Object o) |
| { |
| return EnumMap.this.remove(o) != null; |
| } |
| }; |
| } |
| return keys; |
| } |
| |
| public Collection<V> values() |
| { |
| if (values == null) |
| { |
| values = new AbstractCollection<V>() |
| { |
| public int size() |
| { |
| return cardinality; |
| } |
| |
| public Iterator<V> iterator() |
| { |
| return new Iterator<V>() |
| { |
| int count = 0; |
| int index = -1; |
| |
| public boolean hasNext() |
| { |
| return count < cardinality; |
| } |
| |
| public V next() |
| { |
| ++count; |
| for (++index; store[index] == emptySlot; ++index) |
| ; |
| return store[index]; |
| } |
| |
| public void remove() |
| { |
| --cardinality; |
| store[index] = (V) emptySlot; |
| } |
| }; |
| } |
| |
| public void clear() |
| { |
| EnumMap.this.clear(); |
| } |
| }; |
| } |
| return values; |
| } |
| |
| public Set<Map.Entry<K, V>> entrySet() |
| { |
| if (entries == null) |
| { |
| entries = new AbstractSet<Map.Entry<K, V>>() |
| { |
| public int size() |
| { |
| return cardinality; |
| } |
| |
| public Iterator<Map.Entry<K, V>> iterator() |
| { |
| return new Iterator<Map.Entry<K, V>>() |
| { |
| int count = 0; |
| int index = -1; |
| |
| public boolean hasNext() |
| { |
| return count < cardinality; |
| } |
| |
| public Map.Entry<K,V> next() |
| { |
| ++count; |
| for (++index; store[index] == emptySlot; ++index) |
| ; |
| // FIXME: we could just return something that |
| // only knows the index. That would be cleaner. |
| return new AbstractMap.SimpleEntry<K, V>(enumClass.getEnumConstants()[index], |
| store[index]) |
| { |
| public V setValue(V newVal) |
| { |
| value = newVal; |
| return put(key, newVal); |
| } |
| }; |
| } |
| |
| public void remove() |
| { |
| --cardinality; |
| store[index] = (V) emptySlot; |
| } |
| }; |
| } |
| |
| public void clear() |
| { |
| EnumMap.this.clear(); |
| } |
| |
| public boolean contains(Object o) |
| { |
| if (! (o instanceof Map.Entry)) |
| return false; |
| Map.Entry<K, V> other = (Map.Entry<K, V>) o; |
| return (containsKey(other.getKey()) |
| && AbstractCollection.equals(get(other.getKey()), |
| other.getValue())); |
| } |
| |
| public boolean remove(Object o) |
| { |
| if (! (o instanceof Map.Entry)) |
| return false; |
| Map.Entry<K, V> other = (Map.Entry<K, V>) o; |
| return EnumMap.this.remove(other.getKey()) != null; |
| } |
| }; |
| } |
| return entries; |
| } |
| |
| public boolean equals(Object o) |
| { |
| if (! (o instanceof EnumMap)) |
| return false; |
| EnumMap<K, V> other = (EnumMap<K, V>) o; |
| if (other.enumClass != enumClass || other.cardinality != cardinality) |
| return false; |
| return Arrays.equals(store, other.store); |
| } |
| |
| public EnumMap<K, V> clone() |
| { |
| EnumMap<K, V> result; |
| try |
| { |
| result = (EnumMap<K, V>) super.clone(); |
| } |
| catch (CloneNotSupportedException ignore) |
| { |
| // Can't happen. |
| result = null; |
| } |
| result.store = (V[]) store.clone(); |
| return result; |
| } |
| |
| } |