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