1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21 
22 package org.apache.openoffice.ooxml.schema.parser;
23 
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Vector;
29 
30 import javax.xml.stream.XMLInputFactory;
31 import javax.xml.stream.XMLStreamException;
32 import javax.xml.stream.XMLStreamReader;
33 
34 import org.apache.openoffice.ooxml.schema.model.attribute.Attribute;
35 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup;
36 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference;
37 import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference;
38 import org.apache.openoffice.ooxml.schema.model.base.INode;
39 import org.apache.openoffice.ooxml.schema.model.base.Location;
40 import org.apache.openoffice.ooxml.schema.model.base.Node;
41 import org.apache.openoffice.ooxml.schema.model.base.QualifiedName;
42 import org.apache.openoffice.ooxml.schema.model.complex.All;
43 import org.apache.openoffice.ooxml.schema.model.complex.Any;
44 import org.apache.openoffice.ooxml.schema.model.complex.Choice;
45 import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent;
46 import org.apache.openoffice.ooxml.schema.model.complex.ComplexType;
47 import org.apache.openoffice.ooxml.schema.model.complex.Element;
48 import org.apache.openoffice.ooxml.schema.model.complex.ElementReference;
49 import org.apache.openoffice.ooxml.schema.model.complex.Extension;
50 import org.apache.openoffice.ooxml.schema.model.complex.Group;
51 import org.apache.openoffice.ooxml.schema.model.complex.GroupReference;
52 import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator;
53 import org.apache.openoffice.ooxml.schema.model.complex.Sequence;
54 import org.apache.openoffice.ooxml.schema.model.schema.Schema;
55 import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase;
56 import org.apache.openoffice.ooxml.schema.model.simple.List;
57 import org.apache.openoffice.ooxml.schema.model.simple.Restriction;
58 import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent;
59 import org.apache.openoffice.ooxml.schema.model.simple.SimpleType;
60 import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference;
61 import org.apache.openoffice.ooxml.schema.model.simple.Union;
62 
63 
64 /** Parser for single schema file.
65  *  Imports and includes of other schema files are stored and can be retrieved
66  *  by calling GetImportedSchemas().
67  *
68  *  Typical usage:
69  *  1) Create SchemaParser for top-level schema file.
70  *  2) Call Parse().
71  *  3) Repeat the same recursively for all imported schemas (as returned by
72  *     GetImportedSchemas()).
73  *
74  *  All top level types (complex types, simple types, elements, etc.) are
75  *  stored in the given Schema object.
76  */
77 public class SchemaParser
78 {
SchemaParser( final File aSchemaFile, final Schema aSchema, final SchemaBase aSchemaBase)79     public SchemaParser (
80         final File aSchemaFile,
81         final Schema aSchema,
82         final SchemaBase aSchemaBase)
83     {
84         maSchema = aSchema;
85         maSchemaBase = aSchemaBase;
86         maReader = GetStreamReader(aSchemaFile);
87         msBasename = aSchemaFile.getName();
88         maDirectory = aSchemaFile.getParentFile();
89         maLocalNamespaceMap = new HashMap<>();
90         maImportedSchemas = new Vector<>();
91         maLastLocation = null;
92     }
93 
94 
95 
96 
97     /** Parse the schema file.
98      *  @return
99      *      Return false if there is any error.
100      * @throws XMLStreamException
101      */
Parse()102     public void Parse ()
103         throws XMLStreamException
104     {
105         if (maReader == null)
106             return;
107 
108         while (maReader.hasNext())
109         {
110             final int nCode = maReader.next();
111             switch (nCode)
112             {
113                 case XMLStreamReader.START_ELEMENT:
114                     if (maReader.getLocalName().equals("schema"))
115                     {
116                         ProcessSchemaTag();
117                         ParseSchema();
118 
119                         maLastLocation = maReader.getLocation();
120                     }
121                     else
122                     {
123                         throw CreateErrorException("expecting top level element to be 'schema'");
124                     }
125                     break;
126 
127                 case XMLStreamReader.END_DOCUMENT:
128                     return;
129 
130                 default:
131                     throw CreateErrorException("unexpected XML event %d", nCode);
132             }
133         }
134     }
135 
136 
137 
138 
GetImportedSchemaFilenames()139     public Iterable<File> GetImportedSchemaFilenames ()
140     {
141         return maImportedSchemas;
142     }
143 
144 
145 
146 
GetLineCount()147     public int GetLineCount ()
148     {
149         if (maLastLocation != null)
150             return maLastLocation.getLineNumber();
151         else
152             return 0;
153     }
154 
155 
156 
157 
GetByteCount()158     public int GetByteCount ()
159     {
160         if (maLastLocation != null)
161             return maLastLocation.getCharacterOffset();
162         else
163             return 0;
164     }
165 
166 
167 
168 
169     /** Process the namespace definitions in the outer 'schema' element.
170      */
ProcessSchemaTag()171     private void ProcessSchemaTag ()
172     {
173         GetOptionalAttributeValue("id", null);
174         meAttributeFormDefault = FormDefault.valueOf(
175             GetOptionalAttributeValue("attributeFormDefault", "unqualified"));
176         meElementFormDefault = FormDefault.valueOf(
177             GetOptionalAttributeValue("elementFormDefault", "unqualified"));
178         GetOptionalAttributeValue("blockDefault", null);//=(#all|list of (extension|restriction|substitution))
179         GetOptionalAttributeValue("finalDefault", null);//=(#all|list of (extension|restriction|list|union))
180         msTargetNamespace = GetOptionalAttributeValue("targetNamespace", null);
181         GetOptionalAttributeValue("version", null);
182 
183         for (int nIndex=0; nIndex<maReader.getNamespaceCount(); ++nIndex)
184         {
185             final String sPrefix = maReader.getNamespacePrefix(nIndex);
186             final String sURI = maReader.getNamespaceURI(nIndex);
187             maLocalNamespaceMap.put(sPrefix, sURI);
188             maSchemaBase.Namespaces.ProvideNamespace(sURI, sPrefix);
189         }
190 
191         maLocalNamespaceMap.put(null, msTargetNamespace);
192         maSchemaBase.Namespaces.ProvideNamespace(msTargetNamespace, null);
193     }
194 
195 
196 
197 
ParseSchema()198     private void ParseSchema ()
199         throws XMLStreamException
200     {
201         while (true)
202         {
203             switch (Next())
204             {
205                 case XMLStreamReader.START_ELEMENT:
206                     ProcessTopLevelStartElement();
207                     break;
208 
209                 case XMLStreamReader.END_ELEMENT:
210                     return;
211 
212                 default:
213                     throw CreateErrorException("unexpected event (expteced START_ELEMENT): %d", maReader.getEventType());
214             }
215         }
216     }
217 
218 
219 
220 
ProcessTopLevelStartElement()221     private void ProcessTopLevelStartElement ()
222         throws XMLStreamException
223     {
224         assert(GetAttributeValue("minOccurs") == null);
225         assert(GetAttributeValue("maxOccurs") == null);
226 
227         switch (maReader.getLocalName())
228         {
229             case "attribute":
230                 maSchemaBase.Attributes.Add(ParseAttribute());
231                 break;
232 
233             case "attributeGroup":
234                 maSchemaBase.AttributeGroups.Add(ParseAttributeGroup());
235                 break;
236 
237             case "complexType":
238                 maSchemaBase.ComplexTypes.Add(ParseComplexType());
239                 break;
240 
241             case "element":
242             	final Element aElement = ParseElement(null);
243             	if (maSchema != null)
244             		maSchema.TopLevelElements.Add(aElement);
245                 maSchemaBase.TopLevelElements.Add(aElement);
246                 break;
247 
248             case "group":
249                 maSchemaBase.Groups.Add(ParseGroup(null));
250                 break;
251 
252             case "import":
253                 ParseImport();
254                 break;
255 
256             case "include":
257                 ParseInclude();
258                 break;
259 
260             case "simpleType":
261                 maSchemaBase.SimpleTypes.Add(ParseSimpleType(null));
262                 break;
263 
264             default:
265                 throw CreateErrorException("unexpected top level element %s", maReader.getLocalName());
266         }
267     }
268 
269 
270 
271 
ProcessStartElement(final Node aParent)272     private void ProcessStartElement (final Node aParent)
273         throws XMLStreamException
274     {
275         final String sMinimumOccurrence = GetOptionalAttributeValue("minOccurs", "1");
276         final String sMaximumOccurrence = GetOptionalAttributeValue("maxOccurs", "1");
277 
278         final Node aLocalParent;
279         if ( ! (sMinimumOccurrence.equals("1") && sMaximumOccurrence.equals("1")))
280         {
281             // Occurrence does not only have default values (min=max=1).
282             // Have to create an intermediate node for the occurrence indicator.
283             final OccurrenceIndicator aIndicator = new OccurrenceIndicator(
284                 aParent,
285                 sMinimumOccurrence,
286                 sMaximumOccurrence,
287                 GetLocation());
288             aParent.AddChild(aIndicator);
289             aLocalParent = aIndicator;
290         }
291         else
292             aLocalParent = aParent;
293 
294         switch (maReader.getLocalName())
295         {
296             case "all":
297                 aLocalParent.AddChild(ParseAll(aLocalParent));
298                 break;
299 
300             case "any":
301                 aLocalParent.AddChild(ParseAny(aLocalParent));
302                 break;
303 
304             case "attribute":
305                 aLocalParent.AddAttribute(ParseAttributeOrReference());
306                 break;
307 
308             case "attributeGroup":
309                 aLocalParent.AddAttribute(ParseAttributeGroupOrReference());
310                 break;
311 
312             case "choice":
313                 aLocalParent.AddChild(ParseChoice(aLocalParent));
314                 break;
315 
316             case "complexContent":
317                 aLocalParent.AddChild(ParseComplexContent(aLocalParent));
318                 break;
319 
320             case "element":
321                 aLocalParent.AddChild(ParseElementOrReference(aLocalParent));
322                 break;
323 
324             case "extension":
325                 aLocalParent.AddChild(ParseExtension(aLocalParent));
326                 break;
327 
328             case "group":
329                 aLocalParent.AddChild(ParseGroupOrReference(aLocalParent));
330                 break;
331 
332             case "list":
333                 aLocalParent.AddChild(ParseList(aLocalParent));
334                 break;
335 
336             case "sequence":
337                 aLocalParent.AddChild(ParseSequence(aLocalParent));
338                 break;
339 
340             case "simpleContent":
341                 aLocalParent.AddChild(ParseSimpleContent(aLocalParent));
342                 break;
343 
344             default:
345                 throw CreateErrorException("unsupported content type %s", maReader.getLocalName());
346         }
347     }
348 
349 
350 
351 
IgnoreAnnotation()352     private void IgnoreAnnotation ()
353         throws XMLStreamException
354     {
355         IgnoreContent();
356     }
357 
358 
359 
360 
IgnoreContent()361     private void IgnoreContent ()
362         throws XMLStreamException
363     {
364         while(true)
365         {
366             switch(maReader.next())
367             {
368                 case XMLStreamReader.START_ELEMENT:
369                     IgnoreContent();
370                     break;
371 
372                 case XMLStreamReader.END_ELEMENT:
373                     return;
374 
375                 case XMLStreamReader.COMMENT:
376                 case XMLStreamReader.CHARACTERS:
377                     break;
378 
379                 default:
380                     throw CreateErrorException(
381                         "unexpected XML event %d while ignoring content",
382                         maReader.getEventType());
383             }
384         }
385     }
386 
387 
388 
389 
ParseAll(final Node aParent)390     private All ParseAll (final Node aParent)
391         throws XMLStreamException
392     {
393         assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
394 
395         final All aAll = new All(aParent, GetLocation());
396         ParseContent(aAll);
397         return aAll;
398     }
399 
400 
401 
402 
ParseAny(final Node aParent)403     private Any ParseAny (final Node aParent)
404         throws XMLStreamException
405     {
406         assert(HasOnlyAttributes("minOccurs", "maxOccurs", "namespace", "processContents"));
407 
408         final Any aAny = new Any(
409             aParent,
410             GetLocation(),
411             GetOptionalAttributeValue("processContents", "strict"),
412             GetOptionalAttributeValue("namespace", "##any"));
413         ExpectEndElement("ParseAny");
414         return aAny;
415     }
416 
417 
418 
419 
ParseAttributeGroup()420     private AttributeGroup ParseAttributeGroup ()
421         throws XMLStreamException
422     {
423         assert(HasOnlyAttributes("name"));
424 
425         final AttributeGroup aGroup = new AttributeGroup(GetOptionalQualifiedName("name"), GetLocation());
426 
427         while (true)
428         {
429             switch (Next())
430             {
431                 case XMLStreamReader.START_ELEMENT:
432                     if ( ! maReader.getLocalName().equals("attribute"))
433                         throw CreateErrorException(
434                             "attributeGroup expects element 'attribute' but got %s",
435                             maReader.getLocalName());
436                     else
437                         aGroup.AddAttribute(ParseAttributeOrReference());
438                     break;
439 
440                 case XMLStreamReader.END_ELEMENT:
441                     return aGroup;
442 
443                 default:
444                     throw CreateErrorException(
445                         "unexpected event when parsing attributeGroup: %d",
446                         maReader.getEventType());
447             }
448         }
449     }
450 
451 
452 
453 
ParseAttributeGroupOrReference()454     private INode ParseAttributeGroupOrReference ()
455         throws XMLStreamException
456     {
457         assert(HasOnlyAttributes("ref"));
458 
459         final INode aGroup;
460         if (GetAttributeValue("schemaLocation") != null)
461         {
462             aGroup = ParseAttributeGroup();
463         }
464         else
465         {
466             aGroup = new AttributeGroupReference(
467                 CreateQualifiedName(
468                     GetAttributeValue("ref")),
469                 GetLocation());
470             ExpectEndElement("attribute group or reference");
471         }
472         return aGroup;
473     }
474 
475 
476 
477 
ParseAttributeOrReference()478     private INode ParseAttributeOrReference ()
479         throws XMLStreamException
480     {
481         final INode aAttribute;
482         if (GetAttributeValue("name") != null)
483         {
484             aAttribute = ParseAttribute();
485         }
486         else
487         {
488             assert(HasOnlyAttributes("default", "fixed", "ref", "use"));
489 
490             aAttribute = new AttributeReference(
491                 GetQualifiedName("ref"),
492                 GetOptionalAttributeValue("use", "optional"),
493                 GetOptionalAttributeValue("default", null),
494                 GetOptionalAttributeValue("fixed", null),
495                 meAttributeFormDefault,
496                 GetLocation());
497             ExpectEndElement("attribute reference");
498         }
499         return aAttribute;
500     }
501 
502 
503 
504 
ParseAttribute()505     private Attribute ParseAttribute ()
506         throws XMLStreamException
507     {
508         assert(HasOnlyAttributes("default", "fixed", "name", "type", "use"));
509 
510         final Attribute aAttribute = new Attribute(
511             GetQualifiedName("name"),
512             GetQualifiedName("type"),
513             GetOptionalAttributeValue("use", "optional"),
514             GetOptionalAttributeValue("default", null),
515             GetOptionalAttributeValue("fixed", null),
516             meAttributeFormDefault,
517             GetLocation());
518         ExpectEndElement("attribute");
519 
520         return aAttribute;
521     }
522 
523 
524 
ParseChoice(final Node aParent)525     private Choice ParseChoice (final Node aParent)
526         throws XMLStreamException
527     {
528         assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
529 
530         final Choice aChoice = new Choice(aParent, GetLocation());
531         ParseContent(aChoice);
532         return aChoice;
533     }
534 
535 
536 
537 
ParseComplexContent(final Node aParent)538     private ComplexContent ParseComplexContent (final Node aParent)
539         throws XMLStreamException
540     {
541         assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
542 
543         final ComplexContent aNode = new ComplexContent(aParent, GetLocation());
544         ParseContent(aNode);
545         return aNode;
546     }
547 
548 
549 
550 
ParseComplexType()551     private ComplexType ParseComplexType ()
552         throws XMLStreamException
553     {
554         assert(HasOnlyAttributes("mixed", "name"));
555 
556         final ComplexType aComplexType = new ComplexType(
557             null,
558             GetQualifiedName("name"),
559             GetLocation());
560 
561         ParseContent(aComplexType);
562 
563         return aComplexType;
564     }
565 
566 
567 
568 
ParseElement(final Node aParent)569     private Element ParseElement (final Node aParent)
570         throws XMLStreamException
571     {
572         assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "type"));
573 
574         final Element aElement = new Element(
575             aParent,
576             GetQualifiedName("name"),
577             GetQualifiedName("type"),
578             GetLocation());
579 
580         ExpectEndElement("element");
581 
582         return aElement;
583     }
584 
585 
586 
587 
ParseElementOrReference(final Node aParent)588     private Element ParseElementOrReference (final Node aParent)
589         throws XMLStreamException
590     {
591         assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref", "type"));
592 
593         final Element aElement;
594         final String sName = GetOptionalAttributeValue("name", null);
595         if (sName != null)
596         {
597             aElement = ParseElement(aParent);
598         }
599         else
600         {
601             final String sElementReference = GetOptionalAttributeValue("ref",  null);
602             if (sElementReference != null)
603             {
604                 ExpectEndElement("element reference");
605                 aElement = new ElementReference(
606                     aParent,
607                     CreateQualifiedName(sElementReference),
608                     GetLocation());
609             }
610             else
611             {
612                 throw CreateErrorException("element has no name and no ref");
613             }
614         }
615         return aElement;
616     }
617 
618 
619 
620 
ParseExtension(final Node aParent)621     private Extension ParseExtension (final Node aParent)
622         throws XMLStreamException
623     {
624         assert(HasOnlyAttributes("base", "minOccurs", "maxOccurs"));
625 
626         final Extension aNode = new Extension(
627             aParent,
628             CreateQualifiedName(GetAttributeValue("base")),
629             GetLocation());
630         ParseContent(aNode);
631         return aNode;
632     }
633 
634 
635 
636 
ParseGroup(final Node aParent)637     private Group ParseGroup (final Node aParent)
638         throws XMLStreamException
639     {
640         assert(HasOnlyAttributes("name"));
641 
642         final Group aGroup = new Group(
643             aParent,
644             GetQualifiedName("name"),
645             GetLocation());
646         ParseContent(aGroup);
647         return aGroup;
648     }
649 
650 
651 
652 
ParseGroupOrReference(final Node aParent)653     private Node ParseGroupOrReference (final Node aParent)
654         throws XMLStreamException
655     {
656         assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref"));
657 
658         final Node aGroup;
659         final String sName = GetOptionalAttributeValue("name", null);
660         if (sName != null)
661         {
662             aGroup = ParseGroup(aParent);
663         }
664         else
665         {
666             final String sGroupReference = GetOptionalAttributeValue("ref",  null);
667             if (sGroupReference != null)
668             {
669                 ExpectEndElement("group reference");
670                 aGroup = new GroupReference(
671                     aParent,
672                     CreateQualifiedName(sGroupReference),
673                     GetLocation());
674             }
675             else
676             {
677                 throw CreateErrorException("group has no name and no ref");
678             }
679         }
680         return aGroup;
681     }
682 
683 
684 
685 
ParseRestriction(final Node aParent)686     private Restriction ParseRestriction (final Node aParent)
687         throws XMLStreamException
688     {
689         assert(HasOnlyAttributes("base", "value"));
690 
691         final String sBaseType = GetAttributeValue("base");
692         final Restriction aRestriction = new Restriction(
693             aParent,
694             CreateQualifiedName(sBaseType),
695             GetLocation());
696 
697         while (true)
698         {
699             switch (Next())
700             {
701                 case XMLStreamReader.START_ELEMENT:
702                     final String sValue = GetAttributeValue("value");
703                     switch (maReader.getLocalName())
704                     {
705                         case "enumeration":
706                             aRestriction.AddEnumeration(sValue);
707                             break;
708 
709                         case "minInclusive":
710                             aRestriction.SetMinInclusive(sValue);
711                             break;
712 
713                         case "minExclusive":
714                             aRestriction.SetMinExclusive(sValue);
715                             break;
716 
717                         case "maxInclusive":
718                             aRestriction.SetMaxInclusive(sValue);
719                             break;
720 
721                         case "maxExclusive":
722                             aRestriction.SetMaxExclusive(sValue);
723                             break;
724 
725                         case "length":
726                             aRestriction.SetLength(sValue);
727                             break;
728 
729                         case "minLength":
730                             aRestriction.SetMinLength(sValue);
731                             break;
732 
733                         case "maxLength":
734                             aRestriction.SetMaxLength(sValue);
735                             break;
736 
737                         case "pattern":
738                             aRestriction.SetPattern(sValue);
739                             break;
740 
741                         default:
742                             throw CreateErrorException("unsupported restriction type "+maReader.getLocalName());
743                     }
744                     ExpectEndElement("restriction");
745                     break;
746 
747                 case XMLStreamReader.END_ELEMENT:
748                     return aRestriction;
749 
750                 default:
751                     throw CreateErrorException("unexpected XML event while parsing restrictions: %d", maReader.getEventType());
752             }
753         }
754     }
755 
756 
757 
758 
ParseList(final Node aParent)759     private List ParseList (final Node aParent)
760         throws XMLStreamException
761     {
762         assert(HasOnlyAttributes("itemType"));
763 
764         final List aList = new List(
765             aParent,
766             GetQualifiedName("itemType"),
767             GetLocation());
768         ExpectEndElement("list");
769         return aList;
770     }
771 
772 
773 
774 
ParseSequence(final Node aParent)775     private Sequence ParseSequence (final Node aParent)
776         throws XMLStreamException
777     {
778         assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name"));
779 
780         final Sequence aSequence = new Sequence(
781             aParent,
782             GetOptionalQualifiedName("name"),
783             GetLocation());
784         ParseContent(aSequence);
785         return aSequence;
786     }
787 
788 
789 
790 
ParseSimpleContent(final Node aParent)791     private SimpleContent ParseSimpleContent (final Node aParent)
792         throws XMLStreamException
793     {
794         assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
795 
796         final SimpleContent aSimpleContent = new SimpleContent(
797             aParent,
798             GetLocation());
799         ParseContent(aSimpleContent);
800         return aSimpleContent;
801     }
802 
803 
804 
805 
ParseSimpleType(final Node aParent)806     private SimpleType ParseSimpleType (final Node aParent)
807         throws XMLStreamException
808     {
809         assert(HasOnlyAttributes("final", "name"));
810 
811         final SimpleType aType = new SimpleType(
812             aParent,
813             GetQualifiedName("name"),
814             GetLocation());
815         final String sFinalValue = GetOptionalAttributeValue("final", null);
816         if (sFinalValue != null)
817             aType.SetFinal(sFinalValue.split("\\s+"));
818 
819         while (true)
820         {
821             switch (Next())
822             {
823                 case XMLStreamReader.START_ELEMENT:
824                     switch(maReader.getLocalName())
825                     {
826                         case "list":
827                             aType.AddChild(ParseList(aType));
828                             break;
829 
830                         case "restriction":
831                             aType.AddChild(ParseRestriction(aType));
832                             break;
833 
834                         case "union":
835                             aType.AddChild(ParseUnion(aType));
836                             break;
837 
838                         default:
839                             throw CreateErrorException("unsupported simple type part %s", maReader.getLocalName());
840                     }
841                     break;
842 
843                 case XMLStreamReader.END_ELEMENT:
844                     return aType;
845 
846                 default:
847                     throw CreateErrorException("unexpected XML event in ParseSimpleType: %s", maReader.getEventType());
848             }
849         }
850     }
851 
852 
853 
854 
ParseUnion(final Node aParent)855     private Union ParseUnion (final Node aParent)
856         throws XMLStreamException
857     {
858         assert(HasOnlyAttributes("memberTypes"));
859 
860         final Union aUnion = new Union(
861             aParent,
862             GetLocation());
863         final String[] aMemberTypes = GetAttributeValue("memberTypes").split("\\s+");
864         for (int nIndex=0; nIndex<aMemberTypes.length; ++nIndex)
865             aUnion.AddChild(
866                 new SimpleTypeReference(
867                     aUnion,
868                     CreateQualifiedName(aMemberTypes[nIndex]),
869                     GetLocation()));
870 
871         ParseContent(aUnion);
872 
873         return aUnion;
874     }
875 
876 
ParseContent(final Node aParent)877     private void ParseContent (final Node aParent)
878         throws XMLStreamException
879     {
880         while(true)
881         {
882             switch(Next())
883             {
884                 case XMLStreamReader.START_ELEMENT:
885                     ProcessStartElement(aParent);
886                     break;
887 
888                 case XMLStreamReader.END_ELEMENT:
889                     return;
890 
891                 default:
892                     throw CreateErrorException(
893                         "unexpected XML event %d while parsing content of %s",
894                         maReader.getEventType(),
895                         aParent.toString());
896             }
897         }
898     }
899 
900 
901 
902 
ParseImport()903     private void ParseImport ()
904         throws XMLStreamException
905     {
906         assert(HasOnlyAttributes("id", "namespace", "schemaLocation"));
907 
908         final String sFilename = GetOptionalAttributeValue("schemaLocation", null);
909         if (sFilename == null)
910         {
911             final String sNamespaceName = GetAttributeValue("namespace");
912             if (sNamespaceName.equals(XmlNamespace.URI))
913             {
914                 XmlNamespace.Apply(maSchemaBase);
915                 maLocalNamespaceMap.put(XmlNamespace.Prefix, XmlNamespace.URI);
916             }
917             else
918                 throw CreateErrorException("invalid import");
919         }
920         else
921         {
922             maImportedSchemas.add(new File(maDirectory, sFilename));
923         }
924 
925         ExpectEndElement("import");
926     }
927 
928 
929 
930 
ParseInclude()931     private void ParseInclude ()
932         throws XMLStreamException
933     {
934         assert(HasOnlyAttributes("id", "schemaLocation"));
935 
936         final String sFilename = GetOptionalAttributeValue("schemaLocation", null);
937         if (sFilename == null)
938         {
939             throw CreateErrorException("invalid include");
940         }
941         else
942         {
943             maImportedSchemas.add(new File(maDirectory, sFilename));
944         }
945 
946         ExpectEndElement("include");
947     }
948 
949 
950 
951 
ExpectEndElement(final String sCaller)952     private void ExpectEndElement (final String sCaller)
953         throws XMLStreamException
954     {
955         final int nNextEvent = Next();
956         if (nNextEvent != XMLStreamReader.END_ELEMENT)
957             throw CreateErrorException("expected END_ELEMENT of %s but got %s",
958                 sCaller,
959                 nNextEvent);
960     }
961 
962 
963 
964 
965     /** Return the next relevant token from the XML stream.
966      *  Ignores comments.
967      *  @return
968      *      Returns the event code of the next relevant XML event or END_DOCUMENT when the end of the file has been reached.
969      */
Next()970     private int Next ()
971         throws XMLStreamException
972     {
973         while (maReader.hasNext())
974         {
975             switch (maReader.next())
976             {
977                 case XMLStreamReader.COMMENT:
978                     // Ignore comments.
979                     break;
980 
981                 case XMLStreamReader.CHARACTERS:
982                     // Ignore whitespace.
983                     if (maReader.getText().matches("^\\s+$"))
984                         break;
985                     else
986                     {
987                         // Character events are not expected in schema files
988                         // and therefore not supported.
989                         // Alternatively, they could easily be ignored.
990                         throw CreateErrorException("unexpected CHARACTERS event with text [%s]", maReader.getText());
991                     }
992 
993                 case XMLStreamReader.START_ELEMENT:
994                     switch (maReader.getLocalName())
995                     {
996                         case "annotation":
997                             IgnoreAnnotation();
998                             break;
999                         case "unique":
1000                             // Not supported.
1001                             IgnoreContent();
1002                             break;
1003                         default:
1004                             return XMLStreamReader.START_ELEMENT;
1005                     }
1006                     break;
1007 
1008                 default:
1009                     return maReader.getEventType();
1010             }
1011         }
1012         return XMLStreamReader.END_DOCUMENT;
1013     }
1014 
1015 
1016 
1017 
GetAttributeValue(final String sAttributeLocalName)1018     private String GetAttributeValue (final String sAttributeLocalName)
1019     {
1020         return maReader.getAttributeValue(null, sAttributeLocalName);
1021     }
1022 
1023 
1024 
1025 
GetOptionalAttributeValue( final String sAttributeLocalName, final String sDefaultValue)1026     private String GetOptionalAttributeValue (
1027         final String sAttributeLocalName,
1028         final String sDefaultValue)
1029     {
1030         final String sValue = maReader.getAttributeValue(null, sAttributeLocalName);
1031         if (sValue == null)
1032             return sDefaultValue;
1033         else
1034             return sValue;
1035     }
1036 
1037 
1038 
1039 
1040     /** Read the specified attribute and return its value as QualifiedName object.
1041      */
GetQualifiedName(final String sAttributeLocalName)1042     private QualifiedName GetQualifiedName (final String sAttributeLocalName)
1043     {
1044         final String sName = maReader.getAttributeValue(null, sAttributeLocalName);
1045         if (sName == null)
1046             throw CreateErrorException(
1047                 "did not find a qualified name as value of attribute '%s' at %s:%s",
1048                 sAttributeLocalName,
1049                 msBasename,
1050                 maReader.getLocation());
1051         else
1052             return CreateQualifiedName(sName);
1053     }
1054 
1055 
1056 
1057 
GetOptionalQualifiedName(final String sAttributeLocalName)1058     private QualifiedName GetOptionalQualifiedName (final String sAttributeLocalName)
1059     {
1060         final String sName = maReader.getAttributeValue(null, sAttributeLocalName);
1061         if (sName == null)
1062             return null;
1063         else
1064             return CreateQualifiedName(sName);
1065     }
1066 
1067 
1068 
1069 
1070     /** Create a QualifiedName object from the given string.
1071      * @param sName
1072      *     May or may not contain a namespace prefix (separated from the name
1073      *     by a colon).
1074      */
CreateQualifiedName(final String sName)1075     private QualifiedName CreateQualifiedName (final String sName)
1076     {
1077         final String[] aParts = sName.split(":");
1078         final String sNamespaceURL;
1079         final String sLocalPart;
1080         switch (aParts.length)
1081         {
1082             case 1:
1083                 // sName only consists of a local part.
1084                 // Use the target namespace as namespace.
1085                 sNamespaceURL = msTargetNamespace;
1086                 sLocalPart = aParts[0];
1087                 break;
1088 
1089             case 2:
1090                 // sName is of the form prefix:local.
1091                 sNamespaceURL = maLocalNamespaceMap.get(aParts[0]);
1092                 sLocalPart = aParts[1];
1093                 break;
1094 
1095             default:
1096                 throw new RuntimeException(
1097                     "the string '"+sName+"' can not be transformed into a qualified name");
1098         }
1099         if (sNamespaceURL == null)
1100             throw CreateErrorException("can not determine namespace for '%s'", sName);
1101         // Transform the local namespace prefix into a global namespace prefix
1102         // (different schema files can use different prefixes for the same
1103         // namespace URI).
1104         final String sGlobalNamespacePrefix = maSchemaBase.Namespaces.GetNamespacePrefix(sNamespaceURL);
1105         return new QualifiedName(
1106             sNamespaceURL,
1107             sGlobalNamespacePrefix,
1108             sLocalPart);
1109     }
1110 
1111 
1112 
1113 
1114     /** Create an XMLStreamReader for the given file.
1115      *  Returns null when there is an error.
1116      */
GetStreamReader(final File aSchemaFile)1117     private static XMLStreamReader GetStreamReader (final File aSchemaFile)
1118     {
1119         final XMLInputFactory aFactory = (XMLInputFactory)XMLInputFactory.newInstance();
1120         aFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
1121         aFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
1122         aFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
1123 
1124         try
1125         {
1126             return aFactory.createXMLStreamReader(
1127                 aSchemaFile.getName(),
1128                 new FileInputStream(aSchemaFile));
1129         }
1130         catch (Exception aException)
1131         {
1132             aException.printStackTrace();
1133             return null;
1134         }
1135     }
1136 
1137 
1138 
1139 
CreateErrorException( final String sFormat, final Object ... aArgumentList)1140     private RuntimeException CreateErrorException (
1141         final String sFormat,
1142         final Object ... aArgumentList)
1143     {
1144         return new RuntimeException(String.format(sFormat, aArgumentList)
1145             + String.format(" in %s at L%dC%dP%d",
1146                 msBasename,
1147                 maReader.getLocation().getLineNumber(),
1148                 maReader.getLocation().getColumnNumber(),
1149                 maReader.getLocation().getCharacterOffset()));
1150     }
1151 
1152 
1153 
1154 
1155     /** This predicate is only used for debugging to assert
1156      *  that no unsupported attributes are present.
1157      *  If there where then the parser has to be extended.
1158      */
HasOnlyAttributes(final String ... aAttributeNameList)1159     private boolean HasOnlyAttributes (final String ... aAttributeNameList)
1160     {
1161         for (int nIndex=0,nCount=maReader.getAttributeCount(); nIndex<nCount; ++nIndex)
1162         {
1163             final String sAttributeName = maReader.getAttributeLocalName(nIndex);
1164             boolean bFound = false;
1165             for (final String sName : aAttributeNameList)
1166                 if (sAttributeName.equals(sName))
1167                 {
1168                     bFound = true;
1169                     break;
1170                 }
1171             if ( ! bFound)
1172             {
1173                 // Attribute name was not found in the given list.
1174                 System.err.printf("attribute '%s' is not handled\n", sAttributeName);
1175                 return false;
1176             }
1177         }
1178         return true;
1179     }
1180 
1181 
1182 
1183 
GetLocation()1184     private Location GetLocation ()
1185     {
1186         final javax.xml.stream.Location aLocation = maReader.getLocation();
1187         return new Location(
1188             msBasename,
1189             aLocation.getLineNumber(),
1190             aLocation.getColumnNumber(),
1191             aLocation.getCharacterOffset());
1192     }
1193 
1194 
1195 
1196 
1197     private final Schema maSchema;
1198     private final SchemaBase maSchemaBase;
1199     private final XMLStreamReader maReader;
1200     private final String msBasename;
1201     private final File maDirectory;
1202     private String msTargetNamespace;
1203     /// Map of namespace prefix to URI, local to the currently read schema file.
1204     private final Map<String,String> maLocalNamespaceMap;
1205     /// The names of other schema files referenced by import elements.
1206     private final Vector<File> maImportedSchemas;
1207     /// Some values for tracking the number of read bytes and lines.
1208     private javax.xml.stream.Location maLastLocation;
1209     private FormDefault meAttributeFormDefault;
1210     private FormDefault meElementFormDefault;
1211 }
1212