1/*
2 * Copyright (C) 2009 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.common.collect;
18
19import static org.junit.contrib.truth.Truth.ASSERT;
20
21/**
22 * Tests common methods in {@link ImmutableTable}
23 *
24 * @author gak@google.com (Gregory Kick)
25 */
26public class ImmutableTableTest extends AbstractTableReadTest {
27  @Override protected Table<String, Integer, Character> create(Object... data) {
28    ImmutableTable.Builder<String, Integer, Character> builder =
29        ImmutableTable.builder();
30    for (int i = 0; i < data.length; i = i + 3) {
31      builder.put((String) data[i], (Integer) data[i + 1],
32          (Character) data[i + 2]);
33    }
34    return builder.build();
35  }
36
37  public void testBuilder() {
38    ImmutableTable.Builder<Character, Integer, String> builder =
39        new ImmutableTable.Builder<Character, Integer, String>();
40    assertEquals(ImmutableTable.of(), builder.build());
41    assertEquals(ImmutableTable.of('a', 1, "foo"), builder
42        .put('a', 1, "foo")
43        .build());
44    Table<Character, Integer, String> expectedTable = HashBasedTable.create();
45    expectedTable.put('a', 1, "foo");
46    expectedTable.put('b', 1, "bar");
47    expectedTable.put('a', 2, "baz");
48    Table<Character, Integer, String> otherTable = HashBasedTable.create();
49    otherTable.put('b', 1, "bar");
50    otherTable.put('a', 2, "baz");
51    assertEquals(expectedTable, builder
52        .putAll(otherTable)
53        .build());
54  }
55
56  public void testBuilder_withImmutableCell() {
57    ImmutableTable.Builder<Character, Integer, String> builder =
58        new ImmutableTable.Builder<Character, Integer, String>();
59    assertEquals(ImmutableTable.of('a', 1, "foo"), builder
60        .put(Tables.immutableCell('a', 1, "foo"))
61        .build());
62  }
63
64  public void testBuilder_withImmutableCellAndNullContents() {
65    ImmutableTable.Builder<Character, Integer, String> builder =
66        new ImmutableTable.Builder<Character, Integer, String>();
67    try {
68      builder.put(Tables.immutableCell((Character) null, 1, "foo"));
69      fail();
70    } catch (NullPointerException e) {
71      // success
72    }
73    try {
74      builder.put(Tables.immutableCell('a', (Integer) null, "foo"));
75      fail();
76    } catch (NullPointerException e) {
77      // success
78    }
79    try {
80      builder.put(Tables.immutableCell('a', 1, (String) null));
81      fail();
82    } catch (NullPointerException e) {
83      // success
84    }
85  }
86
87  private static class StringHolder {
88    String string;
89  }
90
91  public void testBuilder_withMutableCell() {
92    ImmutableTable.Builder<Character, Integer, String> builder =
93        new ImmutableTable.Builder<Character, Integer, String>();
94
95    final StringHolder holder = new StringHolder();
96    holder.string = "foo";
97    Table.Cell<Character, Integer, String> mutableCell =
98        new Tables.AbstractCell<Character, Integer, String>() {
99          @Override public Character getRowKey() {
100            return 'K';
101          }
102          @Override public Integer getColumnKey() {
103            return 42;
104          }
105          @Override public String getValue() {
106            return holder.string;
107          }
108        };
109
110    // Add the mutable cell to the builder
111    builder.put(mutableCell);
112
113    // Mutate the value
114    holder.string = "bar";
115
116    // Make sure it uses the original value.
117    assertEquals(ImmutableTable.of('K', 42, "foo"), builder.build());
118  }
119
120  public void testBuilder_noDuplicates() {
121    ImmutableTable.Builder<Character, Integer, String> builder =
122        new ImmutableTable.Builder<Character, Integer, String>()
123            .put('a', 1, "foo")
124            .put('a', 1, "bar");
125    try {
126      builder.build();
127      fail();
128    } catch (IllegalArgumentException e) {
129      // success
130    }
131  }
132
133  public void testBuilder_noNulls() {
134    ImmutableTable.Builder<Character, Integer, String> builder =
135        new ImmutableTable.Builder<Character, Integer, String>();
136    try {
137      builder.put(null, 1, "foo");
138      fail();
139    } catch (NullPointerException e) {
140      // success
141    }
142    try {
143      builder.put('a', null, "foo");
144      fail();
145    } catch (NullPointerException e) {
146      // success
147    }
148    try {
149      builder.put('a', 1, null);
150      fail();
151    } catch (NullPointerException e) {
152      // success
153    }
154  }
155
156  private static <R, C, V> void validateTableCopies(Table<R, C, V> original) {
157    Table<R, C, V> copy = ImmutableTable.copyOf(original);
158    assertEquals(original, copy);
159    validateViewOrdering(original, copy);
160
161    Table<R, C, V> built
162        = ImmutableTable.<R, C, V>builder().putAll(original).build();
163    assertEquals(original, built);
164    validateViewOrdering(original, built);
165  }
166
167  private static <R, C, V> void validateViewOrdering(
168      Table<R, C, V> original, Table<R, C, V> copy) {
169    assertTrue(Iterables.elementsEqual(original.cellSet(), copy.cellSet()));
170    assertTrue(Iterables.elementsEqual(original.rowKeySet(), copy.rowKeySet()));
171    assertTrue(Iterables.elementsEqual(original.values(), copy.values()));
172  }
173
174  public void testCopyOf() {
175    Table<Character, Integer, String> table = TreeBasedTable.create();
176    validateTableCopies(table);
177    table.put('b', 2, "foo");
178    validateTableCopies(table);
179    table.put('b', 1, "bar");
180    table.put('a', 2, "baz");
181    validateTableCopies(table);
182    // Even though rowKeySet, columnKeySet, and cellSet have the same
183    // iteration ordering, row has an inconsistent ordering.
184    ASSERT.that(table.row('b').keySet()).hasContentsInOrder(1, 2);
185    ASSERT.that(ImmutableTable.copyOf(table).row('b').keySet())
186        .hasContentsInOrder(2, 1);
187  }
188
189  public void testCopyOfSparse() {
190    Table<Character, Integer, String> table = TreeBasedTable.create();
191    table.put('x', 2, "foo");
192    table.put('r', 1, "bar");
193    table.put('c', 3, "baz");
194    table.put('b', 7, "cat");
195    table.put('e', 5, "dog");
196    table.put('c', 0, "axe");
197    table.put('e', 3, "tub");
198    table.put('r', 4, "foo");
199    table.put('x', 5, "bar");
200    validateTableCopies(table);
201  }
202
203  public void testCopyOfDense() {
204    Table<Character, Integer, String> table = TreeBasedTable.create();
205    table.put('c', 3, "foo");
206    table.put('c', 2, "bar");
207    table.put('c', 1, "baz");
208    table.put('b', 3, "cat");
209    table.put('b', 1, "dog");
210    table.put('a', 3, "foo");
211    table.put('a', 2, "bar");
212    table.put('a', 1, "baz");
213    validateTableCopies(table);
214  }
215
216  public void testBuilder_orderRowsAndColumnsBy_putAll() {
217    Table<Character, Integer, String> table = HashBasedTable.create();
218    table.put('b', 2, "foo");
219    table.put('b', 1, "bar");
220    table.put('a', 2, "baz");
221    ImmutableTable.Builder<Character, Integer, String> builder
222        = ImmutableTable.builder();
223    Table<Character, Integer, String> copy
224        = builder.orderRowsBy(Ordering.natural())
225            .orderColumnsBy(Ordering.natural())
226            .putAll(table).build();
227    ASSERT.that(copy.rowKeySet()).hasContentsInOrder('a', 'b');
228    ASSERT.that(copy.columnKeySet()).hasContentsInOrder(1, 2);
229    ASSERT.that(copy.values()).hasContentsInOrder("baz", "bar", "foo");
230    ASSERT.that(copy.row('b').keySet()).hasContentsInOrder(1, 2);
231  }
232
233  public void testBuilder_orderRowsAndColumnsBy_sparse() {
234    ImmutableTable.Builder<Character, Integer, String> builder
235        = ImmutableTable.builder();
236    builder.orderRowsBy(Ordering.natural());
237    builder.orderColumnsBy(Ordering.natural());
238    builder.put('x', 2, "foo");
239    builder.put('r', 1, "bar");
240    builder.put('c', 3, "baz");
241    builder.put('b', 7, "cat");
242    builder.put('e', 5, "dog");
243    builder.put('c', 0, "axe");
244    builder.put('e', 3, "tub");
245    builder.put('r', 4, "foo");
246    builder.put('x', 5, "bar");
247    Table<Character, Integer, String> table = builder.build();
248    ASSERT.that(table.rowKeySet()).hasContentsInOrder('b', 'c', 'e', 'r', 'x');
249    ASSERT.that(table.columnKeySet()).hasContentsInOrder(0, 1, 2, 3, 4, 5, 7);
250    ASSERT.that(table.values()).hasContentsInOrder("cat", "axe", "baz", "tub",
251        "dog", "bar", "foo", "foo", "bar");
252    ASSERT.that(table.row('c').keySet()).hasContentsInOrder(0, 3);
253    ASSERT.that(table.column(5).keySet()).hasContentsInOrder('e', 'x');
254  }
255
256  public void testBuilder_orderRowsAndColumnsBy_dense() {
257    ImmutableTable.Builder<Character, Integer, String> builder
258        = ImmutableTable.builder();
259    builder.orderRowsBy(Ordering.natural());
260    builder.orderColumnsBy(Ordering.natural());
261    builder.put('c', 3, "foo");
262    builder.put('c', 2, "bar");
263    builder.put('c', 1, "baz");
264    builder.put('b', 3, "cat");
265    builder.put('b', 1, "dog");
266    builder.put('a', 3, "foo");
267    builder.put('a', 2, "bar");
268    builder.put('a', 1, "baz");
269    Table<Character, Integer, String> table = builder.build();
270    ASSERT.that(table.rowKeySet()).hasContentsInOrder('a', 'b', 'c');
271    ASSERT.that(table.columnKeySet()).hasContentsInOrder(1, 2, 3);
272    ASSERT.that(table.values()).hasContentsInOrder("baz", "bar", "foo", "dog",
273        "cat", "baz", "bar", "foo");
274    ASSERT.that(table.row('c').keySet()).hasContentsInOrder(1, 2, 3);
275    ASSERT.that(table.column(1).keySet()).hasContentsInOrder('a', 'b', 'c');
276  }
277
278  public void testBuilder_orderRowsBy_sparse() {
279    ImmutableTable.Builder<Character, Integer, String> builder
280        = ImmutableTable.builder();
281    builder.orderRowsBy(Ordering.natural());
282    builder.put('x', 2, "foo");
283    builder.put('r', 1, "bar");
284    builder.put('c', 3, "baz");
285    builder.put('b', 7, "cat");
286    builder.put('e', 5, "dog");
287    builder.put('c', 0, "axe");
288    builder.put('e', 3, "tub");
289    builder.put('r', 4, "foo");
290    builder.put('x', 5, "bar");
291    Table<Character, Integer, String> table = builder.build();
292    ASSERT.that(table.rowKeySet()).hasContentsInOrder('b', 'c', 'e', 'r', 'x');
293    ASSERT.that(table.column(5).keySet()).hasContentsInOrder('e', 'x');
294  }
295
296  public void testBuilder_orderRowsBy_dense() {
297    ImmutableTable.Builder<Character, Integer, String> builder
298        = ImmutableTable.builder();
299    builder.orderRowsBy(Ordering.natural());
300    builder.put('c', 3, "foo");
301    builder.put('c', 2, "bar");
302    builder.put('c', 1, "baz");
303    builder.put('b', 3, "cat");
304    builder.put('b', 1, "dog");
305    builder.put('a', 3, "foo");
306    builder.put('a', 2, "bar");
307    builder.put('a', 1, "baz");
308    Table<Character, Integer, String> table = builder.build();
309    ASSERT.that(table.rowKeySet()).hasContentsInOrder('a', 'b', 'c');
310    ASSERT.that(table.column(1).keySet()).hasContentsInOrder('a', 'b', 'c');
311  }
312
313  public void testBuilder_orderColumnsBy_sparse() {
314    ImmutableTable.Builder<Character, Integer, String> builder
315        = ImmutableTable.builder();
316    builder.orderColumnsBy(Ordering.natural());
317    builder.put('x', 2, "foo");
318    builder.put('r', 1, "bar");
319    builder.put('c', 3, "baz");
320    builder.put('b', 7, "cat");
321    builder.put('e', 5, "dog");
322    builder.put('c', 0, "axe");
323    builder.put('e', 3, "tub");
324    builder.put('r', 4, "foo");
325    builder.put('x', 5, "bar");
326    Table<Character, Integer, String> table = builder.build();
327    ASSERT.that(table.columnKeySet()).hasContentsInOrder(0, 1, 2, 3, 4, 5, 7);
328    ASSERT.that(table.row('c').keySet()).hasContentsInOrder(0, 3);
329  }
330
331  public void testBuilder_orderColumnsBy_dense() {
332    ImmutableTable.Builder<Character, Integer, String> builder
333        = ImmutableTable.builder();
334    builder.orderColumnsBy(Ordering.natural());
335    builder.put('c', 3, "foo");
336    builder.put('c', 2, "bar");
337    builder.put('c', 1, "baz");
338    builder.put('b', 3, "cat");
339    builder.put('b', 1, "dog");
340    builder.put('a', 3, "foo");
341    builder.put('a', 2, "bar");
342    builder.put('a', 1, "baz");
343    Table<Character, Integer, String> table = builder.build();
344    ASSERT.that(table.columnKeySet()).hasContentsInOrder(1, 2, 3);
345    ASSERT.that(table.row('c').keySet()).hasContentsInOrder(1, 2, 3);
346  }
347}
348