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.anno;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.engine.BeneratorContext;
31  import com.rapiddweller.benerator.engine.DescriptorBasedGenerator;
32  import com.rapiddweller.benerator.factory.ArrayTypeGeneratorFactory;
33  import com.rapiddweller.benerator.factory.CoverageGeneratorFactory;
34  import com.rapiddweller.benerator.factory.DescriptorUtil;
35  import com.rapiddweller.benerator.factory.EquivalenceGeneratorFactory;
36  import com.rapiddweller.benerator.factory.GeneratorFactory;
37  import com.rapiddweller.benerator.factory.GentleDefaultsProvider;
38  import com.rapiddweller.benerator.factory.InstanceGeneratorFactory;
39  import com.rapiddweller.benerator.factory.MeanDefaultsProvider;
40  import com.rapiddweller.benerator.factory.SerialGeneratorFactory;
41  import com.rapiddweller.benerator.factory.StochasticGeneratorFactory;
42  import com.rapiddweller.benerator.wrapper.LastFlagGenerator;
43  import com.rapiddweller.benerator.wrapper.NShotGeneratorProxy;
44  import com.rapiddweller.benerator.wrapper.WrapperFactory;
45  import com.rapiddweller.common.BeanUtil;
46  import com.rapiddweller.common.CollectionUtil;
47  import com.rapiddweller.common.ConfigurationError;
48  import com.rapiddweller.common.ParseException;
49  import com.rapiddweller.common.ProgrammerError;
50  import com.rapiddweller.common.StringUtil;
51  import com.rapiddweller.common.TimeUtil;
52  import com.rapiddweller.common.context.ContextAware;
53  import com.rapiddweller.format.script.ScriptUtil;
54  import com.rapiddweller.format.util.DataFileUtil;
55  import com.rapiddweller.model.data.ArrayElementDescriptor;
56  import com.rapiddweller.model.data.ArrayTypeDescriptor;
57  import com.rapiddweller.model.data.ComplexTypeDescriptor;
58  import com.rapiddweller.model.data.DataModel;
59  import com.rapiddweller.model.data.DefaultDescriptorProvider;
60  import com.rapiddweller.model.data.InstanceDescriptor;
61  import com.rapiddweller.model.data.Mode;
62  import com.rapiddweller.model.data.SimpleTypeDescriptor;
63  import com.rapiddweller.model.data.TypeDescriptor;
64  import com.rapiddweller.model.data.Uniqueness;
65  import com.rapiddweller.platform.db.DBSystem;
66  import com.rapiddweller.platform.db.DefaultDBSystem;
67  import com.rapiddweller.platform.java.BeanDescriptorProvider;
68  import com.rapiddweller.platform.java.Entity2JavaConverter;
69  import com.rapiddweller.script.DatabeneScriptParser;
70  import org.apache.logging.log4j.LogManager;
71  import org.apache.logging.log4j.Logger;
72  
73  import javax.validation.constraints.AssertFalse;
74  import javax.validation.constraints.AssertTrue;
75  import javax.validation.constraints.DecimalMax;
76  import javax.validation.constraints.DecimalMin;
77  import javax.validation.constraints.Digits;
78  import javax.validation.constraints.Future;
79  import javax.validation.constraints.Max;
80  import javax.validation.constraints.Min;
81  import javax.validation.constraints.NotNull;
82  import javax.validation.constraints.Null;
83  import javax.validation.constraints.Past;
84  import javax.validation.constraints.Pattern;
85  import javax.validation.constraints.Size;
86  import java.io.File;
87  import java.io.IOException;
88  import java.lang.annotation.Annotation;
89  import java.lang.reflect.Array;
90  import java.lang.reflect.Field;
91  import java.lang.reflect.Method;
92  import java.text.SimpleDateFormat;
93  import java.util.HashSet;
94  import java.util.Set;
95  
96  /**
97   * Maps Java annotations to descriptor objects.<br/><br/>
98   * Created: 29.04.2010 06:59:02
99   *
100  * @author Volker Bergmann
101  * @since 0.6.1
102  */
103 public class AnnotationMapper extends DefaultDescriptorProvider {
104 
105   private static final Logger LOGGER = LogManager.getLogger(AnnotationMapper.class);
106 
107   private static final Set<String> STANDARD_METHODS;
108 
109   private static final Package BENERATOR_ANNO_PACKAGE = Unique.class.getPackage();
110   private static final Package BEANVAL_ANNO_PACKAGE = Max.class.getPackage();
111 
112   private static final Set<Class<? extends Annotation>> EXPLICITLY_MAPPED_ANNOTATIONS = CollectionUtil.toSet(
113       Bean.class,
114       Database.class,
115       Descriptor.class,
116       InvocationCount.class,
117       Factory.class, Equivalence.class, Coverage.class, Serial.class,
118       Defaults.class, Gentle.class, Mean.class,
119       ThreadPoolSize.class);
120 
121   static {
122     STANDARD_METHODS = new HashSet<>();
123     for (Method method : Annotation.class.getMethods()) {
124       STANDARD_METHODS.add(method.getName());
125     }
126   }
127 
128   private final DataModel dataModel;
129   private final PathResolver pathResolver;
130 
131   private final ArrayTypeGeneratorFactory arrayTypeGeneratorFactory;
132 
133   /**
134    * Instantiates a new Annotation mapper.
135    *
136    * @param dataModel    the data model
137    * @param pathResolver the path resolver
138    */
139   public AnnotationMapper(DataModel dataModel, PathResolver pathResolver) {
140     super("anno", dataModel);
141     this.dataModel = dataModel;
142     this.dataModel.addDescriptorProvider(this);
143     this.arrayTypeGeneratorFactory = new ArrayTypeGeneratorFactory();
144     this.pathResolver = pathResolver;
145   }
146 
147   // interface -------------------------------------------------------------------------------------------------------
148 
149   private static boolean containsConfig(Annotation[] annotations) {
150     for (Annotation annotation : annotations) {
151       Package annoPkg = annotation.annotationType().getPackage();
152       if ((annoPkg == BENERATOR_ANNO_PACKAGE || annoPkg == BEANVAL_ANNO_PACKAGE) &&
153           explicitlyMappedAnnotation(annotation)) {
154         return true;
155       }
156     }
157     return false;
158   }
159 
160   /**
161    * Explicitly mapped annotation boolean.
162    *
163    * @param annotation the annotation
164    * @return the boolean
165    */
166   protected static boolean explicitlyMappedAnnotation(Annotation annotation) {
167     return !EXPLICITLY_MAPPED_ANNOTATIONS
168         .contains(annotation.annotationType());
169   }
170 
171   private static boolean applyDefaultsProvider(Annotation[] annotations, BeneratorContext context) {
172     for (Annotation annotation : annotations) {
173       if (annotation instanceof Defaults) {
174         context.setDefaultsProvider(BeanUtil.newInstance(((Defaults) annotation).value()));
175         return true;
176       } else if (annotation instanceof Gentle) {
177         context.setDefaultsProvider(new GentleDefaultsProvider());
178         return true;
179       } else if (annotation instanceof Mean) {
180         context.setDefaultsProvider(new MeanDefaultsProvider());
181         return true;
182       }
183     }
184     return false;
185   }
186 
187   private static int indexOfLast(MethodDescriptor testMethod) {
188     Annotation[][] paramsAnnotations = testMethod.getParameterAnnotations();
189     for (int i = 0; i < paramsAnnotations.length; i++) {
190       for (Annotation paramAnnotation : paramsAnnotations[i]) {
191         if (paramAnnotation.annotationType() == Last.class) {
192           return i;
193         }
194       }
195     }
196     return -1;
197   }
198 
199   // helper methods --------------------------------------------------------------------------------------------------
200 
201   @SuppressWarnings({"unchecked", "rawtypes"})
202   private static Generator<Object[]> createDescriptorBasedGenerator(Descriptor annotation, MethodDescriptor testMethod, BeneratorContext context) {
203     String filename = null;
204     try {
205       if (annotation.file().length() > 0) {
206         filename = annotation.file();
207       } else {
208         filename = testMethod.getDeclaringClass().getName().replace('.', File.separatorChar) + ".ben.xml";
209       }
210       String testName;
211       if (annotation.name().length() > 0) {
212         testName = annotation.name();
213       } else {
214         testName = testMethod.getName();
215       }
216       return (Generator) new DescriptorBasedGenerator(filename, testName, context);
217     } catch (IOException e) {
218       throw new RuntimeException("Error opening file " + filename, e);
219     }
220   }
221 
222   private static void parseDatabase(Database annotation, BeneratorContext context) {
223     DBSystem db;
224     if (!StringUtil.isEmpty(annotation.environment())) {
225       db = new DefaultDBSystem(annotation.id(), annotation.environment(), context.getDataModel());
226     } else {
227       db = new DefaultDBSystem(annotation.id(), annotation.url(), annotation.driver(),
228           annotation.user(), annotation.password(), context.getDataModel());
229     }
230     if (!StringUtil.isEmpty(annotation.catalog())) {
231       db.setCatalog(annotation.catalog());
232     }
233     if (!StringUtil.isEmpty(annotation.schema())) {
234       db.setSchema(annotation.schema());
235     }
236     db.setLazy(true);
237     context.setGlobal(db.getId(), db);
238   }
239 
240   private static void parseBean(Bean annotation, BeneratorContext context) {
241     Object bean = instantiateBean(annotation, context);
242     applyProperties(annotation.properties(), bean, context);
243     context.setGlobal(annotation.id(), bean);
244     if (bean instanceof ContextAware) {
245       ((ContextAware) bean).setContext(context);
246     }
247   }
248 
249   private static Object instantiateBean(Bean beanAnno, BeneratorContext context) {
250     String beanSpec = beanAnno.spec();
251     Class<?> beanClass = beanAnno.type();
252     if (!StringUtil.isEmpty(beanSpec)) {
253       try {
254         if (beanClass != Object.class) {
255           throw new ConfigurationError("'type' and 'spec' exclude each other in a @Bean");
256         }
257         return DatabeneScriptParser.parseBeanSpec(beanSpec).evaluate(context);
258       } catch (ParseException e) {
259         throw new ConfigurationError("Error parsing bean spec: " + beanSpec, e);
260       }
261     } else if (beanClass != Object.class) {
262       return BeanUtil.newInstance(beanClass);
263     } else {
264       throw new ConfigurationError("@Bean is missing 'type' or 'spec' attribute");
265     }
266   }
267 
268   private static void applyProperties(Property[] properties, Object bean, BeneratorContext context) {
269     for (Property property : properties) {
270       Object value = resolveProperty(property, bean, context);
271       BeanUtil.setPropertyValue(bean, property.name(), value, true, true);
272     }
273   }
274 
275   private static Object resolveProperty(Property property, Object bean, BeneratorContext context) {
276     if (!StringUtil.isEmpty(property.value())) {
277       if (!StringUtil.isEmpty(property.ref())) {
278         throw new ConfigurationError("'value' and 'ref' exclude each other in a @Property");
279       }
280       Object value = ScriptUtil.evaluate(property.value(), context);
281       if (value instanceof String) {
282         value = StringUtil.unescape((String) value);
283       }
284       return value;
285     } else if (!StringUtil.isEmpty(property.ref())) {
286       return context.get(property.ref());
287     } else {
288       throw new ConfigurationError("@Property is missing 'value' or 'ref' attribute");
289     }
290   }
291 
292   private static void mapSizeAnnotation(Size size, InstanceDescriptor instanceDescriptor) {
293     setDetail("minLength", size.min(), instanceDescriptor);
294     setDetail("maxLength", size.max(), instanceDescriptor);
295   }
296 
297   private static void mapPatternAnnotation(Pattern pattern, InstanceDescriptor instanceDescriptor) {
298     if (!StringUtil.isEmpty(pattern.regexp())) {
299       setDetail("pattern", pattern.regexp(), instanceDescriptor);
300     }
301   }
302 
303   private static void mapSourceSetting(String value, String detailName, InstanceDescriptor instanceDescriptor) {
304     if (!StringUtil.isEmpty(value)) {
305       setDetail(detailName, value, instanceDescriptor);
306     }
307   }
308 
309   private static void mapFormatted(Source source, InstanceDescriptor instanceDescriptor) {
310     switch (source.format()) {
311       case formatted:
312       case raw:
313         setDetail("format", source.format().name(), instanceDescriptor);
314         break;
315       case globalDefault:
316         break;
317       default:
318         throw new UnsupportedOperationException("Not a supported Format: " + source.format());
319     }
320   }
321 
322   private static void mapValuesAnnotation(Values annotation, InstanceDescriptor instanceDescriptor) throws Exception {
323     Method method = annotation.annotationType().getMethod("value");
324     String[] values = (String[]) method.invoke(annotation);
325     StringBuilder builder = new StringBuilder();
326     for (int i = 0; i < values.length; i++) {
327       if (i > 0) {
328         builder.append(',');
329       }
330       builder.append("'").append(values[i].replace("'", "\\'")).append("'");
331     }
332     ((SimpleTypeDescriptor) instanceDescriptor.getLocalType(false)).setValues(builder.toString());
333   }
334 
335   private static void mapOffsetAnnotation(Offset annotation, InstanceDescriptor instanceDescriptor) {
336     if (annotation.value() != 0) {
337       instanceDescriptor.getLocalType().setOffset(annotation.value());
338     }
339   }
340 
341   private static void mapMinDateAnnotation(MinDate annotation, InstanceDescriptor instanceDescriptor) {
342     TypeDescriptor localType = instanceDescriptor.getLocalType();
343     if (!(localType instanceof SimpleTypeDescriptor)) {
344       throw new ConfigurationError("@MinDate can only be applied to Date types");
345     }
346     ((SimpleTypeDescriptor) localType).setMin(annotation.value());
347   }
348 
349   private static void mapMaxDateAnnotation(MaxDate annotation, InstanceDescriptor instanceDescriptor) {
350     TypeDescriptor localType = instanceDescriptor.getLocalType();
351     if (!(localType instanceof SimpleTypeDescriptor)) {
352       throw new ConfigurationError("@MaxDate can only be applied to Date types");
353     }
354     ((SimpleTypeDescriptor) localType).setMax(annotation.value());
355   }
356 
357   private static void mapAnyValueTypeAnnotation(Annotation annotation, InstanceDescriptor instanceDescriptor) throws Exception {
358     Method method = annotation.annotationType().getMethod("value");
359     Object value = normalize(method.invoke(annotation));
360     String detailName = StringUtil.uncapitalize(annotation.annotationType().getSimpleName());
361     setDetail(detailName, value, instanceDescriptor);
362   }
363 
364   private static void mapBeanValidationParameter(Annotation annotation, InstanceDescriptor element) {
365     SimpleTypeDescriptorweller/model/data/SimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor typeDescriptor = (SimpleTypeDescriptor) element.getLocalType(false);
366     if (annotation instanceof AssertFalse) {
367       typeDescriptor.setTrueQuota(0.);
368     } else if (annotation instanceof AssertTrue) {
369       typeDescriptor.setTrueQuota(1.);
370     } else if (annotation instanceof DecimalMax) {
371       typeDescriptor.setMax(String.valueOf(DescriptorUtil.convertType(((DecimalMax) annotation).value(), typeDescriptor)));
372     } else if (annotation instanceof DecimalMin) {
373       typeDescriptor.setMin(String.valueOf(DescriptorUtil.convertType(((DecimalMin) annotation).value(), typeDescriptor)));
374     } else if (annotation instanceof Digits) {
375       Digits digits = (Digits) annotation;
376       typeDescriptor.setGranularity(String.valueOf(Math.pow(10, -digits.fraction())));
377     } else if (annotation instanceof Future) {
378       typeDescriptor.setMin(new SimpleDateFormat("yyyy-MM-dd").format(TimeUtil.tomorrow()));
379     } else if (annotation instanceof Max) {
380       typeDescriptor.setMax(String.valueOf(((Max) annotation).value()));
381     } else if (annotation instanceof Min) {
382       typeDescriptor.setMin(String.valueOf(((Min) annotation).value()));
383     } else if (annotation instanceof NotNull) {
384       element.setNullable(false);
385       element.setNullQuota(0.);
386     } else if (annotation instanceof Null) {
387       element.setNullable(true);
388       element.setNullQuota(1.);
389     } else if (annotation instanceof Past) {
390       typeDescriptor.setMax(new SimpleDateFormat("yyyy-MM-dd").format(TimeUtil.yesterday()));
391     } else if (annotation instanceof Pattern) {
392       typeDescriptor.setPattern(((Pattern) annotation).regexp());
393     } else if (annotation instanceof Size) {
394       Size size = (Size) annotation;
395       typeDescriptor.setMinLength(size.min());
396       typeDescriptor.setMaxLength(size.max());
397     }
398   }
399 
400   private static void setDetail(String detailName, Object detailValue, InstanceDescriptor instanceDescriptor) {
401     if (instanceDescriptor.supportsDetail(detailName)) {
402       instanceDescriptor.setDetailValue(detailName, detailValue);
403     } else {
404       instanceDescriptor.getLocalType().setDetailValue(detailName, detailValue);
405     }
406   }
407 
408   private static Object normalize(Object value) {
409     if (value == null) {
410       return null;
411     }
412     if (value instanceof String && ((String) value).length() == 0) {
413       return null;
414     }
415     if (value.getClass().isArray() && Array.getLength(value) == 0) {
416       return null;
417     }
418     return value;
419   }
420 
421   /**
422    * Parses @{@link Database} and @{@link Bean} annotations attached to a class,
423    * initializes the related objects and puts them into the {@link BeneratorContext}
424    *
425    * @param annotations the annotations
426    * @param context     the context
427    */
428   public void parseClassAnnotations(Annotation[] annotations, BeneratorContext context) {
429     for (Annotation annotation : annotations) {
430       if (annotation instanceof Database) {
431         parseDatabase((Database) annotation, context);
432       } else if (annotation instanceof Bean) {
433         parseBean((Bean) annotation, context);
434       }
435     }
436   }
437 
438   /**
439    * scans test class attributes for attributes with @{@link Source} annotation
440    * and initializes them with a value from the referred source object
441    *
442    * @param attribute the attribute
443    * @param context   the context
444    * @return the generator
445    */
446   public Generator<?> createAndInitAttributeGenerator(Field attribute, BeneratorContext context) {
447     Source sourceAnno = attribute.getAnnotation(Source.class);
448     if (sourceAnno != null) {
449       return createAndInitAttributeSourceGenerator(sourceAnno, attribute, context);
450     } else {
451       return null;
452     }
453   }
454 
455   /**
456    * Create and init method params generator generator.
457    *
458    * @param testMethod the test method
459    * @param context    the context
460    * @return the generator
461    */
462   public Generator<Object[]> createAndInitMethodParamsGenerator(Method testMethod, BeneratorContext context) {
463     return createAndInitMethodParamsGenerator(new MethodDescriptor(testMethod), context);
464   }
465 
466   /**
467    * Create and init method params generator generator.
468    *
469    * @param testMethod the test method
470    * @param context    the context
471    * @return the generator
472    */
473   public Generator<Object[]> createAndInitMethodParamsGenerator(MethodDescriptor testMethod, BeneratorContext context) {
474     applyMethodGeneratorFactory(testMethod, context);
475 
476     // Evaluate @Bean and @Database annotations attached to the test method
477     if (testMethod.getAnnotation(Bean.class) != null) {
478       parseBean(testMethod.getAnnotation(Bean.class), context);
479     }
480     if (testMethod.getAnnotation(Database.class) != null) {
481       parseDatabase(testMethod.getAnnotation(Database.class), context);
482     }
483 
484     // map the native Java bean informations to an array descriptor
485     ArrayTypeDescriptor type = createMethodParamsType(testMethod);
486     InstanceDescriptor instance = createMethodParamsInstanceDescriptor(testMethod, type);
487     Uniqueness uniqueness = (instance.isUnique() != null && instance.isUnique() ? Uniqueness.ORDERED : Uniqueness.NONE);
488 
489     // create and return generator
490     Generator<Object[]> generator = createGenerator(type, testMethod, uniqueness, context);
491     generator.init(context);
492     return generator;
493   }
494 
495   /**
496    * Create method params type array type descriptor.
497    *
498    * @param testMethod the test method
499    * @return the array type descriptor
500    */
501   protected ArrayTypeDescriptor createMethodParamsType(MethodDescriptor testMethod) {
502     ArrayTypeDescriptor nativeType = createNativeParamsDescriptor(testMethod);
503     return createConfiguredParamsDescriptor(testMethod, nativeType);
504   }
505 
506   /**
507    * Create method params instance descriptor instance descriptor.
508    *
509    * @param testMethod the test method
510    * @param type       the type
511    * @return the instance descriptor
512    */
513   protected InstanceDescriptor createMethodParamsInstanceDescriptor(
514       MethodDescriptor testMethod, ArrayTypeDescriptor type) {
515     InstanceDescriptorptor.html#InstanceDescriptor">InstanceDescriptor instance = new InstanceDescriptor(testMethod.getName(), this, type);
516     for (Annotation annotation : testMethod.getAnnotations()) {
517       mapParamAnnotation(annotation, instance, testMethod.getDeclaringClass());
518     }
519     return instance;
520   }
521 
522   /**
523    * Create native params descriptor array type descriptor.
524    *
525    * @param testMethod the test method
526    * @return the array type descriptor
527    */
528   protected ArrayTypeDescriptor createNativeParamsDescriptor(MethodDescriptor testMethod) {
529     ArrayTypeDescriptorl#ArrayTypeDescriptor">ArrayTypeDescriptor nativeDescriptor = new ArrayTypeDescriptor(testMethod.getName() + "_native", this);
530     Class<?>[] paramTypes = testMethod.getParameterTypes();
531     for (int i = 0; i < paramTypes.length; i++) {
532       TypeDescriptor elementType = dataModel.getTypeDescriptor(paramTypes[i].getName());
533       BeanDescriptorProvider beanDescriptorProvider = dataModel.getBeanDescriptorProvider();
534       ArrayElementDescriptor#ArrayElementDescriptor">ArrayElementDescriptor elementDescriptor = new ArrayElementDescriptor(i, beanDescriptorProvider, elementType);
535       if (elementDescriptor.isNullable() == null) { // assure an explicit setting for nullability
536         if (BeanUtil.isPrimitiveType(paramTypes[i].getName())) {
537           elementDescriptor.setNullable(false); // primitives can never be null
538         } else {
539           elementDescriptor.setNullable(
540               elementDescriptor.getNullQuota() == null ||
541                   ((Double) elementDescriptor
542                       .getDeclaredDetailValue("nullQuota")) !=
543                       0.); // if nullQuota == 0, then set nullable to false
544         }
545       }
546       nativeDescriptor.addElement(elementDescriptor);
547     }
548     return nativeDescriptor;
549   }
550 
551   private ArrayTypeDescriptor createConfiguredParamsDescriptor(
552       MethodDescriptor testMethod, ArrayTypeDescriptor nativeDescriptor) {
553     ArrayTypeDescriptorscriptor.html#ArrayTypeDescriptor">ArrayTypeDescriptor type = new ArrayTypeDescriptor(
554         testMethod.getName() + "_configured", this, nativeDescriptor);
555     Class<?>[] parameterTypes = testMethod.getParameterTypes();
556     Annotation[][] paramAnnos = testMethod.getParameterAnnotations();
557     for (int i = 0; i < parameterTypes.length; i++) {
558       ArrayElementDescriptor parentElement = nativeDescriptor.getElement(i);
559       if (containsConfig(paramAnnos[i])) {
560         TypeDescriptor parentElementType = parentElement.getTypeDescriptor();
561         TypeDescriptor elementType = DescriptorUtil.deriveType(
562             parentElementType.getName(), parentElementType);
563         ArrayElementDescriptoriptor.html#ArrayElementDescriptor">ArrayElementDescriptor element = new ArrayElementDescriptor(i, this, elementType);
564         element.setParent(parentElement);
565         for (Annotation annotation : paramAnnos[i]) {
566           mapParamAnnotation(annotation, element, testMethod.getDeclaringClass());
567         }
568         type.addElement(element);
569       }
570     }
571     return type;
572   }
573 
574   /**
575    * Apply method generator factory.
576    *
577    * @param testMethod the test method
578    * @param context    the context
579    */
580   protected void applyMethodGeneratorFactory(MethodDescriptor testMethod, BeneratorContext context) {
581     boolean configured = applyGeneratorFactory(testMethod.getAnnotations(), context);
582     if (!configured) {
583       applyClassGeneratorFactory(testMethod.getDeclaringClass().getAnnotations(), context);
584     }
585     applyMethodDefaultsProvider(testMethod, context);
586   }
587 
588   private void applyClassGeneratorFactory(Annotation[] annotations, BeneratorContext context) {
589     boolean configured = applyGeneratorFactory(annotations, context);
590     if (!configured) {
591       context.setGeneratorFactory(context.getGeneratorFactory());
592     }
593   }
594 
595   /**
596    * Apply generator factory boolean.
597    *
598    * @param annotations the annotations
599    * @param context     the context
600    * @return the boolean
601    */
602   protected boolean applyGeneratorFactory(Annotation[] annotations, BeneratorContext context) {
603     boolean configured = false;
604     for (Annotation annotation : annotations) {
605       if (annotation instanceof Factory) {
606         GeneratorFactory factory = BeanUtil.newInstance(((Factory) annotation).value());
607         factory.setDefaultsProvider(context.getDefaultsProvider());
608         context.setGeneratorFactory(factory);
609         return true;
610       } else if (annotation instanceof Equivalence) {
611         context.setGeneratorFactory(new EquivalenceGeneratorFactory());
612         return true;
613       } else if (annotation instanceof Coverage) {
614         context.setGeneratorFactory(new CoverageGeneratorFactory());
615         return true;
616       } else if (annotation instanceof Stochastic) {
617         context.setGeneratorFactory(new StochasticGeneratorFactory());
618         return true;
619       } else if (annotation instanceof Serial) {
620         context.setGeneratorFactory(new SerialGeneratorFactory());
621         return true;
622       }
623     }
624     return configured;
625   }
626 
627   /**
628    * Apply method defaults provider.
629    *
630    * @param testMethod the test method
631    * @param context    the context
632    */
633   protected void applyMethodDefaultsProvider(MethodDescriptor testMethod, BeneratorContext context) {
634     // check if the method is annotated with an individual DefaultsProvider...
635     boolean configured = applyDefaultsProvider(testMethod.getAnnotations(), context);
636     // ... otherwise check for a class-wide DefaultsProvider annotation...
637     if (!configured) {
638       applyDefaultsProvider(testMethod.getDeclaringClass().getAnnotations(), context);
639     }
640     // ...otherwise the GeneratorFactory's DefaultProvider is used
641   }
642 
643   /*
644       @SuppressWarnings("unchecked")
645       private Generator<Object[]> createMethodSourceGenerator(com.rapiddweller.benerator.anno.Source source,
646               Method testMethod, ArrayTypeDescriptor type, BeneratorContext context) {
647           Generator<Object[]> baseGenerator = (Generator<Object[]>) arrayTypeGeneratorFactory.createGenerator(
648                   type, testMethod.getName(), false, Uniqueness.NONE, context);
649           return baseGenerator;
650       }
651   */
652   @SuppressWarnings({"unchecked", "rawtypes"})
653   private Generator<?> createAndInitAttributeSourceGenerator(
654       Source source, Field attribute, BeneratorContext context) {
655     String attName = attribute.getName();
656     TypeDescriptor typeDescriptor = createTypeDescriptor(attribute.getType());
657     InstanceDescriptoror.html#InstanceDescriptor">InstanceDescriptor descriptor = new InstanceDescriptor(attName, this, typeDescriptor);
658     Class<?> testClass = attribute.getDeclaringClass();
659     mapParamAnnotation(source, descriptor, testClass);
660     Offset offset = attribute.getAnnotation(Offset.class);
661     if (offset != null) {
662       mapParamAnnotation(offset, descriptor, testClass);
663     }
664     Generator generator = InstanceGeneratorFactory.createSingleInstanceGenerator(
665         descriptor, Uniqueness.NONE, context);
666     generator = WrapperFactory.applyConverter(generator, new Entity2JavaConverter());
667     generator.init(context);
668     return generator;
669   }
670 
671   /**
672    * Create generator generator.
673    *
674    * @param type       the type
675    * @param testMethod the test method
676    * @param uniqueness the uniqueness
677    * @param context    the context
678    * @return the generator
679    */
680   @SuppressWarnings("unchecked")
681   protected Generator<Object[]> createGenerator(ArrayTypeDescriptor type,
682                                                 MethodDescriptor testMethod, Uniqueness uniqueness, BeneratorContext context) {
683     Generator<Object[]> generator;
684 
685     Descriptor descriptorBasedAnno = testMethod.getAnnotation(Descriptor.class);
686     if (descriptorBasedAnno != null) {
687       context.setGeneratorFactory(new StochasticGeneratorFactory());
688       generator = createDescriptorBasedGenerator(descriptorBasedAnno, testMethod, context);
689     } else {
690       generator = (Generator<Object[]>) arrayTypeGeneratorFactory.createGenerator(
691           type, testMethod.getName(), false, uniqueness, context);
692     }
693     Class<?> generatedType = generator.getGeneratedType();
694     if (generatedType != Object[].class && generatedType != Object.class) {
695       throw new ProgrammerError("Expected Generator<Object[]> or Generator<Object>, but found Generator<" +
696           generatedType.getClass().getSimpleName() + '>');
697     }
698 
699     // evaluate @TestFeed annotation
700     InvocationCount testCount = testMethod.getAnnotation(InvocationCount.class);
701     if (testCount != null) {
702       generator = new NShotGeneratorProxy<>(generator, testCount.value());
703     }
704 
705     // apply LastInstanceDetector
706     generator = WrapperFactory.applyLastProductDetector(generator);
707 
708     int indexOfLast = indexOfLast(testMethod);
709     if (indexOfLast >= 0) {
710       generator = new LastFlagGenerator(generator, indexOfLast);
711     }
712     return generator;
713   }
714 
715   /*
716       private Generator<Object[]> createParamsGenerator(
717               Method testMethod, InstanceDescriptor descriptor, BeneratorContext context) {
718           Uniqueness uniqueness = DescriptorUtil.getUniqueness(descriptor, context);
719           Generator<Object[]> generator = arrayTypeGeneratorFactory.createSimpleArrayGenerator(descriptor.getName(),
720                   (ArrayTypeDescriptor) descriptor.getTypeDescriptor(), uniqueness, context);
721           return generator;
722       }
723   */
724   private <T> void mapParamAnnotation(Annotation annotation, InstanceDescriptor descriptor, Class<?> testClass) {
725     Package annoPackage = annotation.annotationType().getPackage();
726     if (BENERATOR_ANNO_PACKAGE.equals(annoPackage)) {
727       mapBeneratorParamAnnotation(annotation, descriptor, testClass);
728     } else if (BEANVAL_ANNO_PACKAGE.equals(annoPackage)) {
729       mapBeanValidationParameter(annotation, descriptor);
730     }
731   }
732 
733   private void mapBeneratorParamAnnotation(Annotation annotation, InstanceDescriptor instanceDescriptor, Class<?> testClass) {
734     if (LOGGER.isDebugEnabled()) {
735       LOGGER.debug("mapDetails(" + annotation + ", " + instanceDescriptor + ")");
736     }
737     try {
738       Class<?> annotationType = annotation.annotationType();
739       if (annotationType == Unique.class) {
740         instanceDescriptor.setDetailValue("unique", true);
741       } else if (annotationType == Granularity.class) {
742         instanceDescriptor.getLocalType(false).setDetailValue("granularity", String
743             .valueOf(DescriptorUtil.convertType(((Granularity) annotation).value(), (SimpleTypeDescriptor) instanceDescriptor.getLocalType(false))));
744       } else if (annotationType == DecimalGranularity.class) {
745         instanceDescriptor.getLocalType(false).setDetailValue("granularity", String.valueOf(
746             DescriptorUtil.convertType(((DecimalGranularity) annotation).value(), (SimpleTypeDescriptor) instanceDescriptor.getLocalType(false))));
747       } else if (annotationType == SizeDistribution.class) {
748         instanceDescriptor.getLocalType(false).setDetailValue("lengthDistribution", ((SizeDistribution) annotation).value());
749       } else if (annotationType == Pattern.class) {
750         mapPatternAnnotation((Pattern) annotation, instanceDescriptor);
751       } else if (annotationType == Size.class) {
752         mapSizeAnnotation((Size) annotation, instanceDescriptor);
753       } else if (annotationType == Source.class) {
754         mapSourceAnnotation((Source) annotation, instanceDescriptor, testClass);
755       } else if (annotationType == Values.class) {
756         mapValuesAnnotation((Values) annotation, instanceDescriptor);
757       } else if (annotationType == Offset.class) {
758         mapOffsetAnnotation((Offset) annotation, instanceDescriptor);
759       } else if (annotationType == MinDate.class) {
760         mapMinDateAnnotation((MinDate) annotation, instanceDescriptor);
761       } else if (annotationType == MaxDate.class) {
762         mapMaxDateAnnotation((MaxDate) annotation, instanceDescriptor);
763       } else if (annotationType == Last.class) {
764         instanceDescriptor.setMode(Mode.ignored);
765       } else if (explicitlyMappedAnnotation(annotation)) {
766         mapAnyValueTypeAnnotation(annotation, instanceDescriptor);
767       }
768     } catch (Exception e) {
769       throw new ConfigurationError("Error mapping annotation settings", e);
770     }
771   }
772 
773   private void mapSourceAnnotation(Source source, InstanceDescriptor instanceDescriptor, Class<?> testClass) {
774     mapSourceUriOrValue(source.value(), instanceDescriptor, testClass);
775     mapSourceUriOrValue(source.uri(), instanceDescriptor, testClass);
776     mapSourceSetting(source.segment(), "segment", instanceDescriptor);
777     mapSourceSetting(source.id(), "source", instanceDescriptor);
778     mapSourceSetting(source.dataset(), "dataset", instanceDescriptor);
779     mapSourceSetting(source.nesting(), "nesting", instanceDescriptor);
780     mapSourceSetting(source.encoding(), "encoding", instanceDescriptor);
781     mapSourceSetting(source.filter(), "filter", instanceDescriptor);
782     mapSourceSetting(source.selector(), "selector", instanceDescriptor);
783     mapSourceSetting(source.separator(), "separator", instanceDescriptor);
784     mapSourceSetting(source.emptyMarker(), "emptyMarker", instanceDescriptor);
785     mapSourceSetting(source.nullMarker(), "nullMarker", instanceDescriptor);
786     mapFormatted(source, instanceDescriptor);
787     setDetail("rowBased", source.rowBased(), instanceDescriptor);
788   }
789 
790   private void mapSourceUriOrValue(String value, InstanceDescriptor instanceDescriptor, Class<?> testClass) {
791     if (DataFileUtil.isExcelOrCsvDocument(value)) {
792       value = pathResolver.getPathFor(value, testClass);
793     }
794     mapSourceSetting(value, "source", instanceDescriptor);
795   }
796 
797   /**
798    * Create type descriptor type descriptor.
799    *
800    * @param type the type
801    * @return the type descriptor
802    */
803   protected TypeDescriptor createTypeDescriptor(Class<?> type) {
804     String abstractType = dataModel.getBeanDescriptorProvider().abstractType(type);
805     TypeDescriptor baseTypeDescriptor = dataModel.getTypeDescriptor(abstractType);
806     TypeDescriptor typeDescriptor;
807     if (baseTypeDescriptor instanceof SimpleTypeDescriptor) {
808       typeDescriptor = new SimpleTypeDescriptorl#SimpleTypeDescriptor">SimpleTypeDescriptor(type.getName(), this, (SimpleTypeDescriptor) baseTypeDescriptor);
809     } else if (baseTypeDescriptor instanceof ComplexTypeDescriptor) {
810       typeDescriptor = new ComplexTypeDescriptorl#ComplexTypeDescriptor">ComplexTypeDescriptor(type.getName(), this, (ComplexTypeDescriptor) baseTypeDescriptor);
811     } else {
812       throw new ConfigurationError("Cannot handle descriptor: " + baseTypeDescriptor);
813     }
814     return typeDescriptor;
815   }
816 
817 }