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