001/* 002 * Copyright (C) 2007 The Guava Authors 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 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.GwtIncompatible; 024 025import java.io.IOException; 026import java.io.ObjectInputStream; 027import java.io.ObjectOutputStream; 028import java.util.EnumMap; 029import java.util.Map; 030 031/** 032 * A {@code BiMap} backed by two {@code EnumMap} instances. Null keys and values 033 * are not permitted. An {@code EnumBiMap} and its inverse are both 034 * serializable. 035 * 036 * <p>See the Guava User Guide article on <a href= 037 * "https://github.com/google/guava/wiki/NewCollectionTypesExplained#bimap"> 038 * {@code BiMap}</a>. 039 * 040 * @author Mike Bostock 041 * @since 2.0 042 */ 043@GwtCompatible(emulated = true) 044public final class EnumBiMap<K extends Enum<K>, V extends Enum<V>> extends AbstractBiMap<K, V> { 045 private transient Class<K> keyType; 046 private transient Class<V> valueType; 047 048 /** 049 * Returns a new, empty {@code EnumBiMap} using the specified key and value 050 * types. 051 * 052 * @param keyType the key type 053 * @param valueType the value type 054 */ 055 public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V> create( 056 Class<K> keyType, Class<V> valueType) { 057 return new EnumBiMap<K, V>(keyType, valueType); 058 } 059 060 /** 061 * Returns a new bimap with the same mappings as the specified map. If the 062 * specified map is an {@code EnumBiMap}, the new bimap has the same types as 063 * the provided map. Otherwise, the specified map must contain at least one 064 * mapping, in order to determine the key and value types. 065 * 066 * @param map the map whose mappings are to be placed in this map 067 * @throws IllegalArgumentException if map is not an {@code EnumBiMap} 068 * instance and contains no mappings 069 */ 070 public static <K extends Enum<K>, V extends Enum<V>> EnumBiMap<K, V> create(Map<K, V> map) { 071 EnumBiMap<K, V> bimap = create(inferKeyType(map), inferValueType(map)); 072 bimap.putAll(map); 073 return bimap; 074 } 075 076 private EnumBiMap(Class<K> keyType, Class<V> valueType) { 077 super( 078 WellBehavedMap.wrap(new EnumMap<K, V>(keyType)), 079 WellBehavedMap.wrap(new EnumMap<V, K>(valueType))); 080 this.keyType = keyType; 081 this.valueType = valueType; 082 } 083 084 static <K extends Enum<K>> Class<K> inferKeyType(Map<K, ?> map) { 085 if (map instanceof EnumBiMap) { 086 return ((EnumBiMap<K, ?>) map).keyType(); 087 } 088 if (map instanceof EnumHashBiMap) { 089 return ((EnumHashBiMap<K, ?>) map).keyType(); 090 } 091 checkArgument(!map.isEmpty()); 092 return map 093 .keySet() 094 .iterator() 095 .next() 096 .getDeclaringClass(); 097 } 098 099 private static <V extends Enum<V>> Class<V> inferValueType(Map<?, V> map) { 100 if (map instanceof EnumBiMap) { 101 return ((EnumBiMap<?, V>) map).valueType; 102 } 103 checkArgument(!map.isEmpty()); 104 return map 105 .values() 106 .iterator() 107 .next() 108 .getDeclaringClass(); 109 } 110 111 /** Returns the associated key type. */ 112 public Class<K> keyType() { 113 return keyType; 114 } 115 116 /** Returns the associated value type. */ 117 public Class<V> valueType() { 118 return valueType; 119 } 120 121 @Override 122 K checkKey(K key) { 123 return checkNotNull(key); 124 } 125 126 @Override 127 V checkValue(V value) { 128 return checkNotNull(value); 129 } 130 131 /** 132 * @serialData the key class, value class, number of entries, first key, first 133 * value, second key, second value, and so on. 134 */ 135 @GwtIncompatible("java.io.ObjectOutputStream") 136 private void writeObject(ObjectOutputStream stream) throws IOException { 137 stream.defaultWriteObject(); 138 stream.writeObject(keyType); 139 stream.writeObject(valueType); 140 Serialization.writeMap(this, stream); 141 } 142 143 @SuppressWarnings("unchecked") // reading fields populated by writeObject 144 @GwtIncompatible("java.io.ObjectInputStream") 145 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 146 stream.defaultReadObject(); 147 keyType = (Class<K>) stream.readObject(); 148 valueType = (Class<V>) stream.readObject(); 149 setDelegates( 150 WellBehavedMap.wrap(new EnumMap<K, V>(keyType)), 151 WellBehavedMap.wrap(new EnumMap<V, K>(valueType))); 152 Serialization.populateMap(this, stream); 153 } 154 155 @GwtIncompatible("not needed in emulated source.") 156 private static final long serialVersionUID = 0; 157}