blob: 00ef1b98e95095cefb6fd4fa0957f9ff90fe4925 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package java.util;
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkState;
import javaemul.internal.ArrayHelper;
/**
* A {@link java.util.Map} of {@link Enum}s. <a
* href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/EnumMap.html">[Sun
* docs]</a>
*
* @param <K> key type
* @param <V> value type
*/
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> {
private final class EntrySet extends AbstractSet<Entry<K, V>> {
@Override
public void clear() {
EnumMap.this.clear();
}
@Override
public boolean contains(Object o) {
if (o instanceof Map.Entry) {
return containsEntry((Map.Entry<?, ?>) o);
}
return false;
}
@Override
public Iterator<Entry<K, V>> iterator() {
return new EntrySetIterator();
}
@Override
public boolean remove(Object entry) {
if (contains(entry)) {
Object key = ((Map.Entry<?, ?>) entry).getKey();
EnumMap.this.remove(key);
return true;
}
return false;
}
@Override
public int size() {
return EnumMap.this.size();
}
}
private final class EntrySetIterator implements Iterator<Entry<K, V>> {
private Iterator<K> it = keySet.iterator();
private K key;
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Entry<K, V> next() {
key = it.next();
return new MapEntry(key);
}
@Override
public void remove() {
checkState(key != null);
EnumMap.this.remove(key);
key = null;
}
}
private class MapEntry extends AbstractMapEntry<K, V> {
private final K key;
public MapEntry(K key) {
this.key = key;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return values[key.ordinal()];
}
@Override
public V setValue(V value) {
return ArrayHelper.setAt(values, key.ordinal(), value);
}
}
private EnumSet<K> keySet;
private V[] values;
public EnumMap(Class<K> type) {
init(type);
}
public EnumMap(EnumMap<K, ? extends V> m) {
init(m);
}
public EnumMap(Map<K, ? extends V> m) {
if (m instanceof EnumMap) {
init((EnumMap<K, ? extends V>) m);
} else {
checkArgument(!m.isEmpty(), "Specified map is empty");
init(m.keySet().iterator().next().getDeclaringClass());
putAll(m);
}
}
@SuppressWarnings("unchecked")
@Override
public void clear() {
keySet.clear();
values = (V[]) new Object[values.length];
}
public EnumMap<K, V> clone() {
return new EnumMap<K, V>(this);
}
@Override
public boolean containsKey(Object key) {
return keySet.contains(key);
}
@Override
public boolean containsValue(Object value) {
for (K key : keySet) {
if (Objects.equals(value, values[key.ordinal()])) {
return true;
}
}
return false;
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
return new EntrySet();
}
@Override
public V get(Object k) {
return keySet.contains(k) ? values[asOrdinal(k)] : null;
}
@Override
public V put(K key, V value) {
keySet.add(key);
return ArrayHelper.setAt(values, key.ordinal(), value);
}
@Override
public V remove(Object key) {
return keySet.remove(key) ? ArrayHelper.setAt(values, asOrdinal(key), null) : null;
}
@Override
public int size() {
return keySet.size();
}
/**
* Returns <code>key</code> as <code>K</code>. Only runtime checks that
* key is an Enum, not that it's the particular Enum K. Should only be called
* when you are sure <code>key</code> is of type <code>K</code>.
*/
@SuppressWarnings("unchecked")
private K asKey(Object key) {
return (K) key;
}
private int asOrdinal(Object key) {
return asKey(key).ordinal();
}
@SuppressWarnings("unchecked")
private void init(Class<K> type) {
keySet = EnumSet.noneOf(type);
values = (V[]) new Object[keySet.capacity()];
}
private void init(EnumMap<K, ? extends V> m) {
keySet = m.keySet.clone();
values = ArrayHelper.clone(m.values);
}
}