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;
018
019import static com.google.common.collect.testing.Helpers.castOrCopyToList;
020import static com.google.common.collect.testing.Helpers.equal;
021import static com.google.common.collect.testing.Helpers.mapEntry;
022import static java.util.Collections.sort;
023
024import com.google.common.annotations.GwtCompatible;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collection;
028import java.util.Collections;
029import java.util.Comparator;
030import java.util.List;
031import java.util.Map;
032import java.util.Map.Entry;
033import java.util.Set;
034import java.util.SortedMap;
035import java.util.SortedSet;
036import org.checkerframework.checker.nullness.qual.Nullable;
037
038/**
039 * Derived suite generators, split out of the suite builders so that they are available to GWT.
040 *
041 * @author George van den Driessche
042 */
043@GwtCompatible
044@ElementTypesAreNonnullByDefault
045public final class DerivedCollectionGenerators {
046  public static class MapEntrySetGenerator<K extends @Nullable Object, V extends @Nullable Object>
047      implements TestSetGenerator<Entry<K, V>>, DerivedGenerator {
048    private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator;
049
050    public MapEntrySetGenerator(
051        OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
052      this.mapGenerator = mapGenerator;
053    }
054
055    @Override
056    public SampleElements<Entry<K, V>> samples() {
057      return mapGenerator.samples();
058    }
059
060    @Override
061    public Set<Entry<K, V>> create(Object... elements) {
062      return mapGenerator.create(elements).entrySet();
063    }
064
065    @Override
066    public Entry<K, V>[] createArray(int length) {
067      return mapGenerator.createArray(length);
068    }
069
070    @Override
071    public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) {
072      return mapGenerator.order(insertionOrder);
073    }
074
075    @Override
076    public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() {
077      return mapGenerator;
078    }
079  }
080
081  // TODO: investigate some API changes to SampleElements that would tidy up
082  // parts of the following classes.
083
084  static <K extends @Nullable Object, V extends @Nullable Object>
085      TestSetGenerator<K> keySetGenerator(
086          OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
087    TestContainerGenerator<Map<K, V>, Entry<K, V>> generator = mapGenerator.getInnerGenerator();
088    if (generator instanceof TestSortedMapGenerator
089        && ((TestSortedMapGenerator<K, V>) generator).create().keySet() instanceof SortedSet) {
090      return new MapSortedKeySetGenerator<>(mapGenerator);
091    } else {
092      return new MapKeySetGenerator<>(mapGenerator);
093    }
094  }
095
096  public static class MapKeySetGenerator<K extends @Nullable Object, V extends @Nullable Object>
097      implements TestSetGenerator<K>, DerivedGenerator {
098    private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator;
099    private final SampleElements<K> samples;
100
101    public MapKeySetGenerator(OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
102      this.mapGenerator = mapGenerator;
103      SampleElements<Entry<K, V>> mapSamples = this.mapGenerator.samples();
104      this.samples =
105          new SampleElements<>(
106              mapSamples.e0().getKey(),
107              mapSamples.e1().getKey(),
108              mapSamples.e2().getKey(),
109              mapSamples.e3().getKey(),
110              mapSamples.e4().getKey());
111    }
112
113    @Override
114    public SampleElements<K> samples() {
115      return samples;
116    }
117
118    @Override
119    public Set<K> create(Object... elements) {
120      @SuppressWarnings("unchecked")
121      K[] keysArray = (K[]) elements;
122
123      // Start with a suitably shaped collection of entries
124      Collection<Entry<K, V>> originalEntries = mapGenerator.getSampleElements(elements.length);
125
126      // Create a copy of that, with the desired value for each key
127      Collection<Entry<K, V>> entries = new ArrayList<>(elements.length);
128      int i = 0;
129      for (Entry<K, V> entry : originalEntries) {
130        entries.add(Helpers.mapEntry(keysArray[i++], entry.getValue()));
131      }
132
133      return mapGenerator.create(entries.toArray()).keySet();
134    }
135
136    @Override
137    public K[] createArray(int length) {
138      // TODO: with appropriate refactoring of OneSizeGenerator, we can perhaps
139      // tidy this up and get rid of the casts here and in
140      // MapValueCollectionGenerator.
141
142      return ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createKeyArray(length);
143    }
144
145    @Override
146    public Iterable<K> order(List<K> insertionOrder) {
147      V v = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).samples().e0().getValue();
148      List<Entry<K, V>> entries = new ArrayList<>();
149      for (K element : insertionOrder) {
150        entries.add(mapEntry(element, v));
151      }
152
153      List<K> keys = new ArrayList<>();
154      for (Entry<K, V> entry : mapGenerator.order(entries)) {
155        keys.add(entry.getKey());
156      }
157      return keys;
158    }
159
160    @Override
161    public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() {
162      return mapGenerator;
163    }
164  }
165
166  public static class MapSortedKeySetGenerator<
167          K extends @Nullable Object, V extends @Nullable Object>
168      extends MapKeySetGenerator<K, V> implements TestSortedSetGenerator<K>, DerivedGenerator {
169    private final TestSortedMapGenerator<K, V> delegate;
170
171    public MapSortedKeySetGenerator(
172        OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
173      super(mapGenerator);
174      this.delegate = (TestSortedMapGenerator<K, V>) mapGenerator.getInnerGenerator();
175    }
176
177    @Override
178    public SortedSet<K> create(Object... elements) {
179      return (SortedSet<K>) super.create(elements);
180    }
181
182    @Override
183    public K belowSamplesLesser() {
184      return delegate.belowSamplesLesser().getKey();
185    }
186
187    @Override
188    public K belowSamplesGreater() {
189      return delegate.belowSamplesGreater().getKey();
190    }
191
192    @Override
193    public K aboveSamplesLesser() {
194      return delegate.aboveSamplesLesser().getKey();
195    }
196
197    @Override
198    public K aboveSamplesGreater() {
199      return delegate.aboveSamplesGreater().getKey();
200    }
201  }
202
203  public static class MapValueCollectionGenerator<
204          K extends @Nullable Object, V extends @Nullable Object>
205      implements TestCollectionGenerator<V>, DerivedGenerator {
206    private final OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator;
207    private final SampleElements<V> samples;
208
209    public MapValueCollectionGenerator(
210        OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> mapGenerator) {
211      this.mapGenerator = mapGenerator;
212      SampleElements<Entry<K, V>> mapSamples = this.mapGenerator.samples();
213      this.samples =
214          new SampleElements<>(
215              mapSamples.e0().getValue(),
216              mapSamples.e1().getValue(),
217              mapSamples.e2().getValue(),
218              mapSamples.e3().getValue(),
219              mapSamples.e4().getValue());
220    }
221
222    @Override
223    public SampleElements<V> samples() {
224      return samples;
225    }
226
227    @Override
228    public Collection<V> create(Object... elements) {
229      @SuppressWarnings("unchecked")
230      V[] valuesArray = (V[]) elements;
231
232      // Start with a suitably shaped collection of entries
233      Collection<Entry<K, V>> originalEntries = mapGenerator.getSampleElements(elements.length);
234
235      // Create a copy of that, with the desired value for each value
236      Collection<Entry<K, V>> entries = new ArrayList<>(elements.length);
237      int i = 0;
238      for (Entry<K, V> entry : originalEntries) {
239        entries.add(Helpers.mapEntry(entry.getKey(), valuesArray[i++]));
240      }
241
242      return mapGenerator.create(entries.toArray()).values();
243    }
244
245    @Override
246    public V[] createArray(int length) {
247      // noinspection UnnecessaryLocalVariable
248      V[] vs = ((TestMapGenerator<K, V>) mapGenerator.getInnerGenerator()).createValueArray(length);
249      return vs;
250    }
251
252    @Override
253    public Iterable<V> order(List<V> insertionOrder) {
254      List<Entry<K, V>> orderedEntries =
255          castOrCopyToList(mapGenerator.order(castOrCopyToList(mapGenerator.getSampleElements(5))));
256      sort(
257          insertionOrder,
258          new Comparator<V>() {
259            @Override
260            public int compare(V left, V right) {
261              // The indexes are small enough for the subtraction trick to be safe.
262              return indexOfEntryWithValue(left) - indexOfEntryWithValue(right);
263            }
264
265            int indexOfEntryWithValue(V value) {
266              for (int i = 0; i < orderedEntries.size(); i++) {
267                if (equal(orderedEntries.get(i).getValue(), value)) {
268                  return i;
269                }
270              }
271              throw new IllegalArgumentException(
272                  "Map.values generator can order only sample values");
273            }
274          });
275      return insertionOrder;
276    }
277
278    @Override
279    public OneSizeTestContainerGenerator<Map<K, V>, Entry<K, V>> getInnerGenerator() {
280      return mapGenerator;
281    }
282  }
283
284  // TODO(cpovirk): could something like this be used elsewhere, e.g., ReserializedListGenerator?
285  static class ForwardingTestMapGenerator<K extends @Nullable Object, V extends @Nullable Object>
286      implements TestMapGenerator<K, V> {
287    TestMapGenerator<K, V> delegate;
288
289    ForwardingTestMapGenerator(TestMapGenerator<K, V> delegate) {
290      this.delegate = delegate;
291    }
292
293    @Override
294    public Iterable<Entry<K, V>> order(List<Entry<K, V>> insertionOrder) {
295      return delegate.order(insertionOrder);
296    }
297
298    @Override
299    public K[] createKeyArray(int length) {
300      return delegate.createKeyArray(length);
301    }
302
303    @Override
304    public V[] createValueArray(int length) {
305      return delegate.createValueArray(length);
306    }
307
308    @Override
309    public SampleElements<Entry<K, V>> samples() {
310      return delegate.samples();
311    }
312
313    @Override
314    public Map<K, V> create(Object... elements) {
315      return delegate.create(elements);
316    }
317
318    @Override
319    public Entry<K, V>[] createArray(int length) {
320      return delegate.createArray(length);
321    }
322  }
323
324  /** Two bounds (from and to) define how to build a subMap. */
325  public enum Bound {
326    INCLUSIVE,
327    EXCLUSIVE,
328    NO_BOUND;
329  }
330
331  public static class SortedSetSubsetTestSetGenerator<E extends @Nullable Object>
332      implements TestSortedSetGenerator<E> {
333    final Bound to;
334    final Bound from;
335    final E firstInclusive;
336    final E lastInclusive;
337    private final Comparator<? super E> comparator;
338    private final TestSortedSetGenerator<E> delegate;
339
340    public SortedSetSubsetTestSetGenerator(
341        TestSortedSetGenerator<E> delegate, Bound to, Bound from) {
342      this.to = to;
343      this.from = from;
344      this.delegate = delegate;
345
346      SortedSet<E> emptySet = delegate.create();
347      this.comparator = emptySet.comparator();
348
349      SampleElements<E> samples = delegate.samples();
350      List<E> samplesList = new ArrayList<>(samples.asList());
351      Collections.sort(samplesList, comparator);
352      this.firstInclusive = samplesList.get(0);
353      this.lastInclusive = samplesList.get(samplesList.size() - 1);
354    }
355
356    public final TestSortedSetGenerator<E> getInnerGenerator() {
357      return delegate;
358    }
359
360    public final Bound getTo() {
361      return to;
362    }
363
364    public final Bound getFrom() {
365      return from;
366    }
367
368    @Override
369    public SampleElements<E> samples() {
370      return delegate.samples();
371    }
372
373    @Override
374    public E[] createArray(int length) {
375      return delegate.createArray(length);
376    }
377
378    @Override
379    public Iterable<E> order(List<E> insertionOrder) {
380      return delegate.order(insertionOrder);
381    }
382
383    @Override
384    public SortedSet<E> create(Object... elements) {
385      List<?> normalValues = (List<?>) Arrays.asList(elements);
386      List<E> extremeValues = new ArrayList<>();
387
388      // nulls are usually out of bounds for a subset, so ban them altogether
389      for (Object o : elements) {
390        if (o == null) {
391          throw new NullPointerException();
392        }
393      }
394
395      // prepare extreme values to be filtered out of view
396      E firstExclusive = delegate.belowSamplesGreater();
397      E lastExclusive = delegate.aboveSamplesLesser();
398      if (from != Bound.NO_BOUND) {
399        extremeValues.add(delegate.belowSamplesLesser());
400        extremeValues.add(delegate.belowSamplesGreater());
401      }
402      if (to != Bound.NO_BOUND) {
403        extremeValues.add(delegate.aboveSamplesLesser());
404        extremeValues.add(delegate.aboveSamplesGreater());
405      }
406
407      // the regular values should be visible after filtering
408      List<@Nullable Object> allEntries = new ArrayList<>();
409      allEntries.addAll(extremeValues);
410      allEntries.addAll(normalValues);
411      SortedSet<E> set = delegate.create(allEntries.toArray());
412
413      return createSubSet(set, firstExclusive, lastExclusive);
414    }
415
416    /** Calls the smallest subSet overload that filters out the extreme values. */
417    SortedSet<E> createSubSet(SortedSet<E> set, E firstExclusive, E lastExclusive) {
418      if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
419        return set.headSet(lastExclusive);
420      } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
421        return set.tailSet(firstInclusive);
422      } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
423        return set.subSet(firstInclusive, lastExclusive);
424      } else {
425        throw new IllegalArgumentException();
426      }
427    }
428
429    @Override
430    public E belowSamplesLesser() {
431      throw new UnsupportedOperationException();
432    }
433
434    @Override
435    public E belowSamplesGreater() {
436      throw new UnsupportedOperationException();
437    }
438
439    @Override
440    public E aboveSamplesLesser() {
441      throw new UnsupportedOperationException();
442    }
443
444    @Override
445    public E aboveSamplesGreater() {
446      throw new UnsupportedOperationException();
447    }
448  }
449
450  /*
451   * TODO(cpovirk): surely we can find a less ugly solution than a class that accepts 3 parameters,
452   * exposes as many getters, does work in the constructor, and has both a superclass and a subclass
453   */
454  public static class SortedMapSubmapTestMapGenerator<
455          K extends @Nullable Object, V extends @Nullable Object>
456      extends ForwardingTestMapGenerator<K, V> implements TestSortedMapGenerator<K, V> {
457    final Bound to;
458    final Bound from;
459    final K firstInclusive;
460    final K lastInclusive;
461    private final Comparator<Entry<K, V>> entryComparator;
462
463    public SortedMapSubmapTestMapGenerator(
464        TestSortedMapGenerator<K, V> delegate, Bound to, Bound from) {
465      super(delegate);
466      this.to = to;
467      this.from = from;
468
469      SortedMap<K, V> emptyMap = delegate.create();
470      this.entryComparator = Helpers.entryComparator(emptyMap.comparator());
471
472      // derive values for inclusive filtering from the input samples
473      SampleElements<Entry<K, V>> samples = delegate.samples();
474      List<Entry<K, V>> samplesList =
475          Arrays.asList(samples.e0(), samples.e1(), samples.e2(), samples.e3(), samples.e4());
476      Collections.sort(samplesList, entryComparator);
477      this.firstInclusive = samplesList.get(0).getKey();
478      this.lastInclusive = samplesList.get(samplesList.size() - 1).getKey();
479    }
480
481    @Override
482    public SortedMap<K, V> create(Object... entries) {
483      List<Entry<K, V>> extremeValues = new ArrayList<>();
484
485      // prepare extreme values to be filtered out of view
486      K firstExclusive = getInnerGenerator().belowSamplesGreater().getKey();
487      K lastExclusive = getInnerGenerator().aboveSamplesLesser().getKey();
488      if (from != Bound.NO_BOUND) {
489        extremeValues.add(getInnerGenerator().belowSamplesLesser());
490        extremeValues.add(getInnerGenerator().belowSamplesGreater());
491      }
492      if (to != Bound.NO_BOUND) {
493        extremeValues.add(getInnerGenerator().aboveSamplesLesser());
494        extremeValues.add(getInnerGenerator().aboveSamplesGreater());
495      }
496
497      // the regular values should be visible after filtering
498      List<Entry<?, ?>> allEntries = new ArrayList<>();
499      allEntries.addAll(extremeValues);
500      for (Object entry : entries) {
501        allEntries.add((Entry<?, ?>) entry);
502      }
503      SortedMap<K, V> map = (SortedMap<K, V>) delegate.create(allEntries.toArray());
504
505      return createSubMap(map, firstExclusive, lastExclusive);
506    }
507
508    /**
509     * Calls the smallest subMap overload that filters out the extreme values. This method is
510     * overridden in NavigableMapTestSuiteBuilder.
511     */
512    SortedMap<K, V> createSubMap(SortedMap<K, V> map, K firstExclusive, K lastExclusive) {
513      if (from == Bound.NO_BOUND && to == Bound.EXCLUSIVE) {
514        return map.headMap(lastExclusive);
515      } else if (from == Bound.INCLUSIVE && to == Bound.NO_BOUND) {
516        return map.tailMap(firstInclusive);
517      } else if (from == Bound.INCLUSIVE && to == Bound.EXCLUSIVE) {
518        return map.subMap(firstInclusive, lastExclusive);
519      } else {
520        throw new IllegalArgumentException();
521      }
522    }
523
524    public final Bound getTo() {
525      return to;
526    }
527
528    public final Bound getFrom() {
529      return from;
530    }
531
532    public final TestSortedMapGenerator<K, V> getInnerGenerator() {
533      return (TestSortedMapGenerator<K, V>) delegate;
534    }
535
536    @Override
537    public Entry<K, V> belowSamplesLesser() {
538      // should never reach here!
539      throw new UnsupportedOperationException();
540    }
541
542    @Override
543    public Entry<K, V> belowSamplesGreater() {
544      // should never reach here!
545      throw new UnsupportedOperationException();
546    }
547
548    @Override
549    public Entry<K, V> aboveSamplesLesser() {
550      // should never reach here!
551      throw new UnsupportedOperationException();
552    }
553
554    @Override
555    public Entry<K, V> aboveSamplesGreater() {
556      // should never reach here!
557      throw new UnsupportedOperationException();
558    }
559  }
560
561  private DerivedCollectionGenerators() {}
562}