View Javadoc
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 }