1 /*
2 * Copyright (C) 2009 The Guava Authors
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
17 package com.google.common.collect;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.base.Objects;
23
24 import java.util.Comparator;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28
29 import javax.annotation.Nullable;
30
31 /**
32 * An immutable {@link Table} with reliable user-specified iteration order.
33 * Does not permit null keys or values.
34 *
35 * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
36 * it has no public or protected constructors. Thus, instances of this class are
37 * guaranteed to be immutable.
38 *
39 * <p>See the Guava User Guide article on <a href=
40 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained">
41 * immutable collections</a>.
42 *
43 * @author Gregory Kick
44 * @since 11.0
45 */
46 @GwtCompatible
47 // TODO(gak): make serializable
48 public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V> {
49 private static final ImmutableTable<Object, Object, Object> EMPTY
50 = new SparseImmutableTable<Object, Object, Object>(
51 ImmutableList.<Cell<Object, Object, Object>>of(),
52 ImmutableSet.of(), ImmutableSet.of());
53
54 /** Returns an empty immutable table. */
55 @SuppressWarnings("unchecked")
56 public static <R, C, V> ImmutableTable<R, C, V> of() {
57 return (ImmutableTable<R, C, V>) EMPTY;
58 }
59
60 /** Returns an immutable table containing a single cell. */
61 public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey,
62 C columnKey, V value) {
63 return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value);
64 }
65
66 /**
67 * Returns an immutable copy of the provided table.
68 *
69 * <p>The {@link Table#cellSet()} iteration order of the provided table
70 * determines the iteration ordering of all views in the returned table. Note
71 * that some views of the original table and the copied table may have
72 * different iteration orders. For more control over the ordering, create a
73 * {@link Builder} and call {@link Builder#orderRowsBy},
74 * {@link Builder#orderColumnsBy}, and {@link Builder#putAll}
75 *
76 * <p>Despite the method name, this method attempts to avoid actually copying
77 * the data when it is safe to do so. The exact circumstances under which a
78 * copy will or will not be performed are undocumented and subject to change.
79 */
80 public static <R, C, V> ImmutableTable<R, C, V> copyOf(
81 Table<? extends R, ? extends C, ? extends V> table) {
82 if (table instanceof ImmutableTable) {
83 @SuppressWarnings("unchecked")
84 ImmutableTable<R, C, V> parameterizedTable
85 = (ImmutableTable<R, C, V>) table;
86 return parameterizedTable;
87 } else {
88 int size = table.size();
89 switch (size) {
90 case 0:
91 return of();
92 case 1:
93 Cell<? extends R, ? extends C, ? extends V> onlyCell
94 = Iterables.getOnlyElement(table.cellSet());
95 return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(),
96 onlyCell.getColumnKey(), onlyCell.getValue());
97 default:
98 ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder
99 = ImmutableSet.builder();
100 for (Cell<? extends R, ? extends C, ? extends V> cell :
101 table.cellSet()) {
102 /*
103 * Must cast to be able to create a Cell<R, C, V> rather than a
104 * Cell<? extends R, ? extends C, ? extends V>
105 */
106 cellSetBuilder.add(cellOf((R) cell.getRowKey(),
107 (C) cell.getColumnKey(), (V) cell.getValue()));
108 }
109 return RegularImmutableTable.forCells(cellSetBuilder.build());
110 }
111 }
112 }
113
114 /**
115 * Returns a new builder. The generated builder is equivalent to the builder
116 * created by the {@link Builder#ImmutableTable.Builder()} constructor.
117 */
118 public static <R, C, V> Builder<R, C, V> builder() {
119 return new Builder<R, C, V>();
120 }
121
122 /**
123 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are
124 * non-null, and returns a new entry with those values.
125 */
126 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) {
127 return Tables.immutableCell(checkNotNull(rowKey), checkNotNull(columnKey),
128 checkNotNull(value));
129 }
130
131 /**
132 * A builder for creating immutable table instances, especially {@code public
133 * static final} tables ("constant tables"). Example: <pre> {@code
134 *
135 * static final ImmutableTable<Integer, Character, String> SPREADSHEET =
136 * new ImmutableTable.Builder<Integer, Character, String>()
137 * .put(1, 'A', "foo")
138 * .put(1, 'B', "bar")
139 * .put(2, 'A', "baz")
140 * .build();}</pre>
141 *
142 * <p>By default, the order in which cells are added to the builder determines
143 * the iteration ordering of all views in the returned table, with {@link
144 * #putAll} following the {@link Table#cellSet()} iteration order. However, if
145 * {@link #orderRowsBy} or {@link #orderColumnsBy} is called, the views are
146 * sorted by the supplied comparators.
147 *
148 * For empty or single-cell immutable tables, {@link #of()} and
149 * {@link #of(Object, Object, Object)} are even more convenient.
150 *
151 * <p>Builder instances can be reused - it is safe to call {@link #build}
152 * multiple times to build multiple tables in series. Each table is a superset
153 * of the tables created before it.
154 *
155 * @since 11.0
156 */
157 public static final class Builder<R, C, V> {
158 private final List<Cell<R, C, V>> cells = Lists.newArrayList();
159 private Comparator<? super R> rowComparator;
160 private Comparator<? super C> columnComparator;
161
162 /**
163 * Creates a new builder. The returned builder is equivalent to the builder
164 * generated by {@link ImmutableTable#builder}.
165 */
166 public Builder() {}
167
168 /**
169 * Specifies the ordering of the generated table's rows.
170 */
171 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) {
172 this.rowComparator = checkNotNull(rowComparator);
173 return this;
174 }
175
176 /**
177 * Specifies the ordering of the generated table's columns.
178 */
179 public Builder<R, C, V> orderColumnsBy(
180 Comparator<? super C> columnComparator) {
181 this.columnComparator = checkNotNull(columnComparator);
182 return this;
183 }
184
185 /**
186 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code
187 * value} in the built table. Duplicate key pairs are not allowed and will
188 * cause {@link #build} to fail.
189 */
190 public Builder<R, C, V> put(R rowKey, C columnKey, V value) {
191 cells.add(cellOf(rowKey, columnKey, value));
192 return this;
193 }
194
195 /**
196 * Adds the given {@code cell} to the table, making it immutable if
197 * necessary. Duplicate key pairs are not allowed and will cause {@link
198 * #build} to fail.
199 */
200 public Builder<R, C, V> put(
201 Cell<? extends R, ? extends C, ? extends V> cell) {
202 if (cell instanceof Tables.ImmutableCell) {
203 checkNotNull(cell.getRowKey());
204 checkNotNull(cell.getColumnKey());
205 checkNotNull(cell.getValue());
206 @SuppressWarnings("unchecked") // all supported methods are covariant
207 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell;
208 cells.add(immutableCell);
209 } else {
210 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
211 }
212 return this;
213 }
214
215 /**
216 * Associates all of the given table's keys and values in the built table.
217 * Duplicate row key column key pairs are not allowed, and will cause
218 * {@link #build} to fail.
219 *
220 * @throws NullPointerException if any key or value in {@code table} is null
221 */
222 public Builder<R, C, V> putAll(
223 Table<? extends R, ? extends C, ? extends V> table) {
224 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
225 put(cell);
226 }
227 return this;
228 }
229
230 /**
231 * Returns a newly-created immutable table.
232 *
233 * @throws IllegalArgumentException if duplicate key pairs were added
234 */
235 public ImmutableTable<R, C, V> build() {
236 int size = cells.size();
237 switch (size) {
238 case 0:
239 return of();
240 case 1:
241 return new SingletonImmutableTable<R, C, V>(
242 Iterables.getOnlyElement(cells));
243 default:
244 return RegularImmutableTable.forCells(
245 cells, rowComparator, columnComparator);
246 }
247 }
248 }
249
250 ImmutableTable() {}
251
252 @Override public ImmutableSet<Cell<R, C, V>> cellSet() {
253 return (ImmutableSet<Cell<R, C, V>>) super.cellSet();
254 }
255
256 @Override
257 abstract ImmutableSet<Cell<R, C, V>> createCellSet();
258
259 @Override
260 final UnmodifiableIterator<Cell<R, C, V>> cellIterator() {
261 throw new AssertionError("should never be called");
262 }
263
264 @Override
265 public ImmutableCollection<V> values() {
266 return (ImmutableCollection<V>) super.values();
267 }
268
269 @Override
270 abstract ImmutableCollection<V> createValues();
271
272 @Override
273 final Iterator<V> valuesIterator() {
274 throw new AssertionError("should never be called");
275 }
276
277 /**
278 * {@inheritDoc}
279 *
280 * @throws NullPointerException if {@code columnKey} is {@code null}
281 */
282 @Override public ImmutableMap<R, V> column(C columnKey) {
283 checkNotNull(columnKey);
284 return Objects.firstNonNull(
285 (ImmutableMap<R, V>) columnMap().get(columnKey),
286 ImmutableMap.<R, V>of());
287 }
288
289 @Override public ImmutableSet<C> columnKeySet() {
290 return columnMap().keySet();
291 }
292
293 /**
294 * {@inheritDoc}
295 *
296 * <p>The value {@code Map<R, V>} instances in the returned map are
297 * {@link ImmutableMap} instances as well.
298 */
299 @Override public abstract ImmutableMap<C, Map<R, V>> columnMap();
300
301 /**
302 * {@inheritDoc}
303 *
304 * @throws NullPointerException if {@code rowKey} is {@code null}
305 */
306 @Override public ImmutableMap<C, V> row(R rowKey) {
307 checkNotNull(rowKey);
308 return Objects.firstNonNull(
309 (ImmutableMap<C, V>) rowMap().get(rowKey),
310 ImmutableMap.<C, V>of());
311 }
312
313 @Override public ImmutableSet<R> rowKeySet() {
314 return rowMap().keySet();
315 }
316
317 /**
318 * {@inheritDoc}
319 *
320 * <p>The value {@code Map<C, V>} instances in the returned map are
321 * {@link ImmutableMap} instances as well.
322 */
323 @Override public abstract ImmutableMap<R, Map<C, V>> rowMap();
324
325 @Override
326 public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
327 return get(rowKey, columnKey) != null;
328 }
329
330 @Override
331 public boolean containsValue(@Nullable Object value) {
332 return values().contains(value);
333 }
334
335 /**
336 * Guaranteed to throw an exception and leave the table unmodified.
337 *
338 * @throws UnsupportedOperationException always
339 * @deprecated Unsupported operation.
340 */
341 @Deprecated @Override public final void clear() {
342 throw new UnsupportedOperationException();
343 }
344
345 /**
346 * Guaranteed to throw an exception and leave the table unmodified.
347 *
348 * @throws UnsupportedOperationException always
349 * @deprecated Unsupported operation.
350 */
351 @Deprecated @Override public final V put(R rowKey, C columnKey, V value) {
352 throw new UnsupportedOperationException();
353 }
354
355 /**
356 * Guaranteed to throw an exception and leave the table unmodified.
357 *
358 * @throws UnsupportedOperationException always
359 * @deprecated Unsupported operation.
360 */
361 @Deprecated @Override public final void putAll(
362 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 @Override public final V remove(Object rowKey, Object columnKey) {
373 throw new UnsupportedOperationException();
374 }
375 }