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;
022
023import java.util.Comparator;
024import java.util.List;
025import java.util.Map;
026
027import javax.annotation.Nullable;
028
029/**
030 * An immutable {@link Table} with reliable user-specified iteration order.
031 * Does not permit null keys or values.
032 *
033 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
034 * it has no public or protected constructors. Thus, instances of this class are
035 * guaranteed to be immutable.
036 *
037 * <p>See the Guava User Guide article on <a href=
038 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
039 * immutable collections</a>.
040 *
041 * @author Gregory Kick
042 * @since 11.0
043 */
044@GwtCompatible
045// TODO(gak): make serializable
046public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> {
047  /** Returns an empty immutable table. */
048  @SuppressWarnings("unchecked")
049  public static final <R, C, V> ImmutableTable<R, C, V> of() {
050    return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE;
051  }
052
053  /** Returns an immutable table containing a single cell. */
054  public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey,
055      C columnKey, V value) {
056    return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value);
057  }
058
059  /**
060   * Returns an immutable copy of the provided table.
061   *
062   * <p>The {@link Table#cellSet()} iteration order of the provided table
063   * determines the iteration ordering of all views in the returned table. Note
064   * that some views of the original table and the copied table may have
065   * different iteration orders. For more control over the ordering, create a
066   * {@link Builder} and call {@link Builder#orderRowsBy},
067   * {@link Builder#orderColumnsBy}, and {@link Builder#putAll}
068   *
069   * <p>Despite the method name, this method attempts to avoid actually copying
070   * the data when it is safe to do so. The exact circumstances under which a
071   * copy will or will not be performed are undocumented and subject to change.
072   */
073  public static final <R, C, V> ImmutableTable<R, C, V> copyOf(
074      Table<? extends R, ? extends C, ? extends V> table) {
075    if (table instanceof ImmutableTable) {
076      @SuppressWarnings("unchecked")
077      ImmutableTable<R, C, V> parameterizedTable
078          = (ImmutableTable<R, C, V>) table;
079      return parameterizedTable;
080    } else {
081      int size = table.size();
082      switch (size) {
083        case 0:
084          return of();
085        case 1:
086          Cell<? extends R, ? extends C, ? extends V> onlyCell
087              = Iterables.getOnlyElement(table.cellSet());
088          return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(),
089              onlyCell.getColumnKey(), onlyCell.getValue());
090        default:
091          ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder
092              = ImmutableSet.builder();
093          for (Cell<? extends R, ? extends C, ? extends V> cell :
094              table.cellSet()) {
095            /*
096             * Must cast to be able to create a Cell<R, C, V> rather than a
097             * Cell<? extends R, ? extends C, ? extends V>
098             */
099            cellSetBuilder.add(cellOf((R) cell.getRowKey(),
100                (C) cell.getColumnKey(), (V) cell.getValue()));
101          }
102          return RegularImmutableTable.forCells(cellSetBuilder.build());
103      }
104    }
105  }
106
107  /**
108   * Returns a new builder. The generated builder is equivalent to the builder
109   * created by the {@link Builder#ImmutableTable.Builder()} constructor.
110   */
111  public static final <R, C, V> Builder<R, C, V> builder() {
112    return new Builder<R, C, V>();
113  }
114
115  /**
116   * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are
117   * non-null, and returns a new entry with those values.
118   */
119  static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
120    return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey),
121        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(
173        Comparator<? super C> columnComparator) {
174      this.columnComparator = checkNotNull(columnComparator);
175      return this;
176    }
177
178    /**
179     * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code
180     * value} in the built table. Duplicate key pairs are not allowed and will
181     * cause {@link #build} to fail.
182     */
183    public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
184      cells.add(cellOf(rowKey, columnKey, value));
185      return this;
186    }
187
188    /**
189     * Adds the given {@code cell} to the table, making it immutable if
190     * necessary. Duplicate key pairs are not allowed and will cause {@link
191     * #build} to fail.
192     */
193    public Builder<R, C, V> put(
194        Cell<? extends R, ? extends C, ? extends V> cell) {
195      if (cell instanceof Tables.ImmutableCell) {
196        checkNotNull(cell.getRowKey());
197        checkNotNull(cell.getColumnKey());
198        checkNotNull(cell.getValue());
199        @SuppressWarnings("unchecked") // all supported methods are covariant
200        Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
201        cells.add(immutableCell);
202      } else {
203        put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
204      }
205      return this;
206    }
207
208    /**
209     * Associates all of the given table's keys and values in the built table.
210     * Duplicate row key column key pairs are not allowed, and will cause
211     * {@link #build} to fail.
212     *
213     * @throws NullPointerException if any key or value in {@code table} is null
214     */
215    public Builder<R, C, V> putAll(
216        Table<? extends R, ? extends C, ? extends V> table) {
217      for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
218        put(cell);
219      }
220      return this;
221    }
222
223    /**
224     * Returns a newly-created immutable table.
225     *
226     * @throws IllegalArgumentException if duplicate key pairs were added
227     */
228    public ImmutableTable<R, C, V> build() {
229      int size = cells.size();
230      switch (size) {
231        case 0:
232          return of();
233        case 1:
234          return new SingletonImmutableTable<R, C, V>(
235              Iterables.getOnlyElement(cells));
236        default:
237         return RegularImmutableTable.forCells(
238             cells, rowComparator, columnComparator);
239      }
240    }
241  }
242
243  ImmutableTable() {}
244
245  @Override public abstract ImmutableSet<Cell<R, C, V>> cellSet();
246
247  /**
248   * {@inheritDoc}
249   *
250   * @throws NullPointerException if {@code columnKey} is {@code null}
251   */
252  @Override public abstract ImmutableMap<R, V> column(C columnKey);
253
254  @Override public abstract ImmutableSet<C> columnKeySet();
255
256  /**
257   * {@inheritDoc}
258   *
259   * <p>The value {@code Map<R, V>} instances in the returned map are
260   * {@link ImmutableMap} instances as well.
261   */
262  @Override public abstract ImmutableMap<C, Map<R, V>> columnMap();
263
264  /**
265   * {@inheritDoc}
266   *
267   * @throws NullPointerException if {@code rowKey} is {@code null}
268   */
269  @Override public abstract ImmutableMap<C, V> row(R rowKey);
270
271  @Override public abstract ImmutableSet<R> rowKeySet();
272
273  /**
274   * {@inheritDoc}
275   *
276   * <p>The value {@code Map<C, V>} instances in the returned map are
277   * {@link ImmutableMap} instances as well.
278   */
279  @Override public abstract ImmutableMap<R, Map<C, V>> rowMap();
280
281  /**
282   * Guaranteed to throw an exception and leave the table unmodified.
283   *
284   * @throws UnsupportedOperationException always
285   * @deprecated Unsupported operation.
286   */
287  @Deprecated @Override public final void clear() {
288    throw new UnsupportedOperationException();
289  }
290
291  /**
292   * Guaranteed to throw an exception and leave the table unmodified.
293   *
294   * @throws UnsupportedOperationException always
295   * @deprecated Unsupported operation.
296   */
297  @Deprecated @Override public final V put(R rowKey, C columnKey, V value) {
298    throw new UnsupportedOperationException();
299  }
300
301  /**
302   * Guaranteed to throw an exception and leave the table unmodified.
303   *
304   * @throws UnsupportedOperationException always
305   * @deprecated Unsupported operation.
306   */
307  @Deprecated @Override public final void putAll(
308      Table<? extends R, ? extends C, ? extends V> table) {
309    throw new UnsupportedOperationException();
310  }
311
312  /**
313   * Guaranteed to throw an exception and leave the table unmodified.
314   *
315   * @throws UnsupportedOperationException always
316   * @deprecated Unsupported operation.
317   */
318  @Deprecated @Override public final V remove(Object rowKey, Object columnKey) {
319    throw new UnsupportedOperationException();
320  }
321
322  @Override public boolean equals(@Nullable Object obj) {
323    if (obj == this) {
324      return true;
325    } else if (obj instanceof Table) {
326      Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
327      return this.cellSet().equals(that.cellSet());
328    } else {
329      return false;
330    }
331  }
332
333  @Override public int hashCode() {
334    return cellSet().hashCode();
335  }
336
337  @Override public String toString() {
338    return rowMap().toString();
339  }
340}