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
017package com.google.common.collect.testing.google;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020import static com.google.common.collect.Lists.newArrayList;
021import static com.google.common.collect.Sets.newTreeSet;
022import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST;
023import static com.google.common.collect.testing.SampleElements.Strings.AFTER_LAST_2;
024import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST;
025import static com.google.common.collect.testing.SampleElements.Strings.BEFORE_FIRST_2;
026import static junit.framework.Assert.assertEquals;
027
028import com.google.common.annotations.GwtCompatible;
029import com.google.common.annotations.GwtIncompatible;
030import com.google.common.collect.ContiguousSet;
031import com.google.common.collect.DiscreteDomain;
032import com.google.common.collect.ImmutableSet;
033import com.google.common.collect.ImmutableSortedSet;
034import com.google.common.collect.Lists;
035import com.google.common.collect.Ordering;
036import com.google.common.collect.Range;
037import com.google.common.collect.Sets;
038import com.google.common.collect.testing.TestCollectionGenerator;
039import com.google.common.collect.testing.TestCollidingSetGenerator;
040import com.google.common.collect.testing.TestIntegerSortedSetGenerator;
041import com.google.common.collect.testing.TestSetGenerator;
042import com.google.common.collect.testing.TestStringListGenerator;
043import com.google.common.collect.testing.TestStringSetGenerator;
044import com.google.common.collect.testing.TestStringSortedSetGenerator;
045import com.google.common.collect.testing.TestUnhashableCollectionGenerator;
046import com.google.common.collect.testing.UnhashableObject;
047import java.util.Arrays;
048import java.util.Collections;
049import java.util.Comparator;
050import java.util.List;
051import java.util.Set;
052import java.util.SortedSet;
053
054/**
055 * Generators of different types of sets and derived collections from sets.
056 *
057 * @author Kevin Bourrillion
058 * @author Jared Levy
059 * @author Hayward Chan
060 */
061@GwtCompatible(emulated = true)
062public class SetGenerators {
063
064  public static class ImmutableSetCopyOfGenerator extends TestStringSetGenerator {
065    @Override
066    protected Set<String> create(String[] elements) {
067      return ImmutableSet.copyOf(elements);
068    }
069  }
070
071  public static class ImmutableSetUnsizedBuilderGenerator extends TestStringSetGenerator {
072    @Override
073    protected Set<String> create(String[] elements) {
074      ImmutableSet.Builder<String> builder = ImmutableSet.builder();
075      for (String e : elements) {
076        builder.add(e);
077      }
078      return builder.build();
079    }
080  }
081
082  public static class ImmutableSetSizedBuilderGenerator extends TestStringSetGenerator {
083    @Override
084    protected Set<String> create(String[] elements) {
085      ImmutableSet.Builder<String> builder =
086          ImmutableSet.builderWithExpectedSize(Sets.newHashSet(elements).size());
087      for (String e : elements) {
088        builder.add(e);
089      }
090      return builder.build();
091    }
092  }
093
094  public static class ImmutableSetTooBigBuilderGenerator extends TestStringSetGenerator {
095    @Override
096    protected Set<String> create(String[] elements) {
097      ImmutableSet.Builder<String> builder =
098          ImmutableSet.builderWithExpectedSize(Sets.newHashSet(elements).size() + 1);
099      for (String e : elements) {
100        builder.add(e);
101      }
102      return builder.build();
103    }
104  }
105
106  public static class ImmutableSetTooSmallBuilderGenerator extends TestStringSetGenerator {
107    @Override
108    protected Set<String> create(String[] elements) {
109      ImmutableSet.Builder<String> builder =
110          ImmutableSet.builderWithExpectedSize(Math.max(0, Sets.newHashSet(elements).size() - 1));
111      for (String e : elements) {
112        builder.add(e);
113      }
114      return builder.build();
115    }
116  }
117
118  public static class ImmutableSetWithBadHashesGenerator extends TestCollidingSetGenerator
119      // Work around a GWT compiler bug.  Not explicitly listing this will
120      // cause the createArray() method missing in the generated javascript.
121      // TODO: Remove this once the GWT bug is fixed.
122      implements TestCollectionGenerator<Object> {
123    @Override
124    public Set<Object> create(Object... elements) {
125      return ImmutableSet.copyOf(elements);
126    }
127  }
128
129  public static class DegeneratedImmutableSetGenerator extends TestStringSetGenerator {
130    // Make sure we get what we think we're getting, or else this test
131    // is pointless
132    @SuppressWarnings("cast")
133    @Override
134    protected Set<String> create(String[] elements) {
135      return (ImmutableSet<String>) ImmutableSet.of(elements[0], elements[0]);
136    }
137  }
138
139  public static class ImmutableSortedSetCopyOfGenerator extends TestStringSortedSetGenerator {
140    @Override
141    protected SortedSet<String> create(String[] elements) {
142      return ImmutableSortedSet.copyOf(elements);
143    }
144  }
145
146  public static class ImmutableSortedSetHeadsetGenerator extends TestStringSortedSetGenerator {
147    @Override
148    protected SortedSet<String> create(String[] elements) {
149      List<String> list = Lists.newArrayList(elements);
150      list.add("zzz");
151      return ImmutableSortedSet.copyOf(list).headSet("zzy");
152    }
153  }
154
155  public static class ImmutableSortedSetTailsetGenerator extends TestStringSortedSetGenerator {
156    @Override
157    protected SortedSet<String> create(String[] elements) {
158      List<String> list = Lists.newArrayList(elements);
159      list.add("\0");
160      return ImmutableSortedSet.copyOf(list).tailSet("\0\0");
161    }
162  }
163
164  public static class ImmutableSortedSetSubsetGenerator extends TestStringSortedSetGenerator {
165    @Override
166    protected SortedSet<String> create(String[] elements) {
167      List<String> list = Lists.newArrayList(elements);
168      list.add("\0");
169      list.add("zzz");
170      return ImmutableSortedSet.copyOf(list).subSet("\0\0", "zzy");
171    }
172  }
173
174  @GwtIncompatible // NavigableSet
175  public static class ImmutableSortedSetDescendingGenerator extends TestStringSortedSetGenerator {
176    @Override
177    protected SortedSet<String> create(String[] elements) {
178      return ImmutableSortedSet.<String>reverseOrder().add(elements).build().descendingSet();
179    }
180  }
181
182  public static class ImmutableSortedSetExplicitComparator extends TestStringSetGenerator {
183
184    private static final Comparator<String> STRING_REVERSED = Collections.reverseOrder();
185
186    @Override
187    protected SortedSet<String> create(String[] elements) {
188      return ImmutableSortedSet.orderedBy(STRING_REVERSED).add(elements).build();
189    }
190
191    @Override
192    public List<String> order(List<String> insertionOrder) {
193      Collections.sort(insertionOrder, Collections.reverseOrder());
194      return insertionOrder;
195    }
196  }
197
198  public static class ImmutableSortedSetExplicitSuperclassComparatorGenerator
199      extends TestStringSetGenerator {
200
201    private static final Comparator<Comparable<?>> COMPARABLE_REVERSED = Collections.reverseOrder();
202
203    @Override
204    protected SortedSet<String> create(String[] elements) {
205      return new ImmutableSortedSet.Builder<String>(COMPARABLE_REVERSED).add(elements).build();
206    }
207
208    @Override
209    public List<String> order(List<String> insertionOrder) {
210      Collections.sort(insertionOrder, Collections.reverseOrder());
211      return insertionOrder;
212    }
213  }
214
215  public static class ImmutableSortedSetReversedOrderGenerator extends TestStringSetGenerator {
216
217    @Override
218    protected SortedSet<String> create(String[] elements) {
219      return ImmutableSortedSet.<String>reverseOrder()
220          .addAll(Arrays.asList(elements).iterator())
221          .build();
222    }
223
224    @Override
225    public List<String> order(List<String> insertionOrder) {
226      Collections.sort(insertionOrder, Collections.reverseOrder());
227      return insertionOrder;
228    }
229  }
230
231  public static class ImmutableSortedSetUnhashableGenerator extends TestUnhashableSetGenerator {
232    @Override
233    public Set<UnhashableObject> create(UnhashableObject[] elements) {
234      return ImmutableSortedSet.copyOf(elements);
235    }
236  }
237
238  public static class ImmutableSetAsListGenerator extends TestStringListGenerator {
239    @Override
240    protected List<String> create(String[] elements) {
241      return ImmutableSet.copyOf(elements).asList();
242    }
243  }
244
245  public static class ImmutableSortedSetAsListGenerator extends TestStringListGenerator {
246    @Override
247    protected List<String> create(String[] elements) {
248      Comparator<String> comparator = createExplicitComparator(elements);
249      ImmutableSet<String> set = ImmutableSortedSet.copyOf(comparator, Arrays.asList(elements));
250      return set.asList();
251    }
252  }
253
254  public static class ImmutableSortedSetSubsetAsListGenerator extends TestStringListGenerator {
255    @Override
256    protected List<String> create(String[] elements) {
257      Comparator<String> comparator = createExplicitComparator(elements);
258      ImmutableSortedSet.Builder<String> builder = ImmutableSortedSet.orderedBy(comparator);
259      builder.add(BEFORE_FIRST);
260      builder.add(elements);
261      builder.add(AFTER_LAST);
262      return builder.build().subSet(BEFORE_FIRST_2, AFTER_LAST).asList();
263    }
264  }
265
266  @GwtIncompatible // NavigableSet
267  public static class ImmutableSortedSetDescendingAsListGenerator extends TestStringListGenerator {
268    @Override
269    protected List<String> create(String[] elements) {
270      Comparator<String> comparator = createExplicitComparator(elements).reverse();
271      return ImmutableSortedSet.orderedBy(comparator)
272          .add(elements)
273          .build()
274          .descendingSet()
275          .asList();
276    }
277  }
278
279  public static class ImmutableSortedSetAsListSubListGenerator extends TestStringListGenerator {
280    @Override
281    protected List<String> create(String[] elements) {
282      Comparator<String> comparator = createExplicitComparator(elements);
283      ImmutableSortedSet.Builder<String> builder = ImmutableSortedSet.orderedBy(comparator);
284      builder.add(BEFORE_FIRST);
285      builder.add(elements);
286      builder.add(AFTER_LAST);
287      return builder.build().asList().subList(1, elements.length + 1);
288    }
289  }
290
291  public static class ImmutableSortedSetSubsetAsListSubListGenerator
292      extends TestStringListGenerator {
293    @Override
294    protected List<String> create(String[] elements) {
295      Comparator<String> comparator = createExplicitComparator(elements);
296      ImmutableSortedSet.Builder<String> builder = ImmutableSortedSet.orderedBy(comparator);
297      builder.add(BEFORE_FIRST);
298      builder.add(BEFORE_FIRST_2);
299      builder.add(elements);
300      builder.add(AFTER_LAST);
301      builder.add(AFTER_LAST_2);
302      return builder
303          .build()
304          .subSet(BEFORE_FIRST_2, AFTER_LAST_2)
305          .asList()
306          .subList(1, elements.length + 1);
307    }
308  }
309
310  public abstract static class TestUnhashableSetGenerator
311      extends TestUnhashableCollectionGenerator<Set<UnhashableObject>>
312      implements TestSetGenerator<UnhashableObject> {}
313
314  private static Ordering<String> createExplicitComparator(String[] elements) {
315    // Collapse equal elements, which Ordering.explicit() doesn't support, while
316    // maintaining the ordering by first occurrence.
317    Set<String> elementsPlus = Sets.newLinkedHashSet();
318    elementsPlus.add(BEFORE_FIRST);
319    elementsPlus.add(BEFORE_FIRST_2);
320    elementsPlus.addAll(Arrays.asList(elements));
321    elementsPlus.add(AFTER_LAST);
322    elementsPlus.add(AFTER_LAST_2);
323    return Ordering.explicit(Lists.newArrayList(elementsPlus));
324  }
325
326  /*
327   * All the ContiguousSet generators below manually reject nulls here. In principle, we'd like to
328   * defer that to Range, since it's ContiguousSet.create() that's used to create the sets. However,
329   * that gets messy here, and we already have null tests for Range.
330   */
331
332  /*
333   * These generators also rely on consecutive integer inputs (not necessarily in order, but no
334   * holes).
335   */
336
337  // SetCreationTester has some tests that pass in duplicates. Dedup them.
338  private static <E extends Comparable<? super E>> SortedSet<E> nullCheckedTreeSet(E[] elements) {
339    SortedSet<E> set = newTreeSet();
340    for (E element : elements) {
341      // Explicit null check because TreeSet wrongly accepts add(null) when empty.
342      set.add(checkNotNull(element));
343    }
344    return set;
345  }
346
347  public static class ContiguousSetGenerator extends AbstractContiguousSetGenerator {
348    @Override
349    protected SortedSet<Integer> create(Integer[] elements) {
350      return checkedCreate(nullCheckedTreeSet(elements));
351    }
352  }
353
354  public static class ContiguousSetHeadsetGenerator extends AbstractContiguousSetGenerator {
355    @Override
356    protected SortedSet<Integer> create(Integer[] elements) {
357      SortedSet<Integer> set = nullCheckedTreeSet(elements);
358      int tooHigh = (set.isEmpty()) ? 0 : set.last() + 1;
359      set.add(tooHigh);
360      return checkedCreate(set).headSet(tooHigh);
361    }
362  }
363
364  public static class ContiguousSetTailsetGenerator extends AbstractContiguousSetGenerator {
365    @Override
366    protected SortedSet<Integer> create(Integer[] elements) {
367      SortedSet<Integer> set = nullCheckedTreeSet(elements);
368      int tooLow = (set.isEmpty()) ? 0 : set.first() - 1;
369      set.add(tooLow);
370      return checkedCreate(set).tailSet(tooLow + 1);
371    }
372  }
373
374  public static class ContiguousSetSubsetGenerator extends AbstractContiguousSetGenerator {
375    @Override
376    protected SortedSet<Integer> create(Integer[] elements) {
377      SortedSet<Integer> set = nullCheckedTreeSet(elements);
378      if (set.isEmpty()) {
379        /*
380         * The (tooLow + 1, tooHigh) arguments below would be invalid because tooLow would be
381         * greater than tooHigh.
382         */
383        return ContiguousSet.create(Range.openClosed(0, 1), DiscreteDomain.integers()).subSet(0, 1);
384      }
385      int tooHigh = set.last() + 1;
386      int tooLow = set.first() - 1;
387      set.add(tooHigh);
388      set.add(tooLow);
389      return checkedCreate(set).subSet(tooLow + 1, tooHigh);
390    }
391  }
392
393  @GwtIncompatible // NavigableSet
394  public static class ContiguousSetDescendingGenerator extends AbstractContiguousSetGenerator {
395    @Override
396    protected SortedSet<Integer> create(Integer[] elements) {
397      return checkedCreate(nullCheckedTreeSet(elements)).descendingSet();
398    }
399
400    /** Sorts the elements in reverse natural order. */
401    @Override
402    public List<Integer> order(List<Integer> insertionOrder) {
403      Collections.sort(insertionOrder, Ordering.natural().reverse());
404      return insertionOrder;
405    }
406  }
407
408  private abstract static class AbstractContiguousSetGenerator
409      extends TestIntegerSortedSetGenerator {
410    protected final ContiguousSet<Integer> checkedCreate(SortedSet<Integer> elementsSet) {
411      List<Integer> elements = newArrayList(elementsSet);
412      /*
413       * A ContiguousSet can't have holes. If a test demands a hole, it should be changed so that it
414       * doesn't need one, or it should be suppressed for ContiguousSet.
415       */
416      for (int i = 0; i < elements.size() - 1; i++) {
417        assertEquals(elements.get(i) + 1, (int) elements.get(i + 1));
418      }
419      Range<Integer> range =
420          (elements.isEmpty()) ? Range.closedOpen(0, 0) : Range.encloseAll(elements);
421      return ContiguousSet.create(range, DiscreteDomain.integers());
422    }
423  }
424}