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.engine.parser.xml;
28  
29  import com.rapiddweller.benerator.BeneratorFactory;
30  import com.rapiddweller.benerator.Consumer;
31  import com.rapiddweller.benerator.Generator;
32  import com.rapiddweller.benerator.composite.GeneratorComponent;
33  import com.rapiddweller.benerator.engine.BeneratorContext;
34  import com.rapiddweller.benerator.engine.CurrentProductGeneration;
35  import com.rapiddweller.benerator.engine.ResourceManager;
36  import com.rapiddweller.benerator.engine.Statement;
37  import com.rapiddweller.benerator.engine.expression.CachedExpression;
38  import com.rapiddweller.benerator.engine.expression.xml.XMLConsumerExpression;
39  import com.rapiddweller.benerator.engine.statement.ConversionStatement;
40  import com.rapiddweller.benerator.engine.statement.GenerateAndConsumeTask;
41  import com.rapiddweller.benerator.engine.statement.GenerateOrIterateStatement;
42  import com.rapiddweller.benerator.engine.statement.LazyStatement;
43  import com.rapiddweller.benerator.engine.statement.TimedGeneratorStatement;
44  import com.rapiddweller.benerator.engine.statement.ValidationStatement;
45  import com.rapiddweller.benerator.factory.DescriptorUtil;
46  import com.rapiddweller.benerator.factory.GeneratorComponentFactory;
47  import com.rapiddweller.benerator.factory.MetaGeneratorFactory;
48  import com.rapiddweller.benerator.parser.ModelParser;
49  import com.rapiddweller.common.CollectionUtil;
50  import com.rapiddweller.common.Context;
51  import com.rapiddweller.common.Converter;
52  import com.rapiddweller.common.ErrorHandler;
53  import com.rapiddweller.common.StringUtil;
54  import com.rapiddweller.common.Validator;
55  import com.rapiddweller.common.xml.XMLUtil;
56  import com.rapiddweller.model.data.ArrayTypeDescriptor;
57  import com.rapiddweller.model.data.ComplexTypeDescriptor;
58  import com.rapiddweller.model.data.ComponentDescriptor;
59  import com.rapiddweller.model.data.DescriptorProvider;
60  import com.rapiddweller.model.data.InstanceDescriptor;
61  import com.rapiddweller.model.data.TypeDescriptor;
62  import com.rapiddweller.model.data.Uniqueness;
63  import com.rapiddweller.model.data.VariableHolder;
64  import com.rapiddweller.script.DatabeneScriptParser;
65  import com.rapiddweller.script.Expression;
66  import com.rapiddweller.script.PrimitiveType;
67  import com.rapiddweller.script.expression.DynamicExpression;
68  import com.rapiddweller.task.PageListener;
69  import org.apache.logging.log4j.LogManager;
70  import org.apache.logging.log4j.Logger;
71  import org.w3c.dom.Element;
72  
73  import java.util.ArrayList;
74  import java.util.HashSet;
75  import java.util.List;
76  import java.util.Map;
77  import java.util.Set;
78  
79  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_CONSUMER;
80  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_CONTAINER;
81  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_CONVERTER;
82  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_COUNT;
83  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_COUNT_DISTRIBUTION;
84  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_CYCLIC;
85  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_DATASET;
86  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_DISTRIBUTION;
87  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_ENCODING;
88  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_FILTER;
89  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_FORMAT;
90  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_GENERATOR;
91  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_LOCALE;
92  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_MAX_COUNT;
93  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_MIN_COUNT;
94  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_NAME;
95  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_NESTING;
96  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_NULL_QUOTA;
97  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_OFFSET;
98  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_ON_ERROR;
99  import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_PAGER;
100 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_PAGESIZE;
101 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SEGMENT;
102 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SELECTOR;
103 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SEPARATOR;
104 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SOURCE;
105 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_STATS;
106 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_SUB_SELECTOR;
107 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_TEMPLATE;
108 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_TYPE;
109 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_UNIQUE;
110 import static com.rapiddweller.benerator.engine.DescriptorConstants.ATT_VALIDATOR;
111 import static com.rapiddweller.benerator.engine.DescriptorConstants.COMPONENT_TYPES;
112 import static com.rapiddweller.benerator.engine.DescriptorConstants.CREATE_ENTITIES_EXT_SETUP;
113 import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_CONSUMER;
114 import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_GENERATE;
115 import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_ITERATE;
116 import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_VALUE;
117 import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_VARIABLE;
118 import static com.rapiddweller.benerator.parser.xml.XmlDescriptorParser.parseStringAttribute;
119 
120 /**
121  * Parses a &lt;generate&gt; or &lt;update&gt; element in a Benerator descriptor file.<br/><br/>
122  * Created: 25.10.2009 01:05:18
123  *
124  * @author Volker Bergmann
125  * @since 0.6.0
126  */
127 public class GenerateOrIterateParser extends AbstractBeneratorDescriptorParser {
128 
129   private static final Logger logger = LogManager.getLogger(GenerateOrIterateParser.class);
130 
131   private static final Set<String> OPTIONAL_ATTRIBUTES = CollectionUtil.toSet(
132       ATT_COUNT, ATT_MIN_COUNT, ATT_MAX_COUNT, ATT_COUNT_DISTRIBUTION,
133       ATT_PAGESIZE, ATT_STATS, ATT_ON_ERROR,
134       ATT_TEMPLATE, ATT_CONSUMER,
135       ATT_NAME, ATT_TYPE, ATT_CONTAINER, ATT_GENERATOR, ATT_VALIDATOR,
136       ATT_CONVERTER, ATT_NULL_QUOTA, ATT_UNIQUE, ATT_DISTRIBUTION, ATT_CYCLIC,
137       ATT_SOURCE, ATT_SEGMENT, ATT_FORMAT, ATT_OFFSET, ATT_SEPARATOR, ATT_ENCODING, ATT_SELECTOR, ATT_SUB_SELECTOR,
138       ATT_DATASET, ATT_NESTING, ATT_LOCALE, ATT_FILTER
139   );
140 
141 
142   private static final Set<String> CONSUMER_EXPECTING_ELEMENTS = CollectionUtil.toSet(EL_GENERATE, EL_ITERATE);
143 
144   // DescriptorParser interface --------------------------------------------------------------------------------------
145 
146   /**
147    * Instantiates a new Generate or iterate parser.
148    */
149   public GenerateOrIterateParser() {
150     super("", null, OPTIONAL_ATTRIBUTES);
151   }
152 
153   private static List<String> createProfilerPath(Statement./../../../com/rapiddweller/benerator/engine/Statement.html#Statement">Statement[] parentPath, Statement currentElement) {
154     List<String> path = new ArrayList<>(parentPath != null ? parentPath.length + 1 : 1);
155     if (parentPath != null) {
156       for (Statement statement : parentPath) {
157         path.add(statement.toString());
158       }
159     }
160     path.add(currentElement.toString());
161     return path;
162   }
163 
164   private static String getNameOrType(Element element) {
165     String result = element.getAttribute(ATT_NAME);
166     if (StringUtil.isEmpty(result)) {
167       result = element.getAttribute(ATT_TYPE);
168     }
169     if (StringUtil.isEmpty(result)) {
170       result = "anonymous";
171     }
172     return result;
173   }
174 
175   // private helpers -------------------------------------------------------------------------------------------------
176 
177   private static Expression<Consumer> parseConsumers(Element entityElement, boolean consumersExpected, ResourceManager resourceManager) {
178     return new CachedExpression<>(new XMLConsumerExpression(entityElement, consumersExpected, resourceManager));
179   }
180 
181   private static InstanceDescriptor mapDescriptorElement(Element element, BeneratorContext context) {
182     // TODO v0.7.1 Make Descriptors an abstraction of the XML file content and convert XML -> Descriptors -> Statements
183 
184     // evaluate type
185     String type = parseStringAttribute(element, ATT_TYPE, context, false);
186     TypeDescriptor localType;
187     DescriptorProvider localDescriptorProvider = context.getLocalDescriptorProvider();
188     if (PrimitiveType.ARRAY.getName().equals(type)
189         || XMLUtil.getChildElements(element, false, EL_VALUE).length > 0) {
190       localType = new ArrayTypeDescriptor(element.getAttribute(ATT_NAME), localDescriptorProvider);
191     } else {
192       TypeDescriptor parentType = context.getDataModel().getTypeDescriptor(type);
193       if (parentType != null) {
194         type = parentType.getName(); // take over capitalization of the parent
195         localType = new ComplexTypeDescriptorTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor(parentType.getName(), localDescriptorProvider, (ComplexTypeDescriptor) parentType);
196       } else {
197         localType = new ComplexTypeDescriptor(type, localDescriptorProvider, "entity");
198       }
199     }
200 
201     // assemble instance descriptor
202     InstanceDescriptorDescriptor.html#InstanceDescriptor">InstanceDescriptor instance = new InstanceDescriptor(type, localDescriptorProvider, type);
203     instance.setLocalType(localType);
204 
205     // map element attributes
206     for (Map.Entry<String, String> attribute : XMLUtil.getAttributes(element).entrySet()) {
207       String attributeName = attribute.getKey();
208       if (!CREATE_ENTITIES_EXT_SETUP.contains(attributeName)) {
209         Object attributeValue = attribute.getValue();
210         if (instance.supportsDetail(attributeName)) {
211           instance.setDetailValue(attributeName, attributeValue);
212         } else {
213           localType.setDetailValue(attributeName, attributeValue);
214         }
215       }
216     }
217 
218     DescriptorUtil.parseComponentConfig(element, instance.getLocalType(), context);
219     return instance;
220   }
221 
222   @Override
223   public boolean supports(Element element, Statement[] parentPath) {
224     String name = element.getNodeName();
225     return EL_GENERATE.equals(name) || EL_ITERATE.equals(name);
226   }
227 
228   @Override
229   public Statement doPStatementong class="jxr_keyword">final Element element, final Statement[] parentPath,
230                            final BeneratorParseContext pContext) {
231     final boolean looped = AbstractBeneratorDescriptorParser.containsLoop(parentPath);
232     final boolean nested = AbstractBeneratorDescriptorParser.containsGeneratorStatement(parentPath);
233     Expression<Statement> expression = new DynamicExpression<>() {
234       @Override
235       public Statement evaluate(Context context) {
236         return parseGenerate(
237             element, parentPath, pContext, (BeneratorContext) context, !looped, nested);
238       }
239 
240       @Override
241       public String toString() {
242         return XMLUtil.formatShort(element);
243       }
244     };
245     Statement statement = new LazyStatement(expression);
246     statement = new TimedGeneratorStatement(getNameOrType(element), statement, createProfilerPath(parentPath, statement), !looped);
247     return statement;
248   }
249 
250   /**
251    * Parse generate generate or iterate statement.
252    *
253    * @param element        the element
254    * @param parentPath     the parent path
255    * @param parsingContext the parsing context
256    * @param context        the context
257    * @param infoLog        the info log
258    * @param nested         the nested
259    * @return the generate or iterate statement
260    */
261   @SuppressWarnings("unchecked")
262   public GenerateOrIterateStatement parseGenerate(Element element, Statement[] parentPath,
263                                                   BeneratorParseContext parsingContext, BeneratorContext context, boolean infoLog, boolean nested) {
264     // parse descriptor
265     InstanceDescriptor descriptor = mapDescriptorElement(element, context);
266 
267     // parse statement
268     Generator<Long> countGenerator = DescriptorUtil.createDynamicCountGenerator(descriptor, 0L, 1L, false, context);
269     Expression<Long> pageSize = parsePageSize(element);
270     Expression<PageListener> pager = (Expression<PageListener>) DatabeneScriptParser.parseBeanSpec(
271         element.getAttribute(ATT_PAGER));
272     Expression<ErrorHandler> errorHandler = parseOnErrorAttribute(element, element.getAttribute(ATT_NAME));
273     Expression<Long> minCount = DescriptorUtil.getMinCount(descriptor, 0L);
274     GenerateOrIterateStatement statement = createStatement(getTaskName(descriptor), countGenerator, minCount, pageSize, pager, infoLog,
275         nested, element, errorHandler, context);
276 
277     // parse task and sub statements
278     GenerateAndConsumeTask task = parseTask(element, parentPath, statement, parsingContext, descriptor, infoLog);
279     statement.setTask(task);
280     return statement;
281   }
282 
283   /**
284    * Create statement generate or iterate statement.
285    *
286    * @param productName    the product name
287    * @param countGenerator the count generator
288    * @param minCount       the min count
289    * @param pageSize       the page size
290    * @param pager          the pager
291    * @param infoLog        the info log
292    * @param nested         the nested
293    * @param element        the element
294    * @param errorHandler   the error handler
295    * @param context        the context
296    * @return the generate or iterate statement
297    */
298   protected GenerateOrIterateStatement createStatement(String productName, Generator<Long> countGenerator,
299                                                        Expression<Long> minCount, Expression<Long> pageSize,
300                                                        Expression<PageListener> pager, boolean infoLog,
301                                                        boolean nested, Element element,
302                                                        Expression<ErrorHandler> errorHandler, BeneratorContext context) {
303     return new GenerateOrIterateStatement(productName, countGenerator, minCount, pageSize, pager,
304         errorHandler, infoLog, nested, context);
305   }
306 
307   @SuppressWarnings({"unchecked", "rawtypes"})
308   private GenerateAndConsumeTask parseTask(Element element, Statement[] parentPath, GenerateOrIterateStatement statement,
309                                            BeneratorParseContext parseContext, InstanceDescriptor descriptor, boolean infoLog) {
310     descriptor.setNullable(false);
311     if (infoLog) {
312       logger.debug("{}", descriptor);
313     }
314 
315     String taskName = getTaskName(descriptor);
316     BeneratorContext context = statement.getContext();
317     BeneratorContext childContext = statement.getChildContext();
318     String productName = getNameOrType(element);
319 
320     // calculate statements
321     List<Statement> statements = new ArrayList<>();
322 
323     // create base generator
324     Generator<?> base = MetaGeneratorFactory.createRootGenerator(descriptor, Uniqueness.NONE, context);
325     statements.add(new CurrentProductGeneration(productName, base));
326 
327     // handle sub elements
328     ModelParserparser/ModelParser.html#ModelParser">ModelParser parser = new ModelParser(childContext);
329     TypeDescriptor type = descriptor.getTypeDescriptor();
330     int arrayIndex = 0;
331     Element[] childElements = XMLUtil.getChildElements(element);
332     Set<String> handledMembers = new HashSet<>();
333     for (Element child : childElements) {
334       String childName = XMLUtil.localName(child);
335       // handle configured member/variable elements
336       InstanceDescriptor componentDescriptor = null;
337       if (EL_VARIABLE.equals(childName)) {
338         componentDescriptor = parser.parseVariable(child, (VariableHolder) type);
339       } else if (COMPONENT_TYPES.contains(childName)) {
340         componentDescriptor = parser.parseComponent(child, (ComplexTypeDescriptor) type);
341         handledMembers.add(componentDescriptor.getName().toLowerCase());
342       } else if (EL_VALUE.equals(childName)) {
343         componentDescriptor = parser.parseSimpleTypeArrayElement(child, (ArrayTypeDescriptor) type, arrayIndex++);
344       }
345       // handle non-member/variable child elements
346       if (componentDescriptor != null) {
347         GeneratorComponent<?> componentGenerator = GeneratorComponentFactory.createGeneratorComponent(
348             componentDescriptor, Uniqueness.NONE, childContext);
349         if (componentGenerator != null) {
350           statements.add(componentGenerator);
351         }
352       } else if (!EL_CONSUMER.equals(childName)) {
353         Statement[] subPath = parseContext.createSubPath(parentPath, statement);
354         Statement subStatement = parseContext.parseChildElement(child, subPath);
355         statements.add(subStatement);
356       }
357     }
358     // if element is a <generate> then add missing members defined in parent descriptors
359     if (EL_GENERATE.equals(element.getNodeName())) {
360       if (!StringUtil.isEmpty(element.getAttribute(ATT_SOURCE))) {
361         syntaxError("'source' not allowed in <generate>", element);
362       }
363       TypeDescriptor pType = type.getParent();
364       if (pType instanceof ComplexTypeDescriptor) {
365         // calculate insertion index
366         int insertionIndex = statements.size() - 1;
367         for (; insertionIndex >= 0; insertionIndex--) {
368           Statement tmp = statements.get(insertionIndex);
369           if (tmp instanceof GeneratorComponent || tmp instanceof CurrentProductGeneration) {
370             break;
371           }
372         }
373         insertionIndex++;
374         // insert generators from parent
375         ComplexTypeDescriptorom/rapiddweller/model/data/ComplexTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor parentType = (ComplexTypeDescriptor) pType;
376         for (ComponentDescriptor component : parentType.getComponents()) {
377           String componentName = component.getName();
378           if (handledMembers.contains(componentName.toLowerCase())) {
379             continue;
380           }
381           GeneratorComponent<?> componentGenerator = GeneratorComponentFactory.createGeneratorComponent(
382               component, Uniqueness.NONE, childContext);
383           statements.add(insertionIndex++, componentGenerator);
384         }
385       }
386     } else { // make sure the <iterate> does not miss a 'source'
387       if (StringUtil.isEmpty(element.getAttribute(ATT_SOURCE))) {
388         syntaxError("'source' mising in <iterate>", element);
389       }
390     }
391 
392     // create task
393     GenerateAndConsumeTask task = createTask(taskName, productName);
394     task.setStatements(statements);
395 
396     // parse converter
397     Converter converter = DescriptorUtil.getConverter(element.getAttribute(ATT_CONVERTER), context);
398     if (converter != null) {
399       task.addStatement(new ConversionStatement(BeneratorFactory.getInstance().configureConverter(converter, context)));
400     }
401 
402     // parse validator
403     Validator validator = DescriptorUtil.getValidator(element.getAttribute(ATT_VALIDATOR), context);
404     if (validator != null) {
405       task.addStatement(new ValidationStatement(BeneratorFactory.getInstance().configureValidator(validator, context)));
406     }
407 
408     // parse consumers
409     boolean consumerExpected = CONSUMER_EXPECTING_ELEMENTS.contains(element.getNodeName());
410     Expression consumer = parseConsumers(element, consumerExpected, task.getResourceManager());
411     task.setConsumer(consumer);
412 
413     return task;
414   }
415 
416   /**
417    * Gets task name.
418    *
419    * @param descriptor the descriptor
420    * @return the task name
421    */
422   protected String getTaskName(InstanceDescriptor descriptor) {
423     String taskName = descriptor.getName();
424     if (taskName == null) {
425       taskName = descriptor.getLocalType().getSource();
426     }
427     return taskName;
428   }
429 
430   /**
431    * Create task generate and consume task.
432    *
433    * @param taskName    the task name
434    * @param productName the product name
435    * @return the generate and consume task
436    */
437   protected GenerateAndConsumeTask createTask(String taskName, String productName) {
438     return new GenerateAndConsumeTask(taskName, productName);
439   }
440 
441 }