001 /*
002 * Copyright (C) 2007 Google Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package com.google.common.collect;
018
019 import static com.google.common.base.Preconditions.checkArgument;
020
021 import java.io.IOException;
022 import java.io.ObjectInputStream;
023 import java.io.ObjectOutputStream;
024 import java.util.EnumMap;
025 import java.util.Map;
026
027 /**
028 * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
029 * are not permitted. An {@code EnumBiMap} and its inverse are both
030 * serializable.
031 *
032 * @author Mike Bostock
033 * @since 2 (imported from Google Collections Library)
034 */
035 public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>>
036 extends AbstractBiMap<K, V> {
037 private transient Class<K> keyType;
038 private transient Class<V> valueType;
039
040 /**
041 * Returns a new, empty {@code EnumBiMap} using the specified key and value
042 * types.
043 *
044 * @param keyType the key type
045 * @param valueType the value type
046 */
047 public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
048 create(Class<K> keyType, Class<V> valueType) {
049 return new EnumBiMap<K, V>(keyType, valueType);
050 }
051
052 /**
053 * Returns a new bimap with the same mappings as the specified map. If the
054 * specified map is an {@code EnumBiMap}, the new bimap has the same types as
055 * the provided map. Otherwise, the specified map must contain at least one
056 * mapping, in order to determine the key and value types.
057 *
058 * @param map the map whose mappings are to be placed in this map
059 * @throws IllegalArgumentException if map is not an {@code EnumBiMap}
060 * instance and contains no mappings
061 */
062 public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
063 create(Map<K, V> map) {
064 EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map));
065 bimap.putAll(map);
066 return bimap;
067 }
068
069 private EnumBiMap(Class<K> keyType, Class<V> valueType) {
070 super(new EnumMap<K, V>(keyType), new EnumMap<V, K>(valueType));
071 this.keyType = keyType;
072 this.valueType = valueType;
073 }
074
075 static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) {
076 if (map instanceof EnumBiMap) {
077 return ((EnumBiMap<K, ?>) map).keyType();
078 }
079 if (map instanceof EnumHashBiMap) {
080 return ((EnumHashBiMap<K, ?>) map).keyType();
081 }
082 checkArgument(!map.isEmpty());
083 return map.keySet().iterator().next().getDeclaringClass();
084 }
085
086 private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) {
087 if (map instanceof EnumBiMap) {
088 return ((EnumBiMap<?, V>) map).valueType;
089 }
090 checkArgument(!map.isEmpty());
091 return map.values().iterator().next().getDeclaringClass();
092 }
093
094 /** Returns the associated key type. */
095 public Class<K> keyType() {
096 return keyType;
097 }
098
099 /** Returns the associated value type. */
100 public Class<V> valueType() {
101 return valueType;
102 }
103
104 /**
105 * @serialData the key class, value class, number of entries, first key, first
106 * value, second key, second value, and so on.
107 */
108 private void writeObject(ObjectOutputStream stream) throws IOException {
109 stream.defaultWriteObject();
110 stream.writeObject(keyType);
111 stream.writeObject(valueType);
112 Serialization.writeMap(this, stream);
113 }
114
115 @SuppressWarnings("unchecked") // reading fields populated by writeObject
116 private void readObject(ObjectInputStream stream)
117 throws IOException, ClassNotFoundException {
118 stream.defaultReadObject();
119 keyType = (Class<K>) stream.readObject();
120 valueType = (Class<V>) stream.readObject();
121 setDelegates(new EnumMap<K, V>(keyType), new EnumMap<V, K>(valueType));
122 Serialization.populateMap(this, stream);
123 }
124
125 private static final long serialVersionUID = 0;
126 }