/* * Copyright (C) 2008 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.collect; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorKnownOrderRemoveSupportedMethod; import static com.google.common.collect.testing.testers.CollectionIteratorTester.getIteratorUnknownOrderRemoveSupportedMethod; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.collect.Table.Cell; import com.google.common.collect.testing.CollectionTestSuiteBuilder; import com.google.common.collect.testing.MapInterfaceTest; import com.google.common.collect.testing.SampleElements; import com.google.common.collect.testing.SetTestSuiteBuilder; import com.google.common.collect.testing.TestSetGenerator; import com.google.common.collect.testing.TestStringCollectionGenerator; import com.google.common.collect.testing.TestStringSetGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; import com.google.common.collect.testing.features.Feature; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; /** * Collection tests for {@link Table} implementations. * * @author Jared Levy * @author Louis Wasserman */ @GwtCompatible(emulated = true) public class TableCollectionTest extends TestCase { private static final Feature<?>[] COLLECTION_FEATURES = { CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES }; private static final Feature<?>[] COLLECTION_FEATURES_ORDER = { CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, CollectionFeature.ALLOWS_NULL_QUERIES }; private static final Feature<?>[] COLLECTION_FEATURES_REMOVE = { CollectionSize.ANY, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.ALLOWS_NULL_QUERIES }; private static final Feature<?>[] COLLECTION_FEATURES_REMOVE_ORDER = { CollectionSize.ANY, CollectionFeature.KNOWN_ORDER, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.ALLOWS_NULL_QUERIES }; @GwtIncompatible("suite") public static Test suite() { TestSuite suite = new TestSuite(); suite.addTestSuite(ArrayRowTests.class); suite.addTestSuite(HashRowTests.class); suite.addTestSuite(TreeRowTests.class); suite.addTestSuite(TransposeRowTests.class); suite.addTestSuite(TransformValueRowTests.class); suite.addTestSuite(UnmodifiableHashRowTests.class); suite.addTestSuite(UnmodifiableTreeRowTests.class); suite.addTestSuite(ArrayColumnTests.class); suite.addTestSuite(HashColumnTests.class); suite.addTestSuite(TreeColumnTests.class); suite.addTestSuite(TransposeColumnTests.class); suite.addTestSuite(TransformValueColumnTests.class); suite.addTestSuite(UnmodifiableHashColumnTests.class); suite.addTestSuite(UnmodifiableTreeColumnTests.class); suite.addTestSuite(ArrayRowMapTests.class); suite.addTestSuite(HashRowMapTests.class); suite.addTestSuite(TreeRowMapTests.class); suite.addTestSuite(TreeRowMapHeadMapTests.class); suite.addTestSuite(TreeRowMapTailMapTests.class); suite.addTestSuite(TreeRowMapSubMapTests.class); suite.addTestSuite(TransformValueRowMapTests.class); suite.addTestSuite(UnmodifiableHashRowMapTests.class); suite.addTestSuite(UnmodifiableTreeRowMapTests.class); suite.addTestSuite(ArrayColumnMapTests.class); suite.addTestSuite(HashColumnMapTests.class); suite.addTestSuite(TreeColumnMapTests.class); suite.addTestSuite(TransformValueColumnMapTests.class); suite.addTestSuite(UnmodifiableHashColumnMapTests.class); suite.addTestSuite(UnmodifiableTreeColumnMapTests.class); // Not testing rowKeySet() or columnKeySet() of Table.transformValues() // since the transformation doesn't affect the row and column key sets. suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = ArrayTable.create( ImmutableList.copyOf(elements), ImmutableList.of(1, 2)); populateForRowKeySet(table, elements); return table.rowKeySet(); } }) .named("ArrayTable.rowKeySet") .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL, CollectionFeature.KNOWN_ORDER, CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = HashBasedTable.create(); populateForRowKeySet(table, elements); return table.rowKeySet(); } }) .named("HashBasedTable.rowKeySet") .withFeatures(COLLECTION_FEATURES_REMOVE) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); return table.rowKeySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.rowKeySet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); table.put("z", 1, 'a'); return table.rowKeySet().headSet("x"); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.rowKeySet.headSet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); table.put("\0", 1, 'a'); return table.rowKeySet().tailSet("a"); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.rowKeySet.tailSet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); table.put("\0", 1, 'a'); table.put("z", 1, 'a'); return table.rowKeySet().subSet("a", "x"); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.rowKeySet.subSet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = HashBasedTable.create(); populateForRowKeySet(table, elements); return Tables.unmodifiableTable(table).rowKeySet(); } }) .named("unmodifiableTable[HashBasedTable].rowKeySet") .withFeatures(COLLECTION_FEATURES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { RowSortedTable<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); return Tables.unmodifiableRowSortedTable(table).rowKeySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("unmodifiableRowSortedTable[TreeBasedTable].rowKeySet") .withFeatures(COLLECTION_FEATURES_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<Integer, String, Character> table = ArrayTable.create( ImmutableList.of(1, 2), ImmutableList.copyOf(elements)); populateForColumnKeySet(table, elements); return table.columnKeySet(); } }) .named("ArrayTable.columnKeySet") .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL, CollectionFeature.KNOWN_ORDER, CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<Integer, String, Character> table = HashBasedTable.create(); populateForColumnKeySet(table, elements); return table.columnKeySet(); } }) .named("HashBasedTable.columnKeySet") .withFeatures(COLLECTION_FEATURES_REMOVE) .suppressing(getIteratorUnknownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<Integer, String, Character> table = TreeBasedTable.create(); populateForColumnKeySet(table, elements); return table.columnKeySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.columnKeySet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<Integer, String, Character> table = HashBasedTable.create(); populateForColumnKeySet(table, elements); return Tables.unmodifiableTable(table).columnKeySet(); } }) .named("unmodifiableTable[HashBasedTable].columnKeySet") .withFeatures(COLLECTION_FEATURES) .suppressing(getIteratorUnknownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { RowSortedTable<Integer, String, Character> table = TreeBasedTable.create(); populateForColumnKeySet(table, elements); return Tables.unmodifiableRowSortedTable(table).columnKeySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("unmodifiableRowSortedTable[TreeBasedTable].columnKeySet") .withFeatures(COLLECTION_FEATURES_ORDER) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { List<Integer> rowKeys = Lists.newArrayList(); for (int i = 0; i < elements.length; i++) { rowKeys.add(i); } Table<Integer, Character, String> table = ArrayTable.create(rowKeys, ImmutableList.of('a')); populateForValues(table, elements); return table.values(); } }) .named("ArrayTable.values") .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL, CollectionFeature.ALLOWS_NULL_VALUES, CollectionFeature.KNOWN_ORDER) .createTestSuite()); suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { Table<Integer, Character, String> table = HashBasedTable.create(); table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); return table.values(); } }) .named("HashBasedTable.values") .withFeatures(COLLECTION_FEATURES_REMOVE) .createTestSuite()); suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { Table<Integer, Character, String> table = TreeBasedTable.create(); table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); return table.values(); } }) .named("TreeBasedTable.values") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .createTestSuite()); final Function<String, String> removeFirstCharacter = new Function<String, String>() { @Override public String apply(String input) { return input.substring(1); } }; suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { Table<Integer, Character, String> table = HashBasedTable.create(); for (int i = 0; i < elements.length; i++) { table.put(i, 'a', "x" + checkNotNull(elements[i])); } return Tables.transformValues(table, removeFirstCharacter).values(); } }) .named("TransformValues.values") .withFeatures(COLLECTION_FEATURES_REMOVE) .createTestSuite()); suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { Table<Integer, Character, String> table = HashBasedTable.create(); table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); return Tables.unmodifiableTable(table).values(); } }) .named("unmodifiableTable[HashBasedTable].values") .withFeatures(COLLECTION_FEATURES) .createTestSuite()); suite.addTest(CollectionTestSuiteBuilder.using( new TestStringCollectionGenerator() { @Override protected Collection<String> create(String[] elements) { RowSortedTable<Integer, Character, String> table = TreeBasedTable.create(); table.put(1, 'a', "foo"); table.clear(); populateForValues(table, elements); return Tables.unmodifiableRowSortedTable(table).values(); } }) .named("unmodifiableTable[TreeBasedTable].values") .withFeatures(COLLECTION_FEATURES_ORDER) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override public SampleElements<Cell<String, Integer, Character>> samples() { return new SampleElements<Cell<String, Integer, Character>>( Tables.immutableCell("bar", 1, 'a'), Tables.immutableCell("bar", 2, 'b'), Tables.immutableCell("bar", 3, (Character) null), Tables.immutableCell("bar", 4, 'b'), Tables.immutableCell("bar", 5, 'b')); } @Override public Set<Cell<String, Integer, Character>> create( Object... elements) { List<Integer> columnKeys = Lists.newArrayList(); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; columnKeys.add(cell.getColumnKey()); } Table<String, Integer, Character> table = ArrayTable.create(ImmutableList.of("bar"), columnKeys); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } return table.cellSet(); } @Override Table<String, Integer, Character> createTable() { throw new UnsupportedOperationException(); } }) .named("ArrayTable.cellSet") .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL, CollectionFeature.KNOWN_ORDER, CollectionFeature.REJECTS_DUPLICATES_AT_CREATION, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override Table<String, Integer, Character> createTable() { return HashBasedTable.create(); } }) .named("HashBasedTable.cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override Table<String, Integer, Character> createTable() { return TreeBasedTable.create(); } }) .named("TreeBasedTable.cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override Table<String, Integer, Character> createTable() { Table<Integer, String, Character> original = TreeBasedTable.create(); return Tables.transpose(original); } }) .named("TransposedTable.cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override Table<String, Integer, Character> createTable() { return HashBasedTable.create(); } @Override public Set<Cell<String, Integer, Character>> create( Object... elements) { Table<String, Integer, Character> table = createTable(); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } return Tables.transformValues(table, Functions.<Character>identity()).cellSet(); } }) .named("TransformValues.cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override Table<String, Integer, Character> createTable() { return Tables.unmodifiableTable(HashBasedTable.<String, Integer, Character> create()); } @Override public Set<Cell<String, Integer, Character>> create( Object... elements) { Table<String, Integer, Character> table = HashBasedTable.create(); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } return Tables.unmodifiableTable(table).cellSet(); } }) .named("unmodifiableTable[HashBasedTable].cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestCellSetGenerator() { @Override RowSortedTable<String, Integer, Character> createTable() { return Tables.unmodifiableRowSortedTable(TreeBasedTable .<String, Integer, Character> create()); } @Override public Set<Cell<String, Integer, Character>> create( Object... elements) { RowSortedTable<String, Integer, Character> table = TreeBasedTable.create(); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } return Tables.unmodifiableRowSortedTable(table).cellSet(); } }) .named("unmodifiableRowSortedTable[TreeBasedTable].cellSet") .withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Iterable<String> rowKeys = ImmutableSet.copyOf(elements); Iterable<Integer> columnKeys = ImmutableList.of(1, 2, 3); Table<String, Integer, Character> table = ArrayTable.create(rowKeys, columnKeys); populateForRowKeySet(table, elements); return table.column(1).keySet(); } }) .named("ArrayTable.column.keySet") .withFeatures(CollectionSize.ONE, CollectionSize.SEVERAL, CollectionFeature.KNOWN_ORDER, CollectionFeature.ALLOWS_NULL_QUERIES) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = HashBasedTable.create(); populateForRowKeySet(table, elements); return table.column(1).keySet(); } }) .named("HashBasedTable.column.keySet") .withFeatures(COLLECTION_FEATURES_REMOVE) .suppressing(getIteratorUnknownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); return table.column(1).keySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("TreeBasedTable.column.keySet") .withFeatures(COLLECTION_FEATURES_REMOVE_ORDER) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = HashBasedTable.create(); populateForRowKeySet(table, elements); return Tables.transformValues(table, Functions.toStringFunction()).column(1).keySet(); } }) .named("TransformValues.column.keySet") .withFeatures(COLLECTION_FEATURES_REMOVE) .suppressing(getIteratorUnknownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { Table<String, Integer, Character> table = HashBasedTable.create(); populateForRowKeySet(table, elements); return Tables.unmodifiableTable(table).column(1).keySet(); } }) .named("unmodifiableTable[HashBasedTable].column.keySet") .withFeatures(COLLECTION_FEATURES) .suppressing(getIteratorUnknownOrderRemoveSupportedMethod()) .createTestSuite()); suite.addTest(SetTestSuiteBuilder.using(new TestStringSetGenerator() { @Override protected Set<String> create(String[] elements) { RowSortedTable<String, Integer, Character> table = TreeBasedTable.create(); populateForRowKeySet(table, elements); return Tables.unmodifiableRowSortedTable(table).column(1).keySet(); } @Override public List<String> order(List<String> insertionOrder) { Collections.sort(insertionOrder); return insertionOrder; } }) .named("unmodifiableRowSortedTable[TreeBasedTable].column.keySet") .withFeatures(COLLECTION_FEATURES_ORDER) .suppressing(getIteratorKnownOrderRemoveSupportedMethod()) .createTestSuite()); return suite; } private static void populateForRowKeySet( Table<String, Integer, Character> table, String[] elements) { for (String row : elements) { table.put(row, 1, 'a'); table.put(row, 2, 'b'); } } private static void populateForColumnKeySet( Table<Integer, String, Character> table, String[] elements) { for (String column : elements) { table.put(1, column, 'a'); table.put(2, column, 'b'); } } private static void populateForValues( Table<Integer, Character, String> table, String[] elements) { for (int i = 0; i < elements.length; i++) { table.put(i, 'a', elements[i]); } } private static abstract class TestCellSetGenerator implements TestSetGenerator<Cell<String, Integer, Character>> { @Override public SampleElements<Cell<String, Integer, Character>> samples() { return new SampleElements<Cell<String, Integer, Character>>( Tables.immutableCell("bar", 1, 'a'), Tables.immutableCell("bar", 2, 'b'), Tables.immutableCell("foo", 3, 'c'), Tables.immutableCell("bar", 1, 'b'), Tables.immutableCell("cat", 2, 'b')); } @Override public Set<Cell<String, Integer, Character>> create( Object... elements) { Table<String, Integer, Character> table = createTable(); for (Object element : elements) { @SuppressWarnings("unchecked") Cell<String, Integer, Character> cell = (Cell<String, Integer, Character>) element; table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue()); } return table.cellSet(); } abstract Table<String, Integer, Character> createTable(); @Override @SuppressWarnings("unchecked") public Cell<String, Integer, Character>[] createArray(int length) { return (Cell<String, Integer, Character>[]) new Cell<?, ?, ?>[length]; } @Override public List<Cell<String, Integer, Character>> order( List<Cell<String, Integer, Character>> insertionOrder) { return insertionOrder; } } private static abstract class MapTests extends MapInterfaceTest<String, Integer> { MapTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(false, allowsNullValues, supportsPut, supportsRemove, supportsClear, supportsIteratorRemove); } @Override protected String getKeyNotInPopulatedMap() { return "four"; } @Override protected Integer getValueNotInPopulatedMap() { return 4; } } private static abstract class RowTests extends MapTests { RowTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(allowsNullValues, supportsPut, supportsRemove, supportsClear, supportsIteratorRemove); } abstract Table<Character, String, Integer> makeTable(); @Override protected Map<String, Integer> makeEmptyMap() { return makeTable().row('a'); } @Override protected Map<String, Integer> makePopulatedMap() { Table<Character, String, Integer> table = makeTable(); table.put('a', "one", 1); table.put('a', "two", 2); table.put('a', "three", 3); table.put('b', "four", 4); return table.row('a'); } } @GwtIncompatible("TODO(hhchan): ArrayTable") public static class ArrayRowTests extends RowTests { public ArrayRowTests() { super(true, true, false, false, false); } @Override protected String getKeyNotInPopulatedMap() { throw new UnsupportedOperationException(); } @Override protected Map<String, Integer> makeEmptyMap() { throw new UnsupportedOperationException(); } @Override protected Table<Character, String, Integer> makeTable() { return ArrayTable.create(Arrays.asList('a', 'b', 'c'), Arrays.asList("one", "two", "three", "four")); } } public static class HashRowTests extends RowTests { public HashRowTests() { super(false, true, true, true, true); } @Override Table<Character, String, Integer> makeTable() { return HashBasedTable.create(); } } public static class TreeRowTests extends RowTests { public TreeRowTests() { super(false, true, true, true, true); } @Override Table<Character, String, Integer> makeTable() { return TreeBasedTable.create(); } } public static class TransposeRowTests extends RowTests { public TransposeRowTests() { super(false, true, true, true, false); } @Override Table<Character, String, Integer> makeTable() { Table<String, Character, Integer> original = TreeBasedTable.create(); return Tables.transpose(original); } } private static final Function<Integer, Integer> DIVIDE_BY_2 = new Function<Integer, Integer>() { @Override public Integer apply(Integer input) { return (input == null) ? null : input / 2; } }; public static class TransformValueRowTests extends RowTests { public TransformValueRowTests() { super(false, false, true, true, true); } @Override Table<Character, String, Integer> makeTable() { Table<Character, String, Integer> table = HashBasedTable.create(); return Tables.transformValues(table, DIVIDE_BY_2); } @Override protected Map<String, Integer> makePopulatedMap() { Table<Character, String, Integer> table = HashBasedTable.create(); table.put('a', "one", 2); table.put('a', "two", 4); table.put('a', "three", 6); table.put('b', "four", 8); return Tables.transformValues(table, DIVIDE_BY_2).row('a'); } } public static class UnmodifiableHashRowTests extends RowTests { public UnmodifiableHashRowTests() { super(false, false, false, false, false); } @Override Table<Character, String, Integer> makeTable() { Table<Character, String, Integer> table = HashBasedTable.create(); return Tables.unmodifiableTable(table); } @Override protected Map<String, Integer> makePopulatedMap() { Table<Character, String, Integer> table = HashBasedTable.create(); table.put('a', "one", 1); table.put('a', "two", 2); table.put('a', "three", 3); table.put('b', "four", 4); return Tables.unmodifiableTable(table).row('a'); } } public static class UnmodifiableTreeRowTests extends RowTests { public UnmodifiableTreeRowTests() { super(false, false, false, false, false); } @Override Table<Character, String, Integer> makeTable() { RowSortedTable<Character, String, Integer> table = TreeBasedTable.create(); return Tables.unmodifiableRowSortedTable(table); } @Override protected Map<String, Integer> makePopulatedMap() { RowSortedTable<Character, String, Integer> table = TreeBasedTable.create(); table.put('a', "one", 1); table.put('a', "two", 2); table.put('a', "three", 3); table.put('b', "four", 4); return Tables.unmodifiableRowSortedTable(table).row('a'); } } private static abstract class ColumnTests extends MapTests { ColumnTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(allowsNullValues, supportsPut, supportsRemove, supportsClear, supportsIteratorRemove); } abstract Table<String, Character, Integer> makeTable(); @Override protected Map<String, Integer> makeEmptyMap() { return makeTable().column('a'); } @Override protected Map<String, Integer> makePopulatedMap() { Table<String, Character, Integer> table = makeTable(); table.put("one", 'a', 1); table.put("two", 'a', 2); table.put("three", 'a', 3); table.put("four", 'b', 4); return table.column('a'); } } @GwtIncompatible("TODO(hhchan): ArrayTable") public static class ArrayColumnTests extends ColumnTests { public ArrayColumnTests() { super(true, true, false, false, false); } @Override protected String getKeyNotInPopulatedMap() { throw new UnsupportedOperationException(); } @Override protected Map<String, Integer> makeEmptyMap() { throw new UnsupportedOperationException(); } @Override Table<String, Character, Integer> makeTable() { return ArrayTable.create(Arrays.asList("one", "two", "three", "four"), Arrays.asList('a', 'b', 'c')); } } public static class HashColumnTests extends ColumnTests { public HashColumnTests() { super(false, true, true, true, false); } @Override Table<String, Character, Integer> makeTable() { return HashBasedTable.create(); } } public static class TreeColumnTests extends ColumnTests { public TreeColumnTests() { super(false, true, true, true, false); } @Override Table<String, Character, Integer> makeTable() { return TreeBasedTable.create(); } } public static class TransposeColumnTests extends ColumnTests { public TransposeColumnTests() { super(false, true, true, true, true); } @Override Table<String, Character, Integer> makeTable() { Table<Character, String, Integer> original = TreeBasedTable.create(); return Tables.transpose(original); } } public static class TransformValueColumnTests extends ColumnTests { public TransformValueColumnTests() { super(false, false, true, true, false); } @Override Table<String, Character, Integer> makeTable() { Table<String, Character, Integer> table = HashBasedTable.create(); return Tables.transformValues(table, DIVIDE_BY_2); } @Override protected Map<String, Integer> makePopulatedMap() { Table<String, Character, Integer> table = HashBasedTable.create(); table.put("one", 'a', 1); table.put("two", 'a', 2); table.put("three", 'a', 3); table.put("four", 'b', 4); return Tables.transformValues(table, DIVIDE_BY_2).column('a'); } } public static class UnmodifiableHashColumnTests extends ColumnTests { public UnmodifiableHashColumnTests() { super(false, false, false, false, false); } @Override Table<String, Character, Integer> makeTable() { Table<String, Character, Integer> table = HashBasedTable.create(); return Tables.unmodifiableTable(table); } @Override protected Map<String, Integer> makePopulatedMap() { Table<String, Character, Integer> table = HashBasedTable.create(); table.put("one", 'a', 1); table.put("two", 'a', 2); table.put("three", 'a', 3); table.put("four", 'b', 4); return Tables.unmodifiableTable(table).column('a'); } } public static class UnmodifiableTreeColumnTests extends ColumnTests { public UnmodifiableTreeColumnTests() { super(false, false, false, false, false); } @Override Table<String, Character, Integer> makeTable() { RowSortedTable<String, Character, Integer> table = TreeBasedTable.create(); return Tables.unmodifiableRowSortedTable(table); } @Override protected Map<String, Integer> makePopulatedMap() { RowSortedTable<String, Character, Integer> table = TreeBasedTable.create(); table.put("one", 'a', 1); table.put("two", 'a', 2); table.put("three", 'a', 3); table.put("four", 'b', 4); return Tables.unmodifiableRowSortedTable(table).column('a'); } } private static abstract class MapMapTests extends MapInterfaceTest<String, Map<Integer, Character>> { MapMapTests(boolean allowsNullValues, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(false, allowsNullValues, false, supportsRemove, supportsClear, supportsIteratorRemove); } @Override protected String getKeyNotInPopulatedMap() { return "cat"; } @Override protected Map<Integer, Character> getValueNotInPopulatedMap() { return ImmutableMap.of(); } /** * The version of this test supplied by {@link MapInterfaceTest} fails for * this particular map implementation, because {@code map.get()} returns a * view collection that changes in the course of a call to {@code remove()}. * Thus, the expectation doesn't hold that {@code map.remove(x)} returns the * same value which {@code map.get(x)} did immediately beforehand. */ @Override public void testRemove() { final Map<String, Map<Integer, Character>> map; final String keyToRemove; try { map = makePopulatedMap(); } catch (UnsupportedOperationException e) { return; } keyToRemove = map.keySet().iterator().next(); if (supportsRemove) { int initialSize = map.size(); map.get(keyToRemove); map.remove(keyToRemove); // This line doesn't hold - see the Javadoc comments above. // assertEquals(expectedValue, oldValue); assertFalse(map.containsKey(keyToRemove)); assertEquals(initialSize - 1, map.size()); } else { try { map.remove(keyToRemove); fail("Expected UnsupportedOperationException."); } catch (UnsupportedOperationException e) { // Expected. } } assertInvariants(map); } } private static abstract class RowMapTests extends MapMapTests { RowMapTests(boolean allowsNullValues, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(allowsNullValues, supportsRemove, supportsClear, supportsIteratorRemove); } abstract Table<String, Integer, Character> makeTable(); @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<String, Integer, Character> table = makeTable(); populateTable(table); return table.rowMap(); } void populateTable(Table<String, Integer, Character> table) { table.put("foo", 1, 'a'); table.put("bar", 1, 'b'); table.put("foo", 3, 'c'); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { return makeTable().rowMap(); } } @GwtIncompatible("TODO(hhchan): ArrayTable") public static class ArrayRowMapTests extends RowMapTests { public ArrayRowMapTests() { super(true, false, false, false); } @Override Table<String, Integer, Character> makeTable() { return ArrayTable.create(Arrays.asList("foo", "bar", "dog"), Arrays.asList(1, 2, 3)); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { throw new UnsupportedOperationException(); } } public static class HashRowMapTests extends RowMapTests { public HashRowMapTests() { super(false, true, true, true); } @Override Table<String, Integer, Character> makeTable() { return HashBasedTable.create(); } } public static class TreeRowMapTests extends RowMapTests { public TreeRowMapTests() { super(false, true, true, true); } @Override Table<String, Integer, Character> makeTable() { return TreeBasedTable.create(); } } public static class TreeRowMapHeadMapTests extends RowMapTests { public TreeRowMapHeadMapTests() { super(false, true, true, true); } @Override TreeBasedTable<String, Integer, Character> makeTable() { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); table.put("z", 1, 'a'); return table; } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { TreeBasedTable<String, Integer, Character> table = makeTable(); populateTable(table); return table.rowMap().headMap("x"); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { return makeTable().rowMap().headMap("x"); } @Override protected String getKeyNotInPopulatedMap() { return "z"; } } public static class TreeRowMapTailMapTests extends RowMapTests { public TreeRowMapTailMapTests() { super(false, true, true, true); } @Override TreeBasedTable<String, Integer, Character> makeTable() { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); table.put("a", 1, 'a'); return table; } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { TreeBasedTable<String, Integer, Character> table = makeTable(); populateTable(table); return table.rowMap().tailMap("b"); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { return makeTable().rowMap().tailMap("b"); } @Override protected String getKeyNotInPopulatedMap() { return "a"; } } public static class TreeRowMapSubMapTests extends RowMapTests { public TreeRowMapSubMapTests() { super(false, true, true, true); } @Override TreeBasedTable<String, Integer, Character> makeTable() { TreeBasedTable<String, Integer, Character> table = TreeBasedTable.create(); table.put("a", 1, 'a'); table.put("z", 1, 'a'); return table; } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { TreeBasedTable<String, Integer, Character> table = makeTable(); populateTable(table); return table.rowMap().subMap("b", "x"); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { return makeTable().rowMap().subMap("b", "x"); } @Override protected String getKeyNotInPopulatedMap() { return "z"; } } private static final Function<String, Character> FIRST_CHARACTER = new Function<String, Character>() { @Override public Character apply(String input) { return input == null ? null : input.charAt(0); } }; public static class TransformValueRowMapTests extends RowMapTests { public TransformValueRowMapTests() { super(false, true, true, true); } @Override Table<String, Integer, Character> makeTable() { Table<String, Integer, String> original = HashBasedTable.create(); return Tables.transformValues(original, FIRST_CHARACTER); } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<String, Integer, String> table = HashBasedTable.create(); table.put("foo", 1, "apple"); table.put("bar", 1, "banana"); table.put("foo", 3, "cat"); return Tables.transformValues(table, FIRST_CHARACTER).rowMap(); } } public static class UnmodifiableHashRowMapTests extends RowMapTests { public UnmodifiableHashRowMapTests() { super(false, false, false, false); } @Override Table<String, Integer, Character> makeTable() { Table<String, Integer, Character> original = HashBasedTable.create(); return Tables.unmodifiableTable(original); } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<String, Integer, Character> table = HashBasedTable.create(); table.put("foo", 1, 'a'); table.put("bar", 1, 'b'); table.put("foo", 3, 'c'); return Tables.unmodifiableTable(table).rowMap(); } } public static class UnmodifiableTreeRowMapTests extends RowMapTests { public UnmodifiableTreeRowMapTests() { super(false, false, false, false); } @Override RowSortedTable<String, Integer, Character> makeTable() { RowSortedTable<String, Integer, Character> original = TreeBasedTable.create(); return Tables.unmodifiableRowSortedTable(original); } @Override protected SortedMap<String, Map<Integer, Character>> makePopulatedMap() { RowSortedTable<String, Integer, Character> table = TreeBasedTable.create(); table.put("foo", 1, 'a'); table.put("bar", 1, 'b'); table.put("foo", 3, 'c'); return Tables.unmodifiableRowSortedTable(table).rowMap(); } } private static abstract class ColumnMapTests extends MapMapTests { ColumnMapTests(boolean allowsNullValues, boolean supportsRemove, boolean supportsClear, boolean supportsIteratorRemove) { super(allowsNullValues, supportsRemove, supportsClear, supportsIteratorRemove); } abstract Table<Integer, String, Character> makeTable(); @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<Integer, String, Character> table = makeTable(); table.put(1, "foo", 'a'); table.put(1, "bar", 'b'); table.put(3, "foo", 'c'); return table.columnMap(); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { return makeTable().columnMap(); } } @GwtIncompatible("TODO(hhchan): ArrayTable") public static class ArrayColumnMapTests extends ColumnMapTests { public ArrayColumnMapTests() { super(true, false, false, false); } @Override Table<Integer, String, Character> makeTable() { return ArrayTable.create(Arrays.asList(1, 2, 3), Arrays.asList("foo", "bar", "dog")); } @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() { throw new UnsupportedOperationException(); } } public static class HashColumnMapTests extends ColumnMapTests { public HashColumnMapTests() { super(false, true, true, false); } @Override Table<Integer, String, Character> makeTable() { return HashBasedTable.create(); } } public static class TreeColumnMapTests extends ColumnMapTests { public TreeColumnMapTests() { super(false, true, true, false); } @Override Table<Integer, String, Character> makeTable() { return TreeBasedTable.create(); } } public static class TransformValueColumnMapTests extends ColumnMapTests { public TransformValueColumnMapTests() { super(false, true, true, false); } @Override Table<Integer, String, Character> makeTable() { Table<Integer, String, String> original = HashBasedTable.create(); return Tables.transformValues(original, FIRST_CHARACTER); } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<Integer, String, String> table = HashBasedTable.create(); table.put(1, "foo", "apple"); table.put(1, "bar", "banana"); table.put(3, "foo", "cat"); return Tables.transformValues(table, FIRST_CHARACTER).columnMap(); } } public static class UnmodifiableHashColumnMapTests extends ColumnMapTests { public UnmodifiableHashColumnMapTests() { super(false, false, false, false); } @Override Table<Integer, String, Character> makeTable() { Table<Integer, String, Character> original = HashBasedTable.create(); return Tables.unmodifiableTable(original); } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { Table<Integer, String, Character> table = HashBasedTable.create(); table.put(1, "foo", 'a'); table.put(1, "bar", 'b'); table.put(3, "foo", 'c'); return Tables.unmodifiableTable(table).columnMap(); } } public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests { public UnmodifiableTreeColumnMapTests() { super(false, false, false, false); } @Override Table<Integer, String, Character> makeTable() { RowSortedTable<Integer, String, Character> original = TreeBasedTable.create(); return Tables.unmodifiableRowSortedTable(original); } @Override protected Map<String, Map<Integer, Character>> makePopulatedMap() { RowSortedTable<Integer, String, Character> table = TreeBasedTable.create(); table.put(1, "foo", 'a'); table.put(1, "bar", 'b'); table.put(3, "foo", 'c'); return Tables.unmodifiableRowSortedTable(table).columnMap(); } } }