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