1 /* 2 * Copyright (C) 2008 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.collect; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import com.google.common.annotations.Beta; 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.base.Function; 25 import com.google.common.base.Objects; 26 import com.google.common.base.Supplier; 27 import com.google.common.collect.Table.Cell; 28 29 import java.io.Serializable; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.Iterator; 33 import java.util.Map; 34 import java.util.Set; 35 import java.util.SortedMap; 36 import java.util.SortedSet; 37 38 import javax.annotation.Nullable; 39 40 /** 41 * Provides static methods that involve a {@code Table}. 42 * 43 * <p>See the Guava User Guide article on <a href= 44 * "http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained#Tables"> 45 * {@code Tables}</a>. 46 * 47 * @author Jared Levy 48 * @author Louis Wasserman 49 * @since 7.0 50 */ 51 @GwtCompatible 52 public final class Tables { Tables()53 private Tables() {} 54 55 /** 56 * Returns an immutable cell with the specified row key, column key, and 57 * value. 58 * 59 * <p>The returned cell is serializable. 60 * 61 * @param rowKey the row key to be associated with the returned cell 62 * @param columnKey the column key to be associated with the returned cell 63 * @param value the value to be associated with the returned cell 64 */ immutableCell( @ullable R rowKey, @Nullable C columnKey, @Nullable V value)65 public static <R, C, V> Cell<R, C, V> immutableCell( 66 @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 67 return new ImmutableCell<R, C, V>(rowKey, columnKey, value); 68 } 69 70 static final class ImmutableCell<R, C, V> 71 extends AbstractCell<R, C, V> implements Serializable { 72 private final R rowKey; 73 private final C columnKey; 74 private final V value; 75 ImmutableCell( @ullable R rowKey, @Nullable C columnKey, @Nullable V value)76 ImmutableCell( 77 @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 78 this.rowKey = rowKey; 79 this.columnKey = columnKey; 80 this.value = value; 81 } 82 83 @Override getRowKey()84 public R getRowKey() { 85 return rowKey; 86 } 87 @Override getColumnKey()88 public C getColumnKey() { 89 return columnKey; 90 } 91 @Override getValue()92 public V getValue() { 93 return value; 94 } 95 96 private static final long serialVersionUID = 0; 97 } 98 99 abstract static class AbstractCell<R, C, V> implements Cell<R, C, V> { 100 // needed for serialization AbstractCell()101 AbstractCell() {} 102 equals(Object obj)103 @Override public boolean equals(Object obj) { 104 if (obj == this) { 105 return true; 106 } 107 if (obj instanceof Cell) { 108 Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj; 109 return Objects.equal(getRowKey(), other.getRowKey()) 110 && Objects.equal(getColumnKey(), other.getColumnKey()) 111 && Objects.equal(getValue(), other.getValue()); 112 } 113 return false; 114 } 115 hashCode()116 @Override public int hashCode() { 117 return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); 118 } 119 toString()120 @Override public String toString() { 121 return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); 122 } 123 } 124 125 /** 126 * Creates a transposed view of a given table that flips its row and column 127 * keys. In other words, calling {@code get(columnKey, rowKey)} on the 128 * generated table always returns the same value as calling {@code 129 * get(rowKey, columnKey)} on the original table. Updating the original table 130 * changes the contents of the transposed table and vice versa. 131 * 132 * <p>The returned table supports update operations as long as the input table 133 * supports the analogous operation with swapped rows and columns. For 134 * example, in a {@link HashBasedTable} instance, {@code 135 * rowKeySet().iterator()} supports {@code remove()} but {@code 136 * columnKeySet().iterator()} doesn't. With a transposed {@link 137 * HashBasedTable}, it's the other way around. 138 */ transpose(Table<R, C, V> table)139 public static <R, C, V> Table<C, R, V> transpose(Table<R, C, V> table) { 140 return (table instanceof TransposeTable) 141 ? ((TransposeTable<R, C, V>) table).original 142 : new TransposeTable<C, R, V>(table); 143 } 144 145 private static class TransposeTable<C, R, V> extends AbstractTable<C, R, V> { 146 final Table<R, C, V> original; 147 TransposeTable(Table<R, C, V> original)148 TransposeTable(Table<R, C, V> original) { 149 this.original = checkNotNull(original); 150 } 151 152 @Override clear()153 public void clear() { 154 original.clear(); 155 } 156 157 @Override column(R columnKey)158 public Map<C, V> column(R columnKey) { 159 return original.row(columnKey); 160 } 161 162 @Override columnKeySet()163 public Set<R> columnKeySet() { 164 return original.rowKeySet(); 165 } 166 167 @Override columnMap()168 public Map<R, Map<C, V>> columnMap() { 169 return original.rowMap(); 170 } 171 172 @Override contains( @ullable Object rowKey, @Nullable Object columnKey)173 public boolean contains( 174 @Nullable Object rowKey, @Nullable Object columnKey) { 175 return original.contains(columnKey, rowKey); 176 } 177 178 @Override containsColumn(@ullable Object columnKey)179 public boolean containsColumn(@Nullable Object columnKey) { 180 return original.containsRow(columnKey); 181 } 182 183 @Override containsRow(@ullable Object rowKey)184 public boolean containsRow(@Nullable Object rowKey) { 185 return original.containsColumn(rowKey); 186 } 187 188 @Override containsValue(@ullable Object value)189 public boolean containsValue(@Nullable Object value) { 190 return original.containsValue(value); 191 } 192 193 @Override get(@ullable Object rowKey, @Nullable Object columnKey)194 public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 195 return original.get(columnKey, rowKey); 196 } 197 198 @Override put(C rowKey, R columnKey, V value)199 public V put(C rowKey, R columnKey, V value) { 200 return original.put(columnKey, rowKey, value); 201 } 202 203 @Override putAll(Table<? extends C, ? extends R, ? extends V> table)204 public void putAll(Table<? extends C, ? extends R, ? extends V> table) { 205 original.putAll(transpose(table)); 206 } 207 208 @Override remove(@ullable Object rowKey, @Nullable Object columnKey)209 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 210 return original.remove(columnKey, rowKey); 211 } 212 213 @Override row(C rowKey)214 public Map<R, V> row(C rowKey) { 215 return original.column(rowKey); 216 } 217 218 @Override rowKeySet()219 public Set<C> rowKeySet() { 220 return original.columnKeySet(); 221 } 222 223 @Override rowMap()224 public Map<C, Map<R, V>> rowMap() { 225 return original.columnMap(); 226 } 227 228 @Override size()229 public int size() { 230 return original.size(); 231 } 232 233 @Override values()234 public Collection<V> values() { 235 return original.values(); 236 } 237 238 // Will cast TRANSPOSE_CELL to a type that always succeeds 239 private static final Function<Cell<?, ?, ?>, Cell<?, ?, ?>> TRANSPOSE_CELL = 240 new Function<Cell<?, ?, ?>, Cell<?, ?, ?>>() { 241 @Override 242 public Cell<?, ?, ?> apply(Cell<?, ?, ?> cell) { 243 return immutableCell( 244 cell.getColumnKey(), cell.getRowKey(), cell.getValue()); 245 } 246 }; 247 248 @SuppressWarnings("unchecked") 249 @Override cellIterator()250 Iterator<Cell<C, R, V>> cellIterator() { 251 return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL); 252 } 253 } 254 255 /** 256 * Creates a table that uses the specified backing map and factory. It can 257 * generate a table based on arbitrary {@link Map} classes. 258 * 259 * <p>The {@code factory}-generated and {@code backingMap} classes determine 260 * the table iteration order. However, the table's {@code row()} method 261 * returns instances of a different class than {@code factory.get()} does. 262 * 263 * <p>Call this method only when the simpler factory methods in classes like 264 * {@link HashBasedTable} and {@link TreeBasedTable} won't suffice. 265 * 266 * <p>The views returned by the {@code Table} methods {@link Table#column}, 267 * {@link Table#columnKeySet}, and {@link Table#columnMap} have iterators that 268 * don't support {@code remove()}. Otherwise, all optional operations are 269 * supported. Null row keys, columns keys, and values are not supported. 270 * 271 * <p>Lookups by row key are often faster than lookups by column key, because 272 * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like 273 * {@code column(columnKey).get(rowKey)} still runs quickly, since the row key 274 * is provided. However, {@code column(columnKey).size()} takes longer, since 275 * an iteration across all row keys occurs. 276 * 277 * <p>Note that this implementation is not synchronized. If multiple threads 278 * access this table concurrently and one of the threads modifies the table, 279 * it must be synchronized externally. 280 * 281 * <p>The table is serializable if {@code backingMap}, {@code factory}, the 282 * maps generated by {@code factory}, and the table contents are all 283 * serializable. 284 * 285 * <p>Note: the table assumes complete ownership over of {@code backingMap} 286 * and the maps returned by {@code factory}. Those objects should not be 287 * manually updated and they should not use soft, weak, or phantom references. 288 * 289 * @param backingMap place to store the mapping from each row key to its 290 * corresponding column key / value map 291 * @param factory supplier of new, empty maps that will each hold all column 292 * key / value mappings for a given row key 293 * @throws IllegalArgumentException if {@code backingMap} is not empty 294 * @since 10.0 295 */ 296 @Beta newCustomTable( Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory)297 public static <R, C, V> Table<R, C, V> newCustomTable( 298 Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { 299 checkArgument(backingMap.isEmpty()); 300 checkNotNull(factory); 301 // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? 302 return new StandardTable<R, C, V>(backingMap, factory); 303 } 304 305 /** 306 * Returns a view of a table where each value is transformed by a function. 307 * All other properties of the table, such as iteration order, are left 308 * intact. 309 * 310 * <p>Changes in the underlying table are reflected in this view. Conversely, 311 * this view supports removal operations, and these are reflected in the 312 * underlying table. 313 * 314 * <p>It's acceptable for the underlying table to contain null keys, and even 315 * null values provided that the function is capable of accepting null input. 316 * The transformed table might contain null values, if the function sometimes 317 * gives a null result. 318 * 319 * <p>The returned table is not thread-safe or serializable, even if the 320 * underlying table is. 321 * 322 * <p>The function is applied lazily, invoked when needed. This is necessary 323 * for the returned table to be a view, but it means that the function will be 324 * applied many times for bulk operations like {@link Table#containsValue} and 325 * {@code Table.toString()}. For this to perform well, {@code function} should 326 * be fast. To avoid lazy evaluation when the returned table doesn't need to 327 * be a view, copy the returned table into a new table of your choosing. 328 * 329 * @since 10.0 330 */ 331 @Beta transformValues( Table<R, C, V1> fromTable, Function<? super V1, V2> function)332 public static <R, C, V1, V2> Table<R, C, V2> transformValues( 333 Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 334 return new TransformedTable<R, C, V1, V2>(fromTable, function); 335 } 336 337 private static class TransformedTable<R, C, V1, V2> 338 extends AbstractTable<R, C, V2> { 339 final Table<R, C, V1> fromTable; 340 final Function<? super V1, V2> function; 341 TransformedTable( Table<R, C, V1> fromTable, Function<? super V1, V2> function)342 TransformedTable( 343 Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 344 this.fromTable = checkNotNull(fromTable); 345 this.function = checkNotNull(function); 346 } 347 contains(Object rowKey, Object columnKey)348 @Override public boolean contains(Object rowKey, Object columnKey) { 349 return fromTable.contains(rowKey, columnKey); 350 } 351 get(Object rowKey, Object columnKey)352 @Override public V2 get(Object rowKey, Object columnKey) { 353 // The function is passed a null input only when the table contains a null 354 // value. 355 return contains(rowKey, columnKey) 356 ? function.apply(fromTable.get(rowKey, columnKey)) : null; 357 } 358 size()359 @Override public int size() { 360 return fromTable.size(); 361 } 362 clear()363 @Override public void clear() { 364 fromTable.clear(); 365 } 366 put(R rowKey, C columnKey, V2 value)367 @Override public V2 put(R rowKey, C columnKey, V2 value) { 368 throw new UnsupportedOperationException(); 369 } 370 putAll( Table<? extends R, ? extends C, ? extends V2> table)371 @Override public void putAll( 372 Table<? extends R, ? extends C, ? extends V2> table) { 373 throw new UnsupportedOperationException(); 374 } 375 remove(Object rowKey, Object columnKey)376 @Override public V2 remove(Object rowKey, Object columnKey) { 377 return contains(rowKey, columnKey) 378 ? function.apply(fromTable.remove(rowKey, columnKey)) : null; 379 } 380 row(R rowKey)381 @Override public Map<C, V2> row(R rowKey) { 382 return Maps.transformValues(fromTable.row(rowKey), function); 383 } 384 column(C columnKey)385 @Override public Map<R, V2> column(C columnKey) { 386 return Maps.transformValues(fromTable.column(columnKey), function); 387 } 388 cellFunction()389 Function<Cell<R, C, V1>, Cell<R, C, V2>> cellFunction() { 390 return new Function<Cell<R, C, V1>, Cell<R, C, V2>>() { 391 @Override public Cell<R, C, V2> apply(Cell<R, C, V1> cell) { 392 return immutableCell( 393 cell.getRowKey(), cell.getColumnKey(), 394 function.apply(cell.getValue())); 395 } 396 }; 397 } 398 399 @Override cellIterator()400 Iterator<Cell<R, C, V2>> cellIterator() { 401 return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); 402 } 403 rowKeySet()404 @Override public Set<R> rowKeySet() { 405 return fromTable.rowKeySet(); 406 } 407 columnKeySet()408 @Override public Set<C> columnKeySet() { 409 return fromTable.columnKeySet(); 410 } 411 412 @Override createValues()413 Collection<V2> createValues() { 414 return Collections2.transform(fromTable.values(), function); 415 } 416 rowMap()417 @Override public Map<R, Map<C, V2>> rowMap() { 418 Function<Map<C, V1>, Map<C, V2>> rowFunction = 419 new Function<Map<C, V1>, Map<C, V2>>() { 420 @Override public Map<C, V2> apply(Map<C, V1> row) { 421 return Maps.transformValues(row, function); 422 } 423 }; 424 return Maps.transformValues(fromTable.rowMap(), rowFunction); 425 } 426 columnMap()427 @Override public Map<C, Map<R, V2>> columnMap() { 428 Function<Map<R, V1>, Map<R, V2>> columnFunction = 429 new Function<Map<R, V1>, Map<R, V2>>() { 430 @Override public Map<R, V2> apply(Map<R, V1> column) { 431 return Maps.transformValues(column, function); 432 } 433 }; 434 return Maps.transformValues(fromTable.columnMap(), columnFunction); 435 } 436 } 437 438 /** 439 * Returns an unmodifiable view of the specified table. This method allows modules to provide 440 * users with "read-only" access to internal tables. Query operations on the returned table 441 * "read through" to the specified table, and attempts to modify the returned table, whether 442 * direct or via its collection views, result in an {@code UnsupportedOperationException}. 443 * 444 * <p>The returned table will be serializable if the specified table is serializable. 445 * 446 * <p>Consider using an {@link ImmutableTable}, which is guaranteed never to change. 447 * 448 * @param table 449 * the table for which an unmodifiable view is to be returned 450 * @return an unmodifiable view of the specified table 451 * @since 11.0 452 */ 453 public static <R, C, V> Table<R, C, V> unmodifiableTable( 454 Table<? extends R, ? extends C, ? extends V> table) { 455 return new UnmodifiableTable<R, C, V>(table); 456 } 457 458 private static class UnmodifiableTable<R, C, V> 459 extends ForwardingTable<R, C, V> implements Serializable { 460 final Table<? extends R, ? extends C, ? extends V> delegate; 461 462 UnmodifiableTable(Table<? extends R, ? extends C, ? extends V> delegate) { 463 this.delegate = checkNotNull(delegate); 464 } 465 466 @SuppressWarnings("unchecked") // safe, covariant cast 467 @Override 468 protected Table<R, C, V> delegate() { 469 return (Table<R, C, V>) delegate; 470 } 471 472 @Override 473 public Set<Cell<R, C, V>> cellSet() { 474 return Collections.unmodifiableSet(super.cellSet()); 475 } 476 477 @Override 478 public void clear() { 479 throw new UnsupportedOperationException(); 480 } 481 482 @Override 483 public Map<R, V> column(@Nullable C columnKey) { 484 return Collections.unmodifiableMap(super.column(columnKey)); 485 } 486 487 @Override 488 public Set<C> columnKeySet() { 489 return Collections.unmodifiableSet(super.columnKeySet()); 490 } 491 492 @Override 493 public Map<C, Map<R, V>> columnMap() { 494 Function<Map<R, V>, Map<R, V>> wrapper = unmodifiableWrapper(); 495 return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); 496 } 497 498 @Override 499 public V put(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 500 throw new UnsupportedOperationException(); 501 } 502 503 @Override 504 public void putAll(Table<? extends R, ? extends C, ? extends V> table) { 505 throw new UnsupportedOperationException(); 506 } 507 508 @Override 509 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 510 throw new UnsupportedOperationException(); 511 } 512 513 @Override 514 public Map<C, V> row(@Nullable R rowKey) { 515 return Collections.unmodifiableMap(super.row(rowKey)); 516 } 517 518 @Override 519 public Set<R> rowKeySet() { 520 return Collections.unmodifiableSet(super.rowKeySet()); 521 } 522 523 @Override 524 public Map<R, Map<C, V>> rowMap() { 525 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 526 return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); 527 } 528 529 @Override 530 public Collection<V> values() { 531 return Collections.unmodifiableCollection(super.values()); 532 } 533 534 private static final long serialVersionUID = 0; 535 } 536 537 /** 538 * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to 539 * provide users with "read-only" access to internal tables. Query operations on the returned 540 * table "read through" to the specified table, and attemps to modify the returned table, whether 541 * direct or via its collection views, result in an {@code UnsupportedOperationException}. 542 * 543 * <p>The returned table will be serializable if the specified table is serializable. 544 * 545 * @param table the row-sorted table for which an unmodifiable view is to be returned 546 * @return an unmodifiable view of the specified table 547 * @since 11.0 548 */ 549 @Beta 550 public static <R, C, V> RowSortedTable<R, C, V> unmodifiableRowSortedTable( 551 RowSortedTable<R, ? extends C, ? extends V> table) { 552 /* 553 * It's not ? extends R, because it's technically not covariant in R. Specifically, 554 * table.rowMap().comparator() could return a comparator that only works for the ? extends R. 555 * Collections.unmodifiableSortedMap makes the same distinction. 556 */ 557 return new UnmodifiableRowSortedMap<R, C, V>(table); 558 } 559 560 static final class UnmodifiableRowSortedMap<R, C, V> extends UnmodifiableTable<R, C, V> 561 implements RowSortedTable<R, C, V> { 562 563 public UnmodifiableRowSortedMap(RowSortedTable<R, ? extends C, ? extends V> delegate) { 564 super(delegate); 565 } 566 567 @Override 568 protected RowSortedTable<R, C, V> delegate() { 569 return (RowSortedTable<R, C, V>) super.delegate(); 570 } 571 572 @Override 573 public SortedMap<R, Map<C, V>> rowMap() { 574 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 575 return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); 576 } 577 578 @Override 579 public SortedSet<R> rowKeySet() { 580 return Collections.unmodifiableSortedSet(delegate().rowKeySet()); 581 } 582 583 private static final long serialVersionUID = 0; 584 } 585 586 @SuppressWarnings("unchecked") 587 private static <K, V> Function<Map<K, V>, Map<K, V>> unmodifiableWrapper() { 588 return (Function) UNMODIFIABLE_WRAPPER; 589 } 590 591 private static final Function<? extends Map<?, ?>, ? extends Map<?, ?>> UNMODIFIABLE_WRAPPER = 592 new Function<Map<Object, Object>, Map<Object, Object>>() { 593 @Override 594 public Map<Object, Object> apply(Map<Object, Object> input) { 595 return Collections.unmodifiableMap(input); 596 } 597 }; 598 599 static boolean equalsImpl(Table<?, ?, ?> table, @Nullable Object obj) { 600 if (obj == table) { 601 return true; 602 } else if (obj instanceof Table) { 603 Table<?, ?, ?> that = (Table<?, ?, ?>) obj; 604 return table.cellSet().equals(that.cellSet()); 605 } else { 606 return false; 607 } 608 } 609 } 610