001package de.cuioss.test.generator.impl;
002
003import static de.cuioss.tools.collect.CollectionLiterals.mutableSet;
004import static java.util.Objects.requireNonNull;
005
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.List;
009import java.util.Set;
010import java.util.SortedSet;
011import java.util.TreeSet;
012
013import de.cuioss.test.generator.Generators;
014import de.cuioss.test.generator.TypedGenerator;
015import de.cuioss.tools.logging.CuiLogger;
016
017/**
018 * Wraps a given {@link TypedGenerator} and provides additional methods for creating {@link List}s
019 * and {@link Set}s
020 *
021 * @param <T> identifying the type of the object being generated
022 * @author Oliver Wolff
023 */
024public class CollectionGenerator<T> implements TypedGenerator<T> {
025
026    private static final CuiLogger log = new CuiLogger(CollectionGenerator.class);
027    private static final String JAVA_UTIL_SORTED_SET = "java.util.SortedSet";
028
029    private static final String JAVA_UTIL_COLLECTION = "java.util.Collection";
030
031    private static final String JAVA_UTIL_SET = "java.util.Set";
032
033    private static final String JAVA_UTIL_LIST = "java.util.List";
034
035    private final TypedGenerator<T> wrapped;
036
037    private final TypedGenerator<Integer> sizeGenerator;
038
039    /**
040     * @param wrapped must not be null
041     * @param sizeGenerator must not be null
042     */
043    public CollectionGenerator(final TypedGenerator<T> wrapped, final TypedGenerator<Integer> sizeGenerator) {
044        super();
045        this.wrapped = requireNonNull(wrapped, "wrapped must not be null");
046        this.sizeGenerator = requireNonNull(sizeGenerator, "sizeGenerator must not be null");
047    }
048
049    /**
050     * Constructor.
051     *
052     * @param wrapped generator, must not be null
053     * @param lowerBound defines the lower bound of the integer generator that determines the
054     *            of {@link Collection} size
055     * @param upperBound defines the upper bound of the integer generator that determines the
056     *            of {@link Collection} size
057     */
058    public CollectionGenerator(final TypedGenerator<T> wrapped, final int lowerBound,
059            final int upperBound) {
060        this(wrapped,
061                Generators.integers(lowerBound, upperBound));
062    }
063
064    /**
065     * Constructor.
066     * using 2 and 12 as bounds of the {@link Collection} size to be created.
067     *
068     * @param wrapped generator, must not be null
069     */
070    public CollectionGenerator(final TypedGenerator<T> wrapped) {
071        this(wrapped, 2, 12);
072    }
073
074    /**
075     * @return the next object from the contained {@link TypedGenerator}
076     */
077    @Override
078    public T next() {
079        return this.wrapped.next();
080    }
081
082    /**
083     * Returns a {@link List} of the elements provided by the generator
084     *
085     * @param count the number of elements within the list
086     * @return a list with a given number of elements.
087     */
088    public List<T> list(final int count) {
089        final List<T> result = new ArrayList<>();
090        for (var i = 0; i < count; i++) {
091            result.add(next());
092        }
093        return result;
094    }
095
096    /**
097     * Returns a {@link Set} of the elements provided by the generator
098     *
099     * @param count the number of elements within the {@link Set}. It defines an upper bound of
100     *            elements, but depending on the elements / the entropy of the generator there may
101     *            be a lower number of elements.
102     * @return a {@link Set} with a given number of elements as maximum.
103     */
104    public Set<T> set(final int count) {
105        final Set<T> result = mutableSet();
106        for (var i = 0; i < count; i++) {
107            result.add(next());
108        }
109        return result;
110    }
111
112    /**
113     * Returns a {@link SortedSet} of the elements provided by the generator
114     *
115     * @param count the number of elements within the {@link Set}. It defines an upper bound of
116     *            elements, but depending on the elements / the entropy of the generator there may
117     *            be a lower number of elements.
118     * @return a {@link Set} with a given number of elements as maximum.
119     */
120    public SortedSet<T> sortedSet(final int count) {
121        return new TreeSet<>(set(count));
122    }
123
124    /**
125     * @return a {@link Set} with a random number of elements as maximum.
126     */
127    public Set<T> set() {
128        return set(this.sizeGenerator.next());
129    }
130
131    /**
132     * @return a {@link List} with a random number of elements as maximum.
133     */
134    public List<T> list() {
135        return list(this.sizeGenerator.next());
136    }
137
138    /**
139     * @return a {@link SortedSet} with a random number of elements as maximum.
140     */
141    public SortedSet<T> sortedSet() {
142        return sortedSet(this.sizeGenerator.next());
143    }
144
145    /**
146     * Generates a concrete {@link Iterable}.
147     * It is smart enough to determine whether the elements are to be wrapped in a {@link List},
148     * {@link Set}, {@link Collection} or {@link SortedSet}.
149     *
150     * @param expectedType type of the expected {@link Iterable}
151     * @return depending on the given expectedType a corresponding {@link Iterable},
152     *         {@link Collection}, {@link List}, {@link SortedSet} or {@link Set}
153     */
154    public Iterable<T> nextCollection(final Class<? extends Iterable<?>> expectedType) {
155        requireNonNull(expectedType, "expectedType must not be null");
156        switch (expectedType.getName()) {
157            case JAVA_UTIL_LIST:
158                return list();
159            case JAVA_UTIL_SET:
160                return set();
161            case JAVA_UTIL_COLLECTION:
162                return list();
163            case JAVA_UTIL_SORTED_SET:
164                return sortedSet();
165            default:
166                log.info("No specific case defined for {}. Returning list-implementation.",
167                        expectedType.getName());
168                return list();
169        }
170    }
171}