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 017package com.google.common.collect; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.GwtCompatible; 022import com.google.common.base.MoreObjects; 023import com.google.errorprone.annotations.CanIgnoreReturnValue; 024import com.google.errorprone.annotations.DoNotMock; 025import java.io.Serializable; 026import java.util.Comparator; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030import org.checkerframework.checker.nullness.compatqual.NullableDecl; 031 032/** 033 * A {@link Table} whose contents will never change, with many other important properties detailed 034 * at {@link ImmutableCollection}. 035 * 036 * <p>See the Guava User Guide article on <a href= 037 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>. 038 * 039 * @author Gregory Kick 040 * @since 11.0 041 */ 042@GwtCompatible 043public abstract class ImmutableTable<R, C, V> extends AbstractTable<R, C, V> 044 implements Serializable { 045 /** Returns an empty immutable table. */ 046 @SuppressWarnings("unchecked") 047 public static <R, C, V> ImmutableTable<R, C, V> of() { 048 return (ImmutableTable<R, C, V>) SparseImmutableTable.EMPTY; 049 } 050 051 /** Returns an immutable table containing a single cell. */ 052 public static <R, C, V> ImmutableTable<R, C, V> of(R rowKey, C columnKey, V value) { 053 return new SingletonImmutableTable<>(rowKey, columnKey, value); 054 } 055 056 /** 057 * Returns an immutable copy of the provided table. 058 * 059 * <p>The {@link Table#cellSet()} iteration order of the provided table determines the iteration 060 * ordering of all views in the returned table. Note that some views of the original table and the 061 * copied table may have different iteration orders. For more control over the ordering, create a 062 * {@link Builder} and call {@link Builder#orderRowsBy}, {@link Builder#orderColumnsBy}, and 063 * {@link Builder#putAll} 064 * 065 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 066 * safe to do so. The exact circumstances under which a copy will or will not be performed are 067 * undocumented and subject to change. 068 */ 069 public static <R, C, V> ImmutableTable<R, C, V> copyOf( 070 Table<? extends R, ? extends C, ? extends V> table) { 071 if (table instanceof ImmutableTable) { 072 @SuppressWarnings("unchecked") 073 ImmutableTable<R, C, V> parameterizedTable = (ImmutableTable<R, C, V>) table; 074 return parameterizedTable; 075 } else { 076 return copyOf(table.cellSet()); 077 } 078 } 079 080 private static <R, C, V> ImmutableTable<R, C, V> copyOf( 081 Iterable<? extends Cell<? extends R, ? extends C, ? extends V>> cells) { 082 ImmutableTable.Builder<R, C, V> builder = ImmutableTable.builder(); 083 for (Cell<? extends R, ? extends C, ? extends V> cell : cells) { 084 builder.put(cell); 085 } 086 return builder.build(); 087 } 088 089 /** 090 * Returns a new builder. The generated builder is equivalent to the builder created by the {@link 091 * Builder#Builder() ImmutableTable.Builder()} constructor. 092 */ 093 public static <R, C, V> Builder<R, C, V> builder() { 094 return new Builder<>(); 095 } 096 097 /** 098 * Verifies that {@code rowKey}, {@code columnKey} and {@code value} are non-null, and returns a 099 * new entry with those values. 100 */ 101 static <R, C, V> Cell<R, C, V> cellOf(R rowKey, C columnKey, V value) { 102 return Tables.immutableCell( 103 checkNotNull(rowKey, "rowKey"), 104 checkNotNull(columnKey, "columnKey"), 105 checkNotNull(value, "value")); 106 } 107 108 /** 109 * A builder for creating immutable table instances, especially {@code public static final} tables 110 * ("constant tables"). Example: 111 * 112 * <pre>{@code 113 * static final ImmutableTable<Integer, Character, String> SPREADSHEET = 114 * new ImmutableTable.Builder<Integer, Character, String>() 115 * .put(1, 'A', "foo") 116 * .put(1, 'B', "bar") 117 * .put(2, 'A', "baz") 118 * .build(); 119 * }</pre> 120 * 121 * <p>By default, the order in which cells are added to the builder determines the iteration 122 * ordering of all views in the returned table, with {@link #putAll} following the {@link 123 * Table#cellSet()} iteration order. However, if {@link #orderRowsBy} or {@link #orderColumnsBy} 124 * is called, the views are sorted by the supplied comparators. 125 * 126 * <p>For empty or single-cell immutable tables, {@link #of()} and {@link #of(Object, Object, 127 * Object)} are even more convenient. 128 * 129 * <p>Builder instances can be reused - it is safe to call {@link #build} multiple times to build 130 * multiple tables in series. Each table is a superset of the tables created before it. 131 * 132 * @since 11.0 133 */ 134 @DoNotMock 135 public static final class Builder<R, C, V> { 136 private final List<Cell<R, C, V>> cells = Lists.newArrayList(); 137 @NullableDecl private Comparator<? super R> rowComparator; 138 @NullableDecl private Comparator<? super C> columnComparator; 139 140 /** 141 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 142 * ImmutableTable#builder}. 143 */ 144 public Builder() {} 145 146 /** Specifies the ordering of the generated table's rows. */ 147 @CanIgnoreReturnValue 148 public Builder<R, C, V> orderRowsBy(Comparator<? super R> rowComparator) { 149 this.rowComparator = checkNotNull(rowComparator, "rowComparator"); 150 return this; 151 } 152 153 /** Specifies the ordering of the generated table's columns. */ 154 @CanIgnoreReturnValue 155 public Builder<R, C, V> orderColumnsBy(Comparator<? super C> columnComparator) { 156 this.columnComparator = checkNotNull(columnComparator, "columnComparator"); 157 return this; 158 } 159 160 /** 161 * Associates the ({@code rowKey}, {@code columnKey}) pair with {@code value} in the built 162 * table. Duplicate key pairs are not allowed and will cause {@link #build} to fail. 163 */ 164 @CanIgnoreReturnValue 165 public Builder<R, C, V> put(R rowKey, C columnKey, V value) { 166 cells.add(cellOf(rowKey, columnKey, value)); 167 return this; 168 } 169 170 /** 171 * Adds the given {@code cell} to the table, making it immutable if necessary. Duplicate key 172 * pairs are not allowed and will cause {@link #build} to fail. 173 */ 174 @CanIgnoreReturnValue 175 public Builder<R, C, V> put(Cell<? extends R, ? extends C, ? extends V> cell) { 176 if (cell instanceof Tables.ImmutableCell) { 177 checkNotNull(cell.getRowKey(), "row"); 178 checkNotNull(cell.getColumnKey(), "column"); 179 checkNotNull(cell.getValue(), "value"); 180 @SuppressWarnings("unchecked") // all supported methods are covariant 181 Cell<R, C, V> immutableCell = (Cell<R, C, V>) cell; 182 cells.add(immutableCell); 183 } else { 184 put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); 185 } 186 return this; 187 } 188 189 /** 190 * Associates all of the given table's keys and values in the built table. Duplicate row key 191 * column key pairs are not allowed, and will cause {@link #build} to fail. 192 * 193 * @throws NullPointerException if any key or value in {@code table} is null 194 */ 195 @CanIgnoreReturnValue 196 public Builder<R, C, V> putAll(Table<? extends R, ? extends C, ? extends V> table) { 197 for (Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) { 198 put(cell); 199 } 200 return this; 201 } 202 203 /** 204 * Returns a newly-created immutable table. 205 * 206 * @throws IllegalArgumentException if duplicate key pairs were added 207 */ 208 public ImmutableTable<R, C, V> build() { 209 int size = cells.size(); 210 switch (size) { 211 case 0: 212 return of(); 213 case 1: 214 return new SingletonImmutableTable<>(Iterables.getOnlyElement(cells)); 215 default: 216 return RegularImmutableTable.forCells(cells, rowComparator, columnComparator); 217 } 218 } 219 } 220 221 ImmutableTable() {} 222 223 @Override 224 public ImmutableSet<Cell<R, C, V>> cellSet() { 225 return (ImmutableSet<Cell<R, C, V>>) super.cellSet(); 226 } 227 228 @Override 229 abstract ImmutableSet<Cell<R, C, V>> createCellSet(); 230 231 @Override 232 final UnmodifiableIterator<Cell<R, C, V>> cellIterator() { 233 throw new AssertionError("should never be called"); 234 } 235 236 @Override 237 public ImmutableCollection<V> values() { 238 return (ImmutableCollection<V>) super.values(); 239 } 240 241 @Override 242 abstract ImmutableCollection<V> createValues(); 243 244 @Override 245 final Iterator<V> valuesIterator() { 246 throw new AssertionError("should never be called"); 247 } 248 249 /** 250 * {@inheritDoc} 251 * 252 * @throws NullPointerException if {@code columnKey} is {@code null} 253 */ 254 @Override 255 public ImmutableMap<R, V> column(C columnKey) { 256 checkNotNull(columnKey, "columnKey"); 257 return MoreObjects.firstNonNull( 258 (ImmutableMap<R, V>) columnMap().get(columnKey), ImmutableMap.<R, V>of()); 259 } 260 261 @Override 262 public ImmutableSet<C> columnKeySet() { 263 return columnMap().keySet(); 264 } 265 266 /** 267 * {@inheritDoc} 268 * 269 * <p>The value {@code Map<R, V>} instances in the returned map are {@link ImmutableMap} instances 270 * as well. 271 */ 272 @Override 273 public abstract ImmutableMap<C, Map<R, V>> columnMap(); 274 275 /** 276 * {@inheritDoc} 277 * 278 * @throws NullPointerException if {@code rowKey} is {@code null} 279 */ 280 @Override 281 public ImmutableMap<C, V> row(R rowKey) { 282 checkNotNull(rowKey, "rowKey"); 283 return MoreObjects.firstNonNull( 284 (ImmutableMap<C, V>) rowMap().get(rowKey), ImmutableMap.<C, V>of()); 285 } 286 287 @Override 288 public ImmutableSet<R> rowKeySet() { 289 return rowMap().keySet(); 290 } 291 292 /** 293 * {@inheritDoc} 294 * 295 * <p>The value {@code Map<C, V>} instances in the returned map are {@link ImmutableMap} instances 296 * as well. 297 */ 298 @Override 299 public abstract ImmutableMap<R, Map<C, V>> rowMap(); 300 301 @Override 302 public boolean contains(@NullableDecl Object rowKey, @NullableDecl Object columnKey) { 303 return get(rowKey, columnKey) != null; 304 } 305 306 @Override 307 public boolean containsValue(@NullableDecl Object value) { 308 return values().contains(value); 309 } 310 311 /** 312 * Guaranteed to throw an exception and leave the table unmodified. 313 * 314 * @throws UnsupportedOperationException always 315 * @deprecated Unsupported operation. 316 */ 317 @Deprecated 318 @Override 319 public final void clear() { 320 throw new UnsupportedOperationException(); 321 } 322 323 /** 324 * Guaranteed to throw an exception and leave the table unmodified. 325 * 326 * @throws UnsupportedOperationException always 327 * @deprecated Unsupported operation. 328 */ 329 @CanIgnoreReturnValue 330 @Deprecated 331 @Override 332 public final V put(R rowKey, C columnKey, V value) { 333 throw new UnsupportedOperationException(); 334 } 335 336 /** 337 * Guaranteed to throw an exception and leave the table unmodified. 338 * 339 * @throws UnsupportedOperationException always 340 * @deprecated Unsupported operation. 341 */ 342 @Deprecated 343 @Override 344 public final void putAll(Table<? extends R, ? extends C, ? extends V> table) { 345 throw new UnsupportedOperationException(); 346 } 347 348 /** 349 * Guaranteed to throw an exception and leave the table unmodified. 350 * 351 * @throws UnsupportedOperationException always 352 * @deprecated Unsupported operation. 353 */ 354 @CanIgnoreReturnValue 355 @Deprecated 356 @Override 357 public final V remove(Object rowKey, Object columnKey) { 358 throw new UnsupportedOperationException(); 359 } 360 361 /** Creates the common serialized form for this table. */ 362 abstract SerializedForm createSerializedForm(); 363 364 /** 365 * Serialized type for all ImmutableTable instances. It captures the logical contents and 366 * preserves iteration order of all views. 367 */ 368 static final class SerializedForm implements Serializable { 369 private final Object[] rowKeys; 370 private final Object[] columnKeys; 371 372 private final Object[] cellValues; 373 private final int[] cellRowIndices; 374 private final int[] cellColumnIndices; 375 376 private SerializedForm( 377 Object[] rowKeys, 378 Object[] columnKeys, 379 Object[] cellValues, 380 int[] cellRowIndices, 381 int[] cellColumnIndices) { 382 this.rowKeys = rowKeys; 383 this.columnKeys = columnKeys; 384 this.cellValues = cellValues; 385 this.cellRowIndices = cellRowIndices; 386 this.cellColumnIndices = cellColumnIndices; 387 } 388 389 static SerializedForm create( 390 ImmutableTable<?, ?, ?> table, int[] cellRowIndices, int[] cellColumnIndices) { 391 return new SerializedForm( 392 table.rowKeySet().toArray(), 393 table.columnKeySet().toArray(), 394 table.values().toArray(), 395 cellRowIndices, 396 cellColumnIndices); 397 } 398 399 Object readResolve() { 400 if (cellValues.length == 0) { 401 return of(); 402 } 403 if (cellValues.length == 1) { 404 return of(rowKeys[0], columnKeys[0], cellValues[0]); 405 } 406 ImmutableList.Builder<Cell<Object, Object, Object>> cellListBuilder = 407 new ImmutableList.Builder<>(cellValues.length); 408 for (int i = 0; i < cellValues.length; i++) { 409 cellListBuilder.add( 410 cellOf(rowKeys[cellRowIndices[i]], columnKeys[cellColumnIndices[i]], cellValues[i])); 411 } 412 return RegularImmutableTable.forOrderedComponents( 413 cellListBuilder.build(), ImmutableSet.copyOf(rowKeys), ImmutableSet.copyOf(columnKeys)); 414 } 415 416 private static final long serialVersionUID = 0; 417 } 418 419 final Object writeReplace() { 420 return createSerializedForm(); 421 } 422}