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
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.GwtCompatible;
022import com.google.common.annotations.GwtIncompatible;
023import com.google.common.annotations.J2ktIncompatible;
024import com.google.common.base.MoreObjects;
025import com.google.errorprone.annotations.CanIgnoreReturnValue;
026import com.google.errorprone.annotations.DoNotCall;
027import com.google.errorprone.annotations.DoNotMock;
028import java.io.InvalidObjectException;
029import java.io.ObjectInputStream;
030import java.io.Serializable;
031import java.util.Comparator;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035import javax.annotation.CheckForNull;
036
037/**
038 * A {@link Table} whose contents will never change, with many other important properties detailed
039 * at {@link ImmutableCollection}.
040 *
041 * <p>See the Guava User Guide article on <a href=
042 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>.
043 *
044 * @author Gregory Kick
045 * @since 11.0
046 */
047@GwtCompatible
048@ElementTypesAreNonnullByDefault
049public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
050    implements Serializable {
051
052  /**
053   * Returns an empty immutable table.
054   *
055   * <p><b>Performance note:</b> the instance returned is a singleton.
056   */
057  @SuppressWarnings("unchecked")
058  public static <R, C, V> ImmutableTable<R, C, V> of() {
059    return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY;
060  }
061
062  /** Returns an immutable table containing a single cell. */
063  public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) {
064    return new SingletonImmutableTable<>(rowKey, columnKey, value);
065  }
066
067  /**
068   * Returns an immutable copy of the provided table.
069   *
070   * <p>The {@link Table#cellSet()} iteration order of the provided table determines the iteration
071   * ordering of all views in the returned table. Note that some views of the original table and the
072   * copied table may have different iteration orders. For more control over the ordering, create a
073   * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and
074   * {@link Builder#putAll}
075   *
076   * <p>Despite the method name, this method attempts to avoid actually copying the data when it is
077   * safe to do so. The exact circumstances under which a copy will or will not be performed are
078   * undocumented and subject to change.
079   */
080  public static <R, C, V> ImmutableTable<R, C, V> copyOf(
081      Table<? extends R, ? extends C, ? extends V> table) {
082    if (table instanceof ImmutableTable) {
083      @SuppressWarnings("unchecked")
084      ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table;
085      return parameterizedTable;
086    } else {
087      return copyOf(table.cellSet());
088    }
089  }
090
091  static <R, C, V> ImmutableTable<R, C, V> copyOf(
092      Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) {
093    ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder();
094    for (Cell<? extends R, ? extends C, ? extends V> cell : cells) {
095      builder.put(cell);
096    }
097    return builder.build();
098  }
099
100  /**
101   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
102   * Builder#Builder() ImmutableTable.Builder()} constructor.
103   */
104  public static <R, C, V> Builder<R, C, V> builder() {
105    return new Builder<>();
106  }
107
108  /**
109   * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a
110   * new entry with those values.
111   */
112  static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
113    return Tables.immutableCell(
114        checkNotNull(rowKey, "rowKey"),
115        checkNotNull(columnKey, "columnKey"),
116        checkNotNull(value, "value"));
117  }
118
119  /**
120   * A builder for creating immutable table instances, especially {@code public static final} tables
121   * ("constant tables"). Example:
122   *
123   * <pre>{@code
124   * static final ImmutableTable<Integer, Character, String> SPREADSHEET =
125   *     new ImmutableTable.Builder<Integer, Character, String>()
126   *         .put(1, 'A', "foo")
127   *         .put(1, 'B', "bar")
128   *         .put(2, 'A', "baz")
129   *         .buildOrThrow();
130   * }</pre>
131   *
132   * <p>By default, the order in which cells are added to the builder determines the iteration
133   * ordering of all views in the returned table, with {@link #putAll} following the {@link
134   * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy}
135   * is called, the views are sorted by the supplied comparators.
136   *
137   * <p>For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object,
138   * Object)} are even more convenient.
139   *
140   * <p>Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to
141   * build multiple tables in series. Each table is a superset of the tables created before it.
142   *
143   * @since 11.0
144   */
145  @DoNotMock
146  public static final class Builder<R, C, V> {
147    private final List<Cell<R, C, V>> cells = Lists.newArrayList();
148    @CheckForNull private Comparator<? super R> rowComparator;
149    @CheckForNull private Comparator<? super C> columnComparator;
150
151    /**
152     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
153     * ImmutableTable#builder}.
154     */
155    public Builder() {}
156
157    /** Specifies the ordering of the generated table's rows. */
158    @CanIgnoreReturnValue
159    public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
160      this.rowComparator = checkNotNull(rowComparator, "rowComparator");
161      return this;
162    }
163
164    /** Specifies the ordering of the generated table's columns. */
165    @CanIgnoreReturnValue
166    public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) {
167      this.columnComparator = checkNotNull(columnComparator, "columnComparator");
168      return this;
169    }
170
171    /**
172     * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built
173     * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail.
174     */
175    @CanIgnoreReturnValue
176    public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
177      cells.add(cellOf(rowKey, columnKey, value));
178      return this;
179    }
180
181    /**
182     * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key
183     * pairs are not allowed and will cause {@link #build} to fail.
184     */
185    @CanIgnoreReturnValue
186    public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) {
187      if (cell instanceof Tables.ImmutableCell) {
188        checkNotNull(cell.getRowKey(), "row");
189        checkNotNull(cell.getColumnKey(), "column");
190        checkNotNull(cell.getValue(), "value");
191        @SuppressWarnings("unchecked") // all supported methods are covariant
192        Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
193        cells.add(immutableCell);
194      } else {
195        put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
196      }
197      return this;
198    }
199
200    /**
201     * Associates all of the given table's keys and values in the built table. Duplicate row key
202     * column key pairs are not allowed, and will cause {@link #build} to fail.
203     *
204     * @throws NullPointerException if any key or value in {@code table} is null
205     */
206    @CanIgnoreReturnValue
207    public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) {
208      for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
209        put(cell);
210      }
211      return this;
212    }
213
214    @CanIgnoreReturnValue
215    Builder<R, C, V> combine(Builder<R, C, V> other) {
216      this.cells.addAll(other.cells);
217      return this;
218    }
219
220    /**
221     * Returns a newly-created immutable table.
222     *
223     * <p>Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method
224     * will throw an exception if there are duplicate key pairs. The {@code build()} method will
225     * soon be deprecated.
226     *
227     * @throws IllegalArgumentException if duplicate key pairs were added
228     */
229    public ImmutableTable<R, C, V> build() {
230      return buildOrThrow();
231    }
232
233    /**
234     * Returns a newly-created immutable table, or throws an exception if duplicate key pairs were
235     * added.
236     *
237     * @throws IllegalArgumentException if duplicate key pairs were added
238     * @since 31.0
239     */
240    public ImmutableTable<R, C, V> buildOrThrow() {
241      int size = cells.size();
242      switch (size) {
243        case 0:
244          return of();
245        case 1:
246          return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells));
247        default:
248          return RegularImmutableTable.forCells(cells, rowComparator, columnComparator);
249      }
250    }
251  }
252
253  ImmutableTable() {}
254
255  @Override
256  public ImmutableSet<Cell<R, C, V>> cellSet() {
257    return (ImmutableSet<Cell<R, C, V>>) super.cellSet();
258  }
259
260  @Override
261  abstract ImmutableSet<Cell<R, C, V>> createCellSet();
262
263  @Override
264  final UnmodifiableIterator<Cell<R, C, V>> cellIterator() {
265    throw new AssertionError("should never be called");
266  }
267
268  @Override
269  public ImmutableCollection<V> values() {
270    return (ImmutableCollection<V>) super.values();
271  }
272
273  @Override
274  abstract ImmutableCollection<V> createValues();
275
276  @Override
277  final Iterator<V> valuesIterator() {
278    throw new AssertionError("should never be called");
279  }
280
281  /**
282   * {@inheritDoc}
283   *
284   * @throws NullPointerException if {@code columnKey} is {@code null}
285   */
286  @Override
287  public ImmutableMap<R, V> column(C columnKey) {
288    checkNotNull(columnKey, "columnKey");
289    return MoreObjects.firstNonNull(
290        (ImmutableMap<R, V>) columnMap().get(columnKey), ImmutableMap.<R, V>of());
291  }
292
293  @Override
294  public ImmutableSet<C> columnKeySet() {
295    return columnMap().keySet();
296  }
297
298  /**
299   * {@inheritDoc}
300   *
301   * <p>The value {@code Map<R, V>} instances in the returned map are {@link ImmutableMap} instances
302   * as well.
303   */
304  @Override
305  public abstract ImmutableMap<C, Map<R, V>> columnMap();
306
307  /**
308   * {@inheritDoc}
309   *
310   * @throws NullPointerException if {@code rowKey} is {@code null}
311   */
312  @Override
313  public ImmutableMap<C, V> row(R rowKey) {
314    checkNotNull(rowKey, "rowKey");
315    return MoreObjects.firstNonNull(
316        (ImmutableMap<C, V>) rowMap().get(rowKey), ImmutableMap.<C, V>of());
317  }
318
319  @Override
320  public ImmutableSet<R> rowKeySet() {
321    return rowMap().keySet();
322  }
323
324  /**
325   * {@inheritDoc}
326   *
327   * <p>The value {@code Map<C, V>} instances in the returned map are {@link ImmutableMap} instances
328   * as well.
329   */
330  @Override
331  public abstract ImmutableMap<R, Map<C, V>> rowMap();
332
333  @Override
334  public boolean contains(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
335    return get(rowKey, columnKey) != null;
336  }
337
338  @Override
339  public boolean containsValue(@CheckForNull Object value) {
340    return values().contains(value);
341  }
342
343  /**
344   * Guaranteed to throw an exception and leave the table unmodified.
345   *
346   * @throws UnsupportedOperationException always
347   * @deprecated Unsupported operation.
348   */
349  @Deprecated
350  @Override
351  @DoNotCall("Always throws UnsupportedOperationException")
352  public final void clear() {
353    throw new UnsupportedOperationException();
354  }
355
356  /**
357   * Guaranteed to throw an exception and leave the table unmodified.
358   *
359   * @throws UnsupportedOperationException always
360   * @deprecated Unsupported operation.
361   */
362  @CanIgnoreReturnValue
363  @Deprecated
364  @Override
365  @DoNotCall("Always throws UnsupportedOperationException")
366  @CheckForNull
367  public final V put(R rowKey, C columnKey, V value) {
368    throw new UnsupportedOperationException();
369  }
370
371  /**
372   * Guaranteed to throw an exception and leave the table unmodified.
373   *
374   * @throws UnsupportedOperationException always
375   * @deprecated Unsupported operation.
376   */
377  @Deprecated
378  @Override
379  @DoNotCall("Always throws UnsupportedOperationException")
380  public final void putAll(Table<? extends R, ? extends C, ? extends V> table) {
381    throw new UnsupportedOperationException();
382  }
383
384  /**
385   * Guaranteed to throw an exception and leave the table unmodified.
386   *
387   * @throws UnsupportedOperationException always
388   * @deprecated Unsupported operation.
389   */
390  @CanIgnoreReturnValue
391  @Deprecated
392  @Override
393  @DoNotCall("Always throws UnsupportedOperationException")
394  @CheckForNull
395  public final V remove(@CheckForNull Object rowKey, @CheckForNull Object columnKey) {
396    throw new UnsupportedOperationException();
397  }
398
399  /** Creates the common serialized form for this table. */
400  abstract SerializedForm createSerializedForm();
401
402  /**
403   * Serialized type for all ImmutableTable instances. It captures the logical contents and
404   * preserves iteration order of all views.
405   */
406  static final class SerializedForm implements Serializable {
407    private final Object[] rowKeys;
408    private final Object[] columnKeys;
409
410    private final Object[] cellValues;
411    private final int[] cellRowIndices;
412    private final int[] cellColumnIndices;
413
414    private SerializedForm(
415        Object[] rowKeys,
416        Object[] columnKeys,
417        Object[] cellValues,
418        int[] cellRowIndices,
419        int[] cellColumnIndices) {
420      this.rowKeys = rowKeys;
421      this.columnKeys = columnKeys;
422      this.cellValues = cellValues;
423      this.cellRowIndices = cellRowIndices;
424      this.cellColumnIndices = cellColumnIndices;
425    }
426
427    static SerializedForm create(
428        ImmutableTable<?, ?, ?> table, int[] cellRowIndices, int[] cellColumnIndices) {
429      return new SerializedForm(
430          table.rowKeySet().toArray(),
431          table.columnKeySet().toArray(),
432          table.values().toArray(),
433          cellRowIndices,
434          cellColumnIndices);
435    }
436
437    Object readResolve() {
438      if (cellValues.length == 0) {
439        return of();
440      }
441      if (cellValues.length == 1) {
442        return of(rowKeys[0], columnKeys[0], cellValues[0]);
443      }
444      ImmutableList.Builder<Cell<Object, Object, Object>> cellListBuilder =
445          new ImmutableList.Builder<>(cellValues.length);
446      for (int i = 0; i < cellValues.length; i++) {
447        cellListBuilder.add(
448            cellOf(rowKeys[cellRowIndices[i]], columnKeys[cellColumnIndices[i]], cellValues[i]));
449      }
450      return RegularImmutableTable.forOrderedComponents(
451          cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys));
452    }
453
454    private static final long serialVersionUID = 0;
455  }
456
457  final Object writeReplace() {
458    return createSerializedForm();
459  }
460
461  @GwtIncompatible // serialization
462  @J2ktIncompatible
463  private void readObject(ObjectInputStream stream) throws InvalidObjectException {
464    throw new InvalidObjectException("Use SerializedForm");
465  }
466}