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.Beta;
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.function.BinaryOperator;
037import java.util.function.Function;
038import java.util.stream.Collector;
039import javax.annotation.CheckForNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042/**
043 * A {@link Table} whose contents will never change, with many other important properties detailed
044 * at {@link ImmutableCollection}.
045 *
046 * <p>See the Guava User Guide article on <a href=
047 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>.
048 *
049 * @author Gregory Kick
050 * @since 11.0
051 */
052@GwtCompatible
053@ElementTypesAreNonnullByDefault
054public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V>
055    implements Serializable {
056
057  /**
058   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each
059   * input element is mapped to one cell in the returned table, with the rows, columns, and values
060   * generated by applying the specified functions.
061   *
062   * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time
063   * if the row, column, or value functions return null on any input.
064   *
065   * @since 33.2.0 (available since 21.0 in guava-jre)
066   */
067  @SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"})
068  @IgnoreJRERequirement // Users will use this only if they're already using streams.
069  @Beta // TODO: b/288085449 - Remove.
070  public static <T extends @Nullable Object, R, C, V>
071      Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
072          Function<? super T, ? extends R> rowFunction,
073          Function<? super T, ? extends C> columnFunction,
074          Function<? super T, ? extends V> valueFunction) {
075    return TableCollectors.toImmutableTable(rowFunction, columnFunction, valueFunction);
076  }
077
078  /**
079   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableTable}. Each
080   * input element is mapped to one cell in the returned table, with the rows, columns, and values
081   * generated by applying the specified functions. If multiple inputs are mapped to the same row
082   * and column pair, they will be combined with the specified merging function in encounter order.
083   *
084   * <p>The returned {@code Collector} will throw a {@code NullPointerException} at collection time
085   * if the row, column, value, or merging functions return null on any input.
086   *
087   * @since 33.2.0 (available since 21.0 in guava-jre)
088   */
089  @SuppressWarnings({"AndroidJdkLibsChecker", "Java7ApiChecker"})
090  @IgnoreJRERequirement // Users will use this only if they're already using streams.
091  @Beta // TODO: b/288085449 - Remove.
092  public static <T extends @Nullable Object, R, C, V>
093      Collector<T, ?, ImmutableTable<R, C, V>> toImmutableTable(
094          Function<? super T, ? extends R> rowFunction,
095          Function<? super T, ? extends C> columnFunction,
096          Function<? super T, ? extends V> valueFunction,
097          BinaryOperator<V> mergeFunction) {
098    return TableCollectors.toImmutableTable(
099        rowFunction, columnFunction, valueFunction, mergeFunction);
100  }
101
102  /**
103   * Returns an empty immutable table.
104   *
105   * <p><b>Performance note:</b> the instance returned is a singleton.
106   */
107  @SuppressWarnings("unchecked")
108  public static <R, C, V> ImmutableTable<R, C, V> of() {
109    return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY;
110  }
111
112  /** Returns an immutable table containing a single cell. */
113  public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) {
114    return new SingletonImmutableTable<>(rowKey, columnKey, value);
115  }
116
117  /**
118   * Returns an immutable copy of the provided table.
119   *
120   * <p>The {@link Table#cellSet()} iteration order of the provided table determines the iteration
121   * ordering of all views in the returned table. Note that some views of the original table and the
122   * copied table may have different iteration orders. For more control over the ordering, create a
123   * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and
124   * {@link Builder#putAll}
125   *
126   * <p>Despite the method name, this method attempts to avoid actually copying the data when it is
127   * safe to do so. The exact circumstances under which a copy will or will not be performed are
128   * undocumented and subject to change.
129   */
130  public static <R, C, V> ImmutableTable<R, C, V> copyOf(
131      Table<? extends R, ? extends C, ? extends V> table) {
132    if (table instanceof ImmutableTable) {
133      @SuppressWarnings("unchecked")
134      ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table;
135      return parameterizedTable;
136    } else {
137      return copyOf(table.cellSet());
138    }
139  }
140
141  static <R, C, V> ImmutableTable<R, C, V> copyOf(
142      Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) {
143    ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder();
144    for (Cell<? extends R, ? extends C, ? extends V> cell : cells) {
145      builder.put(cell);
146    }
147    return builder.build();
148  }
149
150  /**
151   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
152   * Builder#Builder() ImmutableTable.Builder()} constructor.
153   */
154  public static <R, C, V> Builder<R, C, V> builder() {
155    return new Builder<>();
156  }
157
158  /**
159   * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a
160   * new entry with those values.
161   */
162  static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
163    return Tables.immutableCell(
164        checkNotNull(rowKey, "rowKey"),
165        checkNotNull(columnKey, "columnKey"),
166        checkNotNull(value, "value"));
167  }
168
169  /**
170   * A builder for creating immutable table instances, especially {@code public static final} tables
171   * ("constant tables"). Example:
172   *
173   * <pre>{@code
174   * static final ImmutableTable<Integer, Character, String> SPREADSHEET =
175   *     new ImmutableTable.Builder<Integer, Character, String>()
176   *         .put(1, 'A', "foo")
177   *         .put(1, 'B', "bar")
178   *         .put(2, 'A', "baz")
179   *         .buildOrThrow();
180   * }</pre>
181   *
182   * <p>By default, the order in which cells are added to the builder determines the iteration
183   * ordering of all views in the returned table, with {@link #putAll} following the {@link
184   * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy}
185   * is called, the views are sorted by the supplied comparators.
186   *
187   * <p>For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object,
188   * Object)} are even more convenient.
189   *
190   * <p>Builder instances can be reused - it is safe to call {@link #buildOrThrow} multiple times to
191   * build multiple tables in series. Each table is a superset of the tables created before it.
192   *
193   * @since 11.0
194   */
195  @DoNotMock
196  public static final class Builder<R, C, V> {
197    private final List<Cell<R, C, V>> cells = Lists.newArrayList();
198    @CheckForNull private Comparator<? super R> rowComparator;
199    @CheckForNull private Comparator<? super C> columnComparator;
200
201    /**
202     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
203     * ImmutableTable#builder}.
204     */
205    public Builder() {}
206
207    /** Specifies the ordering of the generated table's rows. */
208    @CanIgnoreReturnValue
209    public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
210      this.rowComparator = checkNotNull(rowComparator, "rowComparator");
211      return this;
212    }
213
214    /** Specifies the ordering of the generated table's columns. */
215    @CanIgnoreReturnValue
216    public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) {
217      this.columnComparator = checkNotNull(columnComparator, "columnComparator");
218      return this;
219    }
220
221    /**
222     * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built
223     * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail.
224     */
225    @CanIgnoreReturnValue
226    public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
227      cells.add(cellOf(rowKey, columnKey, value));
228      return this;
229    }
230
231    /**
232     * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key
233     * pairs are not allowed and will cause {@link #build} to fail.
234     */
235    @CanIgnoreReturnValue
236    public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) {
237      if (cell instanceof Tables.ImmutableCell) {
238        checkNotNull(cell.getRowKey(), "row");
239        checkNotNull(cell.getColumnKey(), "column");
240        checkNotNull(cell.getValue(), "value");
241        @SuppressWarnings("unchecked") // all supported methods are covariant
242        Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
243        cells.add(immutableCell);
244      } else {
245        put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
246      }
247      return this;
248    }
249
250    /**
251     * Associates all of the given table's keys and values in the built table. Duplicate row key
252     * column key pairs are not allowed, and will cause {@link #build} to fail.
253     *
254     * @throws NullPointerException if any key or value in {@code table} is null
255     */
256    @CanIgnoreReturnValue
257    public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) {
258      for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
259        put(cell);
260      }
261      return this;
262    }
263
264    @CanIgnoreReturnValue
265    Builder<R, C, V> combine(Builder<R, C, V> other) {
266      this.cells.addAll(other.cells);
267      return this;
268    }
269
270    /**
271     * Returns a newly-created immutable table.
272     *
273     * <p>Prefer the equivalent method {@link #buildOrThrow()} to make it explicit that the method
274     * will throw an exception if there are duplicate key pairs. The {@code build()} method will
275     * soon be deprecated.
276     *
277     * @throws IllegalArgumentException if duplicate key pairs were added
278     */
279    public ImmutableTable<R, C, V> build() {
280      return buildOrThrow();
281    }
282
283    /**
284     * Returns a newly-created immutable table, or throws an exception if duplicate key pairs were
285     * added.
286     *
287     * @throws IllegalArgumentException if duplicate key pairs were added
288     * @since 31.0
289     */
290    public ImmutableTable<R, C, V> buildOrThrow() {
291      int size = cells.size();
292      switch (size) {
293        case 0:
294          return of();
295        case 1:
296          return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells));
297        default:
298          return RegularImmutableTable.forCells(cells, rowComparator, columnComparator);
299      }
300    }
301  }
302
303  ImmutableTable() {}
304
305  @Override
306  public ImmutableSet<Cell<R, C, V>> cellSet() {
307    return (ImmutableSet<Cell<R, C, V>>) super.cellSet();
308  }
309
310  @Override
311  abstract ImmutableSet<Cell<R, C, V>> createCellSet();
312
313  @Override
314  final UnmodifiableIterator<Cell<R, C, V>> cellIterator() {
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 = 0xdecaf;
515}