• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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")
of()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. */
of(R rowKey, C columnKey, V value)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    */
copyOf( Table<? extends R, ? extends C, ? extends V> table)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    */
builder()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    */
cellOf(R rowKey, C columnKey, V value)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      */
Builder()166     public Builder() {}
167 
168     /**
169      * Specifies the ordering of the generated table's rows.
170      */
orderRowsBy(Comparator<? super R> rowComparator)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      */
orderColumnsBy( Comparator<? super C> columnComparator)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      */
put(R rowKey, C columnKey, V value)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      */
put( Cell<? extends R, ? extends C, ? extends V> cell)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      */
putAll( Table<? extends R, ? extends C, ? extends V> table)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      */
build()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 
ImmutableTable()250   ImmutableTable() {}
251 
cellSet()252   @Override public ImmutableSet<Cell<R, C, V>> cellSet() {
253     return (ImmutableSet<Cell<R, C, V>>) super.cellSet();
254   }
255 
256   @Override
createCellSet()257   abstract ImmutableSet<Cell<R, C, V>> createCellSet();
258 
259   @Override
cellIterator()260   final UnmodifiableIterator<Cell<R, C, V>> cellIterator() {
261     throw new AssertionError("should never be called");
262   }
263 
264   @Override
values()265   public ImmutableCollection<V> values() {
266     return (ImmutableCollection<V>) super.values();
267   }
268 
269   @Override
createValues()270   abstract ImmutableCollection<V> createValues();
271 
272   @Override
valuesIterator()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    */
column(C columnKey)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 
columnKeySet()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    */
columnMap()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    */
row(R rowKey)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 
rowKeySet()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    */
rowMap()323   @Override public abstract ImmutableMap<R, Map<C, V>> rowMap();
324 
325   @Override
contains(@ullable Object rowKey, @Nullable Object columnKey)326   public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
327     return get(rowKey, columnKey) != null;
328   }
329 
330   @Override
containsValue(@ullable Object value)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    */
clear()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    */
put(R rowKey, C columnKey, V value)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    */
putAll( Table<? extends R, ? extends C, ? extends V> table)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    */
remove(Object rowKey, Object columnKey)372   @Deprecated @Override public final V remove(Object rowKey, Object columnKey) {
373     throw new UnsupportedOperationException();
374   }
375 }
376