1/*
2 * Copyright (C) 2009 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20
21import com.google.common.annotations.Beta;
22import com.google.common.annotations.GwtCompatible;
23
24import java.util.Comparator;
25import java.util.List;
26import java.util.Map;
27
28import javax.annotation.Nullable;
29
30/**
31 * An immutable {@link Table} with reliable user-specified iteration order.
32 * Does not permit null keys or values.
33 *
34 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
35 * it has no public or protected constructors. Thus, instances of this class are
36 * guaranteed to be immutable.
37 *
38 * @author gak@google.com (Gregory Kick)
39 * @since 11.0
40 */
41@Beta
42@GwtCompatible
43// TODO(gak): make serializable
44public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> {
45  /** Returns an empty immutable table. */
46  @SuppressWarnings("unchecked")
47  public static final <R, C, V> ImmutableTable<R, C, V> of() {
48    return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE;
49  }
50
51  /** Returns an immutable table containing a single cell. */
52  public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey,
53      C columnKey, V value) {
54    return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value);
55  }
56
57  /**
58   * Returns an immutable copy of the provided table.
59   *
60   * <p>The {@link Table#cellSet()} iteration order of the provided table
61   * determines the iteration ordering of all views in the returned table. Note
62   * that some views of the original table and the copied table may have
63   * different iteration orders. For more control over the ordering, create a
64   * {@link Builder} and call {@link Builder#orderRowsBy},
65   * {@link Builder#orderColumnsBy}, and {@link Builder#putAll}
66   *
67   * <p>Despite the method name, this method attempts to avoid actually copying
68   * the data when it is safe to do so. The exact circumstances under which a
69   * copy will or will not be performed are undocumented and subject to change.
70   */
71  public static final <R, C, V> ImmutableTable<R, C, V> copyOf(
72      Table<? extends R, ? extends C, ? extends V> table) {
73    if (table instanceof ImmutableTable<?, ?, ?>) {
74      @SuppressWarnings("unchecked")
75      ImmutableTable<R, C, V> parameterizedTable
76          = (ImmutableTable<R, C, V>) table;
77      return parameterizedTable;
78    } else {
79      int size = table.size();
80      switch (size) {
81        case 0:
82          return of();
83        case 1:
84          Cell<? extends R, ? extends C, ? extends V> onlyCell
85              = Iterables.getOnlyElement(table.cellSet());
86          return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(),
87              onlyCell.getColumnKey(), onlyCell.getValue());
88        default:
89          ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder
90              = ImmutableSet.builder();
91          for (Cell<? extends R, ? extends C, ? extends V> cell :
92              table.cellSet()) {
93            /*
94             * Must cast to be able to create a Cell<R, C, V> rather than a
95             * Cell<? extends R, ? extends C, ? extends V>
96             */
97            cellSetBuilder.add(cellOf((R) cell.getRowKey(),
98                (C) cell.getColumnKey(), (V) cell.getValue()));
99          }
100          return RegularImmutableTable.forCells(cellSetBuilder.build());
101      }
102    }
103  }
104
105  /**
106   * Returns a new builder. The generated builder is equivalent to the builder
107   * created by the {@link Builder#Builder()} constructor.
108   */
109  public static final <R, C, V> Builder<R, C, V> builder() {
110    return new Builder<R, C, V>();
111  }
112
113  /**
114   * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are
115   * non-null, and returns a new entry with those values.
116   */
117  static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
118    return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey),
119        checkNotNull(value));
120  }
121
122  /**
123   * A builder for creating immutable table instances, especially {@code public
124   * static final} tables ("constant tables"). Example: <pre>   {@code
125   *
126   *   static final ImmutableTable<Integer, Character, String> SPREADSHEET =
127   *       new ImmutableTable.Builder<Integer, Character, String>()
128   *           .put(1, 'A', "foo")
129   *           .put(1, 'B', "bar")
130   *           .put(2, 'A', "baz")
131   *           .build();}</pre>
132   *
133   * <p>By default, the order in which cells are added to the builder determines
134   * the iteration ordering of all views in the returned table, with {@link
135   * #putAll} following the {@link Table#cellSet()} iteration order. However, if
136   * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are
137   * sorted by the supplied comparators.
138   *
139   * For empty or single-cell immutable tables, {@link #of()} and
140   * {@link #of(Object, Object, Object)} are even more convenient.
141   *
142   * <p>Builder instances can be reused - it is safe to call {@link #build}
143   * multiple times to build multiple tables in series. Each table is a superset
144   * of the tables created before it.
145   *
146   * @since 11.0
147   */
148  public static final class Builder<R, C, V> {
149    private final List<Cell<R, C, V>> cells = Lists.newArrayList();
150    private Comparator<? super R> rowComparator;
151    private Comparator<? super C> columnComparator;
152
153    /**
154     * Creates a new builder. The returned builder is equivalent to the builder
155     * generated by {@link ImmutableTable#builder}.
156     */
157    public Builder() {}
158
159    /**
160     * Specifies the ordering of the generated table's rows.
161     */
162    public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
163      this.rowComparator = checkNotNull(rowComparator);
164      return this;
165    }
166
167    /**
168     * Specifies the ordering of the generated table's columns.
169     */
170    public Builder<R, C, V> orderColumnsBy(
171        Comparator<? super C> columnComparator) {
172      this.columnComparator = checkNotNull(columnComparator);
173      return this;
174    }
175
176    /**
177     * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code
178     * value} in the built table. Duplicate key pairs are not allowed and will
179     * cause {@link #build} to fail.
180     */
181    public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
182      cells.add(cellOf(rowKey, columnKey, value));
183      return this;
184    }
185
186    /**
187     * Adds the given {@code cell} to the table, making it immutable if
188     * necessary. Duplicate key pairs are not allowed and will cause {@link
189     * #build} to fail.
190     */
191    public Builder<R, C, V> put(
192        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(
214        Table<? extends R, ? extends C, ? extends V> table) {
215      for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
216        put(cell);
217      }
218      return this;
219    }
220
221    /**
222     * Returns a newly-created immutable table.
223     *
224     * @throws IllegalArgumentException if duplicate key pairs were added
225     */
226    public ImmutableTable<R, C, V> build() {
227      int size = cells.size();
228      switch (size) {
229        case 0:
230          return of();
231        case 1:
232          return new SingletonImmutableTable<R, C, V>(
233              Iterables.getOnlyElement(cells));
234        default:
235         return RegularImmutableTable.forCells(
236             cells, rowComparator, columnComparator);
237      }
238    }
239  }
240
241  ImmutableTable() {}
242
243  @Override public abstract ImmutableSet<Cell<R, C, V>> cellSet();
244
245  /**
246   * {@inheritDoc}
247   *
248   * @throws NullPointerException if {@code columnKey} is {@code null}
249   */
250  @Override public abstract ImmutableMap<R, V> column(C columnKey);
251
252  @Override public abstract ImmutableSet<C> columnKeySet();
253
254  /**
255   * {@inheritDoc}
256   *
257   * <p>The value {@code Map<R, V>}s in the returned map are
258   * {@link ImmutableMap}s as well.
259   */
260  @Override public abstract ImmutableMap<C, Map<R, V>> columnMap();
261
262  /**
263   * {@inheritDoc}
264   *
265   * @throws NullPointerException if {@code rowKey} is {@code null}
266   */
267  @Override public abstract ImmutableMap<C, V> row(R rowKey);
268
269  @Override public abstract ImmutableSet<R> rowKeySet();
270
271  /**
272   * {@inheritDoc}
273   *
274   * <p>The value {@code Map<C, V>}s in the returned map are
275   * {@link ImmutableMap}s as well.
276   */
277  @Override public abstract ImmutableMap<R, Map<C, V>> rowMap();
278
279  /**
280   * Guaranteed to throw an exception and leave the table unmodified.
281   *
282   * @throws UnsupportedOperationException always
283   */
284  @Override public final void clear() {
285    throw new UnsupportedOperationException();
286  }
287
288  /**
289   * Guaranteed to throw an exception and leave the table unmodified.
290   *
291   * @throws UnsupportedOperationException always
292   */
293  @Override public final V put(R rowKey, C columnKey, V value) {
294    throw new UnsupportedOperationException();
295  }
296
297  /**
298   * Guaranteed to throw an exception and leave the table unmodified.
299   *
300   * @throws UnsupportedOperationException always
301   */
302  @Override public final void putAll(
303      Table<? extends R, ? extends C, ? extends V> table) {
304    throw new UnsupportedOperationException();
305  }
306
307  /**
308   * Guaranteed to throw an exception and leave the table unmodified.
309   *
310   * @throws UnsupportedOperationException always
311   */
312  @Override public final V remove(Object rowKey, Object columnKey) {
313    throw new UnsupportedOperationException();
314  }
315
316  @Override public boolean equals(@Nullable Object obj) {
317    if (obj == this) {
318      return true;
319    } else if (obj instanceof Table<?, ?, ?>) {
320      Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
321      return this.cellSet().equals(that.cellSet());
322    } else {
323      return false;
324    }
325  }
326
327  @Override public int hashCode() {
328    return cellSet().hashCode();
329  }
330
331  @Override public String toString() {
332    return rowMap().toString();
333  }
334}
335