1 /*
2  * Copyright (C) 2007 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 
22 import com.google.common.annotations.GwtCompatible;
23 import com.google.common.annotations.GwtIncompatible;
24 
25 import java.io.IOException;
26 import java.io.ObjectInputStream;
27 import java.io.ObjectOutputStream;
28 import java.util.EnumMap;
29 import java.util.Map;
30 
31 /**
32  * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
33  * are not permitted. An {@code EnumBiMap} and its inverse are both
34  * serializable.
35  *
36  * <p>See the Guava User Guide article on <a href=
37  * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap">
38  * {@code BiMap}</a>.
39  *
40  * @author Mike Bostock
41  * @since 2.0 (imported from Google Collections Library)
42  */
43 @GwtCompatible(emulated = true)
44 public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>>
45     extends AbstractBiMap<K, V> {
46   private transient Class<K> keyType;
47   private transient Class<V> valueType;
48 
49   /**
50    * Returns a new, empty {@code EnumBiMap} using the specified key and value
51    * types.
52    *
53    * @param keyType the key type
54    * @param valueType the value type
55    */
56   public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
create(Class<K> keyType, Class<V> valueType)57       create(Class<K> keyType, Class<V> valueType) {
58     return new EnumBiMap<K, V>(keyType, valueType);
59   }
60 
61   /**
62    * Returns a new bimap with the same mappings as the specified map. If the
63    * specified map is an {@code EnumBiMap}, the new bimap has the same types as
64    * the provided map. Otherwise, the specified map must contain at least one
65    * mapping, in order to determine the key and value types.
66    *
67    * @param map the map whose mappings are to be placed in this map
68    * @throws IllegalArgumentException if map is not an {@code EnumBiMap}
69    *     instance and contains no mappings
70    */
71   public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V>
create(Map<K, V> map)72       create(Map<K, V> map) {
73     EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map));
74     bimap.putAll(map);
75     return bimap;
76   }
77 
EnumBiMap(Class<K> keyType, Class<V> valueType)78   private EnumBiMap(Class<K> keyType, Class<V> valueType) {
79     super(WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
80         WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
81     this.keyType = keyType;
82     this.valueType = valueType;
83   }
84 
inferKeyType(Map<K, ?> map)85   static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) {
86     if (map instanceof EnumBiMap) {
87       return ((EnumBiMap<K, ?>) map).keyType();
88     }
89     if (map instanceof EnumHashBiMap) {
90       return ((EnumHashBiMap<K, ?>) map).keyType();
91     }
92     checkArgument(!map.isEmpty());
93     return map.keySet().iterator().next().getDeclaringClass();
94   }
95 
inferValueType(Map<?, V> map)96   private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) {
97     if (map instanceof EnumBiMap) {
98       return ((EnumBiMap<?, V>) map).valueType;
99     }
100     checkArgument(!map.isEmpty());
101     return map.values().iterator().next().getDeclaringClass();
102   }
103 
104   /** Returns the associated key type. */
keyType()105   public Class<K> keyType() {
106     return keyType;
107   }
108 
109   /** Returns the associated value type. */
valueType()110   public Class<V> valueType() {
111     return valueType;
112   }
113 
114   @Override
checkKey(K key)115   K checkKey(K key) {
116     return checkNotNull(key);
117   }
118 
119   @Override
checkValue(V value)120   V checkValue(V value) {
121     return checkNotNull(value);
122   }
123 
124   /**
125    * @serialData the key class, value class, number of entries, first key, first
126    *     value, second key, second value, and so on.
127    */
128   @GwtIncompatible("java.io.ObjectOutputStream")
writeObject(ObjectOutputStream stream)129   private void writeObject(ObjectOutputStream stream) throws IOException {
130     stream.defaultWriteObject();
131     stream.writeObject(keyType);
132     stream.writeObject(valueType);
133     Serialization.writeMap(this, stream);
134   }
135 
136   @SuppressWarnings("unchecked") // reading fields populated by writeObject
137   @GwtIncompatible("java.io.ObjectInputStream")
readObject(ObjectInputStream stream)138   private void readObject(ObjectInputStream stream)
139       throws IOException, ClassNotFoundException {
140     stream.defaultReadObject();
141     keyType = (Class<K>) stream.readObject();
142     valueType = (Class<V>) stream.readObject();
143     setDelegates(
144         WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
145         WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
146     Serialization.populateMap(this, stream);
147   }
148 
149   @GwtIncompatible("not needed in emulated source.")
150   private static final long serialVersionUID = 0;
151 }
152