1/*
2 * Copyright (C) 2008 The Guava Authors
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 com.google.common.annotations.GwtCompatible;
20import com.google.common.base.Function;
21import com.google.common.collect.Table.Cell;
22import com.google.common.collect.testing.MapInterfaceTest;
23import com.google.common.collect.testing.SampleElements;
24import com.google.common.collect.testing.TestSetGenerator;
25import com.google.common.collect.testing.features.CollectionFeature;
26import com.google.common.collect.testing.features.CollectionSize;
27import com.google.common.collect.testing.features.Feature;
28
29import junit.framework.TestCase;
30
31import java.util.List;
32import java.util.Map;
33import java.util.Set;
34import java.util.SortedMap;
35
36/**
37 * Collection tests for {@link Table} implementations.
38 *
39 * @author Jared Levy
40 * @author Louis Wasserman
41 */
42@GwtCompatible(emulated = true)
43public class TableCollectionTest extends TestCase {
44
45  private static final Feature<?>[] COLLECTION_FEATURES = {
46    CollectionSize.ANY,
47    CollectionFeature.ALLOWS_NULL_QUERIES
48  };
49
50  private static final Feature<?>[] COLLECTION_FEATURES_ORDER = {
51    CollectionSize.ANY,
52    CollectionFeature.KNOWN_ORDER,
53    CollectionFeature.ALLOWS_NULL_QUERIES
54  };
55
56  private static final Feature<?>[] COLLECTION_FEATURES_REMOVE = {
57    CollectionSize.ANY,
58    CollectionFeature.SUPPORTS_REMOVE,
59    CollectionFeature.ALLOWS_NULL_QUERIES
60  };
61
62  private static final Feature<?>[] COLLECTION_FEATURES_REMOVE_ORDER = {
63    CollectionSize.ANY,
64    CollectionFeature.KNOWN_ORDER,
65    CollectionFeature.SUPPORTS_REMOVE,
66    CollectionFeature.ALLOWS_NULL_QUERIES
67  };
68
69  private static void populateForRowKeySet(
70      Table<String, Integer, Character> table, String[] elements) {
71    for (String row : elements) {
72      table.put(row, 1, 'a');
73      table.put(row, 2, 'b');
74    }
75  }
76
77  private static void populateForColumnKeySet(
78      Table<Integer, String, Character> table, String[] elements) {
79    for (String column : elements) {
80      table.put(1, column, 'a');
81      table.put(2, column, 'b');
82    }
83  }
84
85  private static void populateForValues(
86      Table<Integer, Character, String> table, String[] elements) {
87    for (int i = 0; i < elements.length; i++) {
88      table.put(i, 'a', elements[i]);
89    }
90  }
91
92  private static abstract class TestCellSetGenerator
93      implements TestSetGenerator<Cell<String, Integer, Character>> {
94    @Override
95    public SampleElements<Cell<String, Integer, Character>> samples() {
96      return new SampleElements<Cell<String, Integer, Character>>(
97          Tables.immutableCell("bar", 1, 'a'),
98          Tables.immutableCell("bar", 2, 'b'),
99          Tables.immutableCell("foo", 3, 'c'),
100          Tables.immutableCell("bar", 1, 'b'),
101          Tables.immutableCell("cat", 2, 'b'));
102    }
103
104    @Override
105    public Set<Cell<String, Integer, Character>> create(
106        Object... elements) {
107      Table<String, Integer, Character> table = createTable();
108      for (Object element : elements) {
109        @SuppressWarnings("unchecked")
110        Cell<String, Integer, Character> cell
111            = (Cell<String, Integer, Character>) element;
112        table.put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
113      }
114      return table.cellSet();
115    }
116
117    abstract Table<String, Integer, Character> createTable();
118
119    @Override
120    @SuppressWarnings("unchecked")
121    public Cell<String, Integer, Character>[] createArray(int length) {
122      return (Cell<String, Integer, Character>[]) new Cell<?, ?, ?>[length];
123    }
124
125    @Override
126    public List<Cell<String, Integer, Character>> order(
127        List<Cell<String, Integer, Character>> insertionOrder) {
128      return insertionOrder;
129    }
130  }
131
132  private static abstract class MapTests
133      extends MapInterfaceTest<String, Integer> {
134
135    MapTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
136        boolean supportsClear, boolean supportsIteratorRemove) {
137      super(false, allowsNullValues, supportsPut, supportsRemove, supportsClear,
138          supportsIteratorRemove);
139    }
140
141    @Override protected String getKeyNotInPopulatedMap() {
142      return "four";
143    }
144
145    @Override protected Integer getValueNotInPopulatedMap() {
146      return 4;
147    }
148  }
149
150  private static abstract class RowTests extends MapTests {
151    RowTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
152        boolean supportsClear, boolean supportsIteratorRemove) {
153      super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
154          supportsIteratorRemove);
155    }
156
157    abstract Table<Character, String, Integer> makeTable();
158
159    @Override protected Map<String, Integer> makeEmptyMap() {
160      return makeTable().row('a');
161    }
162
163    @Override protected Map<String, Integer> makePopulatedMap() {
164      Table<Character, String, Integer> table = makeTable();
165      table.put('a', "one", 1);
166      table.put('a', "two", 2);
167      table.put('a', "three", 3);
168      table.put('b', "four", 4);
169      return table.row('a');
170    }
171  }
172
173  public static class HashRowTests extends RowTests {
174    public HashRowTests() {
175      super(false, true, true, true, true);
176    }
177
178    @Override Table<Character, String, Integer> makeTable() {
179      return HashBasedTable.create();
180    }
181  }
182
183  public static class TreeRowTests extends RowTests {
184    public TreeRowTests() {
185      super(false, true, true, true, true);
186    }
187
188    @Override Table<Character, String, Integer> makeTable() {
189      return TreeBasedTable.create();
190    }
191  }
192
193  public static class TransposeRowTests extends RowTests {
194    public TransposeRowTests() {
195      super(false, true, true, true, false);
196    }
197
198    @Override Table<Character, String, Integer> makeTable() {
199      Table<String, Character, Integer> original = TreeBasedTable.create();
200      return Tables.transpose(original);
201    }
202  }
203
204  private static final Function<Integer, Integer> DIVIDE_BY_2
205      = new Function<Integer, Integer>() {
206        @Override public Integer apply(Integer input) {
207          return (input == null) ? null : input / 2;
208        }
209  };
210
211  public static class TransformValueRowTests extends RowTests {
212    public TransformValueRowTests() {
213      super(false, false, true, true, true);
214    }
215
216    @Override Table<Character, String, Integer> makeTable() {
217      Table<Character, String, Integer> table = HashBasedTable.create();
218      return Tables.transformValues(table, DIVIDE_BY_2);
219    }
220
221    @Override protected Map<String, Integer> makePopulatedMap() {
222      Table<Character, String, Integer> table = HashBasedTable.create();
223      table.put('a', "one", 2);
224      table.put('a', "two", 4);
225      table.put('a', "three", 6);
226      table.put('b', "four", 8);
227      return Tables.transformValues(table, DIVIDE_BY_2).row('a');
228    }
229  }
230
231  public static class UnmodifiableHashRowTests extends RowTests {
232    public UnmodifiableHashRowTests() {
233      super(false, false, false, false, false);
234    }
235
236    @Override Table<Character, String, Integer> makeTable() {
237      Table<Character, String, Integer> table = HashBasedTable.create();
238      return Tables.unmodifiableTable(table);
239    }
240
241    @Override protected Map<String, Integer> makePopulatedMap() {
242      Table<Character, String, Integer> table = HashBasedTable.create();
243      table.put('a', "one", 1);
244      table.put('a', "two", 2);
245      table.put('a', "three", 3);
246      table.put('b', "four", 4);
247      return Tables.unmodifiableTable(table).row('a');
248    }
249  }
250
251  public static class UnmodifiableTreeRowTests extends RowTests {
252    public UnmodifiableTreeRowTests() {
253      super(false, false, false, false, false);
254    }
255
256    @Override Table<Character, String, Integer> makeTable() {
257      RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
258      return Tables.unmodifiableRowSortedTable(table);
259    }
260
261    @Override protected Map<String, Integer> makePopulatedMap() {
262      RowSortedTable<Character, String, Integer> table = TreeBasedTable.create();
263      table.put('a', "one", 1);
264      table.put('a', "two", 2);
265      table.put('a', "three", 3);
266      table.put('b', "four", 4);
267      return Tables.unmodifiableRowSortedTable(table).row('a');
268    }
269  }
270
271  private static abstract class ColumnTests extends MapTests {
272    ColumnTests(boolean allowsNullValues, boolean supportsPut, boolean supportsRemove,
273        boolean supportsClear, boolean supportsIteratorRemove) {
274      super(allowsNullValues, supportsPut, supportsRemove, supportsClear,
275          supportsIteratorRemove);
276    }
277
278    abstract Table<String, Character, Integer> makeTable();
279
280    @Override protected Map<String, Integer> makeEmptyMap() {
281      return makeTable().column('a');
282    }
283
284    @Override protected Map<String, Integer> makePopulatedMap() {
285      Table<String, Character, Integer> table = makeTable();
286      table.put("one", 'a', 1);
287      table.put("two", 'a', 2);
288      table.put("three", 'a', 3);
289      table.put("four", 'b', 4);
290      return table.column('a');
291    }
292  }
293
294  public static class HashColumnTests extends ColumnTests {
295    public HashColumnTests() {
296      super(false, true, true, true, false);
297    }
298
299    @Override Table<String, Character, Integer> makeTable() {
300      return HashBasedTable.create();
301    }
302  }
303
304  public static class TreeColumnTests extends ColumnTests {
305    public TreeColumnTests() {
306      super(false, true, true, true, false);
307    }
308
309    @Override Table<String, Character, Integer> makeTable() {
310      return TreeBasedTable.create();
311    }
312  }
313
314  public static class TransposeColumnTests extends ColumnTests {
315    public TransposeColumnTests() {
316      super(false, true, true, true, true);
317    }
318
319    @Override Table<String, Character, Integer> makeTable() {
320      Table<Character, String, Integer> original = TreeBasedTable.create();
321      return Tables.transpose(original);
322    }
323  }
324
325  public static class TransformValueColumnTests extends ColumnTests {
326    public TransformValueColumnTests() {
327      super(false, false, true, true, false);
328    }
329
330    @Override Table<String, Character, Integer> makeTable() {
331      Table<String, Character, Integer> table = HashBasedTable.create();
332      return Tables.transformValues(table, DIVIDE_BY_2);
333    }
334
335    @Override protected Map<String, Integer> makePopulatedMap() {
336      Table<String, Character, Integer> table = HashBasedTable.create();
337      table.put("one", 'a', 1);
338      table.put("two", 'a', 2);
339      table.put("three", 'a', 3);
340      table.put("four", 'b', 4);
341      return Tables.transformValues(table, DIVIDE_BY_2).column('a');
342    }
343  }
344
345  public static class UnmodifiableHashColumnTests extends ColumnTests {
346    public UnmodifiableHashColumnTests() {
347      super(false, false, false, false, false);
348    }
349
350    @Override Table<String, Character, Integer> makeTable() {
351      Table<String, Character, Integer> table = HashBasedTable.create();
352      return Tables.unmodifiableTable(table);
353    }
354
355    @Override protected Map<String, Integer> makePopulatedMap() {
356      Table<String, Character, Integer> table = HashBasedTable.create();
357      table.put("one", 'a', 1);
358      table.put("two", 'a', 2);
359      table.put("three", 'a', 3);
360      table.put("four", 'b', 4);
361      return Tables.unmodifiableTable(table).column('a');
362    }
363  }
364
365  public static class UnmodifiableTreeColumnTests extends ColumnTests {
366    public UnmodifiableTreeColumnTests() {
367      super(false, false, false, false, false);
368    }
369
370    @Override Table<String, Character, Integer> makeTable() {
371      RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
372      return Tables.unmodifiableRowSortedTable(table);
373    }
374
375    @Override protected Map<String, Integer> makePopulatedMap() {
376      RowSortedTable<String, Character, Integer> table = TreeBasedTable.create();
377      table.put("one", 'a', 1);
378      table.put("two", 'a', 2);
379      table.put("three", 'a', 3);
380      table.put("four", 'b', 4);
381      return Tables.unmodifiableRowSortedTable(table).column('a');
382    }
383  }
384
385  private static abstract class MapMapTests
386      extends MapInterfaceTest<String, Map<Integer, Character>> {
387
388    MapMapTests(boolean allowsNullValues, boolean supportsRemove,
389        boolean supportsClear, boolean supportsIteratorRemove) {
390      super(false, allowsNullValues, false, supportsRemove, supportsClear,
391          supportsIteratorRemove);
392    }
393
394    @Override protected String getKeyNotInPopulatedMap() {
395      return "cat";
396    }
397
398    @Override protected Map<Integer, Character> getValueNotInPopulatedMap() {
399      return ImmutableMap.of();
400    }
401
402    /**
403     * The version of this test supplied by {@link MapInterfaceTest} fails for
404     * this particular map implementation, because {@code map.get()} returns a
405     * view collection that changes in the course of a call to {@code remove()}.
406     * Thus, the expectation doesn't hold that {@code map.remove(x)} returns the
407     * same value which {@code map.get(x)} did immediately beforehand.
408     */
409    @Override public void testRemove() {
410      final Map<String, Map<Integer, Character>> map;
411      final String keyToRemove;
412      try {
413        map = makePopulatedMap();
414      } catch (UnsupportedOperationException e) {
415        return;
416      }
417      keyToRemove = map.keySet().iterator().next();
418      if (supportsRemove) {
419        int initialSize = map.size();
420        map.get(keyToRemove);
421        map.remove(keyToRemove);
422        // This line doesn't hold - see the Javadoc comments above.
423        // assertEquals(expectedValue, oldValue);
424        assertFalse(map.containsKey(keyToRemove));
425        assertEquals(initialSize - 1, map.size());
426      } else {
427        try {
428          map.remove(keyToRemove);
429          fail("Expected UnsupportedOperationException.");
430        } catch (UnsupportedOperationException e) {
431          // Expected.
432        }
433      }
434      assertInvariants(map);
435    }
436  }
437
438  private static abstract class RowMapTests extends MapMapTests {
439    RowMapTests(boolean allowsNullValues, boolean supportsRemove,
440        boolean supportsClear, boolean supportsIteratorRemove) {
441      super(allowsNullValues, supportsRemove, supportsClear,
442          supportsIteratorRemove);
443    }
444
445    abstract Table<String, Integer, Character> makeTable();
446
447    @Override protected Map<String, Map<Integer, Character>>
448        makePopulatedMap() {
449      Table<String, Integer, Character> table = makeTable();
450      populateTable(table);
451      return table.rowMap();
452    }
453
454    void populateTable(Table<String, Integer, Character> table) {
455      table.put("foo", 1, 'a');
456      table.put("bar", 1, 'b');
457      table.put("foo", 3, 'c');
458    }
459
460    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
461      return makeTable().rowMap();
462    }
463  }
464
465  public static class HashRowMapTests extends RowMapTests {
466    public HashRowMapTests() {
467      super(false, true, true, true);
468    }
469
470    @Override Table<String, Integer, Character> makeTable() {
471      return HashBasedTable.create();
472    }
473  }
474
475  public static class TreeRowMapTests extends RowMapTests {
476    public TreeRowMapTests() {
477      super(false, true, true, true);
478    }
479
480    @Override Table<String, Integer, Character> makeTable() {
481      return TreeBasedTable.create();
482    }
483  }
484
485  public static class TreeRowMapHeadMapTests extends RowMapTests {
486    public TreeRowMapHeadMapTests() {
487      super(false, true, true, true);
488    }
489
490    @Override TreeBasedTable<String, Integer, Character> makeTable() {
491      TreeBasedTable<String, Integer, Character> table =
492          TreeBasedTable.create();
493      table.put("z", 1, 'a');
494      return table;
495    }
496
497    @Override protected Map<String, Map<Integer, Character>>
498        makePopulatedMap() {
499      TreeBasedTable<String, Integer, Character> table = makeTable();
500      populateTable(table);
501      return table.rowMap().headMap("x");
502    }
503
504    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
505      return makeTable().rowMap().headMap("x");
506    }
507
508    @Override protected String getKeyNotInPopulatedMap() {
509      return "z";
510    }
511  }
512
513  public static class TreeRowMapTailMapTests extends RowMapTests {
514    public TreeRowMapTailMapTests() {
515      super(false, true, true, true);
516    }
517
518    @Override TreeBasedTable<String, Integer, Character> makeTable() {
519      TreeBasedTable<String, Integer, Character> table =
520          TreeBasedTable.create();
521      table.put("a", 1, 'a');
522      return table;
523    }
524
525    @Override protected Map<String, Map<Integer, Character>>
526        makePopulatedMap() {
527      TreeBasedTable<String, Integer, Character> table = makeTable();
528      populateTable(table);
529      return table.rowMap().tailMap("b");
530    }
531
532    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
533      return makeTable().rowMap().tailMap("b");
534    }
535
536    @Override protected String getKeyNotInPopulatedMap() {
537      return "a";
538    }
539  }
540
541  public static class TreeRowMapSubMapTests extends RowMapTests {
542    public TreeRowMapSubMapTests() {
543      super(false, true, true, true);
544    }
545
546    @Override TreeBasedTable<String, Integer, Character> makeTable() {
547      TreeBasedTable<String, Integer, Character> table =
548          TreeBasedTable.create();
549      table.put("a", 1, 'a');
550      table.put("z", 1, 'a');
551      return table;
552    }
553
554    @Override protected Map<String, Map<Integer, Character>>
555        makePopulatedMap() {
556      TreeBasedTable<String, Integer, Character> table = makeTable();
557      populateTable(table);
558      return table.rowMap().subMap("b", "x");
559    }
560
561    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
562      return makeTable().rowMap().subMap("b", "x");
563    }
564
565    @Override protected String getKeyNotInPopulatedMap() {
566      return "z";
567    }
568  }
569
570  private static final Function<String, Character> FIRST_CHARACTER =
571      new Function<String, Character>() {
572        @Override
573        public Character apply(String input) {
574          return input == null ? null : input.charAt(0);
575        }
576      };
577
578  public static class TransformValueRowMapTests extends RowMapTests {
579    public TransformValueRowMapTests() {
580      super(false, true, true, true);
581    }
582
583    @Override Table<String, Integer, Character> makeTable() {
584      Table<String, Integer, String> original = HashBasedTable.create();
585      return Tables.transformValues(original, FIRST_CHARACTER);
586    }
587
588    @Override
589    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
590      Table<String, Integer, String> table = HashBasedTable.create();
591      table.put("foo", 1, "apple");
592      table.put("bar", 1, "banana");
593      table.put("foo", 3, "cat");
594      return Tables.transformValues(table, FIRST_CHARACTER).rowMap();
595    }
596  }
597
598  public static class UnmodifiableHashRowMapTests extends RowMapTests {
599    public UnmodifiableHashRowMapTests() {
600      super(false, false, false, false);
601    }
602
603    @Override Table<String, Integer, Character> makeTable() {
604      Table<String, Integer, Character> original = HashBasedTable.create();
605      return Tables.unmodifiableTable(original);
606    }
607
608    @Override
609    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
610      Table<String, Integer, Character> table = HashBasedTable.create();
611      table.put("foo", 1, 'a');
612      table.put("bar", 1, 'b');
613      table.put("foo", 3, 'c');
614      return Tables.unmodifiableTable(table).rowMap();
615    }
616  }
617
618  public static class UnmodifiableTreeRowMapTests extends RowMapTests {
619    public UnmodifiableTreeRowMapTests() {
620      super(false, false, false, false);
621    }
622
623    @Override RowSortedTable<String, Integer, Character> makeTable() {
624      RowSortedTable<String, Integer, Character> original = TreeBasedTable.create();
625      return Tables.unmodifiableRowSortedTable(original);
626    }
627
628    @Override
629    protected SortedMap<String, Map<Integer, Character>> makePopulatedMap() {
630      RowSortedTable<String, Integer, Character> table = TreeBasedTable.create();
631      table.put("foo", 1, 'a');
632      table.put("bar", 1, 'b');
633      table.put("foo", 3, 'c');
634      return Tables.unmodifiableRowSortedTable(table).rowMap();
635    }
636  }
637
638  private static abstract class ColumnMapTests extends MapMapTests {
639    ColumnMapTests(boolean allowsNullValues, boolean supportsRemove,
640        boolean supportsClear, boolean supportsIteratorRemove) {
641      super(allowsNullValues, supportsRemove, supportsClear,
642          supportsIteratorRemove);
643    }
644
645    abstract Table<Integer, String, Character> makeTable();
646
647    @Override protected Map<String, Map<Integer, Character>>
648        makePopulatedMap() {
649      Table<Integer, String, Character> table = makeTable();
650      table.put(1, "foo", 'a');
651      table.put(1, "bar", 'b');
652      table.put(3, "foo", 'c');
653      return table.columnMap();
654    }
655
656    @Override protected Map<String, Map<Integer, Character>> makeEmptyMap() {
657      return makeTable().columnMap();
658    }
659  }
660
661  public static class HashColumnMapTests extends ColumnMapTests {
662    public HashColumnMapTests() {
663      super(false, true, true, false);
664    }
665
666    @Override Table<Integer, String, Character> makeTable() {
667      return HashBasedTable.create();
668    }
669  }
670
671  public static class TreeColumnMapTests extends ColumnMapTests {
672    public TreeColumnMapTests() {
673      super(false, true, true, false);
674    }
675
676    @Override Table<Integer, String, Character> makeTable() {
677      return TreeBasedTable.create();
678    }
679  }
680
681  public static class TransformValueColumnMapTests extends ColumnMapTests {
682    public TransformValueColumnMapTests() {
683      super(false, true, true, false);
684    }
685
686    @Override Table<Integer, String, Character> makeTable() {
687      Table<Integer, String, String> original = HashBasedTable.create();
688      return Tables.transformValues(original, FIRST_CHARACTER);
689    }
690
691    @Override
692    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
693      Table<Integer, String, String> table = HashBasedTable.create();
694      table.put(1, "foo", "apple");
695      table.put(1, "bar", "banana");
696      table.put(3, "foo", "cat");
697      return Tables.transformValues(table, FIRST_CHARACTER).columnMap();
698    }
699  }
700
701  public static class UnmodifiableHashColumnMapTests extends ColumnMapTests {
702    public UnmodifiableHashColumnMapTests() {
703      super(false, false, false, false);
704    }
705
706    @Override Table<Integer, String, Character> makeTable() {
707      Table<Integer, String, Character> original = HashBasedTable.create();
708      return Tables.unmodifiableTable(original);
709    }
710
711    @Override
712    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
713      Table<Integer, String, Character> table = HashBasedTable.create();
714      table.put(1, "foo", 'a');
715      table.put(1, "bar", 'b');
716      table.put(3, "foo", 'c');
717      return Tables.unmodifiableTable(table).columnMap();
718    }
719  }
720
721  public static class UnmodifiableTreeColumnMapTests extends ColumnMapTests {
722    public UnmodifiableTreeColumnMapTests() {
723      super(false, false, false, false);
724    }
725
726    @Override Table<Integer, String, Character> makeTable() {
727      RowSortedTable<Integer, String, Character> original = TreeBasedTable.create();
728      return Tables.unmodifiableRowSortedTable(original);
729    }
730
731    @Override
732    protected Map<String, Map<Integer, Character>> makePopulatedMap() {
733      RowSortedTable<Integer, String, Character> table = TreeBasedTable.create();
734      table.put(1, "foo", 'a');
735      table.put(1, "bar", 'b');
736      table.put(3, "foo", 'c');
737      return Tables.unmodifiableRowSortedTable(table).columnMap();
738    }
739  }
740}
741
742