001/* 002 * Copyright (C) 2008 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.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021 022import com.google.common.annotations.Beta; 023import com.google.common.annotations.GwtCompatible; 024import com.google.common.base.Function; 025import com.google.common.base.Objects; 026import com.google.common.base.Supplier; 027import com.google.common.collect.Table.Cell; 028 029import java.io.Serializable; 030import java.util.Collection; 031import java.util.Collections; 032import java.util.Iterator; 033import java.util.Map; 034import java.util.Set; 035import java.util.SortedMap; 036import java.util.SortedSet; 037 038import javax.annotation.Nullable; 039 040/** 041 * Provides static methods that involve a {@code Table}. 042 * 043 * <p>See the Guava User Guide article on <a href= 044 * "https://github.com/google/guava/wiki/CollectionUtilitiesExplained#tables"> 045 * {@code Tables}</a>. 046 * 047 * @author Jared Levy 048 * @author Louis Wasserman 049 * @since 7.0 050 */ 051@GwtCompatible 052public final class Tables { 053 private Tables() {} 054 055 /** 056 * Returns an immutable cell with the specified row key, column key, and 057 * value. 058 * 059 * <p>The returned cell is serializable. 060 * 061 * @param rowKey the row key to be associated with the returned cell 062 * @param columnKey the column key to be associated with the returned cell 063 * @param value the value to be associated with the returned cell 064 */ 065 public static <R, C, V> Cell<R, C, V> immutableCell( 066 @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 067 return new ImmutableCell<R, C, V>(rowKey, columnKey, value); 068 } 069 070 static final class ImmutableCell<R, C, V> extends AbstractCell<R, C, V> implements Serializable { 071 private final R rowKey; 072 private final C columnKey; 073 private final V value; 074 075 ImmutableCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 076 this.rowKey = rowKey; 077 this.columnKey = columnKey; 078 this.value = value; 079 } 080 081 @Override 082 public R getRowKey() { 083 return rowKey; 084 } 085 086 @Override 087 public C getColumnKey() { 088 return columnKey; 089 } 090 091 @Override 092 public V getValue() { 093 return value; 094 } 095 096 private static final long serialVersionUID = 0; 097 } 098 099 abstract static class AbstractCell<R, C, V> implements Cell<R, C, V> { 100 // needed for serialization 101 AbstractCell() {} 102 103 @Override 104 public boolean equals(Object obj) { 105 if (obj == this) { 106 return true; 107 } 108 if (obj instanceof Cell) { 109 Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj; 110 return Objects.equal(getRowKey(), other.getRowKey()) 111 && Objects.equal(getColumnKey(), other.getColumnKey()) 112 && Objects.equal(getValue(), other.getValue()); 113 } 114 return false; 115 } 116 117 @Override 118 public int hashCode() { 119 return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); 120 } 121 122 @Override 123 public String toString() { 124 return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); 125 } 126 } 127 128 /** 129 * Creates a transposed view of a given table that flips its row and column 130 * keys. In other words, calling {@code get(columnKey, rowKey)} on the 131 * generated table always returns the same value as calling {@code 132 * get(rowKey, columnKey)} on the original table. Updating the original table 133 * changes the contents of the transposed table and vice versa. 134 * 135 * <p>The returned table supports update operations as long as the input table 136 * supports the analogous operation with swapped rows and columns. For 137 * example, in a {@link HashBasedTable} instance, {@code 138 * rowKeySet().iterator()} supports {@code remove()} but {@code 139 * columnKeySet().iterator()} doesn't. With a transposed {@link 140 * HashBasedTable}, it's the other way around. 141 */ 142 public static <R, C, V> Table<C, R, V> transpose(Table<R, C, V> table) { 143 return (table instanceof TransposeTable) 144 ? ((TransposeTable<R, C, V>) table).original 145 : new TransposeTable<C, R, V>(table); 146 } 147 148 private static class TransposeTable<C, R, V> extends AbstractTable<C, R, V> { 149 final Table<R, C, V> original; 150 151 TransposeTable(Table<R, C, V> original) { 152 this.original = checkNotNull(original); 153 } 154 155 @Override 156 public void clear() { 157 original.clear(); 158 } 159 160 @Override 161 public Map<C, V> column(R columnKey) { 162 return original.row(columnKey); 163 } 164 165 @Override 166 public Set<R> columnKeySet() { 167 return original.rowKeySet(); 168 } 169 170 @Override 171 public Map<R, Map<C, V>> columnMap() { 172 return original.rowMap(); 173 } 174 175 @Override 176 public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) { 177 return original.contains(columnKey, rowKey); 178 } 179 180 @Override 181 public boolean containsColumn(@Nullable Object columnKey) { 182 return original.containsRow(columnKey); 183 } 184 185 @Override 186 public boolean containsRow(@Nullable Object rowKey) { 187 return original.containsColumn(rowKey); 188 } 189 190 @Override 191 public boolean containsValue(@Nullable Object value) { 192 return original.containsValue(value); 193 } 194 195 @Override 196 public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 197 return original.get(columnKey, rowKey); 198 } 199 200 @Override 201 public V put(C rowKey, R columnKey, V value) { 202 return original.put(columnKey, rowKey, value); 203 } 204 205 @Override 206 public void putAll(Table<? extends C, ? extends R, ? extends V> table) { 207 original.putAll(transpose(table)); 208 } 209 210 @Override 211 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 212 return original.remove(columnKey, rowKey); 213 } 214 215 @Override 216 public Map<R, V> row(C rowKey) { 217 return original.column(rowKey); 218 } 219 220 @Override 221 public Set<C> rowKeySet() { 222 return original.columnKeySet(); 223 } 224 225 @Override 226 public Map<C, Map<R, V>> rowMap() { 227 return original.columnMap(); 228 } 229 230 @Override 231 public int size() { 232 return original.size(); 233 } 234 235 @Override 236 public Collection<V> values() { 237 return original.values(); 238 } 239 240 // Will cast TRANSPOSE_CELL to a type that always succeeds 241 private static final Function<Cell<?, ?, ?>, Cell<?, ?, ?>> TRANSPOSE_CELL = 242 new Function<Cell<?, ?, ?>, Cell<?, ?, ?>>() { 243 @Override 244 public Cell<?, ?, ?> apply(Cell<?, ?, ?> cell) { 245 return immutableCell(cell.getColumnKey(), cell.getRowKey(), cell.getValue()); 246 } 247 }; 248 249 @SuppressWarnings("unchecked") 250 @Override 251 Iterator<Cell<C, R, V>> cellIterator() { 252 return Iterators.transform(original.cellSet().iterator(), (Function) TRANSPOSE_CELL); 253 } 254 } 255 256 /** 257 * Creates a table that uses the specified backing map and factory. It can 258 * generate a table based on arbitrary {@link Map} classes. 259 * 260 * <p>The {@code factory}-generated and {@code backingMap} classes determine 261 * the table iteration order. However, the table's {@code row()} method 262 * returns instances of a different class than {@code factory.get()} does. 263 * 264 * <p>Call this method only when the simpler factory methods in classes like 265 * {@link HashBasedTable} and {@link TreeBasedTable} won't suffice. 266 * 267 * <p>The views returned by the {@code Table} methods {@link Table#column}, 268 * {@link Table#columnKeySet}, and {@link Table#columnMap} have iterators that 269 * don't support {@code remove()}. Otherwise, all optional operations are 270 * supported. Null row keys, columns keys, and values are not supported. 271 * 272 * <p>Lookups by row key are often faster than lookups by column key, because 273 * the data is stored in a {@code Map<R, Map<C, V>>}. A method call like 274 * {@code column(columnKey).get(rowKey)} still runs quickly, since the row key 275 * is provided. However, {@code column(columnKey).size()} takes longer, since 276 * an iteration across all row keys occurs. 277 * 278 * <p>Note that this implementation is not synchronized. If multiple threads 279 * access this table concurrently and one of the threads modifies the table, 280 * it must be synchronized externally. 281 * 282 * <p>The table is serializable if {@code backingMap}, {@code factory}, the 283 * maps generated by {@code factory}, and the table contents are all 284 * serializable. 285 * 286 * <p>Note: the table assumes complete ownership over of {@code backingMap} 287 * and the maps returned by {@code factory}. Those objects should not be 288 * manually updated and they should not use soft, weak, or phantom references. 289 * 290 * @param backingMap place to store the mapping from each row key to its 291 * corresponding column key / value map 292 * @param factory supplier of new, empty maps that will each hold all column 293 * key / value mappings for a given row key 294 * @throws IllegalArgumentException if {@code backingMap} is not empty 295 * @since 10.0 296 */ 297 @Beta 298 public static <R, C, V> Table<R, C, V> newCustomTable( 299 Map<R, Map<C, V>> backingMap, Supplier<? extends Map<C, V>> factory) { 300 checkArgument(backingMap.isEmpty()); 301 checkNotNull(factory); 302 // TODO(jlevy): Wrap factory to validate that the supplied maps are empty? 303 return new StandardTable<R, C, V>(backingMap, factory); 304 } 305 306 /** 307 * Returns a view of a table where each value is transformed by a function. 308 * All other properties of the table, such as iteration order, are left 309 * intact. 310 * 311 * <p>Changes in the underlying table are reflected in this view. Conversely, 312 * this view supports removal operations, and these are reflected in the 313 * underlying table. 314 * 315 * <p>It's acceptable for the underlying table to contain null keys, and even 316 * null values provided that the function is capable of accepting null input. 317 * The transformed table might contain null values, if the function sometimes 318 * gives a null result. 319 * 320 * <p>The returned table is not thread-safe or serializable, even if the 321 * underlying table is. 322 * 323 * <p>The function is applied lazily, invoked when needed. This is necessary 324 * for the returned table to be a view, but it means that the function will be 325 * applied many times for bulk operations like {@link Table#containsValue} and 326 * {@code Table.toString()}. For this to perform well, {@code function} should 327 * be fast. To avoid lazy evaluation when the returned table doesn't need to 328 * be a view, copy the returned table into a new table of your choosing. 329 * 330 * @since 10.0 331 */ 332 @Beta 333 public static <R, C, V1, V2> Table<R, C, V2> transformValues( 334 Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 335 return new TransformedTable<R, C, V1, V2>(fromTable, function); 336 } 337 338 private static class TransformedTable<R, C, V1, V2> extends AbstractTable<R, C, V2> { 339 final Table<R, C, V1> fromTable; 340 final Function<? super V1, V2> function; 341 342 TransformedTable(Table<R, C, V1> fromTable, Function<? super V1, V2> function) { 343 this.fromTable = checkNotNull(fromTable); 344 this.function = checkNotNull(function); 345 } 346 347 @Override 348 public boolean contains(Object rowKey, Object columnKey) { 349 return fromTable.contains(rowKey, columnKey); 350 } 351 352 @Override 353 public V2 get(Object rowKey, Object columnKey) { 354 // The function is passed a null input only when the table contains a null 355 // value. 356 return contains(rowKey, columnKey) ? function.apply(fromTable.get(rowKey, columnKey)) : null; 357 } 358 359 @Override 360 public int size() { 361 return fromTable.size(); 362 } 363 364 @Override 365 public void clear() { 366 fromTable.clear(); 367 } 368 369 @Override 370 public V2 put(R rowKey, C columnKey, V2 value) { 371 throw new UnsupportedOperationException(); 372 } 373 374 @Override 375 public void putAll(Table<? extends R, ? extends C, ? extends V2> table) { 376 throw new UnsupportedOperationException(); 377 } 378 379 @Override 380 public V2 remove(Object rowKey, Object columnKey) { 381 return contains(rowKey, columnKey) 382 ? function.apply(fromTable.remove(rowKey, columnKey)) 383 : null; 384 } 385 386 @Override 387 public Map<C, V2> row(R rowKey) { 388 return Maps.transformValues(fromTable.row(rowKey), function); 389 } 390 391 @Override 392 public Map<R, V2> column(C columnKey) { 393 return Maps.transformValues(fromTable.column(columnKey), function); 394 } 395 396 Function<Cell<R, C, V1>, Cell<R, C, V2>> cellFunction() { 397 return new Function<Cell<R, C, V1>, Cell<R, C, V2>>() { 398 @Override 399 public Cell<R, C, V2> apply(Cell<R, C, V1> cell) { 400 return immutableCell( 401 cell.getRowKey(), cell.getColumnKey(), function.apply(cell.getValue())); 402 } 403 }; 404 } 405 406 @Override 407 Iterator<Cell<R, C, V2>> cellIterator() { 408 return Iterators.transform(fromTable.cellSet().iterator(), cellFunction()); 409 } 410 411 @Override 412 public Set<R> rowKeySet() { 413 return fromTable.rowKeySet(); 414 } 415 416 @Override 417 public Set<C> columnKeySet() { 418 return fromTable.columnKeySet(); 419 } 420 421 @Override 422 Collection<V2> createValues() { 423 return Collections2.transform(fromTable.values(), function); 424 } 425 426 @Override 427 public Map<R, Map<C, V2>> rowMap() { 428 Function<Map<C, V1>, Map<C, V2>> rowFunction = 429 new Function<Map<C, V1>, Map<C, V2>>() { 430 @Override 431 public Map<C, V2> apply(Map<C, V1> row) { 432 return Maps.transformValues(row, function); 433 } 434 }; 435 return Maps.transformValues(fromTable.rowMap(), rowFunction); 436 } 437 438 @Override 439 public Map<C, Map<R, V2>> columnMap() { 440 Function<Map<R, V1>, Map<R, V2>> columnFunction = 441 new Function<Map<R, V1>, Map<R, V2>>() { 442 @Override 443 public Map<R, V2> apply(Map<R, V1> column) { 444 return Maps.transformValues(column, function); 445 } 446 }; 447 return Maps.transformValues(fromTable.columnMap(), columnFunction); 448 } 449 } 450 451 /** 452 * Returns an unmodifiable view of the specified table. This method allows modules to provide 453 * users with "read-only" access to internal tables. Query operations on the returned table 454 * "read through" to the specified table, and attempts to modify the returned table, whether 455 * direct or via its collection views, result in an {@code UnsupportedOperationException}. 456 * 457 * <p>The returned table will be serializable if the specified table is serializable. 458 * 459 * <p>Consider using an {@link ImmutableTable}, which is guaranteed never to change. 460 * 461 * @param table 462 * the table for which an unmodifiable view is to be returned 463 * @return an unmodifiable view of the specified table 464 * @since 11.0 465 */ 466 public static <R, C, V> Table<R, C, V> unmodifiableTable( 467 Table<? extends R, ? extends C, ? extends V> table) { 468 return new UnmodifiableTable<R, C, V>(table); 469 } 470 471 private static class UnmodifiableTable<R, C, V> extends ForwardingTable<R, C, V> 472 implements Serializable { 473 final Table<? extends R, ? extends C, ? extends V> delegate; 474 475 UnmodifiableTable(Table<? extends R, ? extends C, ? extends V> delegate) { 476 this.delegate = checkNotNull(delegate); 477 } 478 479 @SuppressWarnings("unchecked") // safe, covariant cast 480 @Override 481 protected Table<R, C, V> delegate() { 482 return (Table<R, C, V>) delegate; 483 } 484 485 @Override 486 public Set<Cell<R, C, V>> cellSet() { 487 return Collections.unmodifiableSet(super.cellSet()); 488 } 489 490 @Override 491 public void clear() { 492 throw new UnsupportedOperationException(); 493 } 494 495 @Override 496 public Map<R, V> column(@Nullable C columnKey) { 497 return Collections.unmodifiableMap(super.column(columnKey)); 498 } 499 500 @Override 501 public Set<C> columnKeySet() { 502 return Collections.unmodifiableSet(super.columnKeySet()); 503 } 504 505 @Override 506 public Map<C, Map<R, V>> columnMap() { 507 Function<Map<R, V>, Map<R, V>> wrapper = unmodifiableWrapper(); 508 return Collections.unmodifiableMap(Maps.transformValues(super.columnMap(), wrapper)); 509 } 510 511 @Override 512 public V put(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 513 throw new UnsupportedOperationException(); 514 } 515 516 @Override 517 public void putAll(Table<? extends R, ? extends C, ? extends V> table) { 518 throw new UnsupportedOperationException(); 519 } 520 521 @Override 522 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 523 throw new UnsupportedOperationException(); 524 } 525 526 @Override 527 public Map<C, V> row(@Nullable R rowKey) { 528 return Collections.unmodifiableMap(super.row(rowKey)); 529 } 530 531 @Override 532 public Set<R> rowKeySet() { 533 return Collections.unmodifiableSet(super.rowKeySet()); 534 } 535 536 @Override 537 public Map<R, Map<C, V>> rowMap() { 538 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 539 return Collections.unmodifiableMap(Maps.transformValues(super.rowMap(), wrapper)); 540 } 541 542 @Override 543 public Collection<V> values() { 544 return Collections.unmodifiableCollection(super.values()); 545 } 546 547 private static final long serialVersionUID = 0; 548 } 549 550 /** 551 * Returns an unmodifiable view of the specified row-sorted table. This method allows modules to 552 * provide users with "read-only" access to internal tables. Query operations on the returned 553 * table "read through" to the specified table, and attemps to modify the returned table, whether 554 * direct or via its collection views, result in an {@code UnsupportedOperationException}. 555 * 556 * <p>The returned table will be serializable if the specified table is serializable. 557 * 558 * @param table the row-sorted table for which an unmodifiable view is to be returned 559 * @return an unmodifiable view of the specified table 560 * @since 11.0 561 */ 562 @Beta 563 public static <R, C, V> RowSortedTable<R, C, V> unmodifiableRowSortedTable( 564 RowSortedTable<R, ? extends C, ? extends V> table) { 565 /* 566 * It's not ? extends R, because it's technically not covariant in R. Specifically, 567 * table.rowMap().comparator() could return a comparator that only works for the ? extends R. 568 * Collections.unmodifiableSortedMap makes the same distinction. 569 */ 570 return new UnmodifiableRowSortedMap<R, C, V>(table); 571 } 572 573 static final class UnmodifiableRowSortedMap<R, C, V> extends UnmodifiableTable<R, C, V> 574 implements RowSortedTable<R, C, V> { 575 576 public UnmodifiableRowSortedMap(RowSortedTable<R, ? extends C, ? extends V> delegate) { 577 super(delegate); 578 } 579 580 @Override 581 protected RowSortedTable<R, C, V> delegate() { 582 return (RowSortedTable<R, C, V>) super.delegate(); 583 } 584 585 @Override 586 public SortedMap<R, Map<C, V>> rowMap() { 587 Function<Map<C, V>, Map<C, V>> wrapper = unmodifiableWrapper(); 588 return Collections.unmodifiableSortedMap(Maps.transformValues(delegate().rowMap(), wrapper)); 589 } 590 591 @Override 592 public SortedSet<R> rowKeySet() { 593 return Collections.unmodifiableSortedSet(delegate().rowKeySet()); 594 } 595 596 private static final long serialVersionUID = 0; 597 } 598 599 @SuppressWarnings("unchecked") 600 private static <K, V> Function<Map<K, V>, Map<K, V>> unmodifiableWrapper() { 601 return (Function) UNMODIFIABLE_WRAPPER; 602 } 603 604 private static final Function<? extends Map<?, ?>, ? extends Map<?, ?>> UNMODIFIABLE_WRAPPER = 605 new Function<Map<Object, Object>, Map<Object, Object>>() { 606 @Override 607 public Map<Object, Object> apply(Map<Object, Object> input) { 608 return Collections.unmodifiableMap(input); 609 } 610 }; 611 612 static boolean equalsImpl(Table<?, ?, ?> table, @Nullable Object obj) { 613 if (obj == table) { 614 return true; 615 } else if (obj instanceof Table) { 616 Table<?, ?, ?> that = (Table<?, ?, ?>) obj; 617 return table.cellSet().equals(that.cellSet()); 618 } else { 619 return false; 620 } 621 } 622}