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 }