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    }