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}