1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.xsdc;
18 
19 import com.android.xsdc.tag.*;
20 
21 import org.xml.sax.Attributes;
22 import org.xml.sax.Locator;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.helpers.DefaultHandler;
25 
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Stack;
33 import java.util.stream.Collectors;
34 
35 import javax.xml.namespace.QName;
36 
37 public class XsdHandler extends DefaultHandler {
38     private static class State {
39         final String name;
40         final Map<String, String> attributeMap;
41         final List<XsdTag> tags;
42         boolean deprecated;
43         boolean finalValue;
44         Nullability nullability;
45 
State(String name, Map<String, String> attributeMap)46         State(String name, Map<String, String> attributeMap) {
47             this.name = name;
48             this.attributeMap = Collections.unmodifiableMap(attributeMap);
49             tags = new ArrayList<>();
50             deprecated = false;
51             finalValue = false;
52             nullability = Nullability.UNKNOWN;
53         }
54     }
55 
56     private XmlSchema schema;
57     private final Stack<State> stateStack;
58     private final Map<String, String> namespaces;
59     private Locator locator;
60     private boolean documentationFlag;
61     private boolean enumerationFlag;
62     private List<XsdTag> enumTags;
63     private List<String> includeList;
64 
XsdHandler()65     public XsdHandler() {
66         stateStack = new Stack<>();
67         namespaces = new HashMap<>();
68         documentationFlag = false;
69         enumerationFlag = false;
70         enumTags = new ArrayList<>();
71         includeList = new ArrayList<>();
72     }
73 
getSchema()74     public XmlSchema getSchema() {
75         return schema;
76     }
77 
78     @Override
setDocumentLocator(Locator locator)79     public void setDocumentLocator(Locator locator) {
80         this.locator = locator;
81     }
82 
83     @Override
startPrefixMapping(String prefix, String uri)84     public void startPrefixMapping(String prefix, String uri) {
85         namespaces.put(prefix, uri);
86     }
87 
88     @Override
endPrefixMapping(String prefix)89     public void endPrefixMapping(String prefix) {
90         namespaces.remove(prefix);
91     }
92 
parseQName(String str)93     private QName parseQName(String str) throws XsdParserException {
94         if (str == null) return null;
95         String[] parsed = str.split(":");
96         if (parsed.length == 2) {
97             return new QName(namespaces.get(parsed[0]), parsed[1]);
98         } else if (parsed.length == 1) {
99             return new QName(null, str);
100         }
101         throw new XsdParserException(String.format("QName parse error : %s", str));
102     }
103 
parseQNames(String str)104     private List<QName> parseQNames(String str) throws XsdParserException {
105         List<QName> qNames = new ArrayList<>();
106         if (str == null) return qNames;
107         String[] parsed = str.split("\\s+");
108         for (String s : parsed) {
109             qNames.add(parseQName(s));
110         }
111         return qNames;
112     }
113 
114     @Override
startElement( String uri, String localName, String qName, Attributes attributes)115     public void startElement(
116             String uri, String localName, String qName, Attributes attributes) {
117         // we need to copy attributes because it is mutable..
118         Map<String, String> attributeMap = new HashMap<>();
119         for (int i = 0; i < attributes.getLength(); ++i) {
120             attributeMap.put(attributes.getLocalName(i), attributes.getValue(i));
121         }
122         if (!documentationFlag) {
123             stateStack.push(new State(localName, attributeMap));
124         }
125         if (localName == "documentation") {
126             documentationFlag = true;
127         }
128     }
129 
130     @Override
endElement(String uri, String localName, String qName)131     public void endElement(String uri, String localName, String qName) throws SAXException {
132         if (documentationFlag && localName != "documentation") {
133             return;
134         }
135         try {
136             State state = stateStack.pop();
137             switch (state.name) {
138                 case "schema":
139                     schema = makeSchema(state);
140                     break;
141                 case "element":
142                     stateStack.peek().tags.add(makeElement(state));
143                     break;
144                 case "attribute":
145                     stateStack.peek().tags.add(makeAttribute(state));
146                     break;
147                 case "attributeGroup":
148                     stateStack.peek().tags.add(makeAttributeGroup(state));
149                     break;
150                 case "complexType":
151                     stateStack.peek().tags.add(makeComplexType(state));
152                     break;
153                 case "complexContent":
154                     stateStack.peek().tags.add(makeComplexContent(state));
155                     break;
156                 case "simpleContent":
157                     stateStack.peek().tags.add(makeSimpleContent(state));
158                     break;
159                 case "restriction":
160                     if (enumerationFlag) {
161                         stateStack.peek().tags.add(makeEnumRestriction(state));
162                         enumerationFlag = false;
163                     } else {
164                         stateStack.peek().tags.add(makeGeneralRestriction(state));
165                     }
166                     break;
167                 case "extension":
168                     stateStack.peek().tags.add(makeGeneralExtension(state));
169                     break;
170                 case "simpleType":
171                     stateStack.peek().tags.add(makeSimpleType(state));
172                     break;
173                 case "list":
174                     stateStack.peek().tags.add(makeSimpleTypeList(state));
175                     break;
176                 case "union":
177                     stateStack.peek().tags.add(makeSimpleTypeUnion(state));
178                     break;
179                 case "sequence":
180                     stateStack.peek().tags.addAll(makeSequence(state));
181                     break;
182                 case "choice":
183                     stateStack.peek().tags.addAll(makeChoice(state));
184                     break;
185                 case "all":
186                     stateStack.peek().tags.addAll(makeAll(state));
187                     break;
188                 case "enumeration":
189                     stateStack.peek().tags.add(makeEnumeration(state));
190                     enumerationFlag = true;
191                     break;
192                 case "group":
193                     stateStack.peek().tags.add(makeGroup(state));
194                     break;
195                 case "fractionDigits":
196                 case "length":
197                 case "maxExclusive":
198                 case "maxInclusive":
199                 case "maxLength":
200                 case "minExclusive":
201                 case "minInclusive":
202                 case "minLength":
203                 case "pattern":
204                 case "totalDigits":
205                 case "whiteSpace":
206                     // Tags under simpleType <restriction>. They are ignored.
207                     break;
208                 case "annotation":
209                     stateStack.peek().deprecated = isDeprecated(state.attributeMap, state.tags,
210                             stateStack.peek().deprecated);
211                     stateStack.peek().finalValue = isFinalValue(state.attributeMap, state.tags,
212                             stateStack.peek().finalValue);
213                     stateStack.peek().nullability = getNullability(state.attributeMap, state.tags,
214                             stateStack.peek().nullability);
215                     break;
216                 case "appinfo":
217                     // They function like comments, so are ignored.
218                     break;
219                 case "documentation":
220                     documentationFlag = false;
221                     break;
222                 case "key":
223                 case "keyref":
224                 case "selector":
225                 case "field":
226                 case "unique":
227                     // These tags are not related to xml parsing.
228                     // They are using when validating xml files via xsd file.
229                     // So they are ignored.
230                     break;
231                 case "include":
232                     addInclude(state);
233                     break;
234                 default:
235                     throw new XsdParserException(String.format("unsupported tag : %s", state.name));
236             }
237         } catch (XsdParserException e) {
238             throw new SAXException(
239                     String.format("Line %d, Column %d - %s",
240                             locator.getLineNumber(), locator.getColumnNumber(), e.getMessage()));
241         }
242     }
243 
makeSchema(State state)244     private XmlSchema makeSchema(State state) {
245         Map<String, XsdElement> elementMap = new LinkedHashMap<>();
246         Map<String, XsdType> typeMap = new LinkedHashMap<>();
247         Map<String, XsdAttribute> attrMap = new LinkedHashMap<>();
248         Map<String, XsdAttributeGroup> attrGroupMap = new LinkedHashMap<>();
249         Map<String, XsdGroup> groupMap = new LinkedHashMap<>();
250 
251         state.tags.addAll(enumTags);
252         for (XsdTag tag : state.tags) {
253             if (tag == null) continue;
254             if (tag instanceof XsdElement) {
255                 elementMap.put(tag.getName(), (XsdElement) tag);
256             } else if (tag instanceof XsdAttribute) {
257                 attrMap.put(tag.getName(), (XsdAttribute) tag);
258             } else if (tag instanceof XsdAttributeGroup) {
259                 attrGroupMap.put(tag.getName(), (XsdAttributeGroup) tag);
260             } else if (tag instanceof XsdType) {
261                 typeMap.put(tag.getName(), (XsdType) tag);
262             } else if (tag instanceof XsdGroup) {
263                 groupMap.put(tag.getName(), (XsdGroup) tag);
264             }
265         }
266 
267         return new XmlSchema(elementMap, typeMap, attrMap, attrGroupMap, groupMap, includeList);
268     }
269 
makeElement(State state)270     private XsdElement makeElement(State state) throws XsdParserException {
271         String name = state.attributeMap.get("name");
272         QName typename = parseQName(state.attributeMap.get("type"));
273         QName ref = parseQName(state.attributeMap.get("ref"));
274         String isAbstract = state.attributeMap.get("abstract");
275         String defVal = state.attributeMap.get("default");
276         String substitutionGroup = state.attributeMap.get("substitutionGroup");
277         String maxOccurs = state.attributeMap.get("maxOccurs");
278 
279         if ("true".equals(isAbstract)) {
280             throw new XsdParserException("abstract element is not supported.");
281         }
282         if (defVal != null) {
283             throw new XsdParserException("default value of an element is not supported.");
284         }
285         if (substitutionGroup != null) {
286             throw new XsdParserException("substitution group of an element is not supported.");
287         }
288 
289         boolean multiple = false;
290         if (maxOccurs != null) {
291             if (maxOccurs.equals("0")) return null;
292             if (maxOccurs.equals("unbounded") || Integer.parseInt(maxOccurs) > 1) multiple = true;
293         }
294 
295         XsdType type = null;
296         if (typename != null) {
297             type = new XsdType(null, typename);
298         }
299         for (XsdTag tag : state.tags) {
300             if (tag == null) continue;
301             if (tag instanceof XsdType) {
302                 type = (XsdType) tag;
303             }
304         }
305 
306         return setDeprecatedAndFinal(new XsdElement(name, ref, type, multiple), state.deprecated,
307                 state.finalValue, state.nullability);
308     }
309 
makeAttribute(State state)310     private XsdAttribute makeAttribute(State state) throws XsdParserException {
311         String name = state.attributeMap.get("name");
312         QName typename = parseQName(state.attributeMap.get("type"));
313         QName ref = parseQName(state.attributeMap.get("ref"));
314         String defVal = state.attributeMap.get("default");
315         String use = state.attributeMap.get("use");
316 
317         if (use != null && use.equals("prohibited")) return null;
318 
319         boolean required = false;
320         if (use != null && use.equals("required")) {
321             required = true;
322         }
323 
324         XsdType type = null;
325         if (typename != null) {
326             type = new XsdType(null, typename);
327         }
328         for (XsdTag tag : state.tags) {
329             if (tag == null) continue;
330             if (tag instanceof XsdType) {
331                 type = (XsdType) tag;
332             }
333         }
334 
335         return setDeprecatedAndFinal(new XsdAttribute(name, ref, type, required), state.deprecated,
336                 state.finalValue, state.nullability);
337     }
338 
makeAttributeGroup(State state)339     private XsdAttributeGroup makeAttributeGroup(State state) throws XsdParserException {
340         String name = state.attributeMap.get("name");
341         QName ref = parseQName(state.attributeMap.get("ref"));
342 
343         List<XsdAttribute> attributes = new ArrayList<>();
344         List<XsdAttributeGroup> attributeGroups = new ArrayList<>();
345 
346         for (XsdTag tag : state.tags) {
347             if (tag == null) continue;
348             if (tag instanceof XsdAttribute) {
349                 attributes.add((XsdAttribute) tag);
350             } else if (tag instanceof XsdAttributeGroup) {
351                 attributeGroups.add((XsdAttributeGroup) tag);
352             }
353         }
354 
355         return setDeprecatedAndFinal(new XsdAttributeGroup(name, ref, attributes, attributeGroups),
356                 state.deprecated, state.finalValue, state.nullability);
357     }
358 
makeGroup(State state)359     private XsdGroup makeGroup(State state) throws XsdParserException {
360         String name = state.attributeMap.get("name");
361         QName ref = parseQName(state.attributeMap.get("ref"));
362 
363         List<XsdElement> elements = new ArrayList<>();
364 
365         for (XsdTag tag: state.tags) {
366             if (tag == null) continue;
367             if (tag instanceof XsdElement) {
368                 elements.add((XsdElement) tag);
369             }
370         }
371 
372         return setDeprecatedAndFinal(new XsdGroup(name, ref, elements), state.deprecated,
373                 state.finalValue, state.nullability);
374     }
375 
makeComplexType(State state)376     private XsdComplexType makeComplexType(State state) throws XsdParserException {
377         String name = state.attributeMap.get("name");
378         String isAbstract = state.attributeMap.get("abstract");
379         String mixed = state.attributeMap.get("mixed");
380 
381         if ("true".equals(isAbstract)) {
382             throw new XsdParserException("abstract complex type is not supported.");
383         }
384         if ("true".equals(mixed)) {
385             throw new XsdParserException("mixed option of a complex type is not supported.");
386         }
387 
388         List<XsdAttribute> attributes = new ArrayList<>();
389         List<XsdAttributeGroup> attributeGroups = new ArrayList<>();
390         List<XsdElement> elements = new ArrayList<>();
391         XsdComplexType type = null;
392         XsdGroup group = null;
393 
394         for (XsdTag tag : state.tags) {
395             if (tag == null) continue;
396             if (tag instanceof XsdAttribute) {
397                 attributes.add((XsdAttribute) tag);
398             } else if (tag instanceof XsdAttributeGroup) {
399                 attributeGroups.add((XsdAttributeGroup) tag);
400             } else if (tag instanceof XsdGroup) {
401                 group = (XsdGroup) tag;
402             } else if (tag instanceof XsdElement) {
403                 elements.add((XsdElement) tag);
404             } else if (tag instanceof XsdComplexContent) {
405                 XsdComplexContent child = (XsdComplexContent) tag;
406                 type = setDeprecatedAndFinal(new XsdComplexContent(name, child.getBase(),
407                         child.getAttributes(), child.getAttributeGroups(),
408                         child.getElements(), child.getGroup()), state.deprecated, state.finalValue,
409                         state.nullability);
410             } else if (tag instanceof XsdSimpleContent) {
411                 XsdSimpleContent child = (XsdSimpleContent) tag;
412                 type = setDeprecatedAndFinal(new XsdSimpleContent(name, child.getBase(),
413                         child.getAttributes()), state.deprecated, state.finalValue,
414                         state.nullability);
415             }
416         }
417 
418         return (type != null) ? type : setDeprecatedAndFinal(new XsdComplexContent(name, null,
419                 attributes, attributeGroups, elements, group), state.deprecated, state.finalValue,
420                 state.nullability);
421     }
422 
makeComplexContent(State state)423     private XsdComplexContent makeComplexContent(State state) throws XsdParserException {
424         String mixed = state.attributeMap.get("mixed");
425         if ("true".equals(mixed)) {
426             throw new XsdParserException("mixed option of a complex content is not supported.");
427         }
428 
429         XsdComplexContent content = null;
430         for (XsdTag tag : state.tags) {
431             if (tag == null) continue;
432             if (tag instanceof XsdGeneralExtension) {
433                 XsdGeneralExtension extension = (XsdGeneralExtension) tag;
434                 content = new XsdComplexContent(null, extension.getBase(),
435                         extension.getAttributes(), extension.getAttributeGroups(),
436                         extension.getElements(), extension.getGroup());
437             } else if (tag instanceof XsdGeneralRestriction) {
438                 XsdGeneralRestriction restriction = (XsdGeneralRestriction) tag;
439                 XsdType base = restriction.getBase();
440                 if (base.getRef() != null && base.getRef().getNamespaceURI().equals(
441                         XsdConstants.XSD_NAMESPACE)) {
442                     // restriction of base 'xsd:anyType' is equal to complex content definition
443                     content = new XsdComplexContent(null, null, restriction.getAttributes(),
444                             restriction.getAttributeGroups(), restriction.getElements(),
445                             restriction.getGroup());
446                 } else {
447                     // otherwise ignore restrictions
448                     content = new XsdComplexContent(null, base, null, null, null, null);
449                 }
450             }
451         }
452 
453         return setDeprecatedAndFinal(content, state.deprecated, state.finalValue,
454                 state.nullability);
455     }
456 
makeSimpleContent(State state)457     private XsdSimpleContent makeSimpleContent(State state) {
458         XsdSimpleContent content = null;
459 
460         for (XsdTag tag : state.tags) {
461             if (tag == null) continue;
462             if (tag instanceof XsdGeneralExtension) {
463                 XsdGeneralExtension extension = (XsdGeneralExtension) tag;
464                 content = new XsdSimpleContent(null, extension.getBase(),
465                         extension.getAttributes());
466             } else if (tag instanceof XsdGeneralRestriction) {
467                 XsdGeneralRestriction restriction = (XsdGeneralRestriction) tag;
468                 content = new XsdSimpleContent(null, restriction.getBase(), null);
469             }
470         }
471 
472         return setDeprecatedAndFinal(content, state.deprecated, state.finalValue,
473                 state.nullability);
474     }
475 
makeGeneralRestriction(State state)476     private XsdGeneralRestriction makeGeneralRestriction(State state) throws XsdParserException {
477         QName base = parseQName(state.attributeMap.get("base"));
478 
479         XsdType type = null;
480         if (base != null) {
481             type = new XsdType(null, base);
482         }
483         List<XsdAttribute> attributes = new ArrayList<>();
484         List<XsdAttributeGroup> attributeGroups = new ArrayList<>();
485         List<XsdElement> elements = new ArrayList<>();
486         XsdGroup group = null;
487         for (XsdTag tag : state.tags) {
488             if (tag == null) continue;
489             if (tag instanceof XsdAttribute) {
490                 attributes.add((XsdAttribute) tag);
491             } else if (tag instanceof XsdAttributeGroup) {
492                 attributeGroups.add((XsdAttributeGroup) tag);
493             } else if (tag instanceof XsdElement) {
494                 elements.add((XsdElement) tag);
495             } else if (tag instanceof XsdGroup) {
496                 group = (XsdGroup) tag;
497             }
498         }
499 
500         return setDeprecatedAndFinal(new XsdGeneralRestriction(type, attributes, attributeGroups,
501                 elements, group), state.deprecated, state.finalValue, state.nullability);
502     }
503 
makeGeneralExtension(State state)504     private XsdGeneralExtension makeGeneralExtension(State state) throws XsdParserException {
505         QName base = parseQName(state.attributeMap.get("base"));
506 
507         List<XsdAttribute> attributes = new ArrayList<>();
508         List<XsdAttributeGroup> attributeGroups = new ArrayList<>();
509         List<XsdElement> elements = new ArrayList<>();
510         XsdGroup group = null;
511         for (XsdTag tag : state.tags) {
512             if (tag == null) continue;
513             if (tag instanceof XsdAttribute) {
514                 attributes.add((XsdAttribute) tag);
515             } else if (tag instanceof XsdAttributeGroup) {
516                 attributeGroups.add((XsdAttributeGroup) tag);
517             } else if (tag instanceof XsdElement) {
518                 elements.add((XsdElement) tag);
519             } else if (tag instanceof XsdGroup) {
520                 group = (XsdGroup) tag;
521             }
522         }
523         return setDeprecatedAndFinal(new XsdGeneralExtension(new XsdType(null, base), attributes,
524                 attributeGroups, elements, group), state.deprecated, state.finalValue,
525                 state.nullability);
526     }
527 
makeSimpleType(State state)528     private XsdSimpleType makeSimpleType(State state) throws XsdParserException {
529         String name = state.attributeMap.get("name");
530         XsdSimpleType type = null;
531         for (XsdTag tag : state.tags) {
532             if (tag == null) continue;
533             if (tag instanceof XsdList) {
534                 type = new XsdList(name, ((XsdList) tag).getItemType());
535             } else if (tag instanceof XsdGeneralRestriction) {
536                 type = new XsdRestriction(name, ((XsdGeneralRestriction) tag).getBase(), null);
537             } else if (tag instanceof XsdEnumRestriction) {
538                 if (name == null) {
539                     throw new XsdParserException(
540                             "The name of simpleType for enumeration must be set.");
541                 }
542                 type = new XsdRestriction(name, ((XsdEnumRestriction) tag).getBase(),
543                         ((XsdEnumRestriction) tag).getEnums());
544                 enumTags.add(type);
545             } else if (tag instanceof XsdUnion) {
546                 type = new XsdUnion(name, ((XsdUnion) tag).getMemberTypes());
547             }
548         }
549         return setDeprecatedAndFinal(type, state.deprecated, state.finalValue, state.nullability);
550     }
551 
makeSimpleTypeList(State state)552     private XsdList makeSimpleTypeList(State state) throws XsdParserException {
553         QName itemTypeName = parseQName(state.attributeMap.get("itemType"));
554 
555         XsdType itemType = null;
556         if (itemTypeName != null) {
557             itemType = new XsdType(null, itemTypeName);
558         }
559         for (XsdTag tag : state.tags) {
560             if (tag == null) continue;
561             if (tag instanceof XsdType) {
562                 itemType = (XsdType) tag;
563             }
564         }
565         return setDeprecatedAndFinal(new XsdList(null, itemType), state.deprecated,
566                 state.finalValue, state.nullability);
567     }
568 
makeSimpleTypeUnion(State state)569     private XsdUnion makeSimpleTypeUnion(State state) throws XsdParserException {
570         List<QName> memberTypeNames = parseQNames(state.attributeMap.get("memberTypes"));
571         List<XsdType> memberTypes = memberTypeNames.stream().map(
572                 ref -> new XsdType(null, ref)).collect(Collectors.toList());
573 
574         for (XsdTag tag : state.tags) {
575             if (tag == null) continue;
576             if (tag instanceof XsdType) {
577                 memberTypes.add((XsdType) tag);
578             }
579         }
580 
581         return setDeprecatedAndFinal(new XsdUnion(null, memberTypes), state.deprecated,
582                 state.finalValue, state.nullability);
583     }
584 
makeSequence(State state)585     private static List<XsdTag> makeSequence(State state) throws XsdParserException {
586         String minOccurs = state.attributeMap.get("minOccurs");
587         String maxOccurs = state.attributeMap.get("maxOccurs");
588 
589         if (minOccurs != null || maxOccurs != null) {
590             throw new XsdParserException(
591                     "minOccurs, maxOccurs options of a sequence is not supported");
592         }
593 
594         List<XsdTag> elementsAndGroup = new ArrayList<>();
595         for (XsdTag tag : state.tags) {
596             if (tag == null) continue;
597             if (tag instanceof XsdElement) {
598                 if (maxOccurs != null && (maxOccurs.equals("unbounded")
599                         || Integer.parseInt(maxOccurs) > 1)) {
600                     ((XsdElement)tag).setMultiple(true);
601                 }
602                 elementsAndGroup.add(tag);
603             } else if (tag instanceof XsdGroup) {
604                 elementsAndGroup.add(tag);
605             }
606         }
607         return elementsAndGroup;
608     }
609 
makeChoice(State state)610     private static List<XsdTag> makeChoice(State state) throws XsdParserException {
611         String maxOccurs = state.attributeMap.get("maxOccurs");
612         List<XsdTag> elementsAndGroup = new ArrayList<>();
613 
614         for (XsdTag tag : state.tags) {
615             if (tag == null) continue;
616             if (tag instanceof XsdElement) {
617                 if (maxOccurs != null && (maxOccurs.equals("unbounded")
618                         || Integer.parseInt(maxOccurs) > 1)) {
619                     ((XsdElement)tag).setMultiple(true);
620                 }
621                 XsdElement element = (XsdElement)tag;
622                 elementsAndGroup.add((XsdTag) setDeprecatedAndFinal(new XsdChoice(element.getName(),
623                         element.getRef(), element.getType(), element.isMultiple()),
624                         element.isDeprecated(), element.isFinalValue(), element.getNullability()));
625             } else if (tag instanceof XsdGroup) {
626                 elementsAndGroup.add(tag);
627             }
628         }
629         return elementsAndGroup;
630     }
631 
makeAll(State state)632     private static List<XsdElement> makeAll(State state) throws XsdParserException {
633         List<XsdElement> elements = new ArrayList<>();
634         for (XsdTag tag : state.tags) {
635             if (tag == null) continue;
636             if (tag instanceof XsdElement) {
637                 XsdElement element = (XsdElement)tag;
638                 elements.add(setDeprecatedAndFinal(new XsdAll(element.getName(), element.getRef(),
639                         element.getType(), element.isMultiple()), element.isDeprecated(),
640                         element.isFinalValue(), element.getNullability()));
641             }
642         }
643         return elements;
644     }
645 
makeEnumeration(State state)646     private XsdEnumeration makeEnumeration(State state) throws XsdParserException {
647         String value = state.attributeMap.get("value");
648         return setDeprecatedAndFinal(new XsdEnumeration(value), state.deprecated,
649                 state.finalValue, state.nullability);
650     }
651 
makeEnumRestriction(State state)652     private XsdEnumRestriction makeEnumRestriction(State state) throws XsdParserException {
653         QName base = parseQName(state.attributeMap.get("base"));
654 
655         XsdType type = null;
656         if (base != null) {
657             type = new XsdType(null, base);
658         }
659         List<XsdEnumeration> enums = new ArrayList<>();
660         for (XsdTag tag : state.tags) {
661             if (tag == null) continue;
662             if (tag instanceof XsdEnumeration) {
663                 enums.add((XsdEnumeration) tag);
664             }
665         }
666 
667         return setDeprecatedAndFinal(new XsdEnumRestriction(type, enums), state.deprecated,
668                 state.finalValue, state.nullability);
669     }
670 
addInclude(State state)671     private void addInclude(State state) throws XsdParserException {
672         String fileName = state.attributeMap.get("schemaLocation");
673         includeList.add(fileName);
674     }
675 
isDeprecated(Map<String, String> attributeMap,List<XsdTag> tags, boolean deprecated)676     private boolean isDeprecated(Map<String, String> attributeMap,List<XsdTag> tags,
677             boolean deprecated) throws XsdParserException {
678         String name = attributeMap.get("name");
679         if ("Deprecated".equals(name)) {
680             return true;
681         }
682         return deprecated;
683     }
684 
isFinalValue(Map<String, String> attributeMap,List<XsdTag> tags, boolean finalValue)685     private boolean isFinalValue(Map<String, String> attributeMap,List<XsdTag> tags,
686             boolean finalValue) throws XsdParserException {
687         String name = attributeMap.get("name");
688         if ("final".equals(name)) {
689             return true;
690         }
691         return finalValue;
692     }
693 
getNullability(Map<String, String> attributeMap,List<XsdTag> tags, Nullability nullability)694     private Nullability getNullability(Map<String, String> attributeMap,List<XsdTag> tags,
695             Nullability nullability) throws XsdParserException {
696         String name = attributeMap.get("name");
697         if ("nullable".equals(name)) {
698             return Nullability.NULLABLE;
699         } else if ("nonnull".equals(name)) {
700             return Nullability.NON_NULL;
701         }
702         return nullability;
703     }
704 
setDeprecatedAndFinal(T tag, boolean deprecated, boolean finalValue, Nullability nullability)705     private static <T extends XsdTag> T setDeprecatedAndFinal(T tag, boolean deprecated,
706             boolean finalValue, Nullability nullability) {
707         if (tag != null) {
708             tag.setDeprecated(deprecated);
709             tag.setFinalValue(finalValue);
710             tag.setNullability(nullability);
711         }
712         return tag;
713     }
714 }
715