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
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.common.base.Preconditions.checkState;
21import static com.google.common.collect.CollectPreconditions.checkRemove;
22
23import com.google.common.annotations.GwtCompatible;
24import com.google.common.base.Objects;
25
26import java.io.Serializable;
27import java.util.Collection;
28import java.util.Iterator;
29import java.util.Map;
30import java.util.Set;
31
32import javax.annotation.Nullable;
33
34/**
35 * A general-purpose bimap implementation using any two backing {@code Map}
36 * instances.
37 *
38 * <p>Note that this class contains {@code equals()} calls that keep it from
39 * supporting {@code IdentityHashMap} backing maps.
40 *
41 * @author Kevin Bourrillion
42 * @author Mike Bostock
43 */
44@GwtCompatible(emulated = true)
45abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V>
46    implements BiMap<K, V>, Serializable {
47
48  private transient Map<K, V> delegate;
49  transient AbstractBiMap<V, K> inverse;
50
51  /** Package-private constructor for creating a map-backed bimap. */
52  AbstractBiMap(Map<K, V> forward, Map<V, K> backward) {
53    setDelegates(forward, backward);
54  }
55
56  /** Private constructor for inverse bimap. */
57  private AbstractBiMap(Map<K, V> backward, AbstractBiMap<V, K> forward) {
58    delegate = backward;
59    inverse = forward;
60  }
61
62  @Override protected Map<K, V> delegate() {
63    return delegate;
64  }
65
66  /**
67   * Returns its input, or throws an exception if this is not a valid key.
68   */
69  K checkKey(@Nullable K key) {
70    return key;
71  }
72
73  /**
74   * Returns its input, or throws an exception if this is not a valid value.
75   */
76  V checkValue(@Nullable V value) {
77    return value;
78  }
79
80  /**
81   * Specifies the delegate maps going in each direction. Called by the
82   * constructor and by subclasses during deserialization.
83   */
84  void setDelegates(Map<K, V> forward, Map<V, K> backward) {
85    checkState(delegate == null);
86    checkState(inverse == null);
87    checkArgument(forward.isEmpty());
88    checkArgument(backward.isEmpty());
89    checkArgument(forward != backward);
90    delegate = forward;
91    inverse = new Inverse<V, K>(backward, this);
92  }
93
94  void setInverse(AbstractBiMap<V, K> inverse) {
95    this.inverse = inverse;
96  }
97
98  // Query Operations (optimizations)
99
100  @Override public boolean containsValue(@Nullable Object value) {
101    return inverse.containsKey(value);
102  }
103
104  // Modification Operations
105
106  @Override public V put(@Nullable K key, @Nullable V value) {
107    return putInBothMaps(key, value, false);
108  }
109
110  @Override
111  public V forcePut(@Nullable K key, @Nullable V value) {
112    return putInBothMaps(key, value, true);
113  }
114
115  private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) {
116    checkKey(key);
117    checkValue(value);
118    boolean containedKey = containsKey(key);
119    if (containedKey && Objects.equal(value, get(key))) {
120      return value;
121    }
122    if (force) {
123      inverse().remove(value);
124    } else {
125      checkArgument(!containsValue(value), "value already present: %s", value);
126    }
127    V oldValue = delegate.put(key, value);
128    updateInverseMap(key, containedKey, oldValue, value);
129    return oldValue;
130  }
131
132  private void updateInverseMap(
133      K key, boolean containedKey, V oldValue, V newValue) {
134    if (containedKey) {
135      removeFromInverseMap(oldValue);
136    }
137    inverse.delegate.put(newValue, key);
138  }
139
140  @Override public V remove(@Nullable Object key) {
141    return containsKey(key) ? removeFromBothMaps(key) : null;
142  }
143
144  private V removeFromBothMaps(Object key) {
145    V oldValue = delegate.remove(key);
146    removeFromInverseMap(oldValue);
147    return oldValue;
148  }
149
150  private void removeFromInverseMap(V oldValue) {
151    inverse.delegate.remove(oldValue);
152  }
153
154  // Bulk Operations
155
156  @Override public void putAll(Map<? extends K, ? extends V> map) {
157    for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
158      put(entry.getKey(), entry.getValue());
159    }
160  }
161
162  @Override public void clear() {
163    delegate.clear();
164    inverse.delegate.clear();
165  }
166
167  // Views
168
169  @Override
170  public BiMap<V, K> inverse() {
171    return inverse;
172  }
173
174  private transient Set<K> keySet;
175
176  @Override public Set<K> keySet() {
177    Set<K> result = keySet;
178    return (result == null) ? keySet = new KeySet() : result;
179  }
180
181  private class KeySet extends ForwardingSet<K> {
182    @Override protected Set<K> delegate() {
183      return delegate.keySet();
184    }
185
186    @Override public void clear() {
187      AbstractBiMap.this.clear();
188    }
189
190    @Override public boolean remove(Object key) {
191      if (!contains(key)) {
192        return false;
193      }
194      removeFromBothMaps(key);
195      return true;
196    }
197
198    @Override public boolean removeAll(Collection<?> keysToRemove) {
199      return standardRemoveAll(keysToRemove);
200    }
201
202    @Override public boolean retainAll(Collection<?> keysToRetain) {
203      return standardRetainAll(keysToRetain);
204    }
205
206    @Override public Iterator<K> iterator() {
207      return Maps.keyIterator(entrySet().iterator());
208    }
209  }
210
211  private transient Set<V> valueSet;
212
213  @Override public Set<V> values() {
214    /*
215     * We can almost reuse the inverse's keySet, except we have to fix the
216     * iteration order so that it is consistent with the forward map.
217     */
218    Set<V> result = valueSet;
219    return (result == null) ? valueSet = new ValueSet() : result;
220  }
221
222  private class ValueSet extends ForwardingSet<V> {
223    final Set<V> valuesDelegate = inverse.keySet();
224
225    @Override protected Set<V> delegate() {
226      return valuesDelegate;
227    }
228
229    @Override public Iterator<V> iterator() {
230      return Maps.valueIterator(entrySet().iterator());
231    }
232
233    @Override public Object[] toArray() {
234      return standardToArray();
235    }
236
237    @Override public <T> T[] toArray(T[] array) {
238      return standardToArray(array);
239    }
240
241    @Override public String toString() {
242      return standardToString();
243    }
244  }
245
246  private transient Set<Entry<K, V>> entrySet;
247
248  @Override public Set<Entry<K, V>> entrySet() {
249    Set<Entry<K, V>> result = entrySet;
250    return (result == null) ? entrySet = new EntrySet() : result;
251  }
252
253  private class EntrySet extends ForwardingSet<Entry<K, V>> {
254    final Set<Entry<K, V>> esDelegate = delegate.entrySet();
255
256    @Override protected Set<Entry<K, V>> delegate() {
257      return esDelegate;
258    }
259
260    @Override public void clear() {
261      AbstractBiMap.this.clear();
262    }
263
264    @Override public boolean remove(Object object) {
265      if (!esDelegate.contains(object)) {
266        return false;
267      }
268
269      // safe because esDelgate.contains(object).
270      Entry<?, ?> entry = (Entry<?, ?>) object;
271      inverse.delegate.remove(entry.getValue());
272      /*
273       * Remove the mapping in inverse before removing from esDelegate because
274       * if entry is part of esDelegate, entry might be invalidated after the
275       * mapping is removed from esDelegate.
276       */
277      esDelegate.remove(entry);
278      return true;
279    }
280
281    @Override public Iterator<Entry<K, V>> iterator() {
282      final Iterator<Entry<K, V>> iterator = esDelegate.iterator();
283      return new Iterator<Entry<K, V>>() {
284        Entry<K, V> entry;
285
286        @Override public boolean hasNext() {
287          return iterator.hasNext();
288        }
289
290        @Override public Entry<K, V> next() {
291          entry = iterator.next();
292          final Entry<K, V> finalEntry = entry;
293
294          return new ForwardingMapEntry<K, V>() {
295            @Override protected Entry<K, V> delegate() {
296              return finalEntry;
297            }
298
299            @Override public V setValue(V value) {
300              // Preconditions keep the map and inverse consistent.
301              checkState(contains(this), "entry no longer in map");
302              // similar to putInBothMaps, but set via entry
303              if (Objects.equal(value, getValue())) {
304                return value;
305              }
306              checkArgument(!containsValue(value),
307                  "value already present: %s", value);
308              V oldValue = finalEntry.setValue(value);
309              checkState(Objects.equal(value, get(getKey())),
310                  "entry no longer in map");
311              updateInverseMap(getKey(), true, oldValue, value);
312              return oldValue;
313            }
314          };
315        }
316
317        @Override public void remove() {
318          checkRemove(entry != null);
319          V value = entry.getValue();
320          iterator.remove();
321          removeFromInverseMap(value);
322        }
323      };
324    }
325
326    // See java.util.Collections.CheckedEntrySet for details on attacks.
327
328    @Override public Object[] toArray() {
329      return standardToArray();
330    }
331    @Override public <T> T[] toArray(T[] array) {
332      return standardToArray(array);
333    }
334    @Override public boolean contains(Object o) {
335      return Maps.containsEntryImpl(delegate(), o);
336    }
337    @Override public boolean containsAll(Collection<?> c) {
338      return standardContainsAll(c);
339    }
340    @Override public boolean removeAll(Collection<?> c) {
341      return standardRemoveAll(c);
342    }
343    @Override public boolean retainAll(Collection<?> c) {
344      return standardRetainAll(c);
345    }
346  }
347
348  /** The inverse of any other {@code AbstractBiMap} subclass. */
349  private static class Inverse<K, V> extends AbstractBiMap<K, V> {
350    private Inverse(Map<K, V> backward, AbstractBiMap<V, K> forward) {
351      super(backward, forward);
352    }
353
354    /*
355     * Serialization stores the forward bimap, the inverse of this inverse.
356     * Deserialization calls inverse() on the forward bimap and returns that
357     * inverse.
358     *
359     * If a bimap and its inverse are serialized together, the deserialized
360     * instances have inverse() methods that return the other.
361     */
362
363    @Override
364    K checkKey(K key) {
365      return inverse.checkValue(key);
366    }
367
368    @Override
369    V checkValue(V value) {
370      return inverse.checkKey(value);
371    }
372  }
373}
374
375