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