summaryrefslogtreecommitdiff
path: root/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java')
-rw-r--r--ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java1211
1 files changed, 1211 insertions, 0 deletions
diff --git a/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java
new file mode 100644
index 000000000000..6e7bbb172691
--- /dev/null
+++ b/ooxml/source/framework/SchemaParser/src/org/apache/openoffice/ooxml/schema/parser/SchemaParser.java
@@ -0,0 +1,1211 @@
+/**************************************************************
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*************************************************************/
+
+package org.apache.openoffice.ooxml.schema.parser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.openoffice.ooxml.schema.model.attribute.Attribute;
+import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroup;
+import org.apache.openoffice.ooxml.schema.model.attribute.AttributeGroupReference;
+import org.apache.openoffice.ooxml.schema.model.attribute.AttributeReference;
+import org.apache.openoffice.ooxml.schema.model.base.INode;
+import org.apache.openoffice.ooxml.schema.model.base.Location;
+import org.apache.openoffice.ooxml.schema.model.base.Node;
+import org.apache.openoffice.ooxml.schema.model.base.QualifiedName;
+import org.apache.openoffice.ooxml.schema.model.complex.All;
+import org.apache.openoffice.ooxml.schema.model.complex.Any;
+import org.apache.openoffice.ooxml.schema.model.complex.Choice;
+import org.apache.openoffice.ooxml.schema.model.complex.ComplexContent;
+import org.apache.openoffice.ooxml.schema.model.complex.ComplexType;
+import org.apache.openoffice.ooxml.schema.model.complex.Element;
+import org.apache.openoffice.ooxml.schema.model.complex.ElementReference;
+import org.apache.openoffice.ooxml.schema.model.complex.Extension;
+import org.apache.openoffice.ooxml.schema.model.complex.Group;
+import org.apache.openoffice.ooxml.schema.model.complex.GroupReference;
+import org.apache.openoffice.ooxml.schema.model.complex.OccurrenceIndicator;
+import org.apache.openoffice.ooxml.schema.model.complex.Sequence;
+import org.apache.openoffice.ooxml.schema.model.schema.Schema;
+import org.apache.openoffice.ooxml.schema.model.schema.SchemaBase;
+import org.apache.openoffice.ooxml.schema.model.simple.List;
+import org.apache.openoffice.ooxml.schema.model.simple.Restriction;
+import org.apache.openoffice.ooxml.schema.model.simple.SimpleContent;
+import org.apache.openoffice.ooxml.schema.model.simple.SimpleType;
+import org.apache.openoffice.ooxml.schema.model.simple.SimpleTypeReference;
+import org.apache.openoffice.ooxml.schema.model.simple.Union;
+
+
+/** Parser for single schema file.
+ * Imports and includes of other schema files are stored and can be retrieved
+ * by calling GetImportedSchemas().
+ *
+ * Typical usage:
+ * 1) Create SchemaParser for top-level schema file.
+ * 2) Call Parse().
+ * 3) Repeat the same recursively for all imported schemas (as returned by
+ * GetImportedSchemas()).
+ *
+ * All top level types (complex types, simple types, elements, etc.) are
+ * stored in the given Schema object.
+ */
+public class SchemaParser
+{
+ public SchemaParser (
+ final File aSchemaFile,
+ final Schema aSchema,
+ final SchemaBase aSchemaBase)
+ {
+ maSchema = aSchema;
+ maSchemaBase = aSchemaBase;
+ maReader = GetStreamReader(aSchemaFile);
+ msBasename = aSchemaFile.getName();
+ maDirectory = aSchemaFile.getParentFile();
+ maLocalNamespaceMap = new HashMap<>();
+ maImportedSchemas = new Vector<>();
+ maLastLocation = null;
+ }
+
+
+
+
+ /** Parse the schema file.
+ * @return
+ * Return false if there is any error.
+ * @throws XMLStreamException
+ */
+ public void Parse ()
+ throws XMLStreamException
+ {
+ if (maReader == null)
+ return;
+
+ while (maReader.hasNext())
+ {
+ final int nCode = maReader.next();
+ switch (nCode)
+ {
+ case XMLStreamReader.START_ELEMENT:
+ if (maReader.getLocalName().equals("schema"))
+ {
+ ProcessSchemaTag();
+ ParseSchema();
+
+ maLastLocation = maReader.getLocation();
+ }
+ else
+ {
+ throw CreateErrorException("expecting top level element to be 'schema'");
+ }
+ break;
+
+ case XMLStreamReader.END_DOCUMENT:
+ return;
+
+ default:
+ throw CreateErrorException("unexpected XML event %d", nCode);
+ }
+ }
+ }
+
+
+
+
+ public Iterable<File> GetImportedSchemaFilenames ()
+ {
+ return maImportedSchemas;
+ }
+
+
+
+
+ public int GetLineCount ()
+ {
+ if (maLastLocation != null)
+ return maLastLocation.getLineNumber();
+ else
+ return 0;
+ }
+
+
+
+
+ public int GetByteCount ()
+ {
+ if (maLastLocation != null)
+ return maLastLocation.getCharacterOffset();
+ else
+ return 0;
+ }
+
+
+
+
+ /** Process the namespace definitions in the outer 'schema' element.
+ */
+ private void ProcessSchemaTag ()
+ {
+ GetOptionalAttributeValue("id", null);
+ meAttributeFormDefault = FormDefault.valueOf(
+ GetOptionalAttributeValue("attributeFormDefault", "unqualified"));
+ meElementFormDefault = FormDefault.valueOf(
+ GetOptionalAttributeValue("elementFormDefault", "unqualified"));
+ GetOptionalAttributeValue("blockDefault", null);//=(#all|list of (extension|restriction|substitution))
+ GetOptionalAttributeValue("finalDefault", null);//=(#all|list of (extension|restriction|list|union))
+ msTargetNamespace = GetOptionalAttributeValue("targetNamespace", null);
+ GetOptionalAttributeValue("version", null);
+
+ for (int nIndex=0; nIndex<maReader.getNamespaceCount(); ++nIndex)
+ {
+ final String sPrefix = maReader.getNamespacePrefix(nIndex);
+ final String sURI = maReader.getNamespaceURI(nIndex);
+ maLocalNamespaceMap.put(sPrefix, sURI);
+ maSchemaBase.Namespaces.ProvideNamespace(sURI, sPrefix);
+ }
+
+ maLocalNamespaceMap.put(null, msTargetNamespace);
+ maSchemaBase.Namespaces.ProvideNamespace(msTargetNamespace, null);
+ }
+
+
+
+
+ private void ParseSchema ()
+ throws XMLStreamException
+ {
+ while (true)
+ {
+ switch (Next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ ProcessTopLevelStartElement();
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return;
+
+ default:
+ throw CreateErrorException("unexpected event (expteced START_ELEMENT): %d", maReader.getEventType());
+ }
+ }
+ }
+
+
+
+
+ private void ProcessTopLevelStartElement ()
+ throws XMLStreamException
+ {
+ assert(GetAttributeValue("minOccurs") == null);
+ assert(GetAttributeValue("maxOccurs") == null);
+
+ switch (maReader.getLocalName())
+ {
+ case "attribute":
+ maSchemaBase.Attributes.Add(ParseAttribute());
+ break;
+
+ case "attributeGroup":
+ maSchemaBase.AttributeGroups.Add(ParseAttributeGroup());
+ break;
+
+ case "complexType":
+ maSchemaBase.ComplexTypes.Add(ParseComplexType());
+ break;
+
+ case "element":
+ final Element aElement = ParseElement(null);
+ if (maSchema != null)
+ maSchema.TopLevelElements.Add(aElement);
+ maSchemaBase.TopLevelElements.Add(aElement);
+ break;
+
+ case "group":
+ maSchemaBase.Groups.Add(ParseGroup(null));
+ break;
+
+ case "import":
+ ParseImport();
+ break;
+
+ case "include":
+ ParseInclude();
+ break;
+
+ case "simpleType":
+ maSchemaBase.SimpleTypes.Add(ParseSimpleType(null));
+ break;
+
+ default:
+ throw CreateErrorException("unexpected top level element %s", maReader.getLocalName());
+ }
+ }
+
+
+
+
+ private void ProcessStartElement (final Node aParent)
+ throws XMLStreamException
+ {
+ final String sMinimumOccurrence = GetOptionalAttributeValue("minOccurs", "1");
+ final String sMaximumOccurrence = GetOptionalAttributeValue("maxOccurs", "1");
+
+ final Node aLocalParent;
+ if ( ! (sMinimumOccurrence.equals("1") && sMaximumOccurrence.equals("1")))
+ {
+ // Occurrence does not only have default values (min=max=1).
+ // Have to create an intermediate node for the occurrence indicator.
+ final OccurrenceIndicator aIndicator = new OccurrenceIndicator(
+ aParent,
+ sMinimumOccurrence,
+ sMaximumOccurrence,
+ GetLocation());
+ aParent.AddChild(aIndicator);
+ aLocalParent = aIndicator;
+ }
+ else
+ aLocalParent = aParent;
+
+ switch (maReader.getLocalName())
+ {
+ case "all":
+ aLocalParent.AddChild(ParseAll(aLocalParent));
+ break;
+
+ case "any":
+ aLocalParent.AddChild(ParseAny(aLocalParent));
+ break;
+
+ case "attribute":
+ aLocalParent.AddAttribute(ParseAttributeOrReference());
+ break;
+
+ case "attributeGroup":
+ aLocalParent.AddAttribute(ParseAttributeGroupOrReference());
+ break;
+
+ case "choice":
+ aLocalParent.AddChild(ParseChoice(aLocalParent));
+ break;
+
+ case "complexContent":
+ aLocalParent.AddChild(ParseComplexContent(aLocalParent));
+ break;
+
+ case "element":
+ aLocalParent.AddChild(ParseElementOrReference(aLocalParent));
+ break;
+
+ case "extension":
+ aLocalParent.AddChild(ParseExtension(aLocalParent));
+ break;
+
+ case "group":
+ aLocalParent.AddChild(ParseGroupOrReference(aLocalParent));
+ break;
+
+ case "list":
+ aLocalParent.AddChild(ParseList(aLocalParent));
+ break;
+
+ case "sequence":
+ aLocalParent.AddChild(ParseSequence(aLocalParent));
+ break;
+
+ case "simpleContent":
+ aLocalParent.AddChild(ParseSimpleContent(aLocalParent));
+ break;
+
+ default:
+ throw CreateErrorException("unsupported content type %s", maReader.getLocalName());
+ }
+ }
+
+
+
+
+ private void IgnoreAnnotation ()
+ throws XMLStreamException
+ {
+ IgnoreContent();
+ }
+
+
+
+
+ private void IgnoreContent ()
+ throws XMLStreamException
+ {
+ while(true)
+ {
+ switch(maReader.next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ IgnoreContent();
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return;
+
+ case XMLStreamReader.COMMENT:
+ case XMLStreamReader.CHARACTERS:
+ break;
+
+ default:
+ throw CreateErrorException(
+ "unexpected XML event %d while ignoring content",
+ maReader.getEventType());
+ }
+ }
+ }
+
+
+
+
+ private All ParseAll (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
+
+ final All aAll = new All(aParent, GetLocation());
+ ParseContent(aAll);
+ return aAll;
+ }
+
+
+
+
+ private Any ParseAny (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs", "namespace", "processContents"));
+
+ final Any aAny = new Any(
+ aParent,
+ GetLocation(),
+ GetOptionalAttributeValue("processContents", "strict"),
+ GetOptionalAttributeValue("namespace", "##any"));
+ ExpectEndElement("ParseAny");
+ return aAny;
+ }
+
+
+
+
+ private AttributeGroup ParseAttributeGroup ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("name"));
+
+ final AttributeGroup aGroup = new AttributeGroup(GetOptionalQualifiedName("name"), GetLocation());
+
+ while (true)
+ {
+ switch (Next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ if ( ! maReader.getLocalName().equals("attribute"))
+ throw CreateErrorException(
+ "attributeGroup expects element 'attribute' but got %s",
+ maReader.getLocalName());
+ else
+ aGroup.AddAttribute(ParseAttributeOrReference());
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return aGroup;
+
+ default:
+ throw CreateErrorException(
+ "unexpected event when parsing attributeGroup: %d",
+ maReader.getEventType());
+ }
+ }
+ }
+
+
+
+
+ private INode ParseAttributeGroupOrReference ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("ref"));
+
+ final INode aGroup;
+ if (GetAttributeValue("schemaLocation") != null)
+ {
+ aGroup = ParseAttributeGroup();
+ }
+ else
+ {
+ aGroup = new AttributeGroupReference(
+ CreateQualifiedName(
+ GetAttributeValue("ref")),
+ GetLocation());
+ ExpectEndElement("attribute group or reference");
+ }
+ return aGroup;
+ }
+
+
+
+
+ private INode ParseAttributeOrReference ()
+ throws XMLStreamException
+ {
+ final INode aAttribute;
+ if (GetAttributeValue("name") != null)
+ {
+ aAttribute = ParseAttribute();
+ }
+ else
+ {
+ assert(HasOnlyAttributes("default", "fixed", "ref", "use"));
+
+ aAttribute = new AttributeReference(
+ GetQualifiedName("ref"),
+ GetOptionalAttributeValue("use", "optional"),
+ GetOptionalAttributeValue("default", null),
+ GetOptionalAttributeValue("fixed", null),
+ meAttributeFormDefault,
+ GetLocation());
+ ExpectEndElement("attribute reference");
+ }
+ return aAttribute;
+ }
+
+
+
+
+ private Attribute ParseAttribute ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("default", "fixed", "name", "type", "use"));
+
+ final Attribute aAttribute = new Attribute(
+ GetQualifiedName("name"),
+ GetQualifiedName("type"),
+ GetOptionalAttributeValue("use", "optional"),
+ GetOptionalAttributeValue("default", null),
+ GetOptionalAttributeValue("fixed", null),
+ meAttributeFormDefault,
+ GetLocation());
+ ExpectEndElement("attribute");
+
+ return aAttribute;
+ }
+
+
+
+ private Choice ParseChoice (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
+
+ final Choice aChoice = new Choice(aParent, GetLocation());
+ ParseContent(aChoice);
+ return aChoice;
+ }
+
+
+
+
+ private ComplexContent ParseComplexContent (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
+
+ final ComplexContent aNode = new ComplexContent(aParent, GetLocation());
+ ParseContent(aNode);
+ return aNode;
+ }
+
+
+
+
+ private ComplexType ParseComplexType ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("mixed", "name"));
+
+ final ComplexType aComplexType = new ComplexType(
+ null,
+ GetQualifiedName("name"),
+ GetLocation());
+
+ ParseContent(aComplexType);
+
+ return aComplexType;
+ }
+
+
+
+
+ private Element ParseElement (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "type"));
+
+ final Element aElement = new Element(
+ aParent,
+ GetQualifiedName("name"),
+ GetQualifiedName("type"),
+ GetLocation());
+
+ ExpectEndElement("element");
+
+ return aElement;
+ }
+
+
+
+
+ private Element ParseElementOrReference (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref", "type"));
+
+ final Element aElement;
+ final String sName = GetOptionalAttributeValue("name", null);
+ if (sName != null)
+ {
+ aElement = ParseElement(aParent);
+ }
+ else
+ {
+ final String sElementReference = GetOptionalAttributeValue("ref", null);
+ if (sElementReference != null)
+ {
+ ExpectEndElement("element reference");
+ aElement = new ElementReference(
+ aParent,
+ CreateQualifiedName(sElementReference),
+ GetLocation());
+ }
+ else
+ {
+ throw CreateErrorException("element has no name and no ref");
+ }
+ }
+ return aElement;
+ }
+
+
+
+
+ private Extension ParseExtension (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("base", "minOccurs", "maxOccurs"));
+
+ final Extension aNode = new Extension(
+ aParent,
+ CreateQualifiedName(GetAttributeValue("base")),
+ GetLocation());
+ ParseContent(aNode);
+ return aNode;
+ }
+
+
+
+
+ private Group ParseGroup (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("name"));
+
+ final Group aGroup = new Group(
+ aParent,
+ GetQualifiedName("name"),
+ GetLocation());
+ ParseContent(aGroup);
+ return aGroup;
+ }
+
+
+
+
+ private Node ParseGroupOrReference (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name", "ref"));
+
+ final Node aGroup;
+ final String sName = GetOptionalAttributeValue("name", null);
+ if (sName != null)
+ {
+ aGroup = ParseGroup(aParent);
+ }
+ else
+ {
+ final String sGroupReference = GetOptionalAttributeValue("ref", null);
+ if (sGroupReference != null)
+ {
+ ExpectEndElement("group reference");
+ aGroup = new GroupReference(
+ aParent,
+ CreateQualifiedName(sGroupReference),
+ GetLocation());
+ }
+ else
+ {
+ throw CreateErrorException("group has no name and no ref");
+ }
+ }
+ return aGroup;
+ }
+
+
+
+
+ private Restriction ParseRestriction (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("base", "value"));
+
+ final String sBaseType = GetAttributeValue("base");
+ final Restriction aRestriction = new Restriction(
+ aParent,
+ CreateQualifiedName(sBaseType),
+ GetLocation());
+
+ while (true)
+ {
+ switch (Next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ final String sValue = GetAttributeValue("value");
+ switch (maReader.getLocalName())
+ {
+ case "enumeration":
+ aRestriction.AddEnumeration(sValue);
+ break;
+
+ case "minInclusive":
+ aRestriction.SetMinInclusive(sValue);
+ break;
+
+ case "minExclusive":
+ aRestriction.SetMinExclusive(sValue);
+ break;
+
+ case "maxInclusive":
+ aRestriction.SetMaxInclusive(sValue);
+ break;
+
+ case "maxExclusive":
+ aRestriction.SetMaxExclusive(sValue);
+ break;
+
+ case "length":
+ aRestriction.SetLength(sValue);
+ break;
+
+ case "minLength":
+ aRestriction.SetMinLength(sValue);
+ break;
+
+ case "maxLength":
+ aRestriction.SetMaxLength(sValue);
+ break;
+
+ case "pattern":
+ aRestriction.SetPattern(sValue);
+ break;
+
+ default:
+ throw CreateErrorException("unsupported restriction type "+maReader.getLocalName());
+ }
+ ExpectEndElement("restriction");
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return aRestriction;
+
+ default:
+ throw CreateErrorException("unexpected XML event while parsing restrictions: %d", maReader.getEventType());
+ }
+ }
+ }
+
+
+
+
+ private List ParseList (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("itemType"));
+
+ final List aList = new List(
+ aParent,
+ GetQualifiedName("itemType"),
+ GetLocation());
+ ExpectEndElement("list");
+ return aList;
+ }
+
+
+
+
+ private Sequence ParseSequence (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs", "name"));
+
+ final Sequence aSequence = new Sequence(
+ aParent,
+ GetOptionalQualifiedName("name"),
+ GetLocation());
+ ParseContent(aSequence);
+ return aSequence;
+ }
+
+
+
+
+ private SimpleContent ParseSimpleContent (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("minOccurs", "maxOccurs"));
+
+ final SimpleContent aSimpleContent = new SimpleContent(
+ aParent,
+ GetLocation());
+ ParseContent(aSimpleContent);
+ return aSimpleContent;
+ }
+
+
+
+
+ private SimpleType ParseSimpleType (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("final", "name"));
+
+ final SimpleType aType = new SimpleType(
+ aParent,
+ GetQualifiedName("name"),
+ GetLocation());
+ final String sFinalValue = GetOptionalAttributeValue("final", null);
+ if (sFinalValue != null)
+ aType.SetFinal(sFinalValue.split("\\s+"));
+
+ while (true)
+ {
+ switch (Next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ switch(maReader.getLocalName())
+ {
+ case "list":
+ aType.AddChild(ParseList(aType));
+ break;
+
+ case "restriction":
+ aType.AddChild(ParseRestriction(aType));
+ break;
+
+ case "union":
+ aType.AddChild(ParseUnion(aType));
+ break;
+
+ default:
+ throw CreateErrorException("unsupported simple type part %s", maReader.getLocalName());
+ }
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return aType;
+
+ default:
+ throw CreateErrorException("unexpected XML event in ParseSimpleType: %s", maReader.getEventType());
+ }
+ }
+ }
+
+
+
+
+ private Union ParseUnion (final Node aParent)
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("memberTypes"));
+
+ final Union aUnion = new Union(
+ aParent,
+ GetLocation());
+ final String[] aMemberTypes = GetAttributeValue("memberTypes").split("\\s+");
+ for (int nIndex=0; nIndex<aMemberTypes.length; ++nIndex)
+ aUnion.AddChild(
+ new SimpleTypeReference(
+ aUnion,
+ CreateQualifiedName(aMemberTypes[nIndex]),
+ GetLocation()));
+
+ ParseContent(aUnion);
+
+ return aUnion;
+ }
+
+
+ private void ParseContent (final Node aParent)
+ throws XMLStreamException
+ {
+ while(true)
+ {
+ switch(Next())
+ {
+ case XMLStreamReader.START_ELEMENT:
+ ProcessStartElement(aParent);
+ break;
+
+ case XMLStreamReader.END_ELEMENT:
+ return;
+
+ default:
+ throw CreateErrorException(
+ "unexpected XML event %d while parsing content of %s",
+ maReader.getEventType(),
+ aParent.toString());
+ }
+ }
+ }
+
+
+
+
+ private void ParseImport ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("id", "namespace", "schemaLocation"));
+
+ final String sFilename = GetOptionalAttributeValue("schemaLocation", null);
+ if (sFilename == null)
+ {
+ final String sNamespaceName = GetAttributeValue("namespace");
+ if (sNamespaceName.equals(XmlNamespace.URI))
+ {
+ XmlNamespace.Apply(maSchemaBase);
+ maLocalNamespaceMap.put(XmlNamespace.Prefix, XmlNamespace.URI);
+ }
+ else
+ throw CreateErrorException("invalid import");
+ }
+ else
+ {
+ maImportedSchemas.add(new File(maDirectory, sFilename));
+ }
+
+ ExpectEndElement("import");
+ }
+
+
+
+
+ private void ParseInclude ()
+ throws XMLStreamException
+ {
+ assert(HasOnlyAttributes("id", "schemaLocation"));
+
+ final String sFilename = GetOptionalAttributeValue("schemaLocation", null);
+ if (sFilename == null)
+ {
+ throw CreateErrorException("invalid include");
+ }
+ else
+ {
+ maImportedSchemas.add(new File(maDirectory, sFilename));
+ }
+
+ ExpectEndElement("include");
+ }
+
+
+
+
+ private void ExpectEndElement (final String sCaller)
+ throws XMLStreamException
+ {
+ final int nNextEvent = Next();
+ if (nNextEvent != XMLStreamReader.END_ELEMENT)
+ throw CreateErrorException("expected END_ELEMENT of %s but got %s",
+ sCaller,
+ nNextEvent);
+ }
+
+
+
+
+ /** Return the next relevant token from the XML stream.
+ * Ignores comments.
+ * @return
+ * Returns the event code of the next relevant XML event or END_DOCUMENT when the end of the file has been reached.
+ */
+ private int Next ()
+ throws XMLStreamException
+ {
+ while (maReader.hasNext())
+ {
+ switch (maReader.next())
+ {
+ case XMLStreamReader.COMMENT:
+ // Ignore comments.
+ break;
+
+ case XMLStreamReader.CHARACTERS:
+ // Ignore whitespace.
+ if (maReader.getText().matches("^\\s+$"))
+ break;
+ else
+ {
+ // Character events are not expected in schema files
+ // and therefore not supported.
+ // Alternatively, they could easily be ignored.
+ throw CreateErrorException("unexpected CHARACTERS event with text [%s]", maReader.getText());
+ }
+
+ case XMLStreamReader.START_ELEMENT:
+ switch (maReader.getLocalName())
+ {
+ case "annotation":
+ IgnoreAnnotation();
+ break;
+ case "unique":
+ // Not supported.
+ IgnoreContent();
+ break;
+ default:
+ return XMLStreamReader.START_ELEMENT;
+ }
+ break;
+
+ default:
+ return maReader.getEventType();
+ }
+ }
+ return XMLStreamReader.END_DOCUMENT;
+ }
+
+
+
+
+ private String GetAttributeValue (final String sAttributeLocalName)
+ {
+ return maReader.getAttributeValue(null, sAttributeLocalName);
+ }
+
+
+
+
+ private String GetOptionalAttributeValue (
+ final String sAttributeLocalName,
+ final String sDefaultValue)
+ {
+ final String sValue = maReader.getAttributeValue(null, sAttributeLocalName);
+ if (sValue == null)
+ return sDefaultValue;
+ else
+ return sValue;
+ }
+
+
+
+
+ /** Read the specified attribute and return its value as QualifiedName object.
+ */
+ private QualifiedName GetQualifiedName (final String sAttributeLocalName)
+ {
+ final String sName = maReader.getAttributeValue(null, sAttributeLocalName);
+ if (sName == null)
+ throw CreateErrorException(
+ "did not find a qualified name as value of attribute '%s' at %s:%s",
+ sAttributeLocalName,
+ msBasename,
+ maReader.getLocation());
+ else
+ return CreateQualifiedName(sName);
+ }
+
+
+
+
+ private QualifiedName GetOptionalQualifiedName (final String sAttributeLocalName)
+ {
+ final String sName = maReader.getAttributeValue(null, sAttributeLocalName);
+ if (sName == null)
+ return null;
+ else
+ return CreateQualifiedName(sName);
+ }
+
+
+
+
+ /** Create a QualifiedName object from the given string.
+ * @param sName
+ * May or may not contain a namespace prefix (separated from the name
+ * by a colon).
+ */
+ private QualifiedName CreateQualifiedName (final String sName)
+ {
+ final String[] aParts = sName.split(":");
+ final String sNamespaceURL;
+ final String sLocalPart;
+ switch (aParts.length)
+ {
+ case 1:
+ // sName only consists of a local part.
+ // Use the target namespace as namespace.
+ sNamespaceURL = msTargetNamespace;
+ sLocalPart = aParts[0];
+ break;
+
+ case 2:
+ // sName is of the form prefix:local.
+ sNamespaceURL = maLocalNamespaceMap.get(aParts[0]);
+ sLocalPart = aParts[1];
+ break;
+
+ default:
+ throw new RuntimeException(
+ "the string '"+sName+"' can not be transformed into a qualified name");
+ }
+ if (sNamespaceURL == null)
+ throw CreateErrorException("can not determine namespace for '%s'", sName);
+ // Transform the local namespace prefix into a global namespace prefix
+ // (different schema files can use different prefixes for the same
+ // namespace URI).
+ final String sGlobalNamespacePrefix = maSchemaBase.Namespaces.GetNamespacePrefix(sNamespaceURL);
+ return new QualifiedName(
+ sNamespaceURL,
+ sGlobalNamespacePrefix,
+ sLocalPart);
+ }
+
+
+
+
+ /** Create an XMLStreamReader for the given file.
+ * Returns null when there is an error.
+ */
+ private static XMLStreamReader GetStreamReader (final File aSchemaFile)
+ {
+ final XMLInputFactory aFactory = (XMLInputFactory)XMLInputFactory.newInstance();
+ aFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
+ aFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
+ aFactory.setProperty(XMLInputFactory.IS_COALESCING, false);
+
+ try
+ {
+ return aFactory.createXMLStreamReader(
+ aSchemaFile.getName(),
+ new FileInputStream(aSchemaFile));
+ }
+ catch (Exception aException)
+ {
+ aException.printStackTrace();
+ return null;
+ }
+ }
+
+
+
+
+ private RuntimeException CreateErrorException (
+ final String sFormat,
+ final Object ... aArgumentList)
+ {
+ return new RuntimeException(String.format(sFormat, aArgumentList)
+ + String.format(" in %s at L%dC%dP%d",
+ msBasename,
+ maReader.getLocation().getLineNumber(),
+ maReader.getLocation().getColumnNumber(),
+ maReader.getLocation().getCharacterOffset()));
+ }
+
+
+
+
+ /** This predicate is only used for debugging to assert
+ * that no unsupported attributes are present.
+ * If there where then the parser has to be extended.
+ */
+ private boolean HasOnlyAttributes (final String ... aAttributeNameList)
+ {
+ for (int nIndex=0,nCount=maReader.getAttributeCount(); nIndex<nCount; ++nIndex)
+ {
+ final String sAttributeName = maReader.getAttributeLocalName(nIndex);
+ boolean bFound = false;
+ for (final String sName : aAttributeNameList)
+ if (sAttributeName.equals(sName))
+ {
+ bFound = true;
+ break;
+ }
+ if ( ! bFound)
+ {
+ // Attribute name was not found in the given list.
+ System.err.printf("attribute '%s' is not handled\n", sAttributeName);
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+
+
+ private Location GetLocation ()
+ {
+ final javax.xml.stream.Location aLocation = maReader.getLocation();
+ return new Location(
+ msBasename,
+ aLocation.getLineNumber(),
+ aLocation.getColumnNumber(),
+ aLocation.getCharacterOffset());
+ }
+
+
+
+
+ private final Schema maSchema;
+ private final SchemaBase maSchemaBase;
+ private final XMLStreamReader maReader;
+ private final String msBasename;
+ private final File maDirectory;
+ private String msTargetNamespace;
+ /// Map of namespace prefix to URI, local to the currently read schema file.
+ private final Map<String,String> maLocalNamespaceMap;
+ /// The names of other schema files referenced by import elements.
+ private final Vector<File> maImportedSchemas;
+ /// Some values for tracking the number of read bytes and lines.
+ private javax.xml.stream.Location maLastLocation;
+ private FormDefault meAttributeFormDefault;
+ private FormDefault meElementFormDefault;
+}