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 }