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