001/*
002 * Copyright © 2025 CUI-OpenSource-Software (info@cuioss.de)
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 */
016package de.cuioss.test.generator.impl;
017
018import de.cuioss.test.generator.Generators;
019import de.cuioss.test.generator.TypedGenerator;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026import java.util.SortedSet;
027import java.util.TreeSet;
028import java.util.logging.Level;
029import java.util.logging.Logger;
030
031import static java.util.Objects.requireNonNull;
032
033/**
034 * Enhances a {@link TypedGenerator} with collection generation capabilities.
035 * This wrapper adds methods for creating {@link List}s, {@link Set}s, and other collection types
036 * from any existing generator.
037 * 
038 * <p>Features:</p>
039 * <ul>
040 *   <li>Generates Lists with configurable size</li>
041 *   <li>Creates Sets (both regular and sorted)</li>
042 *   <li>Supports any collection type that can be built from Lists or Sets</li>
043 *   <li>Thread-safe if the wrapped generator is thread-safe</li>
044 * </ul>
045 * 
046 * <p><em>Example usage:</em></p>
047 * <pre>
048 * {@code
049 * // Create a generator for integers
050 * TypedGenerator<Integer> intGen = Generators.integers(1, 100);
051 * 
052 * // Create a collection generator
053 * var collectionGen = new CollectionGenerator<>(intGen);
054 * 
055 * // Generate collections
056 * List<Integer> list = collectionGen.list(5);      // List of 5 integers
057 * Set<Integer> set = collectionGen.set(3);         // Set of 3 integers
058 * Collection<Integer> coll = collectionGen.next(); // Random size collection
059 * }
060 * </pre>
061 *
062 * @param <T> The type of elements to be generated
063 * @author Oliver Wolff
064 */
065public class CollectionGenerator<T> implements TypedGenerator<T> {
066
067    private static final Logger LOGGER = Logger.getLogger(CollectionGenerator.class.getName());
068    private static final String JAVA_UTIL_SORTED_SET = "java.util.SortedSet";
069
070    private static final String JAVA_UTIL_COLLECTION = "java.util.Collection";
071
072    private static final String JAVA_UTIL_SET = "java.util.Set";
073
074    private static final String JAVA_UTIL_LIST = "java.util.List";
075
076    private final TypedGenerator<T> wrapped;
077
078    private final TypedGenerator<Integer> sizeGenerator;
079
080    /**
081     * @param wrapped       must not be null
082     * @param sizeGenerator must not be null
083     */
084    public CollectionGenerator(final TypedGenerator<T> wrapped, final TypedGenerator<Integer> sizeGenerator) {
085        this.wrapped = requireNonNull(wrapped, "wrapped must not be null");
086        this.sizeGenerator = requireNonNull(sizeGenerator, "sizeGenerator must not be null");
087    }
088
089    /**
090     * Constructor.
091     *
092     * @param wrapped    generator, must not be null
093     * @param lowerBound defines the lower bound of the integer generator that
094     *                   determines the of {@link Collection} size
095     * @param upperBound defines the upper bound of the integer generator that
096     *                   determines the of {@link Collection} size
097     */
098    public CollectionGenerator(final TypedGenerator<T> wrapped, final int lowerBound, final int upperBound) {
099        this(wrapped, Generators.integers(lowerBound, upperBound));
100    }
101
102    /**
103     * Constructor. using 2 and 12 as bounds of the {@link Collection} size to be
104     * created.
105     *
106     * @param wrapped generator, must not be null
107     */
108    public CollectionGenerator(final TypedGenerator<T> wrapped) {
109        this(wrapped, 2, 12);
110    }
111
112    /**
113     * @return the next object from the contained {@link TypedGenerator}
114     */
115    @Override
116    public T next() {
117        return wrapped.next();
118    }
119
120    /**
121     * Returns a {@link List} of the elements provided by the generator
122     *
123     * @param count the number of elements within the list
124     * @return a list with a given number of elements.
125     */
126    public List<T> list(final int count) {
127        final List<T> result = new ArrayList<>();
128        for (var i = 0; i < count; i++) {
129            result.add(next());
130        }
131        return result;
132    }
133
134    /**
135     * Returns a {@link Set} of the elements provided by the generator
136     *
137     * @param count the number of elements within the {@link Set}. It defines an
138     *              upper bound of elements, but depending on the elements / the
139     *              entropy of the generator there may be a lower number of
140     *              elements.
141     * @return a {@link Set} with a given number of elements as maximum.
142     */
143    public Set<T> set(final int count) {
144        final Set<T> result = new HashSet<>();
145        for (var i = 0; i < count; i++) {
146            result.add(next());
147        }
148        return result;
149    }
150
151    /**
152     * Returns a {@link SortedSet} of the elements provided by the generator
153     *
154     * @param count the number of elements within the {@link Set}. It defines an
155     *              upper bound of elements, but depending on the elements / the
156     *              entropy of the generator there may be a lower number of
157     *              elements.
158     * @return a {@link Set} with a given number of elements as maximum.
159     */
160    public SortedSet<T> sortedSet(final int count) {
161        return new TreeSet<>(set(count));
162    }
163
164    /**
165     * @return a {@link Set} with a random number of elements as maximum.
166     */
167    public Set<T> set() {
168        return set(sizeGenerator.next());
169    }
170
171    /**
172     * @return a {@link List} with a random number of elements as maximum.
173     */
174    public List<T> list() {
175        return list(sizeGenerator.next());
176    }
177
178    /**
179     * @return a {@link SortedSet} with a random number of elements as maximum.
180     */
181    public SortedSet<T> sortedSet() {
182        return sortedSet(sizeGenerator.next());
183    }
184
185    /**
186     * Generates a concrete {@link Iterable}. It is smart enough to determine
187     * whether the elements are to be wrapped in a {@link List}, {@link Set},
188     * {@link Collection} or {@link SortedSet}.
189     *
190     * @param expectedType type of the expected {@link Iterable}
191     * @return depending on the given expectedType a corresponding {@link Iterable},
192     * {@link Collection}, {@link List}, {@link SortedSet} or {@link Set}
193     */
194    public Iterable<T> nextCollection(final Class<? extends Iterable<?>> expectedType) {
195        requireNonNull(expectedType, "expectedType must not be null");
196        return switch (expectedType.getName()) {
197            case JAVA_UTIL_LIST -> list();
198            case JAVA_UTIL_SET -> set();
199            case JAVA_UTIL_COLLECTION -> list();
200            case JAVA_UTIL_SORTED_SET -> sortedSet();
201            default -> {
202                LOGGER.log(Level.INFO, "No specific case defined for {0}. Returning list-implementation.", expectedType.getName());
203                yield list();
204            }
205        };
206    }
207}