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 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkNotNull; 020 021 import com.google.common.annotations.Beta; 022 import com.google.common.annotations.GwtCompatible; 023 import com.google.common.base.Function; 024 import com.google.common.base.Objects; 025 import com.google.common.collect.Collections2.TransformedCollection; 026 import com.google.common.collect.Table.Cell; 027 028 import java.io.Serializable; 029 import java.util.Collection; 030 import java.util.Map; 031 import java.util.Set; 032 033 import javax.annotation.Nullable; 034 035 /** 036 * Provides static methods that involve a {@code Table}. 037 * 038 * @author Jared Levy 039 * @since 7 040 */ 041 @GwtCompatible 042 @Beta 043 public final class Tables { 044 private Tables() {} 045 046 /** 047 * Returns an immutable cell with the specified row key, column key, and 048 * value. 049 * 050 * <p>The returned cell is serializable. 051 * 052 * @param rowKey the row key to be associated with the returned cell 053 * @param columnKey the column key to be associated with the returned cell 054 * @param value the value to be associated with the returned cell 055 */ 056 public static <R, C, V> Cell<R, C, V> immutableCell( 057 @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 058 return new ImmutableCell<R, C, V>(rowKey, columnKey, value); 059 } 060 061 private static class ImmutableCell<R, C, V> 062 extends AbstractCell<R, C, V> implements Serializable { 063 final R rowKey; 064 final C columnKey; 065 final V value; 066 067 ImmutableCell( 068 @Nullable R rowKey, @Nullable C columnKey, @Nullable V value) { 069 this.rowKey = rowKey; 070 this.columnKey = columnKey; 071 this.value = value; 072 } 073 074 @Override 075 public R getRowKey() { 076 return rowKey; 077 } 078 @Override 079 public C getColumnKey() { 080 return columnKey; 081 } 082 @Override 083 public V getValue() { 084 return value; 085 } 086 087 private static final long serialVersionUID = 0; 088 } 089 090 abstract static class AbstractCell<R, C, V> implements Cell<R, C, V> { 091 // needed for serialization 092 AbstractCell() {} 093 094 @Override public boolean equals(Object obj) { 095 if (obj == this) { 096 return true; 097 } 098 if (obj instanceof Cell) { 099 Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj; 100 return Objects.equal(getRowKey(), other.getRowKey()) 101 && Objects.equal(getColumnKey(), other.getColumnKey()) 102 && Objects.equal(getValue(), other.getValue()); 103 } 104 return false; 105 } 106 107 @Override public int hashCode() { 108 return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); 109 } 110 111 @Override public String toString() { 112 return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); 113 } 114 } 115 116 /** 117 * Creates a transposed view of a given table that flips its row and column 118 * keys. In other words, calling {@code get(columnKey, rowKey)} on the 119 * generated table always returns the same value as calling {@code 120 * get(rowKey, columnKey)} on the original table. Updating the original table 121 * changes the contents of the transposed table and vice versa. 122 * 123 * <p>The returned table supports update operations as long as the input table 124 * supports the analogous operation with swapped rows and columns. For 125 * example, in a {@link HashBasedTable} instance, {@code 126 * rowKeySet().iterator()} supports {@code remove()} but {@code 127 * columnKeySet().iterator()} doesn't. With a transposed {@link 128 * HashBasedTable}, it's the other way around. 129 */ 130 public static <R, C, V> Table<C, R, V> transpose(Table<R, C, V> table) { 131 return (table instanceof TransposeTable) 132 ? ((TransposeTable<R, C, V>) table).original 133 : new TransposeTable<C, R, V>(table); 134 } 135 136 private static class TransposeTable<C, R, V> implements Table<C, R, V> { 137 final Table<R, C, V> original; 138 139 TransposeTable(Table<R, C, V> original) { 140 this.original = checkNotNull(original); 141 } 142 143 @Override 144 public void clear() { 145 original.clear(); 146 } 147 148 @Override 149 public Map<C, V> column(R columnKey) { 150 return original.row(columnKey); 151 } 152 153 @Override 154 public Set<R> columnKeySet() { 155 return original.rowKeySet(); 156 } 157 158 @Override 159 public Map<R, Map<C, V>> columnMap() { 160 return original.rowMap(); 161 } 162 163 @Override 164 public boolean contains( 165 @Nullable Object rowKey, @Nullable Object columnKey) { 166 return original.contains(columnKey, rowKey); 167 } 168 169 @Override 170 public boolean containsColumn(@Nullable Object columnKey) { 171 return original.containsRow(columnKey); 172 } 173 174 @Override 175 public boolean containsRow(@Nullable Object rowKey) { 176 return original.containsColumn(rowKey); 177 } 178 179 @Override 180 public boolean containsValue(@Nullable Object value) { 181 return original.containsValue(value); 182 } 183 184 @Override 185 public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 186 return original.get(columnKey, rowKey); 187 } 188 189 @Override 190 public boolean isEmpty() { 191 return original.isEmpty(); 192 } 193 194 @Override 195 public V put(C rowKey, R columnKey, V value) { 196 return original.put(columnKey, rowKey, value); 197 } 198 199 @Override 200 public void putAll(Table<? extends C, ? extends R, ? extends V> table) { 201 original.putAll(transpose(table)); 202 } 203 204 @Override 205 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 206 return original.remove(columnKey, rowKey); 207 } 208 209 @Override 210 public Map<R, V> row(C rowKey) { 211 return original.column(rowKey); 212 } 213 214 @Override 215 public Set<C> rowKeySet() { 216 return original.columnKeySet(); 217 } 218 219 @Override 220 public Map<C, Map<R, V>> rowMap() { 221 return original.columnMap(); 222 } 223 224 @Override 225 public int size() { 226 return original.size(); 227 } 228 229 @Override 230 public Collection<V> values() { 231 return original.values(); 232 } 233 234 @Override public boolean equals(@Nullable Object obj) { 235 if (obj == this) { 236 return true; 237 } 238 if (obj instanceof Table) { 239 Table<?, ?, ?> other = (Table<?, ?, ?>) obj; 240 return cellSet().equals(other.cellSet()); 241 } 242 return false; 243 } 244 245 @Override public int hashCode() { 246 return cellSet().hashCode(); 247 } 248 249 @Override public String toString() { 250 return rowMap().toString(); 251 } 252 253 // Will cast TRANSPOSE_CELL to a type that always succeeds 254 @SuppressWarnings("unchecked") // eclipse doesn't like the raw type 255 private static final Function<Cell<?, ?, ?>, Cell<?, ?, ?>> TRANSPOSE_CELL = 256 new Function<Cell<?, ?, ?>, Cell<?, ?, ?>>() { 257 @Override 258 public Cell<?, ?, ?> apply(Cell<?, ?, ?> cell) { 259 return immutableCell( 260 cell.getColumnKey(), cell.getRowKey(), cell.getValue()); 261 } 262 }; 263 264 CellSet cellSet; 265 266 @Override 267 public Set<Cell<C, R, V>> cellSet() { 268 CellSet result = cellSet; 269 return (result == null) ? cellSet = new CellSet() : result; 270 } 271 272 class CellSet extends TransformedCollection<Cell<R, C, V>, Cell<C, R, V>> 273 implements Set<Cell<C, R, V>> { 274 // Casting TRANSPOSE_CELL to a type that always succeeds 275 @SuppressWarnings("unchecked") 276 CellSet() { 277 super(original.cellSet(), (Function) TRANSPOSE_CELL); 278 } 279 280 @Override public boolean equals(Object obj) { 281 if (obj == this) { 282 return true; 283 } 284 if (!(obj instanceof Set)) { 285 return false; 286 } 287 Set<?> os = (Set<?>) obj; 288 if (os.size() != size()) { 289 return false; 290 } 291 return containsAll(os); 292 } 293 294 @Override public int hashCode() { 295 return Sets.hashCodeImpl(this); 296 } 297 298 @Override public boolean contains(Object obj) { 299 if (obj instanceof Cell) { 300 Cell<?, ?, ?> cell = (Cell<?, ?, ?>) obj; 301 return original.cellSet().contains(immutableCell( 302 cell.getColumnKey(), cell.getRowKey(), cell.getValue())); 303 } 304 return false; 305 } 306 307 @Override public boolean remove(Object obj) { 308 if (obj instanceof Cell) { 309 Cell<?, ?, ?> cell = (Cell<?, ?, ?>) obj; 310 return original.cellSet().remove(immutableCell( 311 cell.getColumnKey(), cell.getRowKey(), cell.getValue())); 312 } 313 return false; 314 } 315 } 316 } 317 }