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