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.platform.xml;
28  
29  import com.rapiddweller.benerator.engine.BeneratorContext;
30  import com.rapiddweller.benerator.engine.ResourceManager;
31  import com.rapiddweller.benerator.engine.ResourceManagerSupport;
32  import com.rapiddweller.benerator.engine.parser.xml.BeanParser;
33  import com.rapiddweller.benerator.engine.parser.xml.BeneratorParseContext;
34  import com.rapiddweller.benerator.engine.parser.xml.IncludeParser;
35  import com.rapiddweller.benerator.engine.statement.BeanStatement;
36  import com.rapiddweller.benerator.engine.statement.IncludeStatement;
37  import com.rapiddweller.benerator.parser.ModelParser;
38  import com.rapiddweller.common.Assert;
39  import com.rapiddweller.common.BeanUtil;
40  import com.rapiddweller.common.CollectionUtil;
41  import com.rapiddweller.common.ConfigurationError;
42  import com.rapiddweller.common.Context;
43  import com.rapiddweller.common.IOUtil;
44  import com.rapiddweller.common.StringUtil;
45  import com.rapiddweller.common.context.ContextAware;
46  import com.rapiddweller.common.xml.XMLUtil;
47  import com.rapiddweller.model.data.AlternativeGroupDescriptor;
48  import com.rapiddweller.model.data.ComplexTypeDescriptor;
49  import com.rapiddweller.model.data.ComponentDescriptor;
50  import com.rapiddweller.model.data.DataModel;
51  import com.rapiddweller.model.data.DefaultDescriptorProvider;
52  import com.rapiddweller.model.data.FeatureDescriptor;
53  import com.rapiddweller.model.data.InstanceDescriptor;
54  import com.rapiddweller.model.data.Mode;
55  import com.rapiddweller.model.data.PartDescriptor;
56  import com.rapiddweller.model.data.SimpleTypeDescriptor;
57  import com.rapiddweller.model.data.TypeDescriptor;
58  import com.rapiddweller.model.data.UnionSimpleTypeDescriptor;
59  import com.rapiddweller.model.data.UnresolvedTypeDescriptor;
60  import com.rapiddweller.script.Expression;
61  import com.rapiddweller.script.PrimitiveType;
62  import com.rapiddweller.script.expression.ConstantExpression;
63  import org.apache.logging.log4j.LogManager;
64  import org.apache.logging.log4j.Logger;
65  import org.w3c.dom.Document;
66  import org.w3c.dom.Element;
67  
68  import java.io.Closeable;
69  import java.io.IOException;
70  import java.util.HashMap;
71  import java.util.Map;
72  import java.util.Set;
73  
74  import static com.rapiddweller.common.xml.XMLUtil.getNamespaces;
75  import static com.rapiddweller.common.xml.XMLUtil.getTargetNamespace;
76  import static com.rapiddweller.common.xml.XMLUtil.localName;
77  import static com.rapiddweller.common.xml.XMLUtil.normalizedAttributeValue;
78  import static com.rapiddweller.common.xml.XMLUtil.parse;
79  import static com.rapiddweller.script.expression.ExpressionUtil.constant;
80  
81  /**
82   * Parses an XML schema file into a benerator metadata structure.<br/>
83   * <br/>
84   * Created: 27.02.2008 09:40:45
85   *
86   * @author Volker Bergmann
87   * @since 0.5.0
88   */
89  public class XMLSchemaDescriptorProvider extends DefaultDescriptorProvider implements ContextAware, ResourceManager {
90  
91    /**
92     * The constant INCLUDE.
93     */
94    public static final String INCLUDE = "include";
95    /**
96     * The constant IMPORT.
97     */
98    public static final String IMPORT = "import";
99    /**
100    * The constant SIMPLE_TYPE.
101    */
102   public static final String SIMPLE_TYPE = "simpleType";
103   /**
104    * The constant COMPLEX_TYPE.
105    */
106   public static final String COMPLEX_TYPE = "complexType";
107   /**
108    * The constant ANNOTATION.
109    */
110   public static final String ANNOTATION = "annotation";
111   /**
112    * The constant SEQUENCE.
113    */
114   public static final String SEQUENCE = "sequence";
115   /**
116    * The constant CHOICE.
117    */
118   public static final String CHOICE = "choice";
119   /**
120    * The constant EXTENSION.
121    */
122   public static final String EXTENSION = "extension";
123   /**
124    * The constant UNION.
125    */
126   public static final String UNION = "union";
127   /**
128    * The constant ELEMENT.
129    */
130   public static final String ELEMENT = "element";
131   /**
132    * The constant GROUP.
133    */
134   public static final String GROUP = "group";
135   /**
136    * The constant ATTRIBUTE_GROUP.
137    */
138   public static final String ATTRIBUTE_GROUP = "attributeGroup";
139   /**
140    * The constant NAME.
141    */
142   public static final String NAME = "name";
143   /**
144    * The constant RESTRICTION.
145    */
146   public static final String RESTRICTION = "restriction";
147   /**
148    * The constant BASE.
149    */
150   public static final String BASE = "base";
151   /**
152    * The constant VALUE.
153    */
154   public static final String VALUE = "value";
155   /**
156    * The constant LENGTH.
157    */
158   public static final String LENGTH = "length";
159   /**
160    * The constant MIN_INCLUSIVE.
161    */
162   public static final String MIN_INCLUSIVE = "minInclusive";
163   /**
164    * The constant MIN_EXCLUSIVE.
165    */
166   public static final String MIN_EXCLUSIVE = "minExclusive";
167   /**
168    * The constant MAX_EXCLUSIVE.
169    */
170   public static final String MAX_EXCLUSIVE = "maxExclusive";
171   /**
172    * The constant MAX_INCLUSIVE.
173    */
174   public static final String MAX_INCLUSIVE = "maxInclusive";
175   /**
176    * The constant ENUMERATION.
177    */
178   public static final String ENUMERATION = "enumeration";
179   private static final String SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
180   private static final String TYPE = "type";
181   private static final String REF = "ref";
182   private static final String KEY = "key";
183   private static final String KEYREF = "keyref";
184   private static final String UNIQUE = "unique";
185   private static final String ALL = "all";
186   private static final String COMPLEX_CONTENT = "complexContent";
187   private static final String SIMPLE_CONTENT = "simpleContent";
188   private static final String ATTRIBUTE = "attribute";
189   private static final Logger LOGGER = LogManager.getLogger(XMLSchemaDescriptorProvider.class);
190 
191   // attributes ------------------------------------------------------------------------------------------------------
192   private BeneratorContext context;
193   private String schemaUri;
194 
195   private final ModelParser parser;
196   private Map<String, String> namespaces;
197   private final ResourceManager resourceManager = new ResourceManagerSupport();
198 
199 
200   // constructors ----------------------------------------------------------------------------------------------------
201 
202   /**
203    * Instantiates a new Xml schema descriptor provider.
204    *
205    * @param schemaUri the schema uri
206    * @param context   the context
207    */
208   public XMLSchemaDescriptorProvider(String schemaUri, BeneratorContext context) {
209     super(schemaUri, context.getDataModel(), true);
210     new XMLNativeTypeDescriptorProvider(SCHEMA_NAMESPACE, dataModel);
211     this.namespaces = new HashMap<>();
212     parser = new ModelParser(context);
213     setContext(context);
214     setSchemaUri(schemaUri);
215   }
216 
217   // interface -------------------------------------------------------------------------------------------------------
218 
219   private static void parseOccurrences(Element element, InstanceDescriptor descriptor) {
220     Long minOccurs = XMLUtil.getLongAttribute(element, "minOccurs", 1L);
221     String maxOccursString = element.getAttribute("maxOccurs");
222     Long maxOccurs = 1L;
223     if (!StringUtil.isEmpty(maxOccursString)) {
224       maxOccurs = ("unbounded".equals(maxOccursString) ? null : Long.parseLong(maxOccursString));
225     }
226     if (minOccurs.equals(maxOccurs) && descriptor.getCount() != null) {
227       descriptor.setCount(constant(maxOccurs));
228       descriptor.setMinCount(null);
229       descriptor.setMaxCount(null);
230     } else {
231       descriptor.setCount(null);
232       if (descriptor.getMinCount() == null) {
233         descriptor.setMinCount(constant(minOccurs));
234       }
235       if (descriptor.getMaxCount() == null) {
236         descriptor.setMaxCount(constant(maxOccurs));
237       }
238     }
239   }
240 
241   private static void parseRestrictionChildren(Element restriction,
242                                                SimpleTypeDescriptor descriptor) {
243     Element[] children = XMLUtil.getChildElements(restriction);
244     for (Element child : children) {
245       String nodeName = localName(child);
246       String value = child.getAttribute(VALUE);
247       if (ENUMERATION.equals(nodeName)) {
248         if (PrimitiveType.STRING.equals(descriptor.getPrimitiveType())) {
249           descriptor.addValue("'" + value + "'");
250         } else {
251           descriptor.addValue(value);
252         }
253       } else if (MIN_INCLUSIVE.equals(nodeName)) {
254         descriptor.setMin(value);
255         descriptor.setMinInclusive(true);
256       } else if (MIN_EXCLUSIVE.equals(nodeName)) {
257         descriptor.setMin(value);
258         descriptor.setMinInclusive(false);
259       } else if (MAX_INCLUSIVE.equals(nodeName)) {
260         descriptor.setMax(value);
261         descriptor.setMaxInclusive(true);
262       } else if (MAX_EXCLUSIVE.equals(nodeName)) {
263         descriptor.setMax(value);
264         descriptor.setMaxInclusive(false);
265       } else if (LENGTH.equals(nodeName)) {
266         int length = Integer.parseInt(value);
267         descriptor.setMinLength(length);
268         descriptor.setMaxLength(length);
269       } else if (BeanUtil.hasProperty(descriptor.getClass(), nodeName)) {
270         BeanUtil.setPropertyValue(descriptor, nodeName, value, false);
271       } else {
272         LOGGER.warn("Ignoring restriction " + nodeName + ": " + value);
273       }
274     }
275   }
276 
277   private static UnsupportedOperationException unsupportedElementType(Element element, Element parent) {
278     String message = "Element type " + element.getNodeName() + " not supported";
279     if (parent != null) {
280       message += " in " + parent.getNodeName();
281     }
282     return new UnsupportedOperationException(message);
283   }
284 
285   // ResourceManager interface implementation ------------------------------------------------------------------------
286 
287   /**
288    * Sets schema uri.
289    *
290    * @param schemaUri the schema uri
291    */
292   public void setSchemaUri(String schemaUri) {
293     this.schemaUri = schemaUri;
294     checkSchema();
295   }
296 
297   /**
298    * Gets context.
299    *
300    * @return the context
301    */
302   public BeneratorContext getContext() {
303     return context;
304   }
305 
306   // private helpers -------------------------------------------------------------------------------------------------
307 
308   @Override
309   public void setContext(Context context) {
310     this.context = (BeneratorContext) context;
311     checkSchema();
312   }
313 
314   @Override
315   public boolean addResource(Closeable resource) {
316     return resourceManager.addResource(resource);
317   }
318 
319   @Override
320   public void close() {
321     resourceManager.close();
322   }
323 
324   private void checkSchema() {
325     if (!StringUtil.isEmpty(schemaUri) && context != null) {
326       try {
327         Document document = parse(schemaUri);
328         this.namespaces = getNamespaces(document);
329         this.id = getTargetNamespace(document);
330         dataModel.addDescriptorProvider(this);
331         parseStructure(document);
332         parseDetails(document);
333       } catch (IOException e) {
334         throw new ConfigurationError("Error parsing schemaUri: " + schemaUri, e);
335       }
336     }
337   }
338 
339   private void parseStructure(Document document) throws IOException {
340     LOGGER.debug("parseStructure()");
341     Element root = document.getDocumentElement();
342     Element[] childElements = XMLUtil.getChildElements(root);
343     for (Element element : childElements) {
344       String nodeName = localName(element);
345       String nameAttribute = element.getAttribute("name");
346       Set<String> COMPLEX_ELEMENTS = CollectionUtil.toSet(COMPLEX_TYPE, GROUP, ATTRIBUTE_GROUP);
347       if (COMPLEX_ELEMENTS.contains(nodeName)) {
348         addTypeDescriptor(new ComplexTypeDescriptor(nameAttribute, this));
349       } else if (SIMPLE_TYPE.equals(nodeName)) {
350         addTypeDescriptor(new SimpleTypeDescriptor(nameAttribute, this));
351       } else if (ELEMENT.equals(nodeName)) {
352         String typeName = element.getAttribute("type");
353         if (!StringUtil.isEmpty(typeName)) {
354           TypeDescriptor elementType = dataModel.getTypeDescriptor(typeName);
355           if (elementType instanceof SimpleTypeDescriptor) {
356             addTypeDescriptor(new SimpleTypeDescriptor(nameAttribute, this));
357           } else if (elementType instanceof ComplexTypeDescriptor) {
358             addTypeDescriptor(new ComplexTypeDescriptor(nameAttribute, this));
359           } else {
360             addTypeDescriptor(new UnresolvedTypeDescriptor(nameAttribute, this, typeName));
361           }
362         } else if (XMLUtil.getChildElements(element, false, "complexType").length > 0) {
363           addTypeDescriptor(new ComplexTypeDescriptor(nameAttribute, this));
364         } else if (XMLUtil.getChildElements(element, false, SIMPLE_TYPE).length > 0) {
365           addTypeDescriptor(new SimpleTypeDescriptor(nameAttribute, this));
366         } else {
367           addTypeDescriptor(new ComplexTypeDescriptor(nameAttribute, this));
368         }
369       } else if (ANNOTATION.equals(nodeName)) {
370         parseDocumentAnnotation(element);
371       } else if (IMPORT.equals(nodeName)) {
372         parseImport(element);
373       } else if (INCLUDE.equals(nodeName)) {
374         parseStructureOfInclude(element);
375       }
376     }
377     resolveTypes();
378   }
379 
380   private void resolveTypes() {
381     boolean unresolved = false;
382     do {
383       for (TypeDescriptor type : typeMap.values()) {
384         if (type instanceof UnresolvedTypeDescriptor) {
385           TypeDescriptor parent = type.getParent();
386           if (parent instanceof SimpleTypeDescriptor) {
387             addTypeDescriptor(new SimpleTypeDescriptor(type.getName(), this, type.getParentName()));
388           } else if (parent instanceof ComplexTypeDescriptor) {
389             addTypeDescriptor(new ComplexTypeDescriptor(type.getName(), this, type.getParentName()));
390           } else if (parent == null) {
391             throw new ConfigurationError("parentType " + type.getParentName() + " not found for " + type.getName());
392           } else {
393             unresolved = true;
394           }
395 
396         }
397       }
398     } while (unresolved);
399   }
400 
401   private void parseDetails(Document document) throws IOException {
402     LOGGER.debug("parseDetails()");
403     Element root = document.getDocumentElement();
404     Element[] childElements = XMLUtil.getChildElements(root);
405     for (Element element : childElements) {
406       String nodeName = localName(element);
407       if (ELEMENT.equals(nodeName)) {
408         parseTopLevelElement(element);
409       } else if (COMPLEX_TYPE.equals(nodeName)) {
410         parseComplexType(element, null, null, true);
411       } else if (SIMPLE_TYPE.equals(nodeName)) {
412         addTypeDescriptor(parseSimpleType(null, element));
413       } else if (GROUP.equals(nodeName)) {
414         parseGroup(element);
415       } else if (ATTRIBUTE_GROUP.equals(nodeName)) {
416         parseAttributeGroup(element);
417       } else if (IMPORT.equals(nodeName)) {
418         parseImport(element);
419       } else if (INCLUDE.equals(nodeName)) {
420         parseDetailsOfInclude(element);
421       } else if (!ANNOTATION.equals(nodeName)) {
422         throw unsupportedElementType(element, root);
423       }
424     }
425   }
426 
427   private void parseDocumentAnnotation(Element element) {
428     Annotationtion.html#Annotation">Annotation annotation = new Annotation(element);
429     Element appInfo = annotation.getAppInfo();
430     if (appInfo == null) {
431       return;
432     }
433     for (Element child : XMLUtil.getChildElements(appInfo)) {
434       String childName = XMLUtil.localName(child);
435       if (INCLUDE.equals(childName)) {
436         IncludeStatementom/rapiddweller/benerator/engine/statement/IncludeStatement.html#IncludeStatement">IncludeStatement statement = (IncludeStatement) new IncludeParser().parse(child, null, new BeneratorParseContext(this));
437         statement.execute(context);
438       } else if ("bean".equals(childName)) {
439         Expression<?> constructionExpression = BeanParser.parseBeanExpression(child);
440         String id = child.getAttribute("id");
441         BeanStatementt/BeanStatement.html#BeanStatement">BeanStatement beanStatement = new BeanStatement(id, constructionExpression, this);
442         try {
443           beanStatement.execute(context);
444         } finally {
445           IOUtil.close(beanStatement);
446         }
447       } else {
448         throw new UnsupportedOperationException("Document annotation type not supported: "
449             + child.getNodeName());
450       }
451     }
452   }
453 
454   private ComplexTypeDescriptor parseComplexType(Element complexTypeElement, String parentName, Annotation annotationBefore, boolean global) {
455     String name = (parentName != null ? parentName : complexTypeElement.getAttribute(NAME));
456     LOGGER.debug("parseComplexType({})", name);
457     if (name == null) {
458       throw new ConfigurationError("unnamed complex type");
459     }
460     ComplexTypeDescriptoror.html#ComplexTypeDescriptor">ComplexTypeDescriptor descriptor = new ComplexTypeDescriptor(name, this);
461     if (annotationBefore != null) {
462       descriptor = parseElementAppInfo(descriptor, annotationBefore);
463     }
464     Annotation annotation = null;
465     Element[] children = XMLUtil.getChildElements(complexTypeElement);
466     for (Element child : children) {
467       String nodeName = localName(child);
468       if ("annotation".equals(nodeName)) {
469         annotation = new Annotation(child);
470       } else if (SEQUENCE.equals(nodeName)) {
471         parseSequence(child, descriptor);
472       } else if (COMPLEX_CONTENT.equals(nodeName)) {
473         parseComplexContent(child, descriptor);
474       } else if (ALL.equals(nodeName)) {
475         parseAll(child, descriptor);
476       } else if (SIMPLE_CONTENT.equals(nodeName)) {
477         parseSimpleContent(child, descriptor);
478       } else if (ATTRIBUTE.equals(nodeName)) {
479         parseAttribute(child, descriptor);
480       } else if (ATTRIBUTE_GROUP.equals(nodeName)) {
481         ComplexTypeDescriptor group = parseAttributeGroup(child);
482         for (InstanceDescriptor component : group.getParts()) {
483           descriptor.addPart(component);
484         }
485       } else {
486         throw unsupportedElementType(child, complexTypeElement);
487       }
488     }
489     descriptor = parseComplexTypeAppinfo(descriptor, annotation);
490     if (global) {
491       addTypeDescriptor(descriptor);
492     }
493     return descriptor;
494   }
495 
496   private ComplexTypeDescriptor parseComplexTypeAppinfo(
497       ComplexTypeDescriptor descriptor, Annotation annotation) {
498     if (annotation == null || annotation.getAppInfo() == null) {
499       return descriptor;
500     }
501 
502     Element appInfo = annotation.getAppInfo();
503     Element[] infos = XMLUtil.getChildElements(appInfo);
504     if (infos.length > 1) {
505       throw new ConfigurationError("Cannot handle more than one appinfo in a complex type");
506     }
507     Element info = infos[0];
508 
509     parser.parseComplexTypeChild(info, descriptor);
510     return descriptor;
511   }
512 
513   private void parseComplexContent(Element complexContent, ComplexTypeDescriptor owner) {
514     Element[] children = XMLUtil.getChildElements(complexContent);
515     for (Element child : children) {
516       String nodeName = localName(child);
517       if (EXTENSION.equals(nodeName)) {
518         parseExtension(child, owner);
519       } else if (RESTRICTION.equals(nodeName)) {
520         parseComplexRestriction(child, owner);
521       } else {
522         throw unsupportedElementType(child, complexContent);
523       }
524     }
525   }
526 
527   private void parseComplexRestriction(Element restrictionElement, ComplexTypeDescriptor owner) {
528     // TODO v0.8 test this
529     Element[] children = XMLUtil.getChildElements(restrictionElement);
530     for (Element child : children) {
531       String nodeName = localName(child);
532       if (ATTRIBUTE.equals(nodeName)) {
533         parseAttribute(child, owner);
534       } else {
535         throw unsupportedElementType(child, restrictionElement);
536       }
537     }
538   }
539 
540   private void parseSimpleContent(Element simpleContentElement, ComplexTypeDescriptor complexType) {
541     Annotation annotation = null;
542     LOGGER.debug("parseSimpleContent()");
543     for (Element child : XMLUtil.getChildElements(simpleContentElement)) {
544       String localName = localName(child);
545       if (ANNOTATION.equals(localName)) {
546         annotation = new Annotation(child);
547       } else if (RESTRICTION.equals(localName)) {
548         parseSimpleContentRestriction(child, complexType);
549       } else if (EXTENSION.equals(localName)) {
550         parseSimpleContentExtension(child, complexType);
551       } else {
552         throw unsupportedElementType(child, simpleContentElement);
553       }
554     }
555     if (annotation != null && annotation.getAppInfo() != null) {
556       complexType = parseComplexTypeAppinfo(complexType, annotation);
557     }
558   }
559 
560   private void parseSimpleContentRestriction(Element restriction, ComplexTypeDescriptor complexType) {
561     String baseName = restriction.getAttribute("base");
562     Assert.notNull(baseName, "base attribute");
563     TypeDescriptor base = dataModel.getTypeDescriptor(baseName);
564     Assert.notNull(base, "base type");
565     if (!(base instanceof ComplexTypeDescriptor)) {
566       throw new ConfigurationError("Expected ComplexTypeDescriptor for " + baseName + ", found: " +
567           base.getClass().getSimpleName());
568     }
569     complexType.setParent(base);
570     SimpleTypeDescriptor/rapiddweller/model/data/SimpleTypeDescriptor.html#SimpleTypeDescriptor">SimpleTypeDescriptor content = (SimpleTypeDescriptor) complexType.getComponent(ComplexTypeDescriptor.__SIMPLE_CONTENT).getLocalType(false);
571     Assert.notNull(content, "content");
572     parseRestrictionChildren(restriction, content);
573   }
574 
575   private void parseSimpleContentExtension(Element extension, ComplexTypeDescriptor complexType) {
576     String baseName = extension.getAttribute("base");
577     Assert.notNull(baseName, "base attribute");
578     TypeDescriptor base = dataModel.getTypeDescriptor(baseName);
579     Assert.notNull(base, "base type");
580     if (base instanceof SimpleTypeDescriptor) {
581       complexType.addComponent(new PartDescriptor(ComplexTypeDescriptor.__SIMPLE_CONTENT, this, baseName, null,
582           new ConstantExpression<>(1L), new ConstantExpression<>(1L)));
583     } else if (base instanceof ComplexTypeDescriptor) {
584       complexType.setParentName(baseName);
585     } else {
586       throw new UnsupportedOperationException("not a supported type: " + base.getClass());
587     }
588     parseAttributes(extension, complexType);
589   }
590 
591   private void parseExtension(Element extension, ComplexTypeDescriptor descriptor) {
592     String base = extension.getAttribute(BASE);
593     descriptor.setParentName(base);
594     parseAttributes(extension, descriptor);
595   }
596 
597   private void parseAttributes(Element extension, ComplexTypeDescriptor owner) {
598     Element[] children = XMLUtil.getChildElements(extension);
599     for (Element child : children) {
600       String nodeName = localName(child);
601       if (ATTRIBUTE.equals(nodeName)) {
602         parseAttribute(child, owner);
603       } else {
604         throw unsupportedElementType(child, extension);
605       }
606     }
607   }
608 
609   private void parseTopLevelElement(Element element) {
610     String name = element.getAttribute(NAME);
611     LOGGER.debug("parseTopLevelElement({})", name);
612     TypeDescriptor descriptor = null;
613     Annotation annotation = null;
614     Element[] children = XMLUtil.getChildElements(element);
615     for (Element child : children) {
616       String nodeName = localName(child);
617       if (COMPLEX_TYPE.equals(nodeName)) {
618         descriptor = parseComplexType(child, name, annotation, false);
619         annotation = null;
620       } else if (SIMPLE_TYPE.equals(nodeName)) {
621         descriptor = parseSimpleType(name, child);
622       } else if (KEY.equals(nodeName)) {
623         parseKey(child);
624       } else if (KEYREF.equals(nodeName)) {
625         parseKeyRef(child);
626       } else if (ANNOTATION.equals(nodeName)) {
627         annotation = new Annotation(child);
628       } else {
629         throw unsupportedElementType(child, element);
630       }
631     }
632 
633     if (descriptor == null) {
634       String type = element.getAttribute("type");
635       if (!StringUtil.isEmpty(type)) {
636         descriptor = parseTopLevelElementWithType(element);
637       }
638     }
639     descriptor = parseElementAppInfo(descriptor, annotation);
640     if (descriptor == null) {
641       descriptor = new ComplexTypeDescriptor(name, this);
642     }
643     addTypeDescriptor(descriptor);
644   }
645 
646   private void parseContainedElement(Element element, ComplexTypeDescriptor owner) {
647     String name = element.getAttribute(NAME);
648     LOGGER.debug("parseElement({})", element.getAttribute(NAME));
649     Assert.notNull(owner, "owner");
650     PartDescriptor descriptor = null;
651     if (!StringUtil.isEmpty(element.getAttribute(REF))) {
652       descriptor = parseElementRef(element);
653     }
654     Annotation annotation = null;
655     Element[] children = XMLUtil.getChildElements(element);
656     for (Element child : children) {
657       String nodeName = localName(child);
658       if (COMPLEX_TYPE.equals(nodeName)) {
659         ComplexTypeDescriptor type = parseComplexType(child, name, annotation, false);
660         descriptor = new PartDescriptor(name, this, type);
661         annotation = null;
662       } else if (SIMPLE_TYPE.equals(nodeName)) {
663         SimpleTypeDescriptor simpleType = parseSimpleType(name, child);
664         ComplexTypeDescriptor complexType = wrapSimpleTypeWithComplexType(simpleType);
665         descriptor = new PartDescriptor(name, this, complexType);
666       } else if (KEY.equals(nodeName)) {
667         parseKey(child);
668       } else if (KEYREF.equals(nodeName)) {
669         parseKeyRef(child);
670       } else if (UNIQUE.equals(nodeName)) {
671         parseUnique(child);
672       } else if (ANNOTATION.equals(nodeName)) {
673         annotation = new Annotation(child);
674       } else {
675         throw unsupportedElementType(child, element);
676       }
677     }
678 
679     if (descriptor == null) {
680       String type = element.getAttribute("type");
681       if (!StringUtil.isEmpty(type)) {
682         descriptor = parseElementWithType(element);
683       }
684     } else {
685       parseElementAppInfo(descriptor, annotation);
686     }
687     if (descriptor == null) {
688       descriptor = new PartDescriptor(name, this, "string"); // possibly there i a more useful default type
689     }
690     parseOccurrences(element, descriptor);
691     if ("false".equals(element.getAttribute("nillable"))) {
692       descriptor.setNullable(false);
693     }
694     owner.addComponent(descriptor);
695   }
696 
697   private ComplexTypeDescriptor wrapSimpleTypeWithComplexType(SimpleTypeDescriptor simpleType) {
698     ComplexTypeDescriptorr.html#ComplexTypeDescriptor">ComplexTypeDescriptor complexType = new ComplexTypeDescriptor(simpleType.getName(), this);
699     complexType.addComponent(new PartDescriptor(ComplexTypeDescriptor.__SIMPLE_CONTENT, this, simpleType));
700     return complexType;
701   }
702 
703   @SuppressWarnings("static-method")
704   private void parseUnique(Element child) {
705     // TODO v1.0 automatically support uniqueness
706     LOGGER.warn("<unique> is not supported. Please define own annotations or setup for uniqueness assurance");
707   }
708 
709   private PartDescriptor parseElementRef(Element element) {
710     String refName = element.getAttribute(REF);
711     if (StringUtil.isEmpty(refName)) {
712       throw new ConfigurationError("no ref specified in element");
713     }
714     TypeDescriptor type = dataModel.getTypeDescriptor(refName);
715     PartDescriptor descriptor;
716     if (type instanceof SimpleTypeDescriptor) {
717       ComplexTypeDescriptorr.html#ComplexTypeDescriptor">ComplexTypeDescriptor complexType = new ComplexTypeDescriptor(refName, this);
718       complexType.addComponent(new PartDescriptor(ComplexTypeDescriptor.__SIMPLE_CONTENT, this, refName));
719       descriptor = new PartDescriptor(refName, this, complexType);
720     } else {
721       descriptor = new PartDescriptor(refName, this, type);
722     }
723     return descriptor;
724   }
725 
726   @SuppressWarnings("unchecked")
727   private <T extends FeatureDescriptor> T parseElementAppInfo(T descriptor, Annotation annotation) {
728     if (annotation == null || annotation.getAppInfo() == null) {
729       return descriptor;
730     }
731 
732     Element appInfo = annotation.getAppInfo();
733     Element[] infos = XMLUtil.getChildElements(appInfo);
734 
735     for (Element info : infos) {
736       String childName = XMLUtil.localName(info);
737       if ("bean".equals(childName)) {
738         BeanParser.parseBeanExpression(info);
739       } else if ("variable".equals(childName)) {
740         parser.parseVariable(info, (ComplexTypeDescriptor) descriptor);
741       } else if (ATTRIBUTE.equals(childName)) {
742         descriptor = (T) parser.parseAttribute(info, null, (PartDescriptor) descriptor);
743       } else if ("part".equals(childName)) {
744         descriptor = (T) parser.parsePart(info, null, (PartDescriptor) descriptor);
745       } else if (descriptor instanceof ComplexTypeDescriptor) {
746         descriptor = (T) parser.parseComplexType(info, (ComplexTypeDescriptor) descriptor);
747       } else if (descriptor instanceof SimpleTypeDescriptor) {
748         descriptor = (T) parser.parseSimpleType(info, (SimpleTypeDescriptor) descriptor);
749       } else if ("type".equals(childName)) {
750         TypeDescriptor typeDescriptor =
751             (descriptor instanceof InstanceDescriptor../../com/rapiddweller/model/data/InstanceDescriptor.html#InstanceDescriptor">InstanceDescriptor ? ((InstanceDescriptor) descriptor).getTypeDescriptor() : (TypeDescriptor) descriptor);
752         if (typeDescriptor instanceof SimpleTypeDescriptor) {
753           descriptor = (T) parser.parseSimpleType(info, (SimpleTypeDescriptor) typeDescriptor);
754         } else {
755           descriptor = (T) parser.parseComplexType(info, (ComplexTypeDescriptor) typeDescriptor);
756         }
757       } else {
758         throw new UnsupportedOperationException("Unsupported element (" + childName + ") " +
759             "or descriptor type: " + descriptor.getClass().getName());
760       }
761     }
762     return descriptor;
763   }
764 
765   /**
766    * Parses code like
767    * <pre>
768    *   <xs:element name="variable" type="generator-setup"/>
769    * </pre>
770    *
771    * @param element supported elements
772    */
773   private TypeDescriptor parseTopLevelElementWithType(Element element) {
774     String name = element.getAttribute(NAME);
775     String typeName = element.getAttribute("type");
776     TypeDescriptor type = getType(typeName);
777     if (type == null) {
778       type = getType(name);
779     }
780     if (type != null) {
781       if (type instanceof SimpleTypeDescriptor) {
782         return new SimpleTypeDescriptor(name, this, typeName);
783       } else if (type instanceof ComplexTypeDescriptor) {
784         return new ComplexTypeDescriptor(name, this, typeName);
785       } else {
786         throw new UnsupportedOperationException("Unsupported descriptor: " + type);
787       }
788     } else {
789       throw new UnsupportedOperationException("Unsupported type: " + typeName);
790     }
791   }
792 
793   private PartDescriptor parseElementWithType(Element element) {
794     String name = element.getAttribute(NAME);
795     String typeName = element.getAttribute("type");
796     TypeDescriptor type = getType(typeName);
797     if (type == null) {
798       throw new ConfigurationError("Undefined type: " + typeName);
799     }
800     PartDescriptor refDesc;
801     if (type instanceof SimpleTypeDescriptor) {
802       // the element wraps a simple type
803       SimpleTypeDescriptortor.html#SimpleTypeDescriptor">SimpleTypeDescriptor localType = new SimpleTypeDescriptor(name, this, typeName);
804       ComplexTypeDescriptor contentType = wrapSimpleTypeWithComplexType(localType);
805       refDesc = new PartDescriptor(name, this, contentType);
806       Element anno = XMLUtil.getChildElement(element, false, false, "annotation");
807       if (anno != null) {
808         parseSimpleTypeAppinfo(new Annotation(anno), localType);
809       }
810     } else {
811       ComplexTypeDescriptortor.html#ComplexTypeDescriptor">ComplexTypeDescriptor localType = new ComplexTypeDescriptor(name, this, typeName);
812       refDesc = new PartDescriptor(name, this, localType);
813       Element anno = XMLUtil.getChildElement(element, false, false, "annotation");
814       if (anno != null) {
815         refDesc = parseAttributeAppinfo(new Annotation(anno), refDesc);
816       }
817     }
818     parseOccurrences(element, refDesc);
819     return refDesc;
820   }
821 
822   private TypeDescriptor getType(String typeName) {
823     int sep = typeName.indexOf(':');
824     if (sep < 0) {
825       return dataModel.getTypeDescriptor(typeName);
826     }
827     String nsAlias = typeName.substring(0, sep);
828     String namespace = getNamespaceForAlias(nsAlias);
829     String typeInNs = typeName.substring(sep + 1);
830     return dataModel.getTypeDescriptor(namespace, typeInNs);
831   }
832 
833   private String getNamespaceForAlias(String nsAlias) {
834     return namespaces.get(nsAlias);
835   }
836 
837   @SuppressWarnings("static-method")
838   private void parseKeyRef(Element child) {
839     // TODO v1.0 implement parseKeyRef
840     LOGGER.warn("KeyRefs are not supported, yet. Ignoring keyRef: " + child.getAttribute("name"));
841   }
842 
843   @SuppressWarnings("static-method")
844   private void parseKey(Element child) {
845     // TODO v1.0 implement parseKey
846     LOGGER.warn("Keys are not supported, yet. Ignoring key: " + child.getAttribute("name"));
847   }
848 
849   @SuppressWarnings("null")
850   private void parseAttribute(Element attributeElement, ComplexTypeDescriptor owner) {
851     String name = attributeElement.getAttribute(NAME);
852     LOGGER.debug("parseAttribute({})", name);
853     if (StringUtil.isEmpty(name)) {
854       throw new ConfigurationError("Unnamed attribute");
855     }
856     Element[] children = XMLUtil.getChildElements(attributeElement);
857     String use = attributeElement.getAttribute("use");
858     Boolean nullable = ("required".equals(use) ? Boolean.FALSE : null);
859     Annotation annotation = null;
860     ComponentDescriptor descriptor = null;
861     for (Element child : children) {
862       String nodeName = localName(child);
863       if ("annotation".equals(nodeName)) {
864         annotation = new Annotation(child);
865       } else if (SIMPLE_TYPE.equals(nodeName)) {
866         descriptor = new PartDescriptor(name, this, parseSimpleType(null, child));
867       } else {
868         throw unsupportedElementType(child, attributeElement);
869       }
870     }
871     String type = attributeElement.getAttribute("type");
872     if (descriptor == null && type != null) {
873       descriptor = new PartDescriptor(name, this, type);
874       if (nullable != null && !nullable) {
875         descriptor.setNullable(false);
876       }
877     }
878     if (annotation != null && annotation.getAppInfo() != null) {
879       descriptor = parseAttributeAppinfo(annotation, descriptor);
880     }
881     String fixed = attributeElement.getAttribute("fixed");
882     if (!StringUtil.isEmpty(fixed)) {
883       ((SimpleTypeDescriptor) descriptor.getLocalType(false)).setValues(fixed);
884     } else {
885       String defaultValue = attributeElement.getAttribute("default");
886       if (!StringUtil.isEmpty(defaultValue)) {
887         ((SimpleTypeDescriptor) descriptor.getLocalType(false)).setValues(defaultValue);
888       }
889     }
890     descriptor.setCount(new ConstantExpression<>(1L));
891     if ("prohibited".equals(attributeElement.getAttribute("use"))) {
892       descriptor.setMode(Mode.ignored);
893     }
894     owner.addComponent(descriptor);
895   }
896 
897   @SuppressWarnings("unchecked")
898   private <T extends ComponentDescriptor> T parseAttributeAppinfo(Annotation annotation, T descriptor) {
899     Element appInfo = annotation.getAppInfo();
900     if (appInfo == null) {
901       return descriptor;
902     }
903     Element[] infos = XMLUtil.getChildElements(appInfo);
904     if (infos.length > 1) {
905       throw new ConfigurationError("Cannot handle more than one appinfo in a simple type");
906     }
907     if (infos.length == 0) {
908       return descriptor;
909     }
910     Element info = infos[0];
911     return (T) parser.parseSimpleTypeComponent(info, null, descriptor);
912   }
913 
914   private SimpleTypeDescriptor parseSimpleType(String name, Element simpleType) {
915     Annotation annotation = null;
916     SimpleTypeDescriptor descriptor = null;
917     if (name == null) {
918       name = simpleType.getAttribute("name");
919     }
920     LOGGER.debug("parseSimpleType({})", name);
921     for (Element child : XMLUtil.getChildElements(simpleType)) {
922       String localName = localName(child);
923       if (ANNOTATION.equals(localName)) {
924         annotation = new Annotation(child);
925       } else if (UNION.equals(localName)) {
926         descriptor = parseUnion(child, name);
927       } else if (RESTRICTION.equals(localName)) {
928         descriptor = parseSimpleTypeRestriction(child, name);
929       } else {
930         throw unsupportedElementType(child, simpleType);
931       }
932     }
933     if (descriptor == null) {
934       String type = simpleType.getAttribute(TYPE);
935       descriptor = new SimpleTypeDescriptor(name, this, type);
936     }
937     if (annotation != null && annotation.getAppInfo() != null) {
938       descriptor = parseSimpleTypeAppinfo(annotation, descriptor);
939     }
940     return descriptor;
941   }
942 
943   private SimpleTypeDescriptor parseSimpleTypeAppinfo(
944       Annotation annotation, SimpleTypeDescriptor descriptor) {
945     Element appInfo = annotation.getAppInfo();
946     if (appInfo != null) {
947       Element[] infos = XMLUtil.getChildElements(appInfo);
948       if (infos.length > 1) {
949         throw new ConfigurationError("Cannot handle more than one appinfo in a simple type");
950       }
951       parser.parseSimpleType(infos[0], descriptor);
952     }
953     return descriptor;
954   }
955 
956   private SimpleTypeDescriptor parseUnion(Element union, String name) {
957     LOGGER.debug("parseUnion({})", name);
958     UnionSimpleTypeDescriptoror.html#UnionSimpleTypeDescriptor">UnionSimpleTypeDescriptor descriptor = new UnionSimpleTypeDescriptor(name, this);
959     Element[] children = XMLUtil.getChildElements(union);
960     for (Element child : children) {
961       String nodeName = localName(child);
962       if (SIMPLE_TYPE.equals(nodeName)) {
963         descriptor.addAlternative(parseSimpleType(null, child));
964       } else {
965         throw unsupportedElementType(child, union);
966       }
967     }
968     String memberTypes = union.getAttribute("memberTypes");
969     if (!StringUtil.isEmpty(memberTypes)) {
970       String[] tokens = StringUtil.tokenize(memberTypes, ' ');
971       for (String token : tokens) {
972         if (!StringUtil.isEmpty(token)) {
973           descriptor.addAlternative(new SimpleTypeDescriptor("_local", this, token));
974         }
975       }
976     }
977     return descriptor;
978   }
979 
980   private SimpleTypeDescriptor parseSimpleTypeRestriction(Element restriction, String name) {
981     String base = XMLUtil.localName(restriction.getAttribute(BASE));
982     SimpleTypeDescriptoror.html#SimpleTypeDescriptor">SimpleTypeDescriptor descriptor = new SimpleTypeDescriptor(name, this, base);
983     parseRestrictionChildren(restriction, descriptor);
984     return descriptor;
985   }
986 
987   @SuppressWarnings("static-method")
988   private void parseImport(Element importElement) {
989     LOGGER.debug("parseImport()");
990     throw unsupportedElementType(importElement, null); // TODO v0.8 implement parseImport()
991   }
992 
993   /**
994    * parses an XML Schema inclusion and adds its types to the {@link DataModel}
995    */
996   private void parseStructureOfInclude(Element includeElement) throws IOException {
997     LOGGER.debug("parseStructureOfInclude()");
998     assert "include".equals(localName(includeElement));
999     String location = includeElement.getAttribute("schemaLocation");
1000     parseStructure(parse(location));
1001   }
1002 
1003   private void parseDetailsOfInclude(Element includeElement) throws IOException {
1004     LOGGER.debug("parseDetailsOfInclude()");
1005     assert "include".equals(localName(includeElement));
1006     String location = includeElement.getAttribute("schemaLocation");
1007     parseDetails(parse(location));
1008   }
1009 
1010   @SuppressWarnings("static-method")
1011   private void parseGroup(Element group) {
1012     LOGGER.debug("parseGroup()");
1013     throw unsupportedElementType(group, null); // TODO v0.8 implement parseGroup()
1014   }
1015 
1016   private ComplexTypeDescriptor parseAttributeGroup(Element group) {
1017     LOGGER.debug("parseAttributeGroup()");
1018     // check if it's an attributeGroup reference
1019     String refName = normalizedAttributeValue(group, REF);
1020     if (refName != null) {
1021       ComplexTypeDescriptorapiddweller/model/data/ComplexTypeDescriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor refdType = (ComplexTypeDescriptor) getType(refName);
1022       if (refdType == null) {
1023         throw new ConfigurationError("referenced attributeGroup not found: " + refName);
1024       }
1025       return refdType;
1026     }
1027     // create a new attributeGroup
1028     String name = normalizedAttributeValue(group, "name");
1029     ComplexTypeDescriptorscriptor.html#ComplexTypeDescriptor">ComplexTypeDescriptor type = new ComplexTypeDescriptor(name, this);
1030     Annotation annotation = null;
1031     for (Element child : XMLUtil.getChildElements(group)) {
1032       String elType = XMLUtil.localName(child);
1033       if (ATTRIBUTE.equals(elType)) {
1034         parseAttribute(child, type);
1035       } else if ("attributeGroup".equals(elType)) {
1036         // TODO v0.8 map as parent relationship (could be several ones)
1037         ComplexTypeDescriptor childGroup = parseAttributeGroup(child);
1038         for (InstanceDescriptor component : childGroup.getParts()) {
1039           type.addPart(component);
1040         }
1041       } else if ("annotation".equals(elType)) {
1042         annotation = new Annotation(child);
1043       } else {
1044         throw unsupportedElementType(child, group);
1045       }
1046     }
1047     if (annotation != null && annotation.getAppInfo() != null) {
1048       LOGGER.warn("ignoring appinfo of attributeGroup: " + name);
1049     }
1050     addTypeDescriptor(type);
1051     return type;
1052   }
1053 
1054   private void parseSequence(Element sequence, ComplexTypeDescriptor owner) {
1055     LOGGER.debug("parseSequence()"); // TODO v0.8 evaluate minCount/maxCount for sequence
1056     parseComponentGroupChildren(sequence, owner);
1057   }
1058 
1059   private void parseChoice(Element choice, ComplexTypeDescriptor owner) {
1060     LOGGER.debug("parseChoice()");
1061     AlternativeGroupDescriptorl#AlternativeGroupDescriptor">AlternativeGroupDescriptor choiceDescriptor = new AlternativeGroupDescriptor(null, this);
1062     parseComponentGroupChildren(choice, choiceDescriptor);
1063     PartDescriptortml#PartDescriptor">PartDescriptor partDescriptor = new PartDescriptor(null, this, choiceDescriptor);
1064     parseOccurrences(choice, partDescriptor);
1065     owner.addComponent(partDescriptor);
1066   }
1067 
1068   private void parseAll(Element all, ComplexTypeDescriptor owner) {
1069     LOGGER.debug("parseAll()"); // TODO v0.8 test
1070     parseComponentGroupChildren(all, owner);
1071   }
1072 
1073   private void parseComponentGroupChildren(Element choice, ComplexTypeDescriptor groupDescriptor) {
1074     Element[] children = XMLUtil.getChildElements(choice);
1075     for (Element child : children) {
1076       String nodeName = localName(child);
1077       if (ELEMENT.equals(nodeName)) {
1078         parseContainedElement(child, groupDescriptor);
1079       } else if (SEQUENCE.equals(nodeName)) {
1080         parseSequence(child, groupDescriptor);
1081       } else if (CHOICE.equals(nodeName)) {
1082         parseChoice(child, groupDescriptor);
1083       } else {
1084         throw unsupportedElementType(child, choice);
1085       }
1086     }
1087   }
1088 
1089 }