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.engine.BeneratorContext;
31  import com.rapiddweller.benerator.primitive.ValueMapper;
32  import com.rapiddweller.benerator.wrapper.WrapperFactory;
33  import com.rapiddweller.common.BeanUtil;
34  import com.rapiddweller.common.ConfigurationError;
35  import com.rapiddweller.common.Converter;
36  import com.rapiddweller.common.TimeUtil;
37  import com.rapiddweller.common.Validator;
38  import com.rapiddweller.common.converter.AnyConverter;
39  import com.rapiddweller.common.converter.FormatFormatConverter;
40  import com.rapiddweller.common.converter.ParseFormatConverter;
41  import com.rapiddweller.common.converter.String2DateConverter;
42  import com.rapiddweller.format.util.DataFileUtil;
43  import com.rapiddweller.model.data.Format;
44  import com.rapiddweller.model.data.SimpleTypeDescriptor;
45  import com.rapiddweller.model.data.TypeDescriptor;
46  import com.rapiddweller.model.data.Uniqueness;
47  import com.rapiddweller.platform.xls.PlatformDescriptor;
48  import com.rapiddweller.script.PrimitiveType;
49  import org.apache.logging.log4j.LogManager;
50  import org.apache.logging.log4j.Logger;
51  
52  import java.text.SimpleDateFormat;
53  import java.util.Date;
54  
55  import static com.rapiddweller.model.data.TypeDescriptor.PATTERN;
56  
57  /**
58   * Creates generators of type instances.<br/><br/>
59   * Created: 05.03.2008 16:51:44
60   *
61   * @param <E> the type parameter
62   * @author Volker Bergmann
63   * @since 0.5.0
64   */
65  public abstract class TypeGeneratorFactory<E extends TypeDescriptor> {
66  
67    /**
68     * The Logger.
69     */
70    protected final Logger logger = LogManager.getLogger(getClass());
71  
72    /**
73     * Create generator generator.
74     *
75     * @param descriptor   the descriptor
76     * @param instanceName the instance name
77     * @param nullable     the nullable
78     * @param uniqueness   the uniqueness
79     * @param context      the context
80     * @return the generator
81     */
82    public Generator<?> createGenerator(E descriptor, String instanceName,
83                                        boolean nullable, Uniqueness uniqueness, BeneratorContext context) {
84      logger.debug("createGenerator({})", descriptor.getName());
85      Generator<?> generator = createRootGenerator(descriptor, instanceName, nullable, uniqueness, context);
86      generator = applyComponentBuilders(generator, descriptor, instanceName, uniqueness, context);
87      generator = wrapWithPostprocessors(generator, descriptor, context);
88      generator = applyOffsetAndCyclic(generator, descriptor, instanceName, uniqueness, context);
89      logger.debug("Created {}", generator);
90      return generator;
91    }
92  
93    /**
94     * Create root generator generator.
95     *
96     * @param descriptor   the descriptor
97     * @param instanceName the instance name
98     * @param nullable     the nullable
99     * @param uniqueness   the uniqueness
100    * @param context      the context
101    * @return the generator
102    */
103   public Generator<?> createRootGenerator(E descriptor, String instanceName,
104                                           boolean nullable, Uniqueness uniqueness, BeneratorContext context) {
105     Generator<?> generator = createExplicitGenerator(descriptor, uniqueness, context);
106     if (generator == null) {
107       generator = createSpecificGenerator(descriptor, instanceName, nullable, uniqueness, context);
108     }
109     if (generator == null) {
110       generator = createInheritedGenerator(descriptor, uniqueness, context);
111     }
112     if (generator == null) {
113       generator = createHeuristicGenerator(descriptor, instanceName, uniqueness, context);
114     }
115     if (generator == null) { // by now, we must have created a generator
116       throw new ConfigurationError("Failed to create root generator for descriptor: " + descriptor);
117     }
118     return generator;
119   }
120 
121   /**
122    * Create explicit generator generator.
123    *
124    * @param type       the type
125    * @param uniqueness the uniqueness
126    * @param context    the context
127    * @return the generator
128    */
129   protected Generator<?> createExplicitGenerator(
130       E type, Uniqueness uniqueness, BeneratorContext context) {
131     Generator<?> generator = DescriptorUtil.getGeneratorByName(type, context);
132     if (generator == null) {
133       generator = createSourceGenerator(type, uniqueness, context);
134     }
135     if (generator == null) {
136       generator = createScriptGenerator(type);
137     }
138     return generator;
139   }
140 
141   /**
142    * Gets generated type.
143    *
144    * @param descriptor the descriptor
145    * @return the generated type
146    */
147   protected abstract Class<?> getGeneratedType(E descriptor);
148 
149   /**
150    * Create source generator generator.
151    *
152    * @param descriptor the descriptor
153    * @param uniqueness the uniqueness
154    * @param context    the context
155    * @return the generator
156    */
157   protected abstract Generator<?> createSourceGenerator(
158       E descriptor, Uniqueness uniqueness, BeneratorContext context);
159 
160   /**
161    * Create specific generator generator.
162    *
163    * @param descriptor   the descriptor
164    * @param instanceName the instance name
165    * @param nullable     the nullable
166    * @param uniqueness   the uniqueness
167    * @param context      the context
168    * @return the generator
169    */
170   protected abstract Generator<?> createSpecificGenerator(E descriptor, String instanceName,
171                                                           boolean nullable, Uniqueness uniqueness, BeneratorContext context);
172 
173   /**
174    * Create inherited generator generator.
175    *
176    * @param type       the type
177    * @param uniqueness the uniqueness
178    * @param context    the context
179    * @return the generator
180    */
181   @SuppressWarnings("unchecked")
182   protected Generator<?> createInheritedGenerator(
183       E type, Uniqueness uniqueness, BeneratorContext context) {
184     while (type.getParent() != null) {
185       type = (E) type.getParent();
186       Generator<?> generator = createExplicitGenerator(type, uniqueness, context);
187       if (generator != null) {
188         return generator;
189       }
190     }
191     return null;
192   }
193 
194   /**
195    * Create heuristic generator generator.
196    *
197    * @param descriptor   the descriptor
198    * @param instanceName the instance name
199    * @param uniqueness   the uniqueness
200    * @param context      the context
201    * @return the generator
202    */
203   protected abstract Generator<?> createHeuristicGenerator(E descriptor, String instanceName,
204                                                            Uniqueness uniqueness, BeneratorContext context);
205 
206   /**
207    * Apply offset and cyclic generator.
208    *
209    * @param generator    the generator
210    * @param descriptor   the descriptor
211    * @param instanceName the instance name
212    * @param uniqueness   the uniqueness
213    * @param context      the context
214    * @return the generator
215    */
216   protected Generator<?> applyOffsetAndCyclic(Generator<?> generator, E descriptor, String instanceName,
217                                               Uniqueness uniqueness, BeneratorContext context) {
218     generator = DescriptorUtil.processOffset(generator, descriptor);
219     generator = DescriptorUtil.processCyclic(generator, descriptor);
220     return generator;
221   }
222 
223   /**
224    * Apply component builders generator.
225    *
226    * @param generator    the generator
227    * @param descriptor   the descriptor
228    * @param instanceName the instance name
229    * @param uniqueness   the uniqueness
230    * @param context      the context
231    * @return the generator
232    */
233   protected Generator<?> applyComponentBuilders(Generator<?> generator, E descriptor, String instanceName,
234                                                 Uniqueness uniqueness, BeneratorContext context) {
235     return generator;
236   }
237 
238   /**
239    * Create script generator generator.
240    *
241    * @param descriptor the descriptor
242    * @return the generator
243    */
244   protected static Generator<?> createScriptGenerator(TypeDescriptor descriptor) {
245     String scriptText = descriptor.getScript();
246     if (scriptText != null) {
247       return FactoryUtil.createScriptGenerator(scriptText);
248     }
249     return null;
250   }
251 
252   /**
253    * Create validating generator generator.
254    *
255    * @param descriptor the descriptor
256    * @param generator  the generator
257    * @param context    the context
258    * @return the generator
259    */
260   @SuppressWarnings({"unchecked", "rawtypes"})
261   protected static Generator<?> createValidatingGenerator(
262       TypeDescriptor descriptor, Generator<?> generator, BeneratorContext context) {
263     Validator validator = DescriptorUtil.getValidator(descriptor.getValidator(), context);
264     if (validator != null) {
265       generator = WrapperFactory.applyValidator(validator, generator);
266     }
267     return generator;
268   }
269 
270   /**
271    * Create converting generator generator.
272    *
273    * @param descriptor the descriptor
274    * @param generator  the generator
275    * @param context    the context
276    * @return the generator
277    */
278   @SuppressWarnings({"unchecked", "rawtypes"})
279   public static Generator<?> createConvertingGenerator(TypeDescriptor descriptor, Generator generator, BeneratorContext context) {
280     Converter<?, ?> converter = DescriptorUtil.getConverter(descriptor.getConverter(), context);
281     if (converter != null) {
282       if (descriptor.getPattern() != null && BeanUtil.hasProperty(converter.getClass(), PATTERN)) {
283         BeanUtil.setPropertyValue(converter, PATTERN, descriptor.getPattern(), false);
284       }
285       generator = WrapperFactory.applyConverter(generator, converter);
286     }
287     return generator;
288   }
289 
290   /**
291    * Wrap with postprocessors generator.
292    *
293    * @param <E>        the type parameter
294    * @param generator  the generator
295    * @param descriptor the descriptor
296    * @param context    the context
297    * @return the generator
298    */
299   @SuppressWarnings("unchecked")
300   static <E> Generator<E> wrapWithPostprocessors(Generator<E> generator, TypeDescriptor descriptor, BeneratorContext context) {
301     generator = (Generator<E>) createConvertingGenerator(descriptor, generator, context);
302     if (descriptor instanceof SimpleTypeDescriptor) {
303       SimpleTypeDescriptorpiddweller/model/data/SimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor simpleType = (SimpleTypeDescriptor) descriptor;
304       generator = (Generator<E>) createMappingGenerator(simpleType, generator);
305       generator = (Generator<E>) createTypeConvertingGenerator(simpleType, generator);
306     }
307     generator = (Generator<E>) createValidatingGenerator(descriptor, generator, context);
308     return generator;
309   }
310 
311   /**
312    * Create mapping generator generator.
313    *
314    * @param descriptor the descriptor
315    * @param generator  the generator
316    * @return the generator
317    */
318   static Generator<?> createMappingGenerator(
319       SimpleTypeDescriptor descriptor, Generator<?> generator) {
320     if (descriptor == null || descriptor.getMap() == null) {
321       return generator;
322     }
323     String mappingSpec = descriptor.getMap();
324     ValueMapper mapper = new ValueMapper(mappingSpec);
325     return WrapperFactory.applyConverter(generator, mapper);
326   }
327 
328   /**
329    * Create type converting generator generator.
330    *
331    * @param descriptor the descriptor
332    * @param generator  the generator
333    * @return the generator
334    */
335   static Generator<?> createTypeConvertingGenerator(
336       SimpleTypeDescriptor descriptor, Generator<?> generator) {
337     if (descriptor == null || descriptor.getPrimitiveType() == null) {
338       return generator;
339     }
340     Converter<?, ?> converter = createConverter(descriptor, generator.getGeneratedType());
341     return (converter != null ? WrapperFactory.applyConverter(generator, converter) : generator);
342   }
343 
344   /**
345    * Create converter converter.
346    *
347    * @param descriptor the descriptor
348    * @param sourceType the source type
349    * @return the converter
350    */
351   @SuppressWarnings({"unchecked", "rawtypes"})
352   public static Converter<?, ?> createConverter(SimpleTypeDescriptor descriptor, Class<?> sourceType) {
353     PrimitiveType primitiveType = descriptor.getPrimitiveType();
354     Class<?> targetType = primitiveType.getJavaType();
355     Converter<?, ?> converter = null;
356     if (Date.class.equals(targetType) && sourceType == String.class) {
357       // String needs to be converted to Date
358       if (descriptor.getPattern() != null) {
359         // We can use the SimpleDateFormat with a pattern
360         String pattern = descriptor.getPattern();
361         converter = new ParseFormatConverter<>(Date.class, new SimpleDateFormat(pattern), false);
362       } else {
363         // we need to expect the standard date format
364         converter = new String2DateConverter<>();
365       }
366     } else if (String.class.equals(targetType) && sourceType == Date.class) {
367       // String needs to be converted to Date
368       if (descriptor.getPattern() != null) {
369         // We can use the SimpleDateFormat with a pattern
370         String pattern = descriptor.getPattern();
371         converter = new FormatFormatConverter<>(Date.class, new SimpleDateFormat(pattern), false);
372       } else {
373         // we need to expect the standard date format
374         converter = new FormatFormatConverter<>(Date.class, TimeUtil.createDefaultDateFormat(), false);
375       }
376     } else if (targetType != sourceType) {
377       converter = new AnyConverter(targetType, descriptor.getPattern());
378     }
379     return converter;
380   }
381 
382   /**
383    * Should nullify each nullable boolean.
384    *
385    * @param context the context
386    * @return the boolean
387    */
388   protected boolean shouldNullifyEachNullable(BeneratorContext context) {
389     return (context.getGeneratorFactory().getDefaultsProvider().defaultNullQuota() == 1.);
390   }
391 
392   /**
393    * Is formatted boolean.
394    *
395    * @param type the type
396    * @return the boolean
397    */
398   protected static boolean isFormatted(TypeDescriptor type) {
399     Format format = type.getFormat();
400     if (format == Format.formatted) {
401       return true;
402     } else if (format == Format.raw) {
403       return false;
404     } else if (!DataFileUtil.isExcelDocument(type.getSource())) {
405       return false;
406     } else {
407       return PlatformDescriptor.isFormattedByDefault();
408     }
409   }
410 
411 }