View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.factory;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.StorageSystem;
31  import com.rapiddweller.benerator.csv.SequencedDatasetCSVGenerator;
32  import com.rapiddweller.benerator.csv.WeightedDatasetCSVGenerator;
33  import com.rapiddweller.benerator.distribution.Distribution;
34  import com.rapiddweller.benerator.distribution.IndividualWeight;
35  import com.rapiddweller.benerator.distribution.sequence.RandomIntegerGenerator;
36  import com.rapiddweller.benerator.engine.BeneratorContext;
37  import com.rapiddweller.benerator.engine.DescriptorConstants;
38  import com.rapiddweller.benerator.sample.ConstantGenerator;
39  import com.rapiddweller.benerator.sample.WeightedCSVSampleGenerator;
40  import com.rapiddweller.benerator.wrapper.AccessingGenerator;
41  import com.rapiddweller.benerator.wrapper.AlternativeGenerator;
42  import com.rapiddweller.benerator.wrapper.AsByteGeneratorWrapper;
43  import com.rapiddweller.benerator.wrapper.ByteArrayGenerator;
44  import com.rapiddweller.benerator.wrapper.DataSourceGenerator;
45  import com.rapiddweller.benerator.wrapper.WrapperFactory;
46  import com.rapiddweller.common.ConfigurationError;
47  import com.rapiddweller.common.Converter;
48  import com.rapiddweller.common.StringUtil;
49  import com.rapiddweller.common.Validator;
50  import com.rapiddweller.common.accessor.GraphAccessor;
51  import com.rapiddweller.common.converter.AnyConverter;
52  import com.rapiddweller.common.converter.ArrayElementExtractor;
53  import com.rapiddweller.common.converter.ConditionalConverter;
54  import com.rapiddweller.common.converter.ConverterChain;
55  import com.rapiddweller.common.converter.DateString2DurationConverter;
56  import com.rapiddweller.common.converter.LiteralParser;
57  import com.rapiddweller.common.converter.ToStringConverter;
58  import com.rapiddweller.common.validator.StringLengthValidator;
59  import com.rapiddweller.format.DataSource;
60  import com.rapiddweller.format.script.ScriptConverterForStrings;
61  import com.rapiddweller.format.util.DataFileUtil;
62  import com.rapiddweller.model.data.SimpleTypeDescriptor;
63  import com.rapiddweller.model.data.UnionSimpleTypeDescriptor;
64  import com.rapiddweller.model.data.Uniqueness;
65  import com.rapiddweller.script.BeanSpec;
66  import com.rapiddweller.script.DatabeneScriptParser;
67  import com.rapiddweller.script.PrimitiveType;
68  
69  import java.lang.annotation.Annotation;
70  import java.sql.Timestamp;
71  import java.text.DateFormat;
72  import java.util.Date;
73  import java.util.Locale;
74  
75  import static com.rapiddweller.model.data.SimpleTypeDescriptor.GRANULARITY;
76  import static com.rapiddweller.model.data.SimpleTypeDescriptor.MAX;
77  import static com.rapiddweller.model.data.SimpleTypeDescriptor.MIN;
78  import static com.rapiddweller.model.data.SimpleTypeDescriptor.PATTERN;
79  
80  /**
81   * Creates generators of simple types.<br/>
82   * <br/>
83   *
84   * @author Volker Bergmann
85   */
86  public class SimpleTypeGeneratorFactory extends TypeGeneratorFactory<SimpleTypeDescriptor> {
87  
88    private static final SimpleTypeGeneratorFactoryatorFactory.html#SimpleTypeGeneratorFactory">SimpleTypeGeneratorFactory INSTANCE = new SimpleTypeGeneratorFactory();
89  
90    /**
91     * Gets instance.
92     *
93     * @return the instance
94     */
95    public static SimpleTypeGeneratorFactory getInstance() {
96      return INSTANCE;
97    }
98  
99    /**
100    * Instantiates a new Simple type generator factory.
101    */
102   protected SimpleTypeGeneratorFactory() {
103   }
104 
105   @Override
106   protected Generator<?> createExplicitGenerator(SimpleTypeDescriptor descriptor, Uniqueness uniqueness,
107                                                  BeneratorContext context) {
108     Generator<?> generator = super.createExplicitGenerator(descriptor, uniqueness, context);
109     if (generator == null) {
110       generator = createConstantGenerator(descriptor, context);
111     }
112     if (generator == null) {
113       generator = createValuesGenerator(descriptor, uniqueness, context);
114     }
115     if (generator == null) {
116       generator = createPatternGenerator(descriptor, uniqueness, context);
117     }
118     return generator;
119   }
120 
121   @Override
122   @SuppressWarnings({"rawtypes", "unchecked"})
123   protected Generator<?> createSpecificGenerator(SimpleTypeDescriptor descriptor, String instanceName,
124                                                  boolean nullable, Uniqueness uniqueness, BeneratorContext context) {
125     Generator<?> generator = InstanceGeneratorFactory.createConfiguredDefaultGenerator(
126         instanceName, uniqueness, context);
127     if (generator == null && nullable && shouldNullifyEachNullable(context)) {
128       generator = new ConstantGenerator(null, getGeneratedType(descriptor));
129     }
130     return generator;
131   }
132 
133   /**
134    * Create values generator generator.
135    *
136    * @param descriptor the descriptor
137    * @param uniqueness the uniqueness
138    * @param context    the context
139    * @return the generator
140    */
141   protected static Generator<?> createValuesGenerator(
142       SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
143     PrimitiveType primitiveType = descriptor.getPrimitiveType();
144     Class<?> targetType = (primitiveType != null ? primitiveType.getJavaType() : String.class);
145     String valueSpec = descriptor.getValues();
146     if (valueSpec == null) {
147       return null;
148     }
149     if ("".equals(valueSpec)) {
150       return new ConstantGenerator<>("");
151     }
152     try {
153       Distribution distribution = FactoryUtil.getDistribution(descriptor.getDistribution(), uniqueness, false, context);
154       return context.getGeneratorFactory().createFromWeightedLiteralList(valueSpec, targetType, distribution, uniqueness.isUnique());
155     } catch (com.rapiddweller.common.ParseException e) {
156       throw new ConfigurationError("Error parsing samples: " + valueSpec, e);
157     }
158   }
159 
160   /**
161    * Create pattern generator generator.
162    *
163    * @param type       the type
164    * @param uniqueness the uniqueness
165    * @param context    the context
166    * @return the generator
167    */
168   protected static Generator<String> createPatternGenerator(SimpleTypeDescriptor type, Uniqueness uniqueness,
169                                                             BeneratorContext context) {
170     String pattern = type.getPattern();
171     if (pattern != null) {
172       return createStringGenerator(type, uniqueness, context);
173     } else {
174       return null;
175     }
176   }
177 
178   /**
179    * Create constant generator generator.
180    *
181    * @param descriptor the descriptor
182    * @param context    the context
183    * @return the generator
184    */
185   @SuppressWarnings({"unchecked", "rawtypes"})
186   protected static Generator<?> createConstantGenerator(
187       SimpleTypeDescriptor descriptor, BeneratorContext context) {
188     Generator<?> generator = null;
189     // check for constant
190     String constant = descriptor.getConstant();
191     if ("".equals(constant)) {
192       generator = new ConstantGenerator<>("");
193     } else if (constant != null) {
194       Object value = LiteralParser.parse(constant);
195       generator = new ConstantGenerator(value);
196     }
197     return generator;
198   }
199 
200   @Override
201   protected Generator<?> createHeuristicGenerator(
202       SimpleTypeDescriptor descriptor, String instanceName, Uniqueness uniqueness, BeneratorContext context) {
203     Generator<?> generator = createTypeGenerator(descriptor, uniqueness, context);
204     if (generator == null) {
205       generator = createStringGenerator(descriptor, uniqueness, context);
206     }
207     return generator;
208   }
209 
210   @Override
211   @SuppressWarnings({"unchecked", "rawtypes"})
212   protected Generator<?> createSourceGenerator(
213       SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
214     String source = descriptor.getSource();
215     if (source == null) {
216       return null;
217     }
218     String selector = descriptor.getSelector();
219     String subSelector = descriptor.getSubSelector();
220     Generator<?> generator;
221     if (context.get(source) != null) {
222       Object sourceObject = context.get(source);
223       if (sourceObject instanceof StorageSystem) {
224         if (!StringUtil.isEmpty(subSelector)) {
225           generator = new DataSourceGenerator(((StorageSystem) sourceObject).query(subSelector, true, context));
226           generator = WrapperFactory.applyHeadCycler(generator);
227         } else {
228           generator = new DataSourceGenerator(((StorageSystem) sourceObject).query(selector, true, context));
229         }
230       } else if (sourceObject instanceof Generator) {
231         generator = (Generator<?>) sourceObject;
232       } else if (sourceObject instanceof DataSource) {
233         DataSource dataSource = (DataSource) sourceObject;
234         generator = new DataSourceGenerator(dataSource);
235       } else {
236         throw new UnsupportedOperationException("Not a supported source: " + sourceObject);
237       }
238     } else if (DataFileUtil.isCsvDocument(source)) {
239       return createSimpleTypeCSVSourceGenerator(descriptor, source, uniqueness, context);
240     } else if (DataFileUtil.isExcelDocument(source)) {
241       return createSimpleTypeXLSSourceGenerator(descriptor, source, uniqueness, context);
242     } else if (DataFileUtil.isPlainTextDocument(source)) {
243       generator = SourceFactory.createTextLineGenerator(source);
244     } else {
245       try {
246         BeanSpec sourceSpec = DatabeneScriptParser.resolveBeanSpec(source, context);
247         generator = createSourceGeneratorFromObject(descriptor, context, sourceSpec);
248       } catch (Exception e) {
249         generator = new AccessingGenerator(Object.class, new GraphAccessor(source), context);
250       }
251     }
252 
253     Distribution distribution = FactoryUtil.getDistribution(descriptor.getDistribution(), uniqueness, false, context);
254     if (distribution != null) {
255       generator = distribution.applyTo(generator, uniqueness.isUnique());
256     }
257 
258     return generator;
259   }
260 
261   @SuppressWarnings({"unchecked", "rawtypes"})
262   private static Generator<?> createSourceGeneratorFromObject(SimpleTypeDescriptor descriptor,
263                                                               BeneratorContext context, BeanSpec sourceSpec) {
264     Object sourceObject = sourceSpec.getBean();
265     Generator<?> generator;
266     if (sourceObject instanceof StorageSystem) {
267       StorageSystem/../com/rapiddweller/benerator/StorageSystem.html#StorageSystem">StorageSystem storage = (StorageSystem) sourceObject;
268       String selector = descriptor.getSelector();
269       String subSelector = descriptor.getSubSelector();
270       if (!StringUtil.isEmpty(subSelector)) {
271         generator = new DataSourceGenerator(storage.queryEntities(descriptor.getName(), subSelector, context));
272         generator = WrapperFactory.applyHeadCycler(generator);
273       } else {
274         generator = new DataSourceGenerator(storage.queryEntities(descriptor.getName(), selector, context));
275       }
276     } else if (sourceObject instanceof Generator) {
277       generator = (Generator<?>) sourceObject;
278     } else {
279       throw new UnsupportedOperationException("Source type not supported: " + sourceObject.getClass());
280     }
281     if (sourceSpec.isReference()) {
282       generator = WrapperFactory.preventClosing(generator);
283     }
284     return generator;
285   }
286 
287   @SuppressWarnings({"unchecked", "rawtypes"})
288   private static Generator<?> createSimpleTypeCSVSourceGenerator(
289       SimpleTypeDescriptor descriptor, String sourceName, Uniqueness uniqueness, BeneratorContext context) {
290     String sourceUri = context.resolveRelativeUri(sourceName);
291     Generator<?> generator;
292     char separator = DescriptorUtil.getSeparator(descriptor, context);
293     boolean rowBased = (descriptor.isRowBased() == null || descriptor.isRowBased());
294     String encoding = descriptor.getEncoding();
295     if (encoding == null) {
296       encoding = context.getDefaultEncoding();
297     }
298     Distribution distribution = FactoryUtil.getDistribution(descriptor.getDistribution(), uniqueness, false, context);
299 
300     String dataset = descriptor.getDataset();
301     String nesting = descriptor.getNesting();
302     if (dataset != null && nesting != null) {
303       if (uniqueness.isUnique()) {
304         generator = new SequencedDatasetCSVGenerator(sourceUri, separator, dataset, nesting,
305             distribution, encoding, new ScriptConverterForStrings(context));
306       } else {
307         generator = new WeightedDatasetCSVGenerator(Object.class, sourceUri, separator, dataset, nesting, false,
308             encoding, new ScriptConverterForStrings(context));
309       }
310     } else if (sourceName.toLowerCase().endsWith(".wgt.csv") || distribution instanceof IndividualWeight) {
311       generator = new WeightedCSVSampleGenerator(
312           Object.class, sourceUri, encoding, new ScriptConverterForStrings(context));
313     } else {
314       Generator<String[]> src = SourceFactory.createCSVGenerator(sourceUri, separator, encoding, true, rowBased);
315       Converter<String[], Object> converterChain = new ConverterChain<>(
316           new ArrayElementExtractor<>(String.class, 0),
317           new ScriptConverterForStrings(context));
318       generator = WrapperFactory.applyConverter(src, converterChain);
319       if (distribution != null) {
320         generator = distribution.applyTo(generator, uniqueness.isUnique());
321       }
322     }
323     return generator;
324   }
325 
326   private static Generator<?> createSimpleTypeXLSSourceGenerator(
327       SimpleTypeDescriptor descriptor, String sourceName, Uniqueness uniqueness, BeneratorContext context) {
328     // TODO v0.8 define common mechanism for file sources CSV, XLS, ... and entity, array, simple type
329     Generator<?> generator;
330     Distribution distribution = FactoryUtil.getDistribution(descriptor.getDistribution(), uniqueness, false, context);
331     Generator<Object[]> src = SourceFactory.createXLSLineGenerator(sourceName);
332     Converter<Object[], Object> converterChain = new ConverterChain<>(
333         new ArrayElementExtractor<>(Object.class, 0),
334         new ConditionalConverter(argument -> (argument instanceof String),
335             new ScriptConverterForStrings(context)));
336     generator = WrapperFactory.applyConverter(src, converterChain);
337     if (distribution != null) {
338       generator = distribution.applyTo(generator, uniqueness.isUnique());
339     }
340     return generator;
341   }
342 
343 
344   @SuppressWarnings("unchecked")
345   private Generator<?> createTypeGenerator(
346       SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
347     if (descriptor instanceof UnionSimpleTypeDescriptor) {
348       return createUnionTypeGenerator((UnionSimpleTypeDescriptor) descriptor, context);
349     }
350     PrimitiveType primitiveType = descriptor.getPrimitiveType();
351     if (primitiveType == null) {
352       return null;
353     }
354     Class<?> targetType = primitiveType.getJavaType();
355     if (Number.class.isAssignableFrom(targetType)) {
356       return createNumberGenerator(descriptor, (Class<? extends Number>) targetType, uniqueness, context);
357     } else if (String.class.isAssignableFrom(targetType)) {
358       return createStringGenerator(descriptor, uniqueness, context);
359     } else if (Boolean.class == targetType) {
360       return createBooleanGenerator(descriptor, context);
361     } else if (Character.class == targetType) {
362       return createCharacterGenerator(descriptor, uniqueness, context);
363     } else if (Date.class == targetType) {
364       return createDateGenerator(descriptor, uniqueness, context);
365     } else if (Timestamp.class == targetType) {
366       return createTimestampGenerator(descriptor, uniqueness, context);
367     } else if (byte[].class == targetType) {
368       return createByteArrayGenerator(descriptor, context);
369     } else {
370       return null;
371     }
372   }
373 
374   @Override
375   protected Class<?> getGeneratedType(SimpleTypeDescriptor descriptor) {
376     PrimitiveType primitiveType = descriptor.getPrimitiveType();
377     if (primitiveType == null) {
378       throw new ConfigurationError("No type configured for " + descriptor.getName());
379     }
380     return primitiveType.getJavaType();
381   }
382 
383   @SuppressWarnings({"unchecked", "rawtypes"})
384   private Generator<?> createUnionTypeGenerator(
385       UnionSimpleTypeDescriptor descriptor, BeneratorContext context) {
386     int n = descriptor.getAlternatives().size();
387     Generator<?>[] sources = new Generator[n];
388     for (int i = 0; i < n; i++) {
389       SimpleTypeDescriptor alternative = descriptor.getAlternatives().get(i);
390       sources[i] = createGenerator(alternative, null, false, Uniqueness.NONE, context);
391     }
392     Class<?> javaType = descriptor.getPrimitiveType().getJavaType();
393     return new AlternativeGenerator(javaType, sources);
394   }
395 
396   private static Generator<?> createByteArrayGenerator(SimpleTypeDescriptor descriptor, BeneratorContext context) {
397     Generator<Byte> byteGenerator = new AsByteGeneratorWrapper<>(new RandomIntegerGenerator(-128, 127, 1));
398     return new ByteArrayGenerator(byteGenerator,
399         DescriptorUtil.getMinLength(descriptor), DescriptorUtil.getMaxLength(descriptor, context.getDefaultsProvider()));
400   }
401 
402   @SuppressWarnings({"unchecked", "rawtypes"})
403   private Generator<Timestamp> createTimestampGenerator(SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
404     Generator<Date> source = createDateGenerator(descriptor, uniqueness, context);
405     Converter<Date, Timestamp> converter = (Converter) new AnyConverter<>(Timestamp.class);
406     return WrapperFactory.applyConverter(source, converter);
407   }
408 
409   private Generator<Date> createDateGenerator(SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
410     Date min = parseDate(descriptor, MIN, null);
411     Date max = parseDate(descriptor, MAX, null);
412     long granularity = parseDateGranularity(descriptor);
413     Distribution distribution = FactoryUtil.getDistribution(
414         descriptor.getDistribution(), uniqueness, true, context);
415     return context.getGeneratorFactory().createDateGenerator(min, max, granularity, distribution);
416   }
417 
418   private static Generator<Character> createCharacterGenerator(
419       SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
420     String pattern = descriptor.getPattern();
421     if (pattern == null) {
422       pattern = ".";
423     }
424     Locale locale = descriptor.getLocale();
425     GeneratorFactory generatorFactory = context.getGeneratorFactory();
426     return generatorFactory.createCharacterGenerator(pattern, locale, uniqueness.isUnique());
427   }
428 
429   private Date parseDate(SimpleTypeDescriptor descriptor, String detailName, Date defaultDate) {
430     String detail = (String) descriptor.getDeclaredDetailValue(detailName);
431     try {
432       if (detail != null) {
433         DateFormat dateFormat = DescriptorUtil.getPatternAsDateFormat(descriptor);
434         return dateFormat.parse(detail);
435       } else {
436         return defaultDate;
437       }
438     } catch (java.text.ParseException e) {
439       logger.error("Error parsing date " + detail, e);
440       return defaultDate;
441     }
442   }
443 
444   private static long parseDateGranularity(SimpleTypeDescriptor descriptor) {
445     String detail = (String) descriptor.getDeclaredDetailValue(DescriptorConstants.ATT_GRANULARITY);
446     if (detail != null) {
447       return DateString2DurationConverter.defaultInstance().convert(detail);
448     } else {
449       return 24 * 3600 * 1000L;
450     }
451   }
452 
453   private static Generator<Boolean> createBooleanGenerator(SimpleTypeDescriptor descriptor, BeneratorContext context) {
454     return context.getGeneratorFactory().createBooleanGenerator(descriptor.getTrueQuota());
455   }
456 
457   private static <T extends Number> Generator<T> createNumberGenerator(
458       SimpleTypeDescriptor descriptor, Class<T> targetType, Uniqueness uniqueness, BeneratorContext context) {
459     T min = DescriptorUtil.getNumberDetail(descriptor, MIN, targetType);
460     Boolean minInclusive = descriptor.isMinInclusive();
461     T max = DescriptorUtil.getNumberDetail(descriptor, MAX, targetType);
462     Boolean maxInclusive = descriptor.isMaxInclusive();
463     T granularity = DescriptorUtil.getNumberDetail(descriptor, GRANULARITY, targetType);
464     Distribution distribution = FactoryUtil.getDistribution(
465         descriptor.getDistribution(), uniqueness, false, context);
466     return context.getGeneratorFactory().createNumberGenerator(targetType, min, minInclusive, max, maxInclusive,
467         granularity, distribution, uniqueness);
468   }
469 
470   private static Generator<String> createStringGenerator(SimpleTypeDescriptor descriptor, Uniqueness uniqueness, BeneratorContext context) {
471     // evaluate max length
472     Integer maxLength = null;
473     SimpleTypeDescriptor tmp = descriptor;
474     while (maxLength == null && tmp != null) {
475       maxLength = tmp.getMaxLength();
476       tmp = tmp.getParent();
477     }
478 
479     // check pattern against null
480     String pattern = ToStringConverter.convert(descriptor.getDetailValue(PATTERN), null);
481 
482     Integer minLength = descriptor.getMinLength();
483     int lengthGranularity = 1;
484     Distribution lengthDistribution = FactoryUtil.getDistribution(
485         descriptor.getLengthDistribution(), Uniqueness.NONE, false, context);
486     Locale locale = descriptor.getLocale();
487     GeneratorFactory factory = context.getGeneratorFactory();
488     return factory.createStringGenerator(pattern, locale, minLength, maxLength, lengthGranularity,
489         lengthDistribution, uniqueness);
490   }
491 
492   /**
493    * Create restriction validator validator.
494    *
495    * @param <A>        the type parameter
496    * @param <T>        the type parameter
497    * @param descriptor the descriptor
498    * @param nullable   the nullable
499    * @param context    the context
500    * @return the validator
501    */
502   @SuppressWarnings("unchecked")
503   protected static <A extends Annotation, T> Validator<T> createRestrictionValidator(
504       SimpleTypeDescriptor descriptor, boolean nullable, GeneratorFactory context) {
505     if ((descriptor.getMinLength() != null || descriptor.getMaxLength() != null) && "string".equals(descriptor.getName())) {
506       Integer minLength = DescriptorUtil.getMinLength(descriptor);
507       Integer maxLength = DescriptorUtil.getMaxLength(descriptor, context.getDefaultsProvider());
508       return (Validator<T>) new StringLengthValidator(minLength, maxLength, nullable);
509     }
510     return null;
511   }
512 
513 }