001    /*
002     * Copyright (C) 2009 Google Inc.
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.Beta;
022    import com.google.common.annotations.GwtCompatible;
023    
024    import java.util.Comparator;
025    import java.util.List;
026    import java.util.Map;
027    
028    import javax.annotation.Nullable;
029    
030    /**
031     * An immutable {@link Table} with reliable user-specified iteration order.
032     * Does not permit null keys or values.
033     *
034     * <p><b>Note</b>: Although this class is not final, it cannot be subclassed as
035     * it has no public or protected constructors. Thus, instances of this class are
036     * guaranteed to be immutable.
037     *
038     * @author [email protected] (Gregory Kick)
039     * @since 11.0
040     */
041    @Beta
042    @GwtCompatible
043    // TODO(gak): make serializable
044    public abstract class ImmutableTable<R, C, V> implements Table<R, C, V> {
045      /** Returns an empty immutable table. */
046      @SuppressWarnings("unchecked")
047      public static final <R, C, V> ImmutableTable<R, C, V> of() {
048        return (ImmutableTable<R, C, V>) EmptyImmutableTable.INSTANCE;
049      }
050    
051      /** Returns an immutable table containing a single cell. */
052      public static final <R, C, V> ImmutableTable<R, C, V> of(R rowKey,
053          C columnKey, V value) {
054        return new SingletonImmutableTable<R, C, V>(rowKey, columnKey, value);
055      }
056    
057      /**
058       * Returns an immutable copy of the provided table.
059       *
060       * <p>The {@link Table#cellSet()} iteration order of the provided table
061       * determines the iteration ordering of all views in the returned table. Note
062       * that some views of the original table and the copied table may have
063       * different iteration orders. For more control over the ordering, create a
064       * {@link Builder} and call {@link Builder#orderRowsBy},
065       * {@link Builder#orderColumnsBy}, and {@link Builder#putAll}
066       *
067       * <p>Despite the method name, this method attempts to avoid actually copying
068       * the data when it is safe to do so. The exact circumstances under which a
069       * copy will or will not be performed are undocumented and subject to change.
070       */
071      public static final <R, C, V> ImmutableTable<R, C, V> copyOf(
072          Table<? extends R, ? extends C, ? extends V> table) {
073        if (table instanceof ImmutableTable<?, ?, ?>) {
074          @SuppressWarnings("unchecked")
075          ImmutableTable<R, C, V> parameterizedTable
076              = (ImmutableTable<R, C, V>) table;
077          return parameterizedTable;
078        } else {
079          int size = table.size();
080          switch (size) {
081            case 0:
082              return of();
083            case 1:
084              Cell<? extends R, ? extends C, ? extends V> onlyCell
085                  = Iterables.getOnlyElement(table.cellSet());
086              return ImmutableTable.<R, C, V>of(onlyCell.getRowKey(),
087                  onlyCell.getColumnKey(), onlyCell.getValue());
088            default:
089              ImmutableSet.Builder<Cell<R, C, V>> cellSetBuilder
090                  = ImmutableSet.builder();
091              for (Cell<? extends R, ? extends C, ? extends V> cell :
092                  table.cellSet()) {
093                /*
094                 * Must cast to be able to create a Cell<R, C, V> rather than a
095                 * Cell<? extends R, ? extends C, ? extends V>
096                 */
097                cellSetBuilder.add(cellOf((R) cell.getRowKey(),
098                    (C) cell.getColumnKey(), (V) cell.getValue()));
099              }
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    }