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.parser;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.engine.BeneratorContext;
31  import com.rapiddweller.common.ArrayFormat;
32  import com.rapiddweller.common.CollectionUtil;
33  import com.rapiddweller.common.ConfigurationError;
34  import com.rapiddweller.common.StringUtil;
35  import com.rapiddweller.common.SyntaxError;
36  import com.rapiddweller.common.converter.ToStringConverter;
37  import com.rapiddweller.common.xml.XMLUtil;
38  import com.rapiddweller.model.data.ArrayElementDescriptor;
39  import com.rapiddweller.model.data.ArrayTypeDescriptor;
40  import com.rapiddweller.model.data.ComplexTypeDescriptor;
41  import com.rapiddweller.model.data.ComponentDescriptor;
42  import com.rapiddweller.model.data.DescriptorProvider;
43  import com.rapiddweller.model.data.Entity;
44  import com.rapiddweller.model.data.EntitySource;
45  import com.rapiddweller.model.data.IdDescriptor;
46  import com.rapiddweller.model.data.InstanceDescriptor;
47  import com.rapiddweller.model.data.PartDescriptor;
48  import com.rapiddweller.model.data.ReferenceDescriptor;
49  import com.rapiddweller.model.data.SimpleTypeDescriptor;
50  import com.rapiddweller.model.data.TypeDescriptor;
51  import com.rapiddweller.model.data.VariableDescriptor;
52  import com.rapiddweller.model.data.VariableHolder;
53  import com.rapiddweller.script.expression.ConstantExpression;
54  import org.w3c.dom.Attr;
55  import org.w3c.dom.Element;
56  import org.w3c.dom.NamedNodeMap;
57  
58  import java.util.Map;
59  import java.util.Set;
60  
61  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_ATTRIBUTE;
62  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_ID;
63  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_PART;
64  import static com.rapiddweller.benerator.engine.DescriptorConstants.EL_REFERENCE;
65  import static com.rapiddweller.benerator.parser.xml.XmlDescriptorParser.parseStringAttribute;
66  import static com.rapiddweller.benerator.parser.xml.XmlDescriptorParser.resolveScript;
67  
68  /**
69   * Parses databene model files.<br/><br/>
70   * Created: 04.03.2008 16:43:09
71   *
72   * @author Volker Bergmann
73   * @since 0.5.0
74   */
75  public class ModelParser {
76  
77    private static final Set<String> SIMPLE_TYPE_COMPONENTS = CollectionUtil.toSet(EL_ATTRIBUTE, EL_REFERENCE, EL_ID);
78  
79    private final BeneratorContext context;
80    private final DescriptorProvider descriptorProvider;
81  
82    /**
83     * Instantiates a new Model parser.
84     *
85     * @param context the context
86     */
87    public ModelParser(BeneratorContext context) {
88      this.context = context;
89      this.descriptorProvider = context.getLocalDescriptorProvider();
90    }
91  
92    /**
93     * Parse component component descriptor.
94     *
95     * @param element the element
96     * @param owner   the owner
97     * @return the component descriptor
98     */
99    public ComponentDescriptor parseComponent(Element element, ComplexTypeDescriptor owner) {
100     String elementName = XMLUtil.localName(element);
101     if (EL_PART.equals(elementName)) {
102       return parsePart(element, owner, null);
103     } else if (SIMPLE_TYPE_COMPONENTS.contains(elementName)) {
104       return parseSimpleTypeComponent(element, owner, null);
105     } else {
106       throw new ConfigurationError("Expected one of these element names: " +
107           EL_ATTRIBUTE + ", " + EL_ID + ", " + EL_REFERENCE + ", or " + EL_PART + ". Found: " + elementName);
108     }
109   }
110 
111   /**
112    * Parse simple type component component descriptor.
113    *
114    * @param element   the element
115    * @param owner     the owner
116    * @param component the component
117    * @return the component descriptor
118    */
119   public ComponentDescriptor parseSimpleTypeComponent(
120       Element element, ComplexTypeDescriptor owner, ComponentDescriptor component) {
121     String name = XMLUtil.localName(element);
122     if (EL_ATTRIBUTE.equals(name)) {
123       return parseAttribute(element, owner, component);
124     } else if (EL_ID.equals(name)) {
125       return parseId(element, owner, component);
126     } else if (EL_REFERENCE.equals(name)) {
127       return parseReference(element, owner, component);
128     } else {
129       throw new ConfigurationError("Expected one of these element names: " +
130           EL_ATTRIBUTE + ", " + EL_ID + " or " + EL_REFERENCE + ". Found: " + name);
131     }
132   }
133 
134   /**
135    * Parse complex type complex type descriptor.
136    *
137    * @param ctElement  the ct element
138    * @param descriptor the descriptor
139    * @return the complex type descriptor
140    */
141   public ComplexTypeDescriptoromplexTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor parseComplexType(Element ctElement, ComplexTypeDescriptor descriptor) {
142     assertElementName(ctElement, "entity", "type");
143     descriptor = new ComplexTypeDescriptor(descriptor.getName(), descriptorProvider, descriptor);
144     mapTypeDetails(ctElement, descriptor);
145     for (Element child : XMLUtil.getChildElements(ctElement)) {
146       parseComplexTypeChild(child, descriptor);
147     }
148     return descriptor;
149   }
150 
151   /**
152    * Parse complex type child.
153    *
154    * @param element    the element
155    * @param descriptor the descriptor
156    */
157   public void parseComplexTypeChild(Element element, ComplexTypeDescriptor descriptor) {
158     String childName = XMLUtil.localName(element);
159     if ("variable".equals(childName)) {
160       parseVariable(element, descriptor);
161     } else {
162       throw new UnsupportedOperationException("element type not supported here: " + childName);
163     }
164   }
165 
166   /**
167    * Parse attribute part descriptor.
168    *
169    * @param element    the element
170    * @param owner      the owner
171    * @param descriptor the descriptor
172    * @return the part descriptor
173    */
174   public PartDescriptor parseAttribute(Element element, ComplexTypeDescriptor owner, ComponentDescriptor descriptor) {
175     assertElementName(element, "attribute");
176     PartDescriptor result;
177     if (descriptor != null) {
178       result = new PartDescriptor(descriptor.getName(), descriptorProvider, descriptor.getType());
179     } else {
180       String typeName = StringUtil.emptyToNull(element.getAttribute("type"));
181       result = new PartDescriptor(element.getAttribute("name"), descriptorProvider, typeName);
182     }
183     mapInstanceDetails(element, false, result);
184     applyDefaultCounts(result);
185     if (owner != null) {
186       ComponentDescriptor parentComponent = owner.getComponent(result.getName());
187       if (parentComponent != null) {
188         TypeDescriptor parentType = parentComponent.getTypeDescriptor();
189         result.getLocalType(false).setParent(parentType);
190       }
191       owner.addComponent(result);
192     }
193     return result;
194   }
195 
196   /**
197    * Parse part part descriptor.
198    *
199    * @param element    the element
200    * @param owner      the owner
201    * @param descriptor the descriptor
202    * @return the part descriptor
203    */
204   public PartDescriptor parsePart(Element element, ComplexTypeDescriptor owner, ComponentDescriptor descriptor) {
205     assertElementName(element, "part");
206     PartDescriptor result;
207     if (descriptor instanceof PartDescriptor) {
208       result = (PartDescriptor) descriptor;
209     } else if (descriptor != null) {
210       result = new PartDescriptor(descriptor.getName(), descriptorProvider, descriptor.getType());
211     } else {
212       String typeName = StringUtil.emptyToNull(element.getAttribute("type"));
213       String partName = element.getAttribute("name");
214       String localTypeName = owner.getName() + "." + partName;
215       if (typeName != null) {
216         result = new PartDescriptor(partName, descriptorProvider, typeName);
217       } else if (element.getNodeName().equals("part")) {
218         result = new PartDescriptor(partName, descriptorProvider,
219             new ComplexTypeDescriptoromplexTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor(localTypeName, descriptorProvider, (ComplexTypeDescriptor) null));
220       } else {
221         result = new PartDescriptor(partName, descriptorProvider,
222             new SimpleTypeDescriptorSimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor(localTypeName, descriptorProvider, (SimpleTypeDescriptor) null));
223       }
224     }
225     mapInstanceDetails(element, true, result);
226     if (result.getLocalType().getSource() == null) {
227       applyDefaultCounts(result);
228     }
229     if (owner != null) {
230       ComponentDescriptor parentComponent = owner.getComponent(result.getName());
231       if (parentComponent != null) {
232         TypeDescriptor parentType = parentComponent.getTypeDescriptor();
233         result.getLocalType(false).setParent(parentType);
234       }
235       owner.addComponent(result);
236     }
237     for (Element childElement : XMLUtil.getChildElements(element)) {
238       parseComponent(childElement, (ComplexTypeDescriptor) result.getLocalType(true));
239     }
240     return result;
241   }
242 
243   /**
244    * Apply default counts.
245    *
246    * @param descriptor the descriptor
247    */
248   public void applyDefaultCounts(PartDescriptor descriptor) {
249     if (descriptor.getDeclaredDetailValue("minCount") == null) {
250       descriptor.setMinCount(new ConstantExpression<>(1L));
251     }
252     if (descriptor.getDeclaredDetailValue("maxCount") == null) {
253       descriptor.setMaxCount(new ConstantExpression<>(1L));
254     }
255   }
256 
257 //    public SimpleTypeDescriptor parseSimpleType(Element element) {
258 //        assertElementName(element, "type");
259 //        return parseSimpleType(element, new SimpleTypeDescriptor(null, descriptorProvider, (String) null));
260 //    }
261 
262   /**
263    * Parse simple type simple type descriptor.
264    *
265    * @param element    the element
266    * @param descriptor the descriptor
267    * @return the simple type descriptor
268    */
269   public SimpleTypeDescriptorta/SimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor parseSimpleType(Element element, SimpleTypeDescriptor descriptor) {
270     assertElementName(element, "type");
271     return mapTypeDetails(element, descriptor);
272   }
273 
274   /**
275    * Parse variable instance descriptor.
276    *
277    * @param varElement the var element
278    * @param owner      the owner
279    * @return the instance descriptor
280    */
281   public InstanceDescriptor parseVariable(Element varElement, VariableHolder owner) {
282     assertElementName(varElement, "variable");
283     String type = StringUtil.emptyToNull(varElement.getAttribute("type"));
284     VariableDescriptoror.html#VariableDescriptor">VariableDescriptor descriptor = new VariableDescriptor(varElement.getAttribute("name"), descriptorProvider, type);
285     VariableDescriptor variable = mapInstanceDetails(varElement, false, descriptor);
286     owner.addVariable(variable);
287     return variable;
288   }
289 
290   /**
291    * Parse simple type array element array element descriptor.
292    *
293    * @param element the element
294    * @param owner   the owner
295    * @param index   the index
296    * @return the array element descriptor
297    */
298   public ArrayElementDescriptor parseSimpleTypeArrayElement(Element element, ArrayTypeDescriptor owner, int index) {
299     ArrayElementDescriptoror.html#ArrayElementDescriptor">ArrayElementDescriptor descriptor = new ArrayElementDescriptor(index, descriptorProvider, element.getAttribute("name"));
300     mapInstanceDetails(element, false, descriptor);
301     owner.addElement(descriptor);
302     return descriptor;
303   }
304 
305   // private helpers -------------------------------------------------------------------------------------------------
306 
307   private <T extends TypeDescriptor> T mapTypeDetails(Element element, T descriptor) {
308     NamedNodeMap attributes = element.getAttributes();
309     for (int i = 0; i < attributes.getLength(); i++) {
310       Attr attr = (Attr) attributes.item(i);
311       String detailValue = parseStringAttribute(attr, context);
312       descriptor.setDetailValue(attr.getName(), detailValue);
313     }
314     return descriptor;
315   }
316 
317   private <T extends InstanceDescriptor> T mapInstanceDetails(
318       Element element, boolean complexType, T descriptor) {
319     TypeDescriptor localType = descriptor.getLocalType();
320     Map<String, String> attributes = XMLUtil.getAttributes(element);
321     for (Map.Entry<String, String> entry : attributes.entrySet()) {
322       String detailName = entry.getKey();
323       if (detailName.equals("type")) {
324         continue;
325       }
326       Object tmp = resolveScript(detailName, entry.getValue(), context);
327       String detailString = ToStringConverter.convert(tmp, null);
328       if (descriptor.supportsDetail(detailName)) {
329         try {
330           descriptor.setDetailValue(detailName, detailString);
331         } catch (IllegalArgumentException e) {
332           throw new SyntaxError("Error parsing '" + detailName + "'", e, String.valueOf(detailString), -1, -1);
333         }
334       } else {
335         if (localType == null) {
336           String partType = attributes.get("type");
337           if (partType == null) {
338             partType = descriptor.getType();
339           }
340           if (partType == null) {
341             String sourceSpec = attributes.get("source");
342             if (sourceSpec != null) {
343               Object source = context.get(sourceSpec);
344               if (source != null) {
345                 if (source instanceof Generator) {
346                   if (((Generator<?>) source).getGeneratedType() == Entity.class) {
347                     partType = "entity";
348                   }
349                 } else if (source instanceof EntitySource) {
350                   partType = "entity";
351                 }
352               } else {
353                 String lcSourceSpec = sourceSpec.toLowerCase();
354                 if (lcSourceSpec.endsWith(".ent.csv")
355                     || lcSourceSpec.endsWith(".ent.fcw")
356                     || lcSourceSpec.endsWith(".dbunit.xml")) {
357                   partType = "entity";
358                 }
359               }
360             }
361           }
362           if (partType != null) {
363             TypeDescriptor localTypeParent = context.getDataModel().getTypeDescriptor(partType);
364             localType = (localTypeParent instanceof ComplexTypeDescriptor ?
365                 new ComplexTypeDescriptor(partType, descriptorProvider, partType) :
366                 new SimpleTypeDescriptor(partType, descriptorProvider, partType));
367           }
368           descriptor.setLocalType(localType);
369         }
370         if (localType == null) {
371           localType = descriptor.getLocalType(complexType); // create new local type
372         }
373         localType.setDetailValue(detailName, detailString);
374       }
375     }
376     return descriptor;
377   }
378 
379   private static void assertElementName(Element element, String... expectedNames) {
380     String elementName = XMLUtil.localName(element);
381     for (String expectedName : expectedNames) {
382       if (elementName.equals(expectedName)) {
383         return;
384       }
385     }
386     String message;
387     if (expectedNames.length == 1) {
388       message = "Expected element '" + expectedNames[0] + "', found: " + elementName;
389     } else {
390       message = "Expected one of these element names: '" + ArrayFormat.format(expectedNames) + "', " +
391           "found: " + elementName;
392     }
393     throw new IllegalArgumentException(message);
394   }
395 
396   private IdDescriptor parseId(Element element, ComplexTypeDescriptor owner, ComponentDescriptor descriptor) {
397     assertElementName(element, "id");
398     IdDescriptor result;
399     IdDescriptor resultTmp;
400     if (descriptor instanceof IdDescriptor) {
401       resultTmp = (IdDescriptor) descriptor;
402     } else if (descriptor != null) {
403       resultTmp = new IdDescriptor(descriptor.getName(), descriptorProvider, descriptor.getType());
404     } else {
405       resultTmp = new IdDescriptor(element.getAttribute("name"), descriptorProvider, element.getAttribute("type"));
406     }
407     result = mapInstanceDetails(element, false, resultTmp);
408     if (owner != null) {
409       ComponentDescriptor parentComponent = owner.getComponent(result.getName());
410       if (parentComponent != null) {
411         TypeDescriptor parentType = parentComponent.getTypeDescriptor();
412         result.getLocalType(false).setParent(parentType);
413       }
414       owner.addComponent(result);
415     }
416     return result;
417   }
418 
419   private ReferenceDescriptor parseReference(Element element, ComplexTypeDescriptor owner, ComponentDescriptor component) {
420     assertElementName(element, "reference");
421     ReferenceDescriptor result;
422     if (component instanceof ReferenceDescriptor) {
423       result = (ReferenceDescriptor) component;
424     } else if (component != null) {
425       result = new ReferenceDescriptor(component.getName(), descriptorProvider, component.getType());
426     } else {
427       result = new ReferenceDescriptor(element.getAttribute("name"), descriptorProvider, StringUtil.emptyToNull(element.getAttribute("type")));
428     }
429     if (owner != null) {
430       ComponentDescriptor parentComponent = owner.getComponent(result.getName());
431       if (parentComponent != null) {
432         TypeDescriptor parentType = parentComponent.getTypeDescriptor();
433         result.getLocalType(false).setParent(parentType);
434       }
435       owner.addComponent(result);
436     }
437     return mapInstanceDetails(element, false, result);
438   }
439 
440 
441 }