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.checkNotNull;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.base.Function;
024import com.google.common.base.Objects;
025import com.google.common.collect.Collections2.TransformedCollection;
026import com.google.common.collect.Table.Cell;
027
028import java.io.Serializable;
029import java.util.Collection;
030import java.util.Map;
031import java.util.Set;
032
033import 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
043public 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}