    001    /*
    002     * Copyright (C) 2009 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.checkNotNull;
    020
    021    import com.google.common.annotations.GwtCompatible;
    022
    023    import java.util.Comparator;
    024    import java.util.List;
    025    import java.util.Map;
    026
    027    import javax.annotation.Nullable;
    028
    029    /**
    030     * An immutable {@link Table} with reliable user-specified iteration order.
    031     * Does not permit null keys or values.
    032     *
    033     * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
    034     * it has no public or protected constructors. Thus, instances of this class are
    035     * guaranteed to be immutable.
    036     *
    037     * <p>See the Guava User Guide article on <a href=
    038     * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
    039     * immutable collections</a>.
    040     *
    041     * @author Gregory Kick
    042     * @since 11.0
    043     */
    044    @GwtCompatible
    045    // TODO(gak): make serializable
    046    public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> {
    047      /** Returns an empty immutable table. */
    048      @SuppressWarnings("unchecked")
    049      public static final <R, C, V> ImmutableTable<R, C, V> of() {
    050        return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE;
    051      }
    052
    053      /** Returns an immutable table containing a single cell. */
    054      public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey,
    055          C columnKey, V value) {
    056        return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value);
    057      }
    058
    059      /**
    060       * Returns an immutable copy of the provided table.
    061       *
    062       * <p>The {@link Table#cellSet()} iteration order of the provided table
    063       * determines the iteration ordering of all views in the returned table. Note
    064       * that some views of the original table and the copied table may have
    065       * different iteration orders. For more control over the ordering, create a
    066       * {@link Builder} and call {@link Builder#orderRowsBy},
    067       * {@link Builder#orderColumnsBy}, and {@link Builder#putAll}
    068       *
    069       * <p>Despite the method name, this method attempts to avoid actually copying
    070       * the data when it is safe to do so. The exact circumstances under which a
    071       * copy will or will not be performed are undocumented and subject to change.
    072       */
    073      public static final <R, C, V> ImmutableTable<R, C, V> copyOf(
    074          Table<? extends R, ? extends C, ? extends V> table) {
    075        if (table instanceof ImmutableTable) {
    076          @SuppressWarnings("unchecked")
    077          ImmutableTable<R, C, V> parameterizedTable
    078              = (ImmutableTable<R, C, V>) table;
    079          return parameterizedTable;
    080        } else {
    081          int size = table.size();
    082          switch (size) {
    083            case 0:
    084              return of();
    085            case 1:
    086              Cell<? extends R, ? extends C, ? extends V> onlyCell
    087                  = Iterables.getOnlyElement(table.cellSet());
    088              return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(),
    089                  onlyCell.getColumnKey(), onlyCell.getValue());
    090            default:
    091              ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder
    092                  = ImmutableSet.builder();
    093              for (Cell<? extends R, ? extends C, ? extends V> cell :
    094                  table.cellSet()) {
    095                /*
    096                 * Must cast to be able to create a Cell<R, C, V> rather than a
    097                 * Cell<? extends R, ? extends C, ? extends V>
    098                 */
    099                cellSetBuilder.add(cellOf((R) cell.getRowKey(),
    100                    (C) cell.getColumnKey(), (V) cell.getValue()));
    101              }
    102              return RegularImmutableTable.forCells(cellSetBuilder.build());
    103          }
    104        }
    105      }
    106
    107      /**
    108       * Returns a new builder. The generated builder is equivalent to the builder
    109       * created by the {@link Builder#ImmutableTable.Builder()} constructor.
    110       */
    111      public static final <R, C, V> Builder<R, C, V> builder() {
    112        return new Builder<R, C, V>();
    113      }
    114
    115      /**
    116       * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are
    117       * non-null, and returns a new entry with those values.
    118       */
    119      static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
    120        return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey),
    121            checkNotNull(value));
    122      }
    123
    124      /**
    125       * A builder for creating immutable table instances, especially {@code public
    126       * static final} tables ("constant tables"). Example: <pre>   {@code
    127       *
    128       *   static final ImmutableTable<Integer, Character, String> SPREADSHEET =
    129       *       new ImmutableTable.Builder<Integer, Character, String>()
    130       *           .put(1, 'A', "foo")
    131       *           .put(1, 'B', "bar")
    132       *           .put(2, 'A', "baz")
    133       *           .build();}</pre>
    134       *
    135       * <p>By default, the order in which cells are added to the builder determines
    136       * the iteration ordering of all views in the returned table, with {@link
    137       * #putAll} following the {@link Table#cellSet()} iteration order. However, if
    138       * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are
    139       * sorted by the supplied comparators.
    140       *
    141       * For empty or single-cell immutable tables, {@link #of()} and
    142       * {@link #of(Object, Object, Object)} are even more convenient.
    143       *
    144       * <p>Builder instances can be reused - it is safe to call {@link #build}
    145       * multiple times to build multiple tables in series. Each table is a superset
    146       * of the tables created before it.
    147       *
    148       * @since 11.0
    149       */
    150      public static final class Builder<R, C, V> {
    151        private final List<Cell<R, C, V>> cells = Lists.newArrayList();
    152        private Comparator<? super R> rowComparator;
    153        private Comparator<? super C> columnComparator;
    154
    155        /**
    156         * Creates a new builder. The returned builder is equivalent to the builder
    157         * generated by {@link ImmutableTable#builder}.
    158         */
    159        public Builder() {}
    160
    161        /**
    162         * Specifies the ordering of the generated table's rows.
    163         */
    164        public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
    165          this.rowComparator = checkNotNull(rowComparator);
    166          return this;
    167        }
    168
    169        /**
    170         * Specifies the ordering of the generated table's columns.
    171         */
    172        public Builder<R, C, V> orderColumnsBy(
    173            Comparator<? super C> columnComparator) {
    174          this.columnComparator = checkNotNull(columnComparator);
    175          return this;
    176        }
    177
    178        /**
    179         * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code
    180         * value} in the built table. Duplicate key pairs are not allowed and will
    181         * cause {@link #build} to fail.
    182         */
    183        public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
    184          cells.add(cellOf(rowKey, columnKey, value));
    185          return this;
    186        }
    187
    188        /**
    189         * Adds the given {@code cell} to the table, making it immutable if
    190         * necessary. Duplicate key pairs are not allowed and will cause {@link
    191         * #build} to fail.
    192         */
    193        public Builder<R, C, V> put(
    194            Cell<? extends R, ? extends C, ? extends V> cell) {
    195          if (cell instanceof Tables.ImmutableCell) {
    196            checkNotNull(cell.getRowKey());
    197            checkNotNull(cell.getColumnKey());
    198            checkNotNull(cell.getValue());
    199            @SuppressWarnings("unchecked") // all supported methods are covariant
    200            Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
    201            cells.add(immutableCell);
    202          } else {
    203            put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
    204          }
    205          return this;
    206        }
    207
    208        /**
    209         * Associates all of the given table's keys and values in the built table.
    210         * Duplicate row key column key pairs are not allowed, and will cause
    211         * {@link #build} to fail.
    212         *
    213         * @throws NullPointerException if any key or value in {@code table} is null
    214         */
    215        public Builder<R, C, V> putAll(
    216            Table<? extends R, ? extends C, ? extends V> table) {
    217          for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
    218            put(cell);
    219          }
    220          return this;
    221        }
    222
    223        /**
    224         * Returns a newly-created immutable table.
    225         *
    226         * @throws IllegalArgumentException if duplicate key pairs were added
    227         */
    228        public ImmutableTable<R, C, V> build() {
    229          int size = cells.size();
    230          switch (size) {
    231            case 0:
    232              return of();
    233            case 1:
    234              return new SingletonImmutableTable<R, C, V>(
    235                  Iterables.getOnlyElement(cells));
    236            default:
    237             return RegularImmutableTable.forCells(
    238                 cells, rowComparator, columnComparator);
    239          }
    240        }
    241      }
    242
    243      ImmutableTable() {}
    244
    245      @Override public abstract ImmutableSet<Cell<R, C, V>> cellSet();
    246
    247      /**
    248       * {@inheritDoc}
    249       *
    250       * @throws NullPointerException if {@code columnKey} is {@code null}
    251       */
    252      @Override public abstract ImmutableMap<R, V> column(C columnKey);
    253
    254      @Override public abstract ImmutableSet<C> columnKeySet();
    255
    256      /**
    257       * {@inheritDoc}
    258       *
    259       * <p>The value {@code Map<R, V>}s in the returned map are
    260       * {@link ImmutableMap}s as well.
    261       */
    262      @Override public abstract ImmutableMap<C, Map<R, V>> columnMap();
    263
    264      /**
    265       * {@inheritDoc}
    266       *
    267       * @throws NullPointerException if {@code rowKey} is {@code null}
    268       */
    269      @Override public abstract ImmutableMap<C, V> row(R rowKey);
    270
    271      @Override public abstract ImmutableSet<R> rowKeySet();
    272
    273      /**
    274       * {@inheritDoc}
    275       *
    276       * <p>The value {@code Map<C, V>}s in the returned map are
    277       * {@link ImmutableMap}s as well.
    278       */
    279      @Override public abstract ImmutableMap<R, Map<C, V>> rowMap();
    280
    281      /**
    282       * Guaranteed to throw an exception and leave the table unmodified.
    283       *
    284       * @throws UnsupportedOperationException always
    285       */
    286      @Override public final void clear() {
    287        throw new UnsupportedOperationException();
    288      }
    289
    290      /**
    291       * Guaranteed to throw an exception and leave the table unmodified.
    292       *
    293       * @throws UnsupportedOperationException always
    294       */
    295      @Override public final V put(R rowKey, C columnKey, V value) {
    296        throw new UnsupportedOperationException();
    297      }
    298
    299      /**
    300       * Guaranteed to throw an exception and leave the table unmodified.
    301       *
    302       * @throws UnsupportedOperationException always
    303       */
    304      @Override public final void putAll(
    305          Table<? extends R, ? extends C, ? extends V> table) {
    306        throw new UnsupportedOperationException();
    307      }
    308
    309      /**
    310       * Guaranteed to throw an exception and leave the table unmodified.
    311       *
    312       * @throws UnsupportedOperationException always
    313       */
    314      @Override public final V remove(Object rowKey, Object columnKey) {
    315        throw new UnsupportedOperationException();
    316      }
    317
    318      @Override public boolean equals(@Nullable Object obj) {
    319        if (obj == this) {
    320          return true;
    321        } else if (obj instanceof Table) {
    322          Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
    323          return this.cellSet().equals(that.cellSet());
    324        } else {
    325          return false;
    326        }
    327      }
    328
    329      @Override public int hashCode() {
    330        return cellSet().hashCode();
    331      }
    332
    333      @Override public String toString() {
    334        return rowMap().toString();
    335      }
    336    }