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