1/*
2 * Copyright (C) 2013 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package com.google.common.collect;
16
17import com.google.common.annotations.GwtCompatible;
18
19import java.util.AbstractCollection;
20import java.util.AbstractSet;
21import java.util.Collection;
22import java.util.Iterator;
23import java.util.Map;
24import java.util.Set;
25
26import javax.annotation.Nullable;
27
28/**
29 * Skeletal, implementation-agnostic implementation of the {@link Table} interface.
30 *
31 * @author Louis Wasserman
32 */
33@GwtCompatible
34abstract class AbstractTable<R, C, V> implements Table<R, C, V> {
35
36  @Override
37  public boolean containsRow(@Nullable Object rowKey) {
38    return Maps.safeContainsKey(rowMap(), rowKey);
39  }
40
41  @Override
42  public boolean containsColumn(@Nullable Object columnKey) {
43    return Maps.safeContainsKey(columnMap(), columnKey);
44  }
45
46  @Override
47  public Set<R> rowKeySet() {
48    return rowMap().keySet();
49  }
50
51  @Override
52  public Set<C> columnKeySet() {
53    return columnMap().keySet();
54  }
55
56  @Override
57  public boolean containsValue(@Nullable Object value) {
58    for (Map<C, V> row : rowMap().values()) {
59      if (row.containsValue(value)) {
60        return true;
61      }
62    }
63    return false;
64  }
65
66  @Override
67  public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
68    Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
69    return row != null && Maps.safeContainsKey(row, columnKey);
70  }
71
72  @Override
73  public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
74    Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
75    return (row == null) ? null : Maps.safeGet(row, columnKey);
76  }
77
78  @Override
79  public boolean isEmpty() {
80    return size() == 0;
81  }
82
83  @Override
84  public void clear() {
85    Iterators.clear(cellSet().iterator());
86  }
87
88  @Override
89  public V remove(@Nullable Object rowKey, @Nullable Object columnKey) {
90    Map<C, V> row = Maps.safeGet(rowMap(), rowKey);
91    return (row == null) ? null : Maps.safeRemove(row, columnKey);
92  }
93
94  @Override
95  public V put(R rowKey, C columnKey, V value) {
96    return row(rowKey).put(columnKey, value);
97  }
98
99  @Override
100  public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
101    for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
102      put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
103    }
104  }
105
106  private transient Set<Cell<R, C, V>> cellSet;
107
108  @Override
109  public Set<Cell<R, C, V>> cellSet() {
110    Set<Cell<R, C, V>> result = cellSet;
111    return (result == null) ? cellSet = createCellSet() : result;
112  }
113
114  Set<Cell<R, C, V>> createCellSet() {
115    return new CellSet();
116  }
117
118  abstract Iterator<Table.Cell<R, C, V>> cellIterator();
119
120  class CellSet extends AbstractSet<Cell<R, C, V>> {
121    @Override
122    public boolean contains(Object o) {
123      if (o instanceof Cell) {
124        Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
125        Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
126        return row != null && Collections2.safeContains(
127            row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
128      }
129      return false;
130    }
131
132    @Override
133    public boolean remove(@Nullable Object o) {
134      if (o instanceof Cell) {
135        Cell<?, ?, ?> cell = (Cell<?, ?, ?>) o;
136        Map<C, V> row = Maps.safeGet(rowMap(), cell.getRowKey());
137        return row != null && Collections2.safeRemove(
138            row.entrySet(), Maps.immutableEntry(cell.getColumnKey(), cell.getValue()));
139      }
140      return false;
141    }
142
143    @Override
144    public void clear() {
145      AbstractTable.this.clear();
146    }
147
148    @Override
149    public Iterator<Table.Cell<R, C, V>> iterator() {
150      return cellIterator();
151    }
152
153    @Override
154    public int size() {
155      return AbstractTable.this.size();
156    }
157  }
158
159  private transient Collection<V> values;
160
161  @Override
162  public Collection<V> values() {
163    Collection<V> result = values;
164    return (result == null) ? values = createValues() : result;
165  }
166
167  Collection<V> createValues() {
168    return new Values();
169  }
170
171  Iterator<V> valuesIterator() {
172    return new TransformedIterator<Cell<R, C, V>, V>(cellSet().iterator()) {
173      @Override
174      V transform(Cell<R, C, V> cell) {
175        return cell.getValue();
176      }
177    };
178  }
179
180  class Values extends AbstractCollection<V> {
181    @Override
182    public Iterator<V> iterator() {
183      return valuesIterator();
184    }
185
186    @Override
187    public boolean contains(Object o) {
188      return containsValue(o);
189    }
190
191    @Override
192    public void clear() {
193      AbstractTable.this.clear();
194    }
195
196    @Override
197    public int size() {
198      return AbstractTable.this.size();
199    }
200  }
201
202  @Override public boolean equals(@Nullable Object obj) {
203    return Tables.equalsImpl(this, obj);
204  }
205
206  @Override public int hashCode() {
207    return cellSet().hashCode();
208  }
209
210  /**
211   * Returns the string representation {@code rowMap().toString()}.
212   */
213  @Override public String toString() {
214    return rowMap().toString();
215  }
216}
217