001/*
002 * Copyright (C) 2008 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;
021import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
022
023import com.google.common.annotations.GwtCompatible;
024import com.google.common.annotations.J2ktIncompatible;
025import com.google.common.base.Function;
026import com.google.common.base.Objects;
027import com.google.common.base.Supplier;
028import com.google.common.collect.Table.Cell;
029import java.io.Serializable;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.Iterator;
033import java.util.Map;
034import java.util.Set;
035import java.util.SortedMap;
036import java.util.SortedSet;
037import java.util.function.BinaryOperator;
038import java.util.stream.Collector;
039import javax.annotation.CheckForNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042/**
043 * Provides static methods that involve a {@code Table}.
044 *
045 * <p>See the Guava User Guide article on <a href=
046 * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#tables">{@code Tables}</a>.
047 *
048 * @author Jared Levy
049 * @author Louis Wasserman
050 * @since 7.0
051 */
052@GwtCompatible
053public final class Tables {
054  private Tables() {}
055
056  /**
057   * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the
058   * specified supplier, whose cells are generated by applying the provided mapping functions to the
059   * input elements. Cells are inserted into the generated {@code Table} in encounter order.
060   *
061   * <p>If multiple input elements map to the same row and column, an {@code IllegalStateException}
062   * is thrown when the collection operation is performed.
063   *
064   * <p>To collect to an {@link ImmutableTable}, use {@link ImmutableTable#toImmutableTable}.
065   *
066   * @since 33.2.0 (available since 21.0 in guava-jre)
067   */
068  @SuppressWarnings("Java7ApiChecker")
069  @IgnoreJRERequirement // Users will use this only if they're already using streams.
070  public static <
071          T extends @Nullable Object,
072          R extends @Nullable Object,
073          C extends @Nullable Object,
074          V,
075          I extends Table<R, C, V>>
076      Collector<T, ?, I> toTable(
077          java.util.function.Function<? super T, ? extends R> rowFunction,
078          java.util.function.Function<? super T, ? extends C> columnFunction,
079          java.util.function.Function<? super T, ? extends V> valueFunction,
080          java.util.function.Supplier<I> tableSupplier) {
081    return TableCollectors.<T, R, C, V, I>toTable(
082        rowFunction, columnFunction, valueFunction, tableSupplier);
083  }
084
085  /**
086   * Returns a {@link Collector} that accumulates elements into a {@code Table} created using the
087   * specified supplier, whose cells are generated by applying the provided mapping functions to the
088   * input elements. Cells are inserted into the generated {@code Table} in encounter order.
089   *
090   * <p>If multiple input elements map to the same row and column, the specified merging function is
091   * used to combine the values. Like {@link
092   * java.util.stream.Collectors#toMap(java.util.function.Function, java.util.function.Function,
093   * BinaryOperator, java.util.function.Supplier)}, this Collector throws a {@code
094   * NullPointerException} on null values returned from {@code valueFunction}, and treats nulls
095   * returned from {@code mergeFunction} as removals of that row/column pair.
096   *
097   * @since 33.2.0 (available since 21.0 in guava-jre)
098   */
099  @SuppressWarnings("Java7ApiChecker")
100  @IgnoreJRERequirement // Users will use this only if they're already using streams.
101  public static <
102          T extends @Nullable Object,
103          R extends @Nullable Object,
104          C extends @Nullable Object,
105          V,
106          I extends Table<R, C, V>>
107      Collector<T, ?, I> toTable(
108          java.util.function.Function<? super T, ? extends R> rowFunction,
109          java.util.function.Function<? super T, ? extends C> columnFunction,
110          java.util.function.Function<? super T, ? extends V> valueFunction,
111          BinaryOperator<V> mergeFunction,
112          java.util.function.Supplier<I> tableSupplier) {
113    return TableCollectors.<T, R, C, V, I>toTable(
114        rowFunction, columnFunction, valueFunction, mergeFunction, tableSupplier);
115  }
116
117  /**
118   * Returns an immutable cell with the specified row key, column key, and value.
119   *
120   * <p>The returned cell is serializable.
121   *
122   * @param rowKey the row key to be associated with the returned cell
123   * @param columnKey the column key to be associated with the returned cell
124   * @param value the value to be associated with the returned cell
125   */
126  public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
127      Cell<R, C, V> immutableCell(
128          @ParametricNullness R rowKey,
129          @ParametricNullness C columnKey,
130          @ParametricNullness V value) {
131    return new ImmutableCell<>(rowKey, columnKey, value);
132  }
133
134  static final class ImmutableCell<
135          R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
136      extends AbstractCell<R, C, V> implements Serializable {
137    @ParametricNullness private final R rowKey;
138    @ParametricNullness private final C columnKey;
139    @ParametricNullness private final V value;
140
141    ImmutableCell(
142        @ParametricNullness R rowKey,
143        @ParametricNullness C columnKey,
144        @ParametricNullness V value) {
145      this.rowKey = rowKey;
146      this.columnKey = columnKey;
147      this.value = value;
148    }
149
150    @Override
151    @ParametricNullness
152    public R getRowKey() {
153      return rowKey;
154    }
155
156    @Override
157    @ParametricNullness
158    public C getColumnKey() {
159      return columnKey;
160    }
161
162    @Override
163    @ParametricNullness
164    public V getValue() {
165      return value;
166    }
167
168    private static final long serialVersionUID = 0;
169  }
170
171  abstract static class AbstractCell<
172          R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
173      implements Cell<R, C, V> {
174    // needed for serialization
175    AbstractCell() {}
176
177    @Override
178    public boolean equals(@CheckForNull Object obj) {
179      if (obj == this) {
180        return true;
181      }
182      if (obj instanceof Cell) {
183        Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj;
184        return Objects.equal(getRowKey(), other.getRowKey())
185            && Objects.equal(getColumnKey(), other.getColumnKey())
186            && Objects.equal(getValue(), other.getValue());
187      }
188      return false;
189    }
190
191    @Override
192    public int hashCode() {
193      return Objects.hashCode(getRowKey(), getColumnKey(), getValue());
194    }
195
196    @Override
197    public String toString() {
198      return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue();
199    }
200  }
201
202  /**
203   * Creates a transposed view of a given table that flips its row and column keys. In other words,
204   * calling {@code get(columnKey, rowKey)} on the generated table always returns the same value as
205   * calling {@code get(rowKey, columnKey)} on the original table. Updating the original table
206   * changes the contents of the transposed table and vice versa.
207   *
208   * <p>The returned table supports update operations as long as the input table supports the
209   * analogous operation with swapped rows and columns. For example, in a {@link HashBasedTable}
210   * instance, {@code rowKeySet().iterator()} supports {@code remove()} but {@code
211   * columnKeySet().iterator()} doesn't. With a transposed {@link HashBasedTable}, it's the other
212   * way around.
213   */
214  public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
215      Table<C, R, V> transpose(Table<R, C, V> table) {
216    return (table instanceof TransposeTable)
217        ? ((TransposeTable<R, C, V>) table).original
218        : new TransposeTable<C, R, V>(table);
219  }
220
221  private static class TransposeTable<
222          C extends @Nullable Object, R extends @Nullable Object, V extends @Nullable Object>
223      extends AbstractTable<C, R, V> {
224    final Table<R, C, V> original;
225
226    TransposeTable(Table<R, C, V> original) {
227      this.original = checkNotNull(original);
228    }
229
230    @Override
231    public void clear() {
232      original.clear();
233    }
234
235    @Override
236    public Map<C, V> column(@ParametricNullness R columnKey) {
237      return original.row(columnKey);
238    }
239
240    @Override
241    public Set<R> columnKeySet() {
242      return original.rowKeySet();
243    }
244
245    @Override
246    public Map<R, Map<C, V>> columnMap() {
247      return original.rowMap();
248    }
249
250    @Override
251    public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
252      return original.contains(columnKey, rowKey);
253    }
254
255    @Override
256    public boolean containsColumn(@CheckForNull Object columnKey) {
257      return original.containsRow(columnKey);
258    }
259
260    @Override
261    public boolean containsRow(@CheckForNull Object rowKey) {
262      return original.containsColumn(rowKey);
263    }
264
265    @Override
266    public boolean containsValue(@CheckForNull Object value) {
267      return original.containsValue(value);
268    }
269
270    @Override
271    @CheckForNull
272    public V get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
273      return original.get(columnKey, rowKey);
274    }
275
276    @Override
277    @CheckForNull
278    public V put(
279        @ParametricNullness C rowKey,
280        @ParametricNullness R columnKey,
281        @ParametricNullness V value) {
282      return original.put(columnKey, rowKey, value);
283    }
284
285    @Override
286    public void putAll(Table<? extends C, ? extends R, ? extends V> table) {
287      original.putAll(transpose(table));
288    }
289
290    @Override
291    @CheckForNull
292    public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
293      return original.remove(columnKey, rowKey);
294    }
295
296    @Override
297    public Map<R, V> row(@ParametricNullness C rowKey) {
298      return original.column(rowKey);
299    }
300
301    @Override
302    public Set<C> rowKeySet() {
303      return original.columnKeySet();
304    }
305
306    @Override
307    public Map<C, Map<R, V>> rowMap() {
308      return original.columnMap();
309    }
310
311    @Override
312    public int size() {
313      return original.size();
314    }
315
316    @Override
317    public Collection<V> values() {
318      return original.values();
319    }
320
321    @Override
322    Iterator<Cell<C, R, V>> cellIterator() {
323      return Iterators.transform(original.cellSet().iterator(), Tables::transposeCell);
324    }
325  }
326
327  private static <
328          R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
329      Cell<C, R, V> transposeCell(Cell<R, C, V> cell) {
330    return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue());
331  }
332
333  /**
334   * Creates a table that uses the specified backing map and factory. It can generate a table based
335   * on arbitrary {@link Map} classes.
336   *
337   * <p>The {@code factory}-generated and {@code backingMap} classes determine the table iteration
338   * order. However, the table's {@code row()} method returns instances of a different class than
339   * {@code factory.get()} does.
340   *
341   * <p>Call this method only when the simpler factory methods in classes like {@link
342   * HashBasedTable} and {@link TreeBasedTable} won't suffice.
343   *
344   * <p>The views returned by the {@code Table} methods {@link Table#column}, {@link
345   * Table#columnKeySet}, and {@link Table#columnMap} have iterators that don't support {@code
346   * remove()}. Otherwise, all optional operations are supported. Null row keys, columns keys, and
347   * values are not supported.
348   *
349   * <p>Lookups by row key are often faster than lookups by column key, because the data is stored
350   * in a {@code Map<R, Map<C, V>>}. A method call like {@code column(columnKey).get(rowKey)} still
351   * runs quickly, since the row key is provided. However, {@code column(columnKey).size()} takes
352   * longer, since an iteration across all row keys occurs.
353   *
354   * <p>Note that this implementation is not synchronized. If multiple threads access this table
355   * concurrently and one of the threads modifies the table, it must be synchronized externally.
356   *
357   * <p>The table is serializable if {@code backingMap}, {@code factory}, the maps generated by
358   * {@code factory}, and the table contents are all serializable.
359   *
360   * <p>Note: the table assumes complete ownership over of {@code backingMap} and the maps returned
361   * by {@code factory}. Those objects should not be manually updated and they should not use soft,
362   * weak, or phantom references.
363   *
364   * @param backingMap place to store the mapping from each row key to its corresponding column key
365   *     / value map
366   * @param factory supplier of new, empty maps that will each hold all column key / value mappings
367   *     for a given row key
368   * @throws IllegalArgumentException if {@code backingMap} is not empty
369   * @since 10.0
370   */
371  public static <R, C, V> Table<R, C, V> newCustomTable(
372      Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) {
373    checkArgument(backingMap.isEmpty());
374    checkNotNull(factory);
375    // TODO(jlevy): Wrap factory to validate that the supplied maps are empty?
376    return new StandardTable<>(backingMap, factory);
377  }
378
379  /**
380   * Returns a view of a table where each value is transformed by a function. All other properties
381   * of the table, such as iteration order, are left intact.
382   *
383   * <p>Changes in the underlying table are reflected in this view. Conversely, this view supports
384   * removal operations, and these are reflected in the underlying table.
385   *
386   * <p>It's acceptable for the underlying table to contain null keys, and even null values provided
387   * that the function is capable of accepting null input. The transformed table might contain null
388   * values, if the function sometimes gives a null result.
389   *
390   * <p>The returned table is not thread-safe or serializable, even if the underlying table is.
391   *
392   * <p>The function is applied lazily, invoked when needed. This is necessary for the returned
393   * table to be a view, but it means that the function will be applied many times for bulk
394   * operations like {@link Table#containsValue} and {@code Table.toString()}. For this to perform
395   * well, {@code function} should be fast. To avoid lazy evaluation when the returned table doesn't
396   * need to be a view, copy the returned table into a new table of your choosing.
397   *
398   * @since 10.0
399   */
400  public static <
401          R extends @Nullable Object,
402          C extends @Nullable Object,
403          V1 extends @Nullable Object,
404          V2 extends @Nullable Object>
405      Table<R, C, V2> transformValues(
406          Table<R, C, V1> fromTable, Function<? super V1, V2> function) {
407    return new TransformedTable<>(fromTable, function);
408  }
409
410  private static class TransformedTable<
411          R extends @Nullable Object,
412          C extends @Nullable Object,
413          V1 extends @Nullable Object,
414          V2 extends @Nullable Object>
415      extends AbstractTable<R, C, V2> {
416    final Table<R, C, V1> fromTable;
417    final Function<? super V1, V2> function;
418
419    TransformedTable(Table<R, C, V1> fromTable, Function<? super V1, V2> function) {
420      this.fromTable = checkNotNull(fromTable);
421      this.function = checkNotNull(function);
422    }
423
424    @Override
425    public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
426      return fromTable.contains(rowKey, columnKey);
427    }
428
429    @Override
430    @CheckForNull
431    public V2 get(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
432      // The function is passed a null input only when the table contains a null
433      // value.
434      // The cast is safe because of the contains() check.
435      return contains(rowKey, columnKey)
436          ? function.apply(uncheckedCastNullableTToT(fromTable.get(rowKey, columnKey)))
437          : null;
438    }
439
440    @Override
441    public int size() {
442      return fromTable.size();
443    }
444
445    @Override
446    public void clear() {
447      fromTable.clear();
448    }
449
450    @Override
451    @CheckForNull
452    public V2 put(
453        @ParametricNullness R rowKey,
454        @ParametricNullness C columnKey,
455        @ParametricNullness V2 value) {
456      throw new UnsupportedOperationException();
457    }
458
459    @Override
460    public void putAll(Table<? extends R, ? extends C, ? extends V2> table) {
461      throw new UnsupportedOperationException();
462    }
463
464    @Override
465    @CheckForNull
466    public V2 remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
467      return contains(rowKey, columnKey)
468          // The cast is safe because of the contains() check.
469          ? function.apply(uncheckedCastNullableTToT(fromTable.remove(rowKey, columnKey)))
470          : null;
471    }
472
473    @Override
474    public Map<C, V2> row(@ParametricNullness R rowKey) {
475      return Maps.transformValues(fromTable.row(rowKey), function);
476    }
477
478    @Override
479    public Map<R, V2> column(@ParametricNullness C columnKey) {
480      return Maps.transformValues(fromTable.column(columnKey), function);
481    }
482
483    Function<Cell<R, C, V1>, Cell<R, C, V2>> cellFunction() {
484      return new Function<Cell<R, C, V1>, Cell<R, C, V2>>() {
485        @Override
486        public Cell<R, C, V2> apply(Cell<R, C, V1> cell) {
487          return immutableCell(
488              cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue()));
489        }
490      };
491    }
492
493    @Override
494    Iterator<Cell<R, C, V2>> cellIterator() {
495      return Iterators.transform(fromTable.cellSet().iterator(), cellFunction());
496    }
497
498    @Override
499    public Set<R> rowKeySet() {
500      return fromTable.rowKeySet();
501    }
502
503    @Override
504    public Set<C> columnKeySet() {
505      return fromTable.columnKeySet();
506    }
507
508    @Override
509    Collection<V2> createValues() {
510      return Collections2.transform(fromTable.values(), function);
511    }
512
513    @Override
514    public Map<R, Map<C, V2>> rowMap() {
515      Function<Map<C, V1>, Map<C, V2>> rowFunction =
516          new Function<Map<C, V1>, Map<C, V2>>() {
517            @Override
518            public Map<C, V2> apply(Map<C, V1> row) {
519              return Maps.transformValues(row, function);
520            }
521          };
522      return Maps.transformValues(fromTable.rowMap(), rowFunction);
523    }
524
525    @Override
526    public Map<C, Map<R, V2>> columnMap() {
527      Function<Map<R, V1>, Map<R, V2>> columnFunction =
528          new Function<Map<R, V1>, Map<R, V2>>() {
529            @Override
530            public Map<R, V2> apply(Map<R, V1> column) {
531              return Maps.transformValues(column, function);
532            }
533          };
534      return Maps.transformValues(fromTable.columnMap(), columnFunction);
535    }
536  }
537
538  /**
539   * Returns an unmodifiable view of the specified table. This method allows modules to provide
540   * users with "read-only" access to internal tables. Query operations on the returned table "read
541   * through" to the specified table, and attempts to modify the returned table, whether direct or
542   * via its collection views, result in an {@code UnsupportedOperationException}.
543   *
544   * <p>The returned table will be serializable if the specified table is serializable.
545   *
546   * <p>Consider using an {@link ImmutableTable}, which is guaranteed never to change.
547   *
548   * @since 11.0
549   */
550  public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
551      Table<R, C, V> unmodifiableTable(Table<? extends R, ? extends C, ? extends V> table) {
552    return new UnmodifiableTable<>(table);
553  }
554
555  private static class UnmodifiableTable<
556          R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
557      extends ForwardingTable<R, C, V> implements Serializable {
558    final Table<? extends R, ? extends C, ? extends V> delegate;
559
560    UnmodifiableTable(Table<? extends R, ? extends C, ? extends V> delegate) {
561      this.delegate = checkNotNull(delegate);
562    }
563
564    @SuppressWarnings("unchecked") // safe, covariant cast
565    @Override
566    protected Table<R, C, V> delegate() {
567      return (Table<R, C, V>) delegate;
568    }
569
570    @Override
571    public Set<Cell<R, C, V>> cellSet() {
572      return Collections.unmodifiableSet(super.cellSet());
573    }
574
575    @Override
576    public void clear() {
577      throw new UnsupportedOperationException();
578    }
579
580    @Override
581    public Map<R, V> column(@ParametricNullness C columnKey) {
582      return Collections.unmodifiableMap(super.column(columnKey));
583    }
584
585    @Override
586    public Set<C> columnKeySet() {
587      return Collections.unmodifiableSet(super.columnKeySet());
588    }
589
590    @Override
591    public Map<C, Map<R, V>> columnMap() {
592      Function<Map<R, V>, Map<R, V>> wrapper = unmodifiableWrapper();
593      return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper));
594    }
595
596    @Override
597    @CheckForNull
598    public V put(
599        @ParametricNullness R rowKey,
600        @ParametricNullness C columnKey,
601        @ParametricNullness V value) {
602      throw new UnsupportedOperationException();
603    }
604
605    @Override
606    public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
607      throw new UnsupportedOperationException();
608    }
609
610    @Override
611    @CheckForNull
612    public V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
613      throw new UnsupportedOperationException();
614    }
615
616    @Override
617    public Map<C, V> row(@ParametricNullness R rowKey) {
618      return Collections.unmodifiableMap(super.row(rowKey));
619    }
620
621    @Override
622    public Set<R> rowKeySet() {
623      return Collections.unmodifiableSet(super.rowKeySet());
624    }
625
626    @Override
627    public Map<R, Map<C, V>> rowMap() {
628      Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper();
629      return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper));
630    }
631
632    @Override
633    public Collection<V> values() {
634      return Collections.unmodifiableCollection(super.values());
635    }
636
637    private static final long serialVersionUID = 0;
638  }
639
640  /**
641   * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to
642   * provide users with "read-only" access to internal tables. Query operations on the returned
643   * table "read through" to the specified table, and attempts to modify the returned table, whether
644   * direct or via its collection views, result in an {@code UnsupportedOperationException}.
645   *
646   * <p>The returned table will be serializable if the specified table is serializable.
647   *
648   * @param table the row-sorted table for which an unmodifiable view is to be returned
649   * @return an unmodifiable view of the specified table
650   * @since 11.0
651   */
652  public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
653      RowSortedTable<R, C, V> unmodifiableRowSortedTable(
654          RowSortedTable<R, ? extends C, ? extends V> table) {
655    /*
656     * It's not ? extends R, because it's technically not covariant in R. Specifically,
657     * table.rowMap().comparator() could return a comparator that only works for the ? extends R.
658     * Collections.unmodifiableSortedMap makes the same distinction.
659     */
660    return new UnmodifiableRowSortedMap<>(table);
661  }
662
663  private static final class UnmodifiableRowSortedMap<
664          R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
665      extends UnmodifiableTable<R, C, V> implements RowSortedTable<R, C, V> {
666
667    public UnmodifiableRowSortedMap(RowSortedTable<R, ? extends C, ? extends V> delegate) {
668      super(delegate);
669    }
670
671    @Override
672    protected RowSortedTable<R, C, V> delegate() {
673      return (RowSortedTable<R, C, V>) super.delegate();
674    }
675
676    @Override
677    public SortedMap<R, Map<C, V>> rowMap() {
678      Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper();
679      return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper));
680    }
681
682    @Override
683    public SortedSet<R> rowKeySet() {
684      return Collections.unmodifiableSortedSet(delegate().rowKeySet());
685    }
686
687    private static final long serialVersionUID = 0;
688  }
689
690  @SuppressWarnings("unchecked")
691  private static <K extends @Nullable Object, V extends @Nullable Object>
692      Function<Map<K, V>, Map<K, V>> unmodifiableWrapper() {
693    return (Function) UNMODIFIABLE_WRAPPER;
694  }
695
696  private static final Function<? extends Map<?, ?>, ? extends Map<?, ?>> UNMODIFIABLE_WRAPPER =
697      new Function<Map<Object, Object>, Map<Object, Object>>() {
698        @Override
699        public Map<Object, Object> apply(Map<Object, Object> input) {
700          return Collections.unmodifiableMap(input);
701        }
702      };
703
704  /**
705   * Returns a synchronized (thread-safe) table backed by the specified table. In order to guarantee
706   * serial access, it is critical that <b>all</b> access to the backing table is accomplished
707   * through the returned table.
708   *
709   * <p>It is imperative that the user manually synchronize on the returned table when accessing any
710   * of its collection views:
711   *
712   * <pre>{@code
713   * Table<R, C, V> table = Tables.synchronizedTable(HashBasedTable.<R, C, V>create());
714   * ...
715   * Map<C, V> row = table.row(rowKey);  // Needn't be in synchronized block
716   * ...
717   * synchronized (table) {  // Synchronizing on table, not row!
718   *   Iterator<Entry<C, V>> i = row.entrySet().iterator(); // Must be in synchronized block
719   *   while (i.hasNext()) {
720   *     foo(i.next());
721   *   }
722   * }
723   * }</pre>
724   *
725   * <p>Failure to follow this advice may result in non-deterministic behavior.
726   *
727   * <p>The returned table will be serializable if the specified table is serializable.
728   *
729   * @param table the table to be wrapped in a synchronized view
730   * @return a synchronized view of the specified table
731   * @since 22.0
732   */
733  @J2ktIncompatible // Synchronized
734  public static <R extends @Nullable Object, C extends @Nullable Object, V extends @Nullable Object>
735      Table<R, C, V> synchronizedTable(Table<R, C, V> table) {
736    return Synchronized.table(table, null);
737  }
738
739  static boolean equalsImpl(Table<?, ?, ?> table, @CheckForNull Object obj) {
740    if (obj == table) {
741      return true;
742    } else if (obj instanceof Table) {
743      Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
744      return table.cellSet().equals(that.cellSet());
745    } else {
746      return false;
747    }
748  }
749}