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