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