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    
017    package com.google.common.collect;
018    
019    import static com.google.common.base.Preconditions.checkNotNull;
020    
021    import com.google.common.annotations.GwtCompatible;
022    
023    import java.util.Comparator;
024    import java.util.List;
025    import java.util.Map;
026    
027    import 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
046    public 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>}s in the returned map are
260       * {@link ImmutableMap}s 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>}s in the returned map are
277       * {@link ImmutableMap}s 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       */
286      @Override public final void clear() {
287        throw new UnsupportedOperationException();
288      }
289    
290      /**
291       * Guaranteed to throw an exception and leave the table unmodified.
292       *
293       * @throws UnsupportedOperationException always
294       */
295      @Override public final V put(R rowKey, C columnKey, V value) {
296        throw new UnsupportedOperationException();
297      }
298    
299      /**
300       * Guaranteed to throw an exception and leave the table unmodified.
301       *
302       * @throws UnsupportedOperationException always
303       */
304      @Override public final void putAll(
305          Table<? extends R, ? extends C, ? extends V> table) {
306        throw new UnsupportedOperationException();
307      }
308    
309      /**
310       * Guaranteed to throw an exception and leave the table unmodified.
311       *
312       * @throws UnsupportedOperationException always
313       */
314      @Override public final V remove(Object rowKey, Object columnKey) {
315        throw new UnsupportedOperationException();
316      }
317    
318      @Override public boolean equals(@Nullable Object obj) {
319        if (obj == this) {
320          return true;
321        } else if (obj instanceof Table) {
322          Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
323          return this.cellSet().equals(that.cellSet());
324        } else {
325          return false;
326        }
327      }
328    
329      @Override public int hashCode() {
330        return cellSet().hashCode();
331      }
332    
333      @Override public String toString() {
334        return rowMap().toString();
335      }
336    }