Coverage Summary for Class: AbstractBiMap (com.google.common.collect)
| Class | Method, % | Line, % |
|---|---|---|
| AbstractBiMap | 0% (0/25) | 0% (0/69) |
| AbstractBiMap$1 | 0% (0/4) | 0% (0/9) |
| AbstractBiMap$BiMapEntry | 0% (0/3) | 0% (0/12) |
| AbstractBiMap$EntrySet | 0% (0/12) | 0% (0/17) |
| AbstractBiMap$Inverse | 0% (0/6) | 0% (0/8) |
| AbstractBiMap$KeySet | 0% (0/7) | 0% (0/10) |
| AbstractBiMap$ValueSet | 0% (0/7) | 0% (0/7) |
| Total | 0% (0/64) | 0% (0/132) |
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.checkState; 21 import static com.google.common.collect.CollectPreconditions.checkRemove; 22 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.annotations.GwtIncompatible; 25 import com.google.common.base.Objects; 26 import com.google.errorprone.annotations.CanIgnoreReturnValue; 27 import com.google.j2objc.annotations.RetainedWith; 28 import com.google.j2objc.annotations.WeakOuter; 29 import java.io.IOException; 30 import java.io.ObjectInputStream; 31 import java.io.ObjectOutputStream; 32 import java.io.Serializable; 33 import java.util.Collection; 34 import java.util.Iterator; 35 import java.util.Map; 36 import java.util.Set; 37 import java.util.function.BiFunction; 38 import org.checkerframework.checker.nullness.qual.Nullable; 39 40 /** 41 * A general-purpose bimap implementation using any two backing {@code Map} instances. 42 * 43 * <p>Note that this class contains {@code equals()} calls that keep it from supporting {@code 44 * IdentityHashMap} backing maps. 45 * 46 * @author Kevin Bourrillion 47 * @author Mike Bostock 48 */ 49 @GwtCompatible(emulated = true) 50 abstract class AbstractBiMap<K, V> extends ForwardingMap<K, V> 51 implements BiMap<K, V>, Serializable { 52 53 private transient @Nullable Map<K, V> delegate; 54 @RetainedWith transient @Nullable AbstractBiMap<V, K> inverse; 55 56 /** Package-private constructor for creating a map-backed bimap. */ 57 AbstractBiMap(Map<K, V> forward, Map<V, K> backward) { 58 setDelegates(forward, backward); 59 } 60 61 /** Private constructor for inverse bimap. */ 62 private AbstractBiMap(Map<K, V> backward, AbstractBiMap<V, K> forward) { 63 delegate = backward; 64 inverse = forward; 65 } 66 67 @Override 68 protected Map<K, V> delegate() { 69 return delegate; 70 } 71 72 /** Returns its input, or throws an exception if this is not a valid key. */ 73 @CanIgnoreReturnValue 74 K checkKey(@Nullable K key) { 75 return key; 76 } 77 78 /** Returns its input, or throws an exception if this is not a valid value. */ 79 @CanIgnoreReturnValue 80 V checkValue(@Nullable V value) { 81 return value; 82 } 83 84 /** 85 * Specifies the delegate maps going in each direction. Called by the constructor and by 86 * subclasses during deserialization. 87 */ 88 void setDelegates(Map<K, V> forward, Map<V, K> backward) { 89 checkState(delegate == null); 90 checkState(inverse == null); 91 checkArgument(forward.isEmpty()); 92 checkArgument(backward.isEmpty()); 93 checkArgument(forward != backward); 94 delegate = forward; 95 inverse = makeInverse(backward); 96 } 97 98 AbstractBiMap<V, K> makeInverse(Map<V, K> backward) { 99 return new Inverse<>(backward, this); 100 } 101 102 void setInverse(AbstractBiMap<V, K> inverse) { 103 this.inverse = inverse; 104 } 105 106 // Query Operations (optimizations) 107 108 @Override 109 public boolean containsValue(@Nullable Object value) { 110 return inverse.containsKey(value); 111 } 112 113 // Modification Operations 114 115 @CanIgnoreReturnValue 116 @Override 117 public V put(@Nullable K key, @Nullable V value) { 118 return putInBothMaps(key, value, false); 119 } 120 121 @CanIgnoreReturnValue 122 @Override 123 public V forcePut(@Nullable K key, @Nullable V value) { 124 return putInBothMaps(key, value, true); 125 } 126 127 private V putInBothMaps(@Nullable K key, @Nullable V value, boolean force) { 128 checkKey(key); 129 checkValue(value); 130 boolean containedKey = containsKey(key); 131 if (containedKey && Objects.equal(value, get(key))) { 132 return value; 133 } 134 if (force) { 135 inverse().remove(value); 136 } else { 137 checkArgument(!containsValue(value), "value already present: %s", value); 138 } 139 V oldValue = delegate.put(key, value); 140 updateInverseMap(key, containedKey, oldValue, value); 141 return oldValue; 142 } 143 144 private void updateInverseMap(K key, boolean containedKey, V oldValue, V newValue) { 145 if (containedKey) { 146 removeFromInverseMap(oldValue); 147 } 148 inverse.delegate.put(newValue, key); 149 } 150 151 @CanIgnoreReturnValue 152 @Override 153 public V remove(@Nullable Object key) { 154 return containsKey(key) ? removeFromBothMaps(key) : null; 155 } 156 157 @CanIgnoreReturnValue 158 private V removeFromBothMaps(Object key) { 159 V oldValue = delegate.remove(key); 160 removeFromInverseMap(oldValue); 161 return oldValue; 162 } 163 164 private void removeFromInverseMap(V oldValue) { 165 inverse.delegate.remove(oldValue); 166 } 167 168 // Bulk Operations 169 170 @Override 171 public void putAll(Map<? extends K, ? extends V> map) { 172 for (Entry<? extends K, ? extends V> entry : map.entrySet()) { 173 put(entry.getKey(), entry.getValue()); 174 } 175 } 176 177 @Override 178 public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { 179 this.delegate.replaceAll(function); 180 inverse.delegate.clear(); 181 Entry<K, V> broken = null; 182 Iterator<Entry<K, V>> itr = this.delegate.entrySet().iterator(); 183 while (itr.hasNext()) { 184 Entry<K, V> entry = itr.next(); 185 K k = entry.getKey(); 186 V v = entry.getValue(); 187 K conflict = inverse.delegate.putIfAbsent(v, k); 188 if (conflict != null) { 189 broken = entry; 190 // We're definitely going to throw, but we'll try to keep the BiMap in an internally 191 // consistent state by removing the bad entry. 192 itr.remove(); 193 } 194 } 195 if (broken != null) { 196 throw new IllegalArgumentException("value already present: " + broken.getValue()); 197 } 198 } 199 200 @Override 201 public void clear() { 202 delegate.clear(); 203 inverse.delegate.clear(); 204 } 205 206 // Views 207 208 @Override 209 public BiMap<V, K> inverse() { 210 return inverse; 211 } 212 213 private transient @Nullable Set<K> keySet; 214 215 @Override 216 public Set<K> keySet() { 217 Set<K> result = keySet; 218 return (result == null) ? keySet = new KeySet() : result; 219 } 220 221 @WeakOuter 222 private class KeySet extends ForwardingSet<K> { 223 @Override 224 protected Set<K> delegate() { 225 return delegate.keySet(); 226 } 227 228 @Override 229 public void clear() { 230 AbstractBiMap.this.clear(); 231 } 232 233 @Override 234 public boolean remove(Object key) { 235 if (!contains(key)) { 236 return false; 237 } 238 removeFromBothMaps(key); 239 return true; 240 } 241 242 @Override 243 public boolean removeAll(Collection<?> keysToRemove) { 244 return standardRemoveAll(keysToRemove); 245 } 246 247 @Override 248 public boolean retainAll(Collection<?> keysToRetain) { 249 return standardRetainAll(keysToRetain); 250 } 251 252 @Override 253 public Iterator<K> iterator() { 254 return Maps.keyIterator(entrySet().iterator()); 255 } 256 } 257 258 private transient @Nullable Set<V> valueSet; 259 260 @Override 261 public Set<V> values() { 262 /* 263 * We can almost reuse the inverse's keySet, except we have to fix the 264 * iteration order so that it is consistent with the forward map. 265 */ 266 Set<V> result = valueSet; 267 return (result == null) ? valueSet = new ValueSet() : result; 268 } 269 270 @WeakOuter 271 private class ValueSet extends ForwardingSet<V> { 272 final Set<V> valuesDelegate = inverse.keySet(); 273 274 @Override 275 protected Set<V> delegate() { 276 return valuesDelegate; 277 } 278 279 @Override 280 public Iterator<V> iterator() { 281 return Maps.valueIterator(entrySet().iterator()); 282 } 283 284 @Override 285 public Object[] toArray() { 286 return standardToArray(); 287 } 288 289 @Override 290 public <T> T[] toArray(T[] array) { 291 return standardToArray(array); 292 } 293 294 @Override 295 public String toString() { 296 return standardToString(); 297 } 298 } 299 300 private transient @Nullable Set<Entry<K, V>> entrySet; 301 302 @Override 303 public Set<Entry<K, V>> entrySet() { 304 Set<Entry<K, V>> result = entrySet; 305 return (result == null) ? entrySet = new EntrySet() : result; 306 } 307 308 class BiMapEntry extends ForwardingMapEntry<K, V> { 309 private final Entry<K, V> delegate; 310 311 BiMapEntry(Entry<K, V> delegate) { 312 this.delegate = delegate; 313 } 314 315 @Override 316 protected Entry<K, V> delegate() { 317 return delegate; 318 } 319 320 @Override 321 public V setValue(V value) { 322 checkValue(value); 323 // Preconditions keep the map and inverse consistent. 324 checkState(entrySet().contains(this), "entry no longer in map"); 325 // similar to putInBothMaps, but set via entry 326 if (Objects.equal(value, getValue())) { 327 return value; 328 } 329 checkArgument(!containsValue(value), "value already present: %s", value); 330 V oldValue = delegate.setValue(value); 331 checkState(Objects.equal(value, get(getKey())), "entry no longer in map"); 332 updateInverseMap(getKey(), true, oldValue, value); 333 return oldValue; 334 } 335 } 336 337 Iterator<Entry<K, V>> entrySetIterator() { 338 final Iterator<Entry<K, V>> iterator = delegate.entrySet().iterator(); 339 return new Iterator<Entry<K, V>>() { 340 @Nullable Entry<K, V> entry; 341 342 @Override 343 public boolean hasNext() { 344 return iterator.hasNext(); 345 } 346 347 @Override 348 public Entry<K, V> next() { 349 entry = iterator.next(); 350 return new BiMapEntry(entry); 351 } 352 353 @Override 354 public void remove() { 355 checkRemove(entry != null); 356 V value = entry.getValue(); 357 iterator.remove(); 358 removeFromInverseMap(value); 359 entry = null; 360 } 361 }; 362 } 363 364 @WeakOuter 365 private class EntrySet extends ForwardingSet<Entry<K, V>> { 366 final Set<Entry<K, V>> esDelegate = delegate.entrySet(); 367 368 @Override 369 protected Set<Entry<K, V>> delegate() { 370 return esDelegate; 371 } 372 373 @Override 374 public void clear() { 375 AbstractBiMap.this.clear(); 376 } 377 378 @Override 379 public boolean remove(Object object) { 380 if (!esDelegate.contains(object)) { 381 return false; 382 } 383 384 // safe because esDelegate.contains(object). 385 Entry<?, ?> entry = (Entry<?, ?>) object; 386 inverse.delegate.remove(entry.getValue()); 387 /* 388 * Remove the mapping in inverse before removing from esDelegate because 389 * if entry is part of esDelegate, entry might be invalidated after the 390 * mapping is removed from esDelegate. 391 */ 392 esDelegate.remove(entry); 393 return true; 394 } 395 396 @Override 397 public Iterator<Entry<K, V>> iterator() { 398 return entrySetIterator(); 399 } 400 401 // See java.util.Collections.CheckedEntrySet for details on attacks. 402 403 @Override 404 public Object[] toArray() { 405 return standardToArray(); 406 } 407 408 @Override 409 public <T> T[] toArray(T[] array) { 410 return standardToArray(array); 411 } 412 413 @Override 414 public boolean contains(Object o) { 415 return Maps.containsEntryImpl(delegate(), o); 416 } 417 418 @Override 419 public boolean containsAll(Collection<?> c) { 420 return standardContainsAll(c); 421 } 422 423 @Override 424 public boolean removeAll(Collection<?> c) { 425 return standardRemoveAll(c); 426 } 427 428 @Override 429 public boolean retainAll(Collection<?> c) { 430 return standardRetainAll(c); 431 } 432 } 433 434 /** The inverse of any other {@code AbstractBiMap} subclass. */ 435 static class Inverse<K, V> extends AbstractBiMap<K, V> { 436 Inverse(Map<K, V> backward, AbstractBiMap<V, K> forward) { 437 super(backward, forward); 438 } 439 440 /* 441 * Serialization stores the forward bimap, the inverse of this inverse. 442 * Deserialization calls inverse() on the forward bimap and returns that 443 * inverse. 444 * 445 * If a bimap and its inverse are serialized together, the deserialized 446 * instances have inverse() methods that return the other. 447 */ 448 449 @Override 450 K checkKey(K key) { 451 return inverse.checkValue(key); 452 } 453 454 @Override 455 V checkValue(V value) { 456 return inverse.checkKey(value); 457 } 458 459 /** @serialData the forward bimap */ 460 @GwtIncompatible // java.io.ObjectOutputStream 461 private void writeObject(ObjectOutputStream stream) throws IOException { 462 stream.defaultWriteObject(); 463 stream.writeObject(inverse()); 464 } 465 466 @GwtIncompatible // java.io.ObjectInputStream 467 @SuppressWarnings("unchecked") // reading data stored by writeObject 468 private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { 469 stream.defaultReadObject(); 470 setInverse((AbstractBiMap<V, K>) stream.readObject()); 471 } 472 473 @GwtIncompatible // Not needed in the emulated source. 474 Object readResolve() { 475 return inverse().inverse(); 476 } 477 478 @GwtIncompatible // Not needed in emulated source. 479 private static final long serialVersionUID = 0; 480 } 481 482 @GwtIncompatible // Not needed in emulated source. 483 private static final long serialVersionUID = 0; 484 }