001 /* 002 * Copyright (C) 2008 Google Inc. 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 public R getRowKey() { 075 return rowKey; 076 } 077 public C getColumnKey() { 078 return columnKey; 079 } 080 public V getValue() { 081 return value; 082 } 083 084 private static final long serialVersionUID = 0; 085 } 086 087 abstract static class AbstractCell<R, C, V> implements Cell<R, C, V> { 088 // needed for serialization 089 AbstractCell() {} 090 091 @Override public boolean equals(Object obj) { 092 if (obj == this) { 093 return true; 094 } 095 if (obj instanceof Cell) { 096 Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj; 097 return Objects.equal(getRowKey(), other.getRowKey()) 098 && Objects.equal(getColumnKey(), other.getColumnKey()) 099 && Objects.equal(getValue(), other.getValue()); 100 } 101 return false; 102 } 103 104 @Override public int hashCode() { 105 return Objects.hashCode(getRowKey(), getColumnKey(), getValue()); 106 } 107 108 @Override public String toString() { 109 return "(" + getRowKey() + "," + getColumnKey() + ")=" + getValue(); 110 } 111 } 112 113 /** 114 * Creates a transposed view of a given table that flips its row and column 115 * keys. In other words, calling {@code get(columnKey, rowKey)} on the 116 * generated table always returns the same value as calling {@code 117 * get(rowKey, columnKey)} on the original table. Updating the original table 118 * changes the contents of the transposed table and vice versa. 119 * 120 * <p>The returned table supports update operations as long as the input table 121 * supports the analogous operation with swapped rows and columns. For 122 * example, in a {@link HashBasedTable} instance, {@code 123 * rowKeySet().iterator()} supports {@code remove()} but {@code 124 * columnKeySet().iterator()} doesn't. With a transposed {@link 125 * HashBasedTable}, it's the other way around. 126 */ 127 public static <R, C, V> Table<C, R, V> transpose(Table<R, C, V> table) { 128 return (table instanceof TransposeTable) 129 ? ((TransposeTable<R, C, V>) table).original 130 : new TransposeTable<C, R, V>(table); 131 } 132 133 private static class TransposeTable<C, R, V> implements Table<C, R, V> { 134 final Table<R, C, V> original; 135 136 TransposeTable(Table<R, C, V> original) { 137 this.original = checkNotNull(original); 138 } 139 140 public void clear() { 141 original.clear(); 142 } 143 144 public Map<C, V> column(R columnKey) { 145 return original.row(columnKey); 146 } 147 148 public Set<R> columnKeySet() { 149 return original.rowKeySet(); 150 } 151 152 public Map<R, Map<C, V>> columnMap() { 153 return original.rowMap(); 154 } 155 156 public boolean contains( 157 @Nullable Object rowKey, @Nullable Object columnKey) { 158 return original.contains(columnKey, rowKey); 159 } 160 161 public boolean containsColumn(@Nullable Object columnKey) { 162 return original.containsRow(columnKey); 163 } 164 165 public boolean containsRow(@Nullable Object rowKey) { 166 return original.containsColumn(rowKey); 167 } 168 169 public boolean containsValue(@Nullable Object value) { 170 return original.containsValue(value); 171 } 172 173 public V get(@Nullable Object rowKey, @Nullable Object columnKey) { 174 return original.get(columnKey, rowKey); 175 } 176 177 public boolean isEmpty() { 178 return original.isEmpty(); 179 } 180 181 public V put(C rowKey, R columnKey, V value) { 182 return original.put(columnKey, rowKey, value); 183 } 184 185 public void putAll(Table<? extends C, ? extends R, ? extends V> table) { 186 original.putAll(transpose(table)); 187 } 188 189 public V remove(@Nullable Object rowKey, @Nullable Object columnKey) { 190 return original.remove(columnKey, rowKey); 191 } 192 193 public Map<R, V> row(C rowKey) { 194 return original.column(rowKey); 195 } 196 197 public Set<C> rowKeySet() { 198 return original.columnKeySet(); 199 } 200 201 public Map<C, Map<R, V>> rowMap() { 202 return original.columnMap(); 203 } 204 205 public int size() { 206 return original.size(); 207 } 208 209 public Collection<V> values() { 210 return original.values(); 211 } 212 213 @Override public boolean equals(@Nullable Object obj) { 214 if (obj == this) { 215 return true; 216 } 217 if (obj instanceof Table) { 218 Table<?, ?, ?> other = (Table<?, ?, ?>) obj; 219 return cellSet().equals(other.cellSet()); 220 } 221 return false; 222 } 223 224 @Override public int hashCode() { 225 return cellSet().hashCode(); 226 } 227 228 @Override public String toString() { 229 return rowMap().toString(); 230 } 231 232 // Will cast TRANSPOSE_CELL to a type that always succeeds 233 @SuppressWarnings("unchecked") // eclipse doesn't like the raw type 234 private static final Function TRANSPOSE_CELL = new Function() { 235 public Object apply(Object from) { 236 Cell<?, ?, ?> cell = (Cell<?, ?, ?>) from; 237 return immutableCell( 238 cell.getColumnKey(), cell.getRowKey(), cell.getValue()); 239 } 240 }; 241 242 CellSet cellSet; 243 244 public Set<Cell<C, R, V>> cellSet() { 245 CellSet result = cellSet; 246 return (result == null) ? cellSet = new CellSet() : result; 247 } 248 249 class CellSet extends TransformedCollection<Cell<R, C, V>, Cell<C, R, V>> 250 implements Set<Cell<C, R, V>> { 251 // Casting TRANSPOSE_CELL to a type that always succeeds 252 @SuppressWarnings("unchecked") 253 CellSet() { 254 super(original.cellSet(), TRANSPOSE_CELL); 255 } 256 257 @Override public boolean equals(Object obj) { 258 if (obj == this) { 259 return true; 260 } 261 if (!(obj instanceof Set)) { 262 return false; 263 } 264 Set<?> os = (Set<?>) obj; 265 if (os.size() != size()) { 266 return false; 267 } 268 return containsAll(os); 269 } 270 271 @Override public int hashCode() { 272 return Sets.hashCodeImpl(this); 273 } 274 275 @Override public boolean contains(Object obj) { 276 if (obj instanceof Cell) { 277 Cell<?, ?, ?> cell = (Cell<?, ?, ?>) obj; 278 return original.cellSet().contains(immutableCell( 279 cell.getColumnKey(), cell.getRowKey(), cell.getValue())); 280 } 281 return false; 282 } 283 284 @Override public boolean remove(Object obj) { 285 if (obj instanceof Cell) { 286 Cell<?, ?, ?> cell = (Cell<?, ?, ?>) obj; 287 return original.cellSet().remove(immutableCell( 288 cell.getColumnKey(), cell.getRowKey(), cell.getValue())); 289 } 290 return false; 291 } 292 } 293 } 294 }