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.NonNullGenerator;
31  import com.rapiddweller.benerator.StorageSystem;
32  import com.rapiddweller.benerator.composite.AlternativeComponentBuilder;
33  import com.rapiddweller.benerator.composite.ArrayElementBuilder;
34  import com.rapiddweller.benerator.composite.ComponentBuilder;
35  import com.rapiddweller.benerator.composite.ConditionalComponentBuilder;
36  import com.rapiddweller.benerator.composite.PlainEntityComponentBuilder;
37  import com.rapiddweller.benerator.composite.SimplifyingSingleSourceArrayGenerator;
38  import com.rapiddweller.benerator.distribution.DistributingGenerator;
39  import com.rapiddweller.benerator.distribution.Distribution;
40  import com.rapiddweller.benerator.distribution.SequenceManager;
41  import com.rapiddweller.benerator.distribution.sequence.ExpandSequence;
42  import com.rapiddweller.benerator.engine.BeneratorContext;
43  import com.rapiddweller.benerator.engine.expression.ScriptExpression;
44  import com.rapiddweller.benerator.primitive.ScriptGenerator;
45  import com.rapiddweller.benerator.wrapper.AsIntegerGeneratorWrapper;
46  import com.rapiddweller.benerator.wrapper.DataSourceGenerator;
47  import com.rapiddweller.benerator.wrapper.SingleSourceArrayGenerator;
48  import com.rapiddweller.benerator.wrapper.SingleSourceCollectionGenerator;
49  import com.rapiddweller.benerator.wrapper.WrapperFactory;
50  import com.rapiddweller.common.ConfigurationError;
51  import com.rapiddweller.common.StringUtil;
52  import com.rapiddweller.common.SyntaxError;
53  import com.rapiddweller.format.script.Script;
54  import com.rapiddweller.format.script.ScriptUtil;
55  import com.rapiddweller.model.data.AlternativeGroupDescriptor;
56  import com.rapiddweller.model.data.ArrayElementDescriptor;
57  import com.rapiddweller.model.data.ComplexTypeDescriptor;
58  import com.rapiddweller.model.data.ComponentDescriptor;
59  import com.rapiddweller.model.data.IdDescriptor;
60  import com.rapiddweller.model.data.PartDescriptor;
61  import com.rapiddweller.model.data.ReferenceDescriptor;
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.script.Expression;
66  import org.apache.logging.log4j.LogManager;
67  import org.apache.logging.log4j.Logger;
68  
69  import java.io.IOException;
70  import java.io.StreamTokenizer;
71  import java.io.StringReader;
72  import java.util.ArrayList;
73  import java.util.Collection;
74  import java.util.HashSet;
75  
76  /**
77   * Creates {@link ComponentBuilder}s.<br/><br/>
78   * Created: 14.10.2007 22:16:34
79   *
80   * @author Volker Bergmann
81   */
82  public class ComponentBuilderFactory extends InstanceGeneratorFactory {
83  
84    /**
85     * Instantiates a new Component builder factory.
86     */
87    protected ComponentBuilderFactory() {
88    }
89  
90    private static final Logger LOGGER = LogManager.getLogger(ComponentBuilderFactory.class);
91  
92    // factory methods for component generators ------------------------------------------------------------------------
93  
94    /**
95     * Create component builder component builder.
96     *
97     * @param descriptor      the descriptor
98     * @param ownerUniqueness the owner uniqueness
99     * @param context         the context
100    * @return the component builder
101    */
102   public static ComponentBuilder<?> createComponentBuilder(
103       ComponentDescriptor descriptor, Uniqueness ownerUniqueness, BeneratorContext context) {
104     LOGGER.debug("createComponentBuilder({})", descriptor.getName());
105 
106     ComponentBuilder<?> result = null;
107     if (descriptor instanceof ArrayElementDescriptor) {
108       result = createPartBuilder(descriptor, ownerUniqueness, context);
109     } else if (descriptor instanceof PartDescriptor) {
110       TypeDescriptor type = descriptor.getTypeDescriptor();
111       if (type instanceof AlternativeGroupDescriptor) {
112         result = createAlternativeGroupBuilder((AlternativeGroupDescriptor) type, ownerUniqueness, context);
113       } else {
114         result = createPartBuilder(descriptor, ownerUniqueness, context);
115       }
116     } else if (descriptor instanceof ReferenceDescriptor) {
117       result = createReferenceBuilder((ReferenceDescriptor) descriptor, context);
118     } else if (descriptor instanceof IdDescriptor) {
119       result = createIdBuilder((IdDescriptor) descriptor, ownerUniqueness, context);
120     } else {
121       throw new ConfigurationError("Not a supported element: " + descriptor.getClass());
122     }
123     result = wrapWithCondition(descriptor, result);
124     return result;
125   }
126 
127   /**
128    * Create script builder component builder.
129    *
130    * @param component the component
131    * @param context   the context
132    * @return the component builder
133    */
134   protected static ComponentBuilder<?> createScriptBuilder(ComponentDescriptor component, BeneratorContext context) {
135     TypeDescriptor type = component.getTypeDescriptor();
136     if (type == null) {
137       return null;
138     }
139     String scriptText = type.getScript();
140     if (scriptText == null) {
141       return null;
142     }
143     Script script = ScriptUtil.parseScriptText(scriptText);
144     Generator<?> generator = new ScriptGenerator(script);
145     generator = DescriptorUtil.createConvertingGenerator(component.getTypeDescriptor(), generator, context);
146     return builderFromGenerator(generator, component, context);
147 
148   }
149 
150   @SuppressWarnings({"unchecked", "rawtypes"})
151   private static ComponentBuilder<?> createAlternativeGroupBuilder(
152       AlternativeGroupDescriptor type, Uniqueness ownerUniqueness, BeneratorContext context) {
153     int i = 0;
154     Collection<ComponentDescriptor> components = type.getComponents();
155     ComponentBuilder<?>[] builders = new ComponentBuilder[components.size()];
156     for (ComponentDescriptor component : components) {
157       builders[i++] = createComponentBuilder(component, ownerUniqueness, context);
158     }
159     return new AlternativeComponentBuilder(builders, type.getScope());
160   }
161 
162   private static ComponentBuilder<?> createPartBuilder(
163       ComponentDescriptor part, Uniqueness ownerUniqueness, BeneratorContext context) {
164     Generator<?> generator = createSingleInstanceGenerator(part, ownerUniqueness, context);
165     generator = createMultiplicityWrapper(part, generator, context);
166     LOGGER.debug("Created {}", generator);
167     return builderFromGenerator(generator, part, context);
168   }
169 
170   /**
171    * Create reference builder component builder.
172    *
173    * @param descriptor the descriptor
174    * @param context    the context
175    * @return the component builder
176    */
177   @SuppressWarnings({"unchecked", "rawtypes"})
178   static ComponentBuilder<?> createReferenceBuilder(ReferenceDescriptor descriptor, BeneratorContext context) {
179     SimpleTypeDescriptorweller/model/data/SimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor typeDescriptor = (SimpleTypeDescriptor) descriptor.getTypeDescriptor();
180 
181     // check uniqueness
182     boolean unique = DescriptorUtil.isUnique(descriptor, context);
183     Uniqueness uniqueness = (unique ? Uniqueness.SIMPLE : Uniqueness.NONE);
184 
185     // do I only need to generate nulls?
186     if (DescriptorUtil.isNullable(descriptor, context) && DescriptorUtil.shouldNullifyEachNullable(descriptor, context)) {
187       return builderFromGenerator(createNullGenerator(descriptor, context), descriptor, context);
188     }
189 
190     // TODO use SimpleTypeGeneratorFactory?
191     Generator<?> generator = null;
192     generator = DescriptorUtil.getGeneratorByName(typeDescriptor, context);
193     if (generator == null) {
194       generator = SimpleTypeGeneratorFactory.createScriptGenerator(typeDescriptor);
195     }
196     if (generator == null) {
197       generator = SimpleTypeGeneratorFactory.createConstantGenerator(typeDescriptor, context);
198     }
199     if (generator == null) {
200       generator = SimpleTypeGeneratorFactory.createValuesGenerator(typeDescriptor, uniqueness, context);
201     }
202 
203     // get distribution
204     Distribution distribution = FactoryUtil.getDistribution(
205         typeDescriptor.getDistribution(), descriptor.getUniqueness(), false, context);
206 
207     // check source
208     if (generator == null) {
209       // check target type
210       String targetTypeName = descriptor.getTargetType();
211       ComplexTypeDescriptoriddweller/model/data/ComplexTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor targetType = (ComplexTypeDescriptor) context.getDataModel().getTypeDescriptor(targetTypeName);
212       if (targetType == null) {
213         throw new ConfigurationError("Type not defined: " + targetTypeName);
214       }
215 
216       // check targetComponent
217       String targetComponent = descriptor.getTargetComponent();
218 
219       // check source
220       String sourceName = typeDescriptor.getSource();
221       if (sourceName == null) {
222         throw new ConfigurationError("'source' is not set for " + descriptor);
223       }
224       Object sourceObject = context.get(sourceName);
225       if (sourceObject instanceof StorageSystem) {
226         StorageSystemom/rapiddweller/benerator/StorageSystem.html#StorageSystem">StorageSystem sourceSystem = (StorageSystem) sourceObject;
227         String selector = typeDescriptor.getSelector();
228         String subSelector = typeDescriptor.getSubSelector();
229         boolean subSelect = !StringUtil.isEmpty(subSelector);
230         String selectorToUse = (subSelect ? subSelector : selector);
231         if (isIndividualSelector(selectorToUse)) {
232           generator = new DataSourceGenerator(sourceSystem.query(selectorToUse, true, context));
233         } else {
234           generator = new DataSourceGenerator(sourceSystem.queryEntityIds(
235               targetTypeName, selectorToUse, context)); // TODO v0.7.2 query by targetComponent
236           if (selectorToUse == null && distribution == null) {
237             if (context.isDefaultOneToOne()) {
238               distribution = new ExpandSequence();
239             } else {
240               distribution = SequenceManager.RANDOM_SEQUENCE;
241             }
242           }
243         }
244         if (subSelect) {
245           generator = WrapperFactory.applyHeadCycler(generator);
246         }
247       } else {
248         throw new ConfigurationError("Not a supported source type: " + sourceName);
249       }
250     }
251 
252 
253     // apply distribution if necessary
254     if (distribution != null) {
255       generator = new DistributingGenerator(generator, distribution, unique);
256     }
257 
258     // check multiplicity
259     generator = ComponentBuilderFactory.createMultiplicityWrapper(descriptor, generator, context);
260     if (LOGGER.isDebugEnabled()) {
261       LOGGER.debug("Created " + generator);
262     }
263 
264     // check 'cyclic' config
265     generator = DescriptorUtil.wrapWithProxy(generator, typeDescriptor);
266     return builderFromGenerator(generator, descriptor, context);
267   }
268 
269   /**
270    * helper method to check for selectors of individual fields like "select x from y" or
271    * "{'select x from y where id=' + z}". For such selectors it returns true, otherwise false
272    *
273    * @param selector the selector
274    * @return the boolean
275    */
276   protected static boolean isIndividualSelector(String selector) {
277     if (selector == null) {
278       return false;
279     }
280     StreamTokenizer tokenizer = new StreamTokenizer(new StringReader(selector));
281     tokenizer.ordinaryChar('\'');
282     tokenizer.ordinaryChar('"');
283     int token;
284     try {
285       while ((token = tokenizer.nextToken()) != StreamTokenizer.TT_EOF) {
286         if (token == StreamTokenizer.TT_WORD) {
287           return StringUtil.startsWithIgnoreCase(tokenizer.sval.trim(), "select");
288         }
289       }
290     } catch (IOException e) {
291       throw new RuntimeException("Unexpected error", e);
292     }
293     return false;
294   }
295 
296   // non-public helpers ----------------------------------------------------------------------------------------------
297 
298   /**
299    * Wrap with condition component builder.
300    *
301    * @param descriptor the descriptor
302    * @param builder    the builder
303    * @return the component builder
304    */
305   @SuppressWarnings({"unchecked", "rawtypes"})
306   static ComponentBuilder<?> wrapWithCondition(ComponentDescriptor descriptor, ComponentBuilder<?> builder) {
307     if (builder == null) {
308       return null;
309     }
310     TypeDescriptor typeDescriptor = descriptor.getTypeDescriptor();
311     if (typeDescriptor == null) {
312       return builder;
313     }
314     String conditionText = typeDescriptor.getCondition();
315     if (!StringUtil.isEmpty(conditionText)) {
316       Expression<Boolean> condition = new ScriptExpression<>(conditionText);
317       return new ConditionalComponentBuilder(builder, condition);
318     } else {
319       return builder;
320     }
321   }
322 
323   /**
324    * Create id builder component builder.
325    *
326    * @param id              the id
327    * @param ownerUniqueness the owner uniqueness
328    * @param context         the context
329    * @return the component builder
330    */
331   static ComponentBuilder<?> createIdBuilder(IdDescriptor id, Uniqueness ownerUniqueness, BeneratorContext context) {
332     Generator<?> generator = createSingleInstanceGenerator(id, Uniqueness.ORDERED, context);
333     if (generator != null) {
334       LOGGER.debug("Created {}", generator);
335     }
336     return builderFromGenerator(generator, id, context);
337   }
338 
339   private static ComponentBuilder<?> builderFromGenerator(
340       Generator<?> source, ComponentDescriptor descriptor, BeneratorContext context) {
341     if (source == null) {
342       return null;
343     }
344     boolean nullability = DescriptorUtil.isNullable(descriptor, context);
345     Double nullQuota = descriptor.getNullQuota();
346     if (nullQuota != null && nullQuota != 0) {
347       source = context.getGeneratorFactory().applyNullSettings(source, nullability, nullQuota);
348     }
349     TypeDescriptor typeDescriptor = descriptor.getTypeDescriptor();
350     String scope = (typeDescriptor != null ? typeDescriptor.getScope() : null);
351     if (descriptor instanceof ArrayElementDescriptor) {
352       int index = ((ArrayElementDescriptor) descriptor).getIndex();
353       return new ArrayElementBuilder(index, source, scope);
354     } else {
355       return new PlainEntityComponentBuilder(descriptor.getName(), source, scope);
356     }
357   }
358 
359   /**
360    * Create multiplicity wrapper generator.
361    *
362    * @param instance  the instance
363    * @param generator the generator
364    * @param context   the context
365    * @return the generator
366    */
367   @SuppressWarnings({"unchecked", "rawtypes"})
368   static Generator<Object> createMultiplicityWrapper(
369       ComponentDescriptor instance, Generator<?> generator, BeneratorContext context) {
370     if (generator == null) {
371       return null;
372     }
373     String container = instance.getContainer();
374     if (container == null) {
375       Generator<Long> longCountGenerator = DescriptorUtil.createDynamicCountGenerator(instance, 1L, 1L, true, context);
376       NonNullGenerator<Integer> countGenerator = WrapperFactory.asNonNullGenerator(
377           new AsIntegerGeneratorWrapper<Number>((Generator) longCountGenerator));
378       return new SimplifyingSingleSourceArrayGenerator(generator, countGenerator);
379     }
380     // handle container
381     Generator<Long> longCountGenerator;
382     if (instance.getLocalType().getSource() != null) {
383       longCountGenerator = DescriptorUtil.createDynamicCountGenerator(instance, null, null, true, context);
384     } else {
385       longCountGenerator = DescriptorUtil.createDynamicCountGenerator(instance, 1L, 1L, true, context);
386     }
387     NonNullGenerator<Integer> countGenerator = WrapperFactory.asNonNullGenerator(
388         new AsIntegerGeneratorWrapper<Number>((Generator) longCountGenerator));
389     switch (container) {
390       case "array":
391         return new SingleSourceArrayGenerator(generator, generator.getGeneratedType(), countGenerator);
392       case "list":
393         return new SingleSourceCollectionGenerator(generator, ArrayList.class, countGenerator);
394       case "set":
395         return new SingleSourceCollectionGenerator(generator, HashSet.class, countGenerator);
396       default:
397         throw new SyntaxError("Not a supported container", container);
398     }
399   }
400 
401 }