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