1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package com.rapiddweller.benerator.factory;
28
29 import com.rapiddweller.benerator.Generator;
30 import com.rapiddweller.benerator.GeneratorProvider;
31 import com.rapiddweller.benerator.NonNullGenerator;
32 import com.rapiddweller.benerator.distribution.Distribution;
33 import com.rapiddweller.benerator.distribution.SequenceManager;
34 import com.rapiddweller.benerator.primitive.EquivalenceStringGenerator;
35 import com.rapiddweller.benerator.primitive.number.NumberQuantizer;
36 import com.rapiddweller.benerator.sample.OneShotGenerator;
37 import com.rapiddweller.benerator.sample.SequenceGenerator;
38 import com.rapiddweller.benerator.wrapper.AlternativeGenerator;
39 import com.rapiddweller.benerator.wrapper.CompositeStringGenerator;
40 import com.rapiddweller.benerator.wrapper.GeneratorChain;
41 import com.rapiddweller.benerator.wrapper.UniqueMultiSourceArrayGenerator;
42 import com.rapiddweller.benerator.wrapper.WrapperFactory;
43 import com.rapiddweller.common.ArrayUtil;
44 import com.rapiddweller.common.Assert;
45 import com.rapiddweller.common.CollectionUtil;
46 import com.rapiddweller.common.ComparableComparator;
47 import com.rapiddweller.common.Converter;
48 import com.rapiddweller.common.NumberUtil;
49 import com.rapiddweller.common.OrderedSet;
50 import com.rapiddweller.common.Period;
51 import com.rapiddweller.common.converter.AnyConverter;
52 import com.rapiddweller.common.converter.ConverterManager;
53 import com.rapiddweller.common.converter.NumberToNumberConverter;
54 import com.rapiddweller.common.math.Interval;
55 import com.rapiddweller.model.data.Uniqueness;
56 import com.rapiddweller.script.DatabeneScriptParser;
57 import com.rapiddweller.script.WeightedSample;
58 import com.rapiddweller.script.math.ArithmeticEngine;
59
60 import java.util.Calendar;
61 import java.util.Collection;
62 import java.util.Date;
63 import java.util.GregorianCalendar;
64 import java.util.List;
65 import java.util.Locale;
66 import java.util.Set;
67 import java.util.TreeSet;
68
69
70
71
72
73
74
75
76
77
78
79 public class EquivalenceGeneratorFactory extends GeneratorFactory {
80
81
82
83
84 public EquivalenceGeneratorFactory() {
85 super(new MeanDefaultsProvider());
86 }
87
88 @Override
89 public <T> Generator<T> createAlternativeGenerator(
90 Class<T> targetType, Generator<T>[] sources, Uniqueness uniqueness) {
91 return new GeneratorChain<>(targetType, true, sources);
92 }
93
94 @Override
95 public <T> Generator<T[]> createCompositeArrayGenerator(
96 Class<T> componentType, Generator<T>[] sources, Uniqueness uniqueness) {
97 return new UniqueMultiSourceArrayGenerator<>(componentType, sources);
98 }
99
100 @Override
101 public <T> Generator<T> createSampleGenerator(Collection<T> values, Class<T> generatedType, boolean unique) {
102 return new SequenceGenerator<>(generatedType, values);
103 }
104
105 @Override
106 @SuppressWarnings({"unchecked", "rawtypes"})
107 public <T> Generator<T> createFromWeightedLiteralList(String valueSpec, Class<T> targetType,
108 Distribution distribution, boolean unique) {
109 List<WeightedSample<?>> samples = CollectionUtil.toList(DatabeneScriptParser.parseWeightedLiteralList(valueSpec));
110 List<T> values = FactoryUtil.extractValues((List) samples);
111 Converter<?, T> typeConverter = new AnyConverter<>(targetType);
112 Collection<T> convertedValues = ConverterManager.convertAll((List) values, typeConverter);
113 return createSampleGenerator(convertedValues, targetType, true);
114 }
115
116 @Override
117 public <T> Generator<T> createWeightedSampleGenerator(Collection<WeightedSample<T>> samples, Class<T> targetType) {
118 List<T> values = FactoryUtil.extractValues(samples);
119 return createSampleGenerator(values, targetType, true);
120 }
121
122
123 @Override
124 public Generator<Date> createDateGenerator(Date min, Date max, long granularity, Distribution distribution) {
125 if (min == null) {
126 min = defaultsProvider.defaultMinDate();
127 }
128 if (max == null) {
129 max = defaultsProvider.defaultMaxDate();
130 }
131 TreeSet<Date> values = new TreeSet<>();
132 values.add(min);
133 values.add(midDate(min, max, granularity));
134 values.add(max);
135 return new SequenceGenerator<>(Date.class, values);
136 }
137
138
139
140
141
142
143
144
145
146 Date midDate(Date min, Date max, long granularity) {
147 int segmentNo = (int) ((max.getTime() - min.getTime()) / granularity / 2);
148 long millisOffset = segmentNo * granularity;
149 Calendar medianDay = new GregorianCalendar();
150 medianDay.setTime(min);
151 long daysOffset = millisOffset / Period.DAY.getMillis();
152 long subDayOffset = millisOffset - daysOffset * Period.DAY.getMillis();
153 medianDay.add(Calendar.DAY_OF_YEAR, (int) daysOffset);
154 medianDay.add(Calendar.MILLISECOND, (int) subDayOffset);
155 return medianDay.getTime();
156 }
157
158 @SuppressWarnings("unchecked")
159 @Override
160 public <T extends Number> NonNullGenerator<T> createNumberGenerator(
161 Class<T> numberType, T min, Boolean minInclusive, T max, Boolean maxInclusive,
162 T granularity, Distribution distribution, Uniqueness uniqueness) {
163 Assert.notNull(numberType, "numberType");
164 boolean quantization = true;
165 if (distribution != null) {
166 return super.createNumberGenerator(numberType, min, minInclusive, max, maxInclusive,
167 granularity, distribution, uniqueness);
168 }
169 if (min == null) {
170 quantization = false;
171 min = (NumberUtil.isLimited(numberType) ? NumberUtil.minValue(numberType) : defaultsProvider.defaultMin(numberType));
172 }
173 if (max == null) {
174 max = (NumberUtil.isLimited(numberType) ? NumberUtil.maxValue(numberType) : defaultsProvider.defaultMax(numberType));
175 }
176 if (granularity == null) {
177 quantization = false;
178 granularity = defaultsProvider.defaultGranularity(numberType);
179 }
180 if (((Comparable<T>) min).compareTo(max) == 0) {
181 return WrapperFactory.asNonNullGenerator(new OneShotGenerator<>(min));
182 }
183 if (minInclusive == null) {
184 minInclusive = true;
185 }
186 if (maxInclusive == null) {
187 maxInclusive = true;
188 }
189
190 NumberToNumberConverter<Number, T> converter = new NumberToNumberConverter<>(Number.class, numberType);
191 ArithmeticEngine engine = ArithmeticEngine.defaultInstance();
192 ValueSet<T> values = new ValueSet<>(min, minInclusive, max, maxInclusive, (quantization ? granularity : null), numberType);
193
194
195 values.addIfViable(min);
196 values.addIfViable((Number) engine.add(min, granularity));
197 values.addIfViable((Number) engine.subtract(max, granularity));
198 values.addIfViable(max);
199
200
201 T zeroExact = converter.convert(0);
202 T zeroApprox = converter.convert(NumberQuantizer.quantize(zeroExact, min, (quantization ? granularity : null), numberType));
203 int minVsZero = ((Comparable<T>) min).compareTo(zeroApprox);
204 int maxVsZero = ((Comparable<T>) max).compareTo(zeroApprox);
205
206 if (minVsZero <= 0 && maxVsZero >= 0) {
207
208 if (((Comparable<T>) zeroApprox).compareTo(zeroExact) == 0) {
209
210
211 Number minusGranularity = (Number) engine.subtract(zeroExact, granularity);
212 values.addIfViable((Number) engine.multiply(minusGranularity, 2));
213 values.addIfViable(minusGranularity);
214 values.addIfViable(zeroExact);
215 values.addIfViable(granularity);
216 values.addIfViable((Number) engine.multiply(granularity, 2));
217 } else {
218 values.addIfViable(zeroApprox);
219 if (((Comparable<T>) zeroApprox).compareTo(zeroExact) > 0) {
220
221 values.addIfViable((Number) engine.subtract(zeroApprox, granularity));
222 } else {
223
224 values.addIfViable((Number) engine.add(zeroApprox, granularity));
225 }
226
227 }
228 }
229 if (minVsZero >= 0 || maxVsZero <= 0) {
230
231 values.addIfViable((Number) engine.divide(engine.add(min, max), 2));
232 }
233 return WrapperFactory.asNonNullGenerator(new SequenceGenerator<>(numberType, values.getAll()));
234 }
235
236 @Override
237 public NonNullGenerator<String> createStringGenerator(Set<Character> chars,
238 Integer minLength, Integer maxLength, int lengthGranularity, Distribution lengthDistribution,
239 Uniqueness uniqueness) {
240 Generator<Character> charGenerator = createCharacterGenerator(chars);
241 if (maxLength == null) {
242 maxLength = defaultsProvider.defaultMaxLength();
243 }
244 Set<Integer> counts = defaultCounts(minLength, maxLength, lengthGranularity);
245 NonNullGenerator<Integer> lengthGenerator = WrapperFactory.asNonNullGenerator(
246 new SequenceGenerator<>(Integer.class, counts));
247 return new EquivalenceStringGenerator<>(charGenerator, lengthGenerator);
248 }
249
250 @SuppressWarnings("unchecked")
251 @Override
252 public NonNullGenerator<String> createCompositeStringGenerator(
253 GeneratorProvider<?> partGeneratorProvider, int minParts, int maxParts, Uniqueness uniqueness) {
254 AlternativeGenerator<String> result = new AlternativeGenerator<>(String.class);
255 Set<Integer> partCounts = defaultCounts(minParts, maxParts, 1);
256 for (int partCount : partCounts) {
257 Generator<String>[] sources = new Generator[partCount];
258 for (int i = 0; i < partCount; i++) {
259 sources[i] = WrapperFactory.asStringGenerator(partGeneratorProvider.create());
260 }
261 result.addSource(new CompositeStringGenerator(true, sources));
262 }
263 return WrapperFactory.asNonNullGenerator(result);
264 }
265
266 @Override
267 public Generator<Character> createCharacterGenerator(String pattern, Locale locale, boolean unique) {
268 Character[] chars = CollectionUtil.toArray(defaultSubSet(FactoryUtil.fullLocaleCharSet(pattern, locale)), Character.class);
269 return new SequenceGenerator<>(Character.class, chars);
270 }
271
272 @Override
273 public NonNullGenerator<Character> createCharacterGenerator(Set<Character> characters) {
274 return WrapperFactory.asNonNullGenerator(
275 new SequenceGenerator<>(Character.class, defaultSubSet(characters)));
276 }
277
278
279
280
281
282
283
284
285
286 protected Set<Integer> defaultCounts(int minParts, int maxParts, int lengthGranularity) {
287 Set<Integer> lengths = new TreeSet<>();
288 lengths.add(minParts);
289 lengths.add(((minParts + maxParts) / 2 - minParts) / lengthGranularity * lengthGranularity + minParts);
290 lengths.add(maxParts);
291 if (maxParts > minParts) {
292 lengths.add(minParts + 1);
293 lengths.add(maxParts - 1);
294 }
295 return lengths;
296 }
297
298 @Override
299 public <T> Generator<T> createSingleValueGenerator(T value, boolean unique) {
300 return new OneShotGenerator<>(value);
301 }
302
303 @Override
304 public <T> Generator<T> createNullGenerator(Class<T> generatedType) {
305 return new OneShotGenerator<>(null, generatedType);
306 }
307
308 @Override
309 public Set<Character> defaultSubSet(Set<Character> characters) {
310 Set<Character> uppers = new TreeSet<>();
311 Set<Character> lowers = new TreeSet<>();
312 Set<Character> digits = new TreeSet<>();
313 Set<Character> spaces = new TreeSet<>();
314 Set<Character> others = new TreeSet<>();
315 for (char c : characters) {
316 if (Character.isUpperCase(c)) {
317 uppers.add(c);
318 } else if (Character.isLowerCase(c)) {
319 lowers.add(c);
320 } else if (Character.isDigit(c)) {
321 digits.add(c);
322 } else if (Character.isWhitespace(c)) {
323 spaces.add(c);
324 } else {
325 others.add(c);
326 }
327 }
328 Set<Character> result = new OrderedSet<>();
329 addSelection(uppers, result);
330 addSelection(lowers, result);
331 addSelection(digits, result);
332 result.addAll(spaces);
333 result.addAll(others);
334 return result;
335 }
336
337
338
339
340
341
342
343 protected void addSelection(Set<Character> ofChars, Set<Character> toChars) {
344 if (ofChars.size() == 0) {
345 return;
346 }
347 Character[] array = CollectionUtil.toArray(ofChars);
348 toChars.add(array[0]);
349 if (array.length >= 3) {
350 toChars.add(array[array.length / 2]);
351 }
352 if (array.length >= 2) {
353 toChars.add(ArrayUtil.lastElementOf(array));
354 }
355 }
356
357
358
359 @Override
360 public Generator<?> applyNullSettings(Generator<?> source, Boolean nullable, Double nullQuota) {
361 if (nullable == null || nullable || (nullQuota != null && nullQuota > 0)) {
362 return WrapperFactory.prependNull(source);
363 } else {
364 return source;
365 }
366 }
367
368 @Override
369 protected Distribution defaultLengthDistribution(Uniqueness uniqueness, boolean required) {
370 return (required ? SequenceManager.STEP_SEQUENCE : null);
371 }
372
373 @SuppressWarnings("SwitchStatementWithTooFewBranches")
374 @Override
375 public Distribution defaultDistribution(Uniqueness uniqueness) {
376 switch (uniqueness) {
377 case NONE:
378 return SequenceManager.RANDOM_SEQUENCE;
379 default:
380 return SequenceManager.STEP_SEQUENCE;
381 }
382 }
383
384 @Override
385 protected double defaultTrueQuota() {
386 return 0.5;
387 }
388
389
390
391
392
393
394 static class ValueSet<T extends Number> {
395
396
397
398
399 final Interval<T> numberRange;
400
401
402
403 final T granularity;
404
405
406
407 final Class<T> numberType;
408 private final TreeSet<T> set;
409
410
411
412
413
414
415
416
417
418
419
420 @SuppressWarnings({"rawtypes", "unchecked"})
421 public ValueSet(T min, boolean minInclusive, T max, boolean maxInclusive, T granularity, Class<T> numberType) {
422 this.set = new TreeSet<>();
423 this.numberRange = new Interval<T>(min, minInclusive, max, maxInclusive, new ComparableComparator());
424 this.granularity = granularity;
425 this.numberType = numberType;
426 }
427
428
429
430
431
432
433 public Collection<T> getAll() {
434 return set;
435 }
436
437
438
439
440
441
442 public void addIfViable(Number value) {
443 T numberToAdd = NumberToNumberConverter.convert(value, numberType);
444 if (numberRange.contains(numberToAdd)) {
445 if (granularity != null) {
446 numberToAdd = NumberQuantizer.quantize(value, numberRange.getMin(), granularity, numberType);
447 }
448 set.add(numberToAdd);
449 }
450 }
451 }
452
453 @Override
454 protected boolean defaultUnique() {
455 return true;
456 }
457
458 }