View Javadoc
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  import java.io.IOException;
25  import java.io.ObjectInputStream;
26  import java.io.ObjectOutputStream;
27  import java.util.EnumMap;
28  import java.util.Map;
29  
30  /**
31   * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values
32   * are not permitted. An {@code EnumBiMap} and its inverse are both
33   * serializable.
34   *
35   * <p>See the Guava User Guide article on <a href=
36   * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap">
37   * {@code BiMap}</a>.
38   *
39   * @author Mike Bostock
40   * @since 2.0
41   */
42  @GwtCompatible(emulated = true)
43  public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>> extends AbstractBiMap<K, V> {
44    private transient Class<K> keyType;
45    private transient Class<V> valueType;
46  
47    /**
48     * Returns a new, empty {@code EnumBiMap} using the specified key and value
49     * types.
50     *
51     * @param keyType the key type
52     * @param valueType the value type
53     */
54    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V> create(
55        Class<K> keyType, Class<V> valueType) {
56      return new EnumBiMap<>(keyType, valueType);
57    }
58  
59    /**
60     * Returns a new bimap with the same mappings as the specified map. If the
61     * specified map is an {@code EnumBiMap}, the new bimap has the same types as
62     * the provided map. Otherwise, the specified map must contain at least one
63     * mapping, in order to determine the key and value types.
64     *
65     * @param map the map whose mappings are to be placed in this map
66     * @throws IllegalArgumentException if map is not an {@code EnumBiMap}
67     *     instance and contains no mappings
68     */
69    public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V> create(Map<K, V> map) {
70      EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map));
71      bimap.putAll(map);
72      return bimap;
73    }
74  
75    private EnumBiMap(Class<K> keyType, Class<V> valueType) {
76      super(
77          WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
78          WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
79      this.keyType = keyType;
80      this.valueType = valueType;
81    }
82  
83    static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) {
84      if (map instanceof EnumBiMap) {
85        return ((EnumBiMap<K, ?>) map).keyType();
86      }
87      if (map instanceof EnumHashBiMap) {
88        return ((EnumHashBiMap<K, ?>) map).keyType();
89      }
90      checkArgument(!map.isEmpty());
91      return map.keySet().iterator().next().getDeclaringClass();
92    }
93  
94    private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) {
95      if (map instanceof EnumBiMap) {
96        return ((EnumBiMap<?, V>) map).valueType;
97      }
98      checkArgument(!map.isEmpty());
99      return map.values().iterator().next().getDeclaringClass();
100   }
101 
102   /** Returns the associated key type. */
103   public Class<K> keyType() {
104     return keyType;
105   }
106 
107   /** Returns the associated value type. */
108   public Class<V> valueType() {
109     return valueType;
110   }
111 
112   @Override
113   K checkKey(K key) {
114     return checkNotNull(key);
115   }
116 
117   @Override
118   V checkValue(V value) {
119     return checkNotNull(value);
120   }
121 
122   /**
123    * @serialData the key class, value class, number of entries, first key, first
124    *     value, second key, second value, and so on.
125    */
126   @GwtIncompatible // java.io.ObjectOutputStream
127   private void writeObject(ObjectOutputStream stream) throws IOException {
128     stream.defaultWriteObject();
129     stream.writeObject(keyType);
130     stream.writeObject(valueType);
131     Serialization.writeMap(this, stream);
132   }
133 
134   @SuppressWarnings("unchecked") // reading fields populated by writeObject
135   @GwtIncompatible // java.io.ObjectInputStream
136   private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
137     stream.defaultReadObject();
138     keyType = (Class<K>) stream.readObject();
139     valueType = (Class<V>) stream.readObject();
140     setDelegates(
141         WellBehavedMap.wrap(new EnumMap<K, V>(keyType)),
142         WellBehavedMap.wrap(new EnumMap<V, K>(valueType)));
143     Serialization.populateMap(this, stream);
144   }
145 
146   @GwtIncompatible // not needed in emulated source.
147   private static final long serialVersionUID = 0;
148 }