Coverage Summary for Class: StandardTable (com.google.common.collect)
| Class | Method, % | Line, % |
|---|---|---|
| StandardTable | 36.7% (11/30) | 32.9% (26/79) |
| StandardTable$CellIterator | 75% (3/4) | 64.3% (9/14) |
| StandardTable$Column | 0% (0/9) | 0% (0/22) |
| StandardTable$Column$EntrySet | 0% (0/8) | 0% (0/19) |
| StandardTable$Column$EntrySetIterator | 0% (0/3) | 0% (0/8) |
| StandardTable$Column$EntrySetIterator$1EntryImpl | 0% (0/4) | 0% (0/4) |
| StandardTable$Column$KeySet | 0% (0/4) | 0% (0/5) |
| StandardTable$Column$Values | 0% (0/4) | 0% (0/5) |
| StandardTable$ColumnKeyIterator | 0% (0/3) | 0% (0/12) |
| StandardTable$ColumnKeySet | 0% (0/7) | 0% (0/38) |
| StandardTable$ColumnMap | 0% (0/7) | 0% (0/7) |
| StandardTable$ColumnMap$ColumnMapEntrySet | 0% (0/7) | 0% (0/25) |
| StandardTable$ColumnMap$ColumnMapEntrySet$1 | 0% (0/2) | 0% (0/2) |
| StandardTable$ColumnMap$ColumnMapValues | 0% (0/4) | 0% (0/24) |
| StandardTable$Row | 38.5% (5/13) | 25.6% (11/43) |
| StandardTable$Row$1 | 75% (3/4) | 50% (3/6) |
| StandardTable$Row$2 | 50% (2/4) | 50% (2/4) |
| StandardTable$RowMap | 60% (3/5) | 60% (3/5) |
| StandardTable$RowMap$EntrySet | 40% (2/5) | 18.8% (3/16) |
| StandardTable$RowMap$EntrySet$1 | 100% (2/2) | 100% (2/2) |
| StandardTable$TableSet | 33.3% (1/3) | 25% (1/4) |
| Total | 24.2% (32/132) | 17.4% (60/344) |
1 /* 2 * Copyright (C) 2008 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.checkNotNull; 20 import static com.google.common.base.Predicates.alwaysTrue; 21 import static com.google.common.base.Predicates.equalTo; 22 import static com.google.common.base.Predicates.in; 23 import static com.google.common.base.Predicates.not; 24 import static com.google.common.collect.Maps.safeContainsKey; 25 import static com.google.common.collect.Maps.safeGet; 26 27 import com.google.common.annotations.GwtCompatible; 28 import com.google.common.base.Function; 29 import com.google.common.base.Predicate; 30 import com.google.common.base.Supplier; 31 import com.google.common.collect.Maps.IteratorBasedAbstractMap; 32 import com.google.common.collect.Maps.ViewCachingAbstractMap; 33 import com.google.common.collect.Sets.ImprovedAbstractSet; 34 import com.google.errorprone.annotations.CanIgnoreReturnValue; 35 import com.google.j2objc.annotations.WeakOuter; 36 import java.io.Serializable; 37 import java.util.Collection; 38 import java.util.Iterator; 39 import java.util.LinkedHashMap; 40 import java.util.Map; 41 import java.util.Map.Entry; 42 import java.util.Set; 43 import java.util.Spliterator; 44 import java.util.Spliterators; 45 import org.checkerframework.checker.nullness.qual.Nullable; 46 47 /** 48 * {@link Table} implementation backed by a map that associates row keys with column key / value 49 * secondary maps. This class provides rapid access to records by the row key alone or by both keys, 50 * but not by just the column key. 51 * 52 * <p>The views returned by {@link #column}, {@link #columnKeySet()}, and {@link #columnMap()} have 53 * iterators that don't support {@code remove()}. Otherwise, all optional operations are supported. 54 * Null row keys, columns keys, and values are not supported. 55 * 56 * <p>Lookups by row key are often faster than lookups by column key, because the data is stored in 57 * a {@code Map<R, Map<C, V>>}. A method call like {@code column(columnKey).get(rowKey)} still runs 58 * quickly, since the row key is provided. However, {@code column(columnKey).size()} takes longer, 59 * since an iteration across all row keys occurs. 60 * 61 * <p>Note that this implementation is not synchronized. If multiple threads access this table 62 * concurrently and one of the threads modifies the table, it must be synchronized externally. 63 * 64 * @author Jared Levy 65 */ 66 @GwtCompatible 67 class StandardTable<R, C, V> extends AbstractTable<R, C, V> implements Serializable { 68 @GwtTransient final Map<R, Map<C, V>> backingMap; 69 @GwtTransient final Supplier<? extends Map<C, V>> factory; 70 71 StandardTable(Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { 72 this.backingMap = backingMap; 73 this.factory = factory; 74 } 75 76 // Accessors 77 78 @Override 79 public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { 80 return rowKey != null && columnKey != null && super.contains(rowKey, columnKey); 81 } 82 83 @Override 84 public boolean containsColumn(@Nullable Object columnKey) { 85 if (columnKey == null) { 86 return false; 87 } 88 for (Map<C, V> map : backingMap.values()) { 89 if (safeContainsKey(map, columnKey)) { 90 return true; 91 } 92 } 93 return false; 94 } 95 96 @Override 97 public boolean containsRow(@Nullable Object rowKey) { 98 return rowKey != null && safeContainsKey(backingMap, rowKey); 99 } 100 101 @Override 102 public boolean containsValue(@Nullable Object value) { 103 return value != null && super.containsValue(value); 104 } 105 106 @Override 107 public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 108 return (rowKey == null || columnKey == null) ? null : super.get(rowKey, columnKey); 109 } 110 111 @Override 112 public boolean isEmpty() { 113 return backingMap.isEmpty(); 114 } 115 116 @Override 117 public int size() { 118 int size = 0; 119 for (Map<C, V> map : backingMap.values()) { 120 size += map.size(); 121 } 122 return size; 123 } 124 125 // Mutators 126 127 @Override 128 public void clear() { 129 backingMap.clear(); 130 } 131 132 private Map<C, V> getOrCreate(R rowKey) { 133 Map<C, V> map = backingMap.get(rowKey); 134 if (map == null) { 135 map = factory.get(); 136 backingMap.put(rowKey, map); 137 } 138 return map; 139 } 140 141 @CanIgnoreReturnValue 142 @Override 143 public V put(R rowKey, C columnKey, V value) { 144 checkNotNull(rowKey); 145 checkNotNull(columnKey); 146 checkNotNull(value); 147 return getOrCreate(rowKey).put(columnKey, value); 148 } 149 150 @CanIgnoreReturnValue 151 @Override 152 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 153 if ((rowKey == null) || (columnKey == null)) { 154 return null; 155 } 156 Map<C, V> map = safeGet(backingMap, rowKey); 157 if (map == null) { 158 return null; 159 } 160 V value = map.remove(columnKey); 161 if (map.isEmpty()) { 162 backingMap.remove(rowKey); 163 } 164 return value; 165 } 166 167 @CanIgnoreReturnValue 168 private Map<R, V> removeColumn(Object column) { 169 Map<R, V> output = new LinkedHashMap<>(); 170 Iterator<Entry<R, Map<C, V>>> iterator = backingMap.entrySet().iterator(); 171 while (iterator.hasNext()) { 172 Entry<R, Map<C, V>> entry = iterator.next(); 173 V value = entry.getValue().remove(column); 174 if (value != null) { 175 output.put(entry.getKey(), value); 176 if (entry.getValue().isEmpty()) { 177 iterator.remove(); 178 } 179 } 180 } 181 return output; 182 } 183 184 private boolean containsMapping(Object rowKey, Object columnKey, Object value) { 185 return value != null && value.equals(get(rowKey, columnKey)); 186 } 187 188 /** Remove a row key / column key / value mapping, if present. */ 189 private boolean removeMapping(Object rowKey, Object columnKey, Object value) { 190 if (containsMapping(rowKey, columnKey, value)) { 191 remove(rowKey, columnKey); 192 return true; 193 } 194 return false; 195 } 196 197 // Views 198 199 /** 200 * Abstract set whose {@code isEmpty()} returns whether the table is empty and whose {@code 201 * clear()} clears all table mappings. 202 */ 203 @WeakOuter 204 private abstract class TableSet<T> extends ImprovedAbstractSet<T> { 205 @Override 206 public boolean isEmpty() { 207 return backingMap.isEmpty(); 208 } 209 210 @Override 211 public void clear() { 212 backingMap.clear(); 213 } 214 } 215 216 /** 217 * {@inheritDoc} 218 * 219 * <p>The set's iterator traverses the mappings for the first row, the mappings for the second 220 * row, and so on. 221 * 222 * <p>Each cell is an immutable snapshot of a row key / column key / value mapping, taken at the 223 * time the cell is returned by a method call to the set or its iterator. 224 */ 225 @Override 226 public Set<Cell<R, C, V>> cellSet() { 227 return super.cellSet(); 228 } 229 230 @Override 231 Iterator<Cell<R, C, V>> cellIterator() { 232 return new CellIterator(); 233 } 234 235 private class CellIterator implements Iterator<Cell<R, C, V>> { 236 final Iterator<Entry<R, Map<C, V>>> rowIterator = backingMap.entrySet().iterator(); 237 @Nullable Entry<R, Map<C, V>> rowEntry; 238 Iterator<Entry<C, V>> columnIterator = Iterators.emptyModifiableIterator(); 239 240 @Override 241 public boolean hasNext() { 242 return rowIterator.hasNext() || columnIterator.hasNext(); 243 } 244 245 @Override 246 public Cell<R, C, V> next() { 247 if (!columnIterator.hasNext()) { 248 rowEntry = rowIterator.next(); 249 columnIterator = rowEntry.getValue().entrySet().iterator(); 250 } 251 Entry<C, V> columnEntry = columnIterator.next(); 252 return Tables.immutableCell(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue()); 253 } 254 255 @Override 256 public void remove() { 257 columnIterator.remove(); 258 if (rowEntry.getValue().isEmpty()) { 259 rowIterator.remove(); 260 rowEntry = null; 261 } 262 } 263 } 264 265 @Override 266 Spliterator<Cell<R, C, V>> cellSpliterator() { 267 return CollectSpliterators.flatMap( 268 backingMap.entrySet().spliterator(), 269 (Entry<R, Map<C, V>> rowEntry) -> 270 CollectSpliterators.map( 271 rowEntry.getValue().entrySet().spliterator(), 272 (Entry<C, V> columnEntry) -> 273 Tables.immutableCell( 274 rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue())), 275 Spliterator.DISTINCT | Spliterator.SIZED, 276 size()); 277 } 278 279 @Override 280 public Map<C, V> row(R rowKey) { 281 return new Row(rowKey); 282 } 283 284 class Row extends IteratorBasedAbstractMap<C, V> { 285 final R rowKey; 286 287 Row(R rowKey) { 288 this.rowKey = checkNotNull(rowKey); 289 } 290 291 @Nullable Map<C, V> backingRowMap; 292 293 Map<C, V> backingRowMap() { 294 return (backingRowMap == null || (backingRowMap.isEmpty() && backingMap.containsKey(rowKey))) 295 ? backingRowMap = computeBackingRowMap() 296 : backingRowMap; 297 } 298 299 Map<C, V> computeBackingRowMap() { 300 return backingMap.get(rowKey); 301 } 302 303 // Call this every time we perform a removal. 304 void maintainEmptyInvariant() { 305 if (backingRowMap() != null && backingRowMap.isEmpty()) { 306 backingMap.remove(rowKey); 307 backingRowMap = null; 308 } 309 } 310 311 @Override 312 public boolean containsKey(Object key) { 313 Map<C, V> backingRowMap = backingRowMap(); 314 return (key != null && backingRowMap != null) && Maps.safeContainsKey(backingRowMap, key); 315 } 316 317 @Override 318 public V get(Object key) { 319 Map<C, V> backingRowMap = backingRowMap(); 320 return (key != null && backingRowMap != null) ? Maps.safeGet(backingRowMap, key) : null; 321 } 322 323 @Override 324 public V put(C key, V value) { 325 checkNotNull(key); 326 checkNotNull(value); 327 if (backingRowMap != null && !backingRowMap.isEmpty()) { 328 return backingRowMap.put(key, value); 329 } 330 return StandardTable.this.put(rowKey, key, value); 331 } 332 333 @Override 334 public V remove(Object key) { 335 Map<C, V> backingRowMap = backingRowMap(); 336 if (backingRowMap == null) { 337 return null; 338 } 339 V result = Maps.safeRemove(backingRowMap, key); 340 maintainEmptyInvariant(); 341 return result; 342 } 343 344 @Override 345 public void clear() { 346 Map<C, V> backingRowMap = backingRowMap(); 347 if (backingRowMap != null) { 348 backingRowMap.clear(); 349 } 350 maintainEmptyInvariant(); 351 } 352 353 @Override 354 public int size() { 355 Map<C, V> map = backingRowMap(); 356 return (map == null) ? 0 : map.size(); 357 } 358 359 @Override 360 Iterator<Entry<C, V>> entryIterator() { 361 final Map<C, V> map = backingRowMap(); 362 if (map == null) { 363 return Iterators.emptyModifiableIterator(); 364 } 365 final Iterator<Entry<C, V>> iterator = map.entrySet().iterator(); 366 return new Iterator<Entry<C, V>>() { 367 @Override 368 public boolean hasNext() { 369 return iterator.hasNext(); 370 } 371 372 @Override 373 public Entry<C, V> next() { 374 return wrapEntry(iterator.next()); 375 } 376 377 @Override 378 public void remove() { 379 iterator.remove(); 380 maintainEmptyInvariant(); 381 } 382 }; 383 } 384 385 @Override 386 Spliterator<Entry<C, V>> entrySpliterator() { 387 Map<C, V> map = backingRowMap(); 388 if (map == null) { 389 return Spliterators.emptySpliterator(); 390 } 391 return CollectSpliterators.map(map.entrySet().spliterator(), this::wrapEntry); 392 } 393 394 Entry<C, V> wrapEntry(final Entry<C, V> entry) { 395 return new ForwardingMapEntry<C, V>() { 396 @Override 397 protected Entry<C, V> delegate() { 398 return entry; 399 } 400 401 @Override 402 public V setValue(V value) { 403 return super.setValue(checkNotNull(value)); 404 } 405 406 @Override 407 public boolean equals(Object object) { 408 // TODO(lowasser): identify why this affects GWT tests 409 return standardEquals(object); 410 } 411 }; 412 } 413 } 414 415 /** 416 * {@inheritDoc} 417 * 418 * <p>The returned map's views have iterators that don't support {@code remove()}. 419 */ 420 @Override 421 public Map<R, V> column(C columnKey) { 422 return new Column(columnKey); 423 } 424 425 private class Column extends ViewCachingAbstractMap<R, V> { 426 final C columnKey; 427 428 Column(C columnKey) { 429 this.columnKey = checkNotNull(columnKey); 430 } 431 432 @Override 433 public V put(R key, V value) { 434 return StandardTable.this.put(key, columnKey, value); 435 } 436 437 @Override 438 public V get(Object key) { 439 return StandardTable.this.get(key, columnKey); 440 } 441 442 @Override 443 public boolean containsKey(Object key) { 444 return StandardTable.this.contains(key, columnKey); 445 } 446 447 @Override 448 public V remove(Object key) { 449 return StandardTable.this.remove(key, columnKey); 450 } 451 452 /** Removes all {@code Column} mappings whose row key and value satisfy the given predicate. */ 453 @CanIgnoreReturnValue 454 boolean removeFromColumnIf(Predicate<? super Entry<R, V>> predicate) { 455 boolean changed = false; 456 Iterator<Entry<R, Map<C, V>>> iterator = backingMap.entrySet().iterator(); 457 while (iterator.hasNext()) { 458 Entry<R, Map<C, V>> entry = iterator.next(); 459 Map<C, V> map = entry.getValue(); 460 V value = map.get(columnKey); 461 if (value != null && predicate.apply(Maps.immutableEntry(entry.getKey(), value))) { 462 map.remove(columnKey); 463 changed = true; 464 if (map.isEmpty()) { 465 iterator.remove(); 466 } 467 } 468 } 469 return changed; 470 } 471 472 @Override 473 Set<Entry<R, V>> createEntrySet() { 474 return new EntrySet(); 475 } 476 477 @WeakOuter 478 private class EntrySet extends ImprovedAbstractSet<Entry<R, V>> { 479 @Override 480 public Iterator<Entry<R, V>> iterator() { 481 return new EntrySetIterator(); 482 } 483 484 @Override 485 public int size() { 486 int size = 0; 487 for (Map<C, V> map : backingMap.values()) { 488 if (map.containsKey(columnKey)) { 489 size++; 490 } 491 } 492 return size; 493 } 494 495 @Override 496 public boolean isEmpty() { 497 return !containsColumn(columnKey); 498 } 499 500 @Override 501 public void clear() { 502 removeFromColumnIf(alwaysTrue()); 503 } 504 505 @Override 506 public boolean contains(Object o) { 507 if (o instanceof Entry) { 508 Entry<?, ?> entry = (Entry<?, ?>) o; 509 return containsMapping(entry.getKey(), columnKey, entry.getValue()); 510 } 511 return false; 512 } 513 514 @Override 515 public boolean remove(Object obj) { 516 if (obj instanceof Entry) { 517 Entry<?, ?> entry = (Entry<?, ?>) obj; 518 return removeMapping(entry.getKey(), columnKey, entry.getValue()); 519 } 520 return false; 521 } 522 523 @Override 524 public boolean retainAll(Collection<?> c) { 525 return removeFromColumnIf(not(in(c))); 526 } 527 } 528 529 private class EntrySetIterator extends AbstractIterator<Entry<R, V>> { 530 final Iterator<Entry<R, Map<C, V>>> iterator = backingMap.entrySet().iterator(); 531 532 @Override 533 protected Entry<R, V> computeNext() { 534 while (iterator.hasNext()) { 535 final Entry<R, Map<C, V>> entry = iterator.next(); 536 if (entry.getValue().containsKey(columnKey)) { 537 @WeakOuter 538 class EntryImpl extends AbstractMapEntry<R, V> { 539 @Override 540 public R getKey() { 541 return entry.getKey(); 542 } 543 544 @Override 545 public V getValue() { 546 return entry.getValue().get(columnKey); 547 } 548 549 @Override 550 public V setValue(V value) { 551 return entry.getValue().put(columnKey, checkNotNull(value)); 552 } 553 } 554 return new EntryImpl(); 555 } 556 } 557 return endOfData(); 558 } 559 } 560 561 @Override 562 Set<R> createKeySet() { 563 return new KeySet(); 564 } 565 566 @WeakOuter 567 private class KeySet extends Maps.KeySet<R, V> { 568 KeySet() { 569 super(Column.this); 570 } 571 572 @Override 573 public boolean contains(Object obj) { 574 return StandardTable.this.contains(obj, columnKey); 575 } 576 577 @Override 578 public boolean remove(Object obj) { 579 return StandardTable.this.remove(obj, columnKey) != null; 580 } 581 582 @Override 583 public boolean retainAll(final Collection<?> c) { 584 return removeFromColumnIf(Maps.<R>keyPredicateOnEntries(not(in(c)))); 585 } 586 } 587 588 @Override 589 Collection<V> createValues() { 590 return new Values(); 591 } 592 593 @WeakOuter 594 private class Values extends Maps.Values<R, V> { 595 Values() { 596 super(Column.this); 597 } 598 599 @Override 600 public boolean remove(Object obj) { 601 return obj != null && removeFromColumnIf(Maps.<V>valuePredicateOnEntries(equalTo(obj))); 602 } 603 604 @Override 605 public boolean removeAll(final Collection<?> c) { 606 return removeFromColumnIf(Maps.<V>valuePredicateOnEntries(in(c))); 607 } 608 609 @Override 610 public boolean retainAll(final Collection<?> c) { 611 return removeFromColumnIf(Maps.<V>valuePredicateOnEntries(not(in(c)))); 612 } 613 } 614 } 615 616 @Override 617 public Set<R> rowKeySet() { 618 return rowMap().keySet(); 619 } 620 621 private transient @Nullable Set<C> columnKeySet; 622 623 /** 624 * {@inheritDoc} 625 * 626 * <p>The returned set has an iterator that does not support {@code remove()}. 627 * 628 * <p>The set's iterator traverses the columns of the first row, the columns of the second row, 629 * etc., skipping any columns that have appeared previously. 630 */ 631 @Override 632 public Set<C> columnKeySet() { 633 Set<C> result = columnKeySet; 634 return (result == null) ? columnKeySet = new ColumnKeySet() : result; 635 } 636 637 @WeakOuter 638 private class ColumnKeySet extends TableSet<C> { 639 @Override 640 public Iterator<C> iterator() { 641 return createColumnKeyIterator(); 642 } 643 644 @Override 645 public int size() { 646 return Iterators.size(iterator()); 647 } 648 649 @Override 650 public boolean remove(Object obj) { 651 if (obj == null) { 652 return false; 653 } 654 boolean changed = false; 655 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 656 while (iterator.hasNext()) { 657 Map<C, V> map = iterator.next(); 658 if (map.keySet().remove(obj)) { 659 changed = true; 660 if (map.isEmpty()) { 661 iterator.remove(); 662 } 663 } 664 } 665 return changed; 666 } 667 668 @Override 669 public boolean removeAll(Collection<?> c) { 670 checkNotNull(c); 671 boolean changed = false; 672 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 673 while (iterator.hasNext()) { 674 Map<C, V> map = iterator.next(); 675 // map.keySet().removeAll(c) can throw a NPE when map is a TreeMap with 676 // natural ordering and c contains a null. 677 if (Iterators.removeAll(map.keySet().iterator(), c)) { 678 changed = true; 679 if (map.isEmpty()) { 680 iterator.remove(); 681 } 682 } 683 } 684 return changed; 685 } 686 687 @Override 688 public boolean retainAll(Collection<?> c) { 689 checkNotNull(c); 690 boolean changed = false; 691 Iterator<Map<C, V>> iterator = backingMap.values().iterator(); 692 while (iterator.hasNext()) { 693 Map<C, V> map = iterator.next(); 694 if (map.keySet().retainAll(c)) { 695 changed = true; 696 if (map.isEmpty()) { 697 iterator.remove(); 698 } 699 } 700 } 701 return changed; 702 } 703 704 @Override 705 public boolean contains(Object obj) { 706 return containsColumn(obj); 707 } 708 } 709 710 /** Creates an iterator that returns each column value with duplicates omitted. */ 711 Iterator<C> createColumnKeyIterator() { 712 return new ColumnKeyIterator(); 713 } 714 715 private class ColumnKeyIterator extends AbstractIterator<C> { 716 // Use the same map type to support TreeMaps with comparators that aren't 717 // consistent with equals(). 718 final Map<C, V> seen = factory.get(); 719 final Iterator<Map<C, V>> mapIterator = backingMap.values().iterator(); 720 Iterator<Entry<C, V>> entryIterator = Iterators.emptyIterator(); 721 722 @Override 723 protected C computeNext() { 724 while (true) { 725 if (entryIterator.hasNext()) { 726 Entry<C, V> entry = entryIterator.next(); 727 if (!seen.containsKey(entry.getKey())) { 728 seen.put(entry.getKey(), entry.getValue()); 729 return entry.getKey(); 730 } 731 } else if (mapIterator.hasNext()) { 732 entryIterator = mapIterator.next().entrySet().iterator(); 733 } else { 734 return endOfData(); 735 } 736 } 737 } 738 } 739 740 /** 741 * {@inheritDoc} 742 * 743 * <p>The collection's iterator traverses the values for the first row, the values for the second 744 * row, and so on. 745 */ 746 @Override 747 public Collection<V> values() { 748 return super.values(); 749 } 750 751 private transient @Nullable Map<R, Map<C, V>> rowMap; 752 753 @Override 754 public Map<R, Map<C, V>> rowMap() { 755 Map<R, Map<C, V>> result = rowMap; 756 return (result == null) ? rowMap = createRowMap() : result; 757 } 758 759 Map<R, Map<C, V>> createRowMap() { 760 return new RowMap(); 761 } 762 763 @WeakOuter 764 class RowMap extends ViewCachingAbstractMap<R, Map<C, V>> { 765 @Override 766 public boolean containsKey(Object key) { 767 return containsRow(key); 768 } 769 770 // performing cast only when key is in backing map and has the correct type 771 @SuppressWarnings("unchecked") 772 @Override 773 public Map<C, V> get(Object key) { 774 return containsRow(key) ? row((R) key) : null; 775 } 776 777 @Override 778 public Map<C, V> remove(Object key) { 779 return (key == null) ? null : backingMap.remove(key); 780 } 781 782 @Override 783 protected Set<Entry<R, Map<C, V>>> createEntrySet() { 784 return new EntrySet(); 785 } 786 787 @WeakOuter 788 class EntrySet extends TableSet<Entry<R, Map<C, V>>> { 789 @Override 790 public Iterator<Entry<R, Map<C, V>>> iterator() { 791 return Maps.asMapEntryIterator( 792 backingMap.keySet(), 793 new Function<R, Map<C, V>>() { 794 @Override 795 public Map<C, V> apply(R rowKey) { 796 return row(rowKey); 797 } 798 }); 799 } 800 801 @Override 802 public int size() { 803 return backingMap.size(); 804 } 805 806 @Override 807 public boolean contains(Object obj) { 808 if (obj instanceof Entry) { 809 Entry<?, ?> entry = (Entry<?, ?>) obj; 810 return entry.getKey() != null 811 && entry.getValue() instanceof Map 812 && Collections2.safeContains(backingMap.entrySet(), entry); 813 } 814 return false; 815 } 816 817 @Override 818 public boolean remove(Object obj) { 819 if (obj instanceof Entry) { 820 Entry<?, ?> entry = (Entry<?, ?>) obj; 821 return entry.getKey() != null 822 && entry.getValue() instanceof Map 823 && backingMap.entrySet().remove(entry); 824 } 825 return false; 826 } 827 } 828 } 829 830 private transient @Nullable ColumnMap columnMap; 831 832 @Override 833 public Map<C, Map<R, V>> columnMap() { 834 ColumnMap result = columnMap; 835 return (result == null) ? columnMap = new ColumnMap() : result; 836 } 837 838 @WeakOuter 839 private class ColumnMap extends ViewCachingAbstractMap<C, Map<R, V>> { 840 // The cast to C occurs only when the key is in the map, implying that it 841 // has the correct type. 842 @SuppressWarnings("unchecked") 843 @Override 844 public Map<R, V> get(Object key) { 845 return containsColumn(key) ? column((C) key) : null; 846 } 847 848 @Override 849 public boolean containsKey(Object key) { 850 return containsColumn(key); 851 } 852 853 @Override 854 public Map<R, V> remove(Object key) { 855 return containsColumn(key) ? removeColumn(key) : null; 856 } 857 858 @Override 859 public Set<Entry<C, Map<R, V>>> createEntrySet() { 860 return new ColumnMapEntrySet(); 861 } 862 863 @Override 864 public Set<C> keySet() { 865 return columnKeySet(); 866 } 867 868 @Override 869 Collection<Map<R, V>> createValues() { 870 return new ColumnMapValues(); 871 } 872 873 @WeakOuter 874 class ColumnMapEntrySet extends TableSet<Entry<C, Map<R, V>>> { 875 @Override 876 public Iterator<Entry<C, Map<R, V>>> iterator() { 877 return Maps.asMapEntryIterator( 878 columnKeySet(), 879 new Function<C, Map<R, V>>() { 880 @Override 881 public Map<R, V> apply(C columnKey) { 882 return column(columnKey); 883 } 884 }); 885 } 886 887 @Override 888 public int size() { 889 return columnKeySet().size(); 890 } 891 892 @Override 893 public boolean contains(Object obj) { 894 if (obj instanceof Entry) { 895 Entry<?, ?> entry = (Entry<?, ?>) obj; 896 if (containsColumn(entry.getKey())) { 897 // The cast to C occurs only when the key is in the map, implying 898 // that it has the correct type. 899 @SuppressWarnings("unchecked") 900 C columnKey = (C) entry.getKey(); 901 return get(columnKey).equals(entry.getValue()); 902 } 903 } 904 return false; 905 } 906 907 @Override 908 public boolean remove(Object obj) { 909 if (contains(obj)) { 910 Entry<?, ?> entry = (Entry<?, ?>) obj; 911 removeColumn(entry.getKey()); 912 return true; 913 } 914 return false; 915 } 916 917 @Override 918 public boolean removeAll(Collection<?> c) { 919 /* 920 * We can't inherit the normal implementation (which calls 921 * Sets.removeAllImpl(Set, *Collection*) because, under some 922 * circumstances, it attempts to call columnKeySet().iterator().remove, 923 * which is unsupported. 924 */ 925 checkNotNull(c); 926 return Sets.removeAllImpl(this, c.iterator()); 927 } 928 929 @Override 930 public boolean retainAll(Collection<?> c) { 931 checkNotNull(c); 932 boolean changed = false; 933 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 934 if (!c.contains(Maps.immutableEntry(columnKey, column(columnKey)))) { 935 removeColumn(columnKey); 936 changed = true; 937 } 938 } 939 return changed; 940 } 941 } 942 943 @WeakOuter 944 private class ColumnMapValues extends Maps.Values<C, Map<R, V>> { 945 ColumnMapValues() { 946 super(ColumnMap.this); 947 } 948 949 @Override 950 public boolean remove(Object obj) { 951 for (Entry<C, Map<R, V>> entry : ColumnMap.this.entrySet()) { 952 if (entry.getValue().equals(obj)) { 953 removeColumn(entry.getKey()); 954 return true; 955 } 956 } 957 return false; 958 } 959 960 @Override 961 public boolean removeAll(Collection<?> c) { 962 checkNotNull(c); 963 boolean changed = false; 964 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 965 if (c.contains(column(columnKey))) { 966 removeColumn(columnKey); 967 changed = true; 968 } 969 } 970 return changed; 971 } 972 973 @Override 974 public boolean retainAll(Collection<?> c) { 975 checkNotNull(c); 976 boolean changed = false; 977 for (C columnKey : Lists.newArrayList(columnKeySet().iterator())) { 978 if (!c.contains(column(columnKey))) { 979 removeColumn(columnKey); 980 changed = true; 981 } 982 } 983 return changed; 984 } 985 } 986 } 987 988 private static final long serialVersionUID = 0; 989 }