<?xml version="1.0" encoding="UTF-8"?>
<!--

    Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.

    This program and the accompanying materials are made available under the
    terms of the Eclipse Distribution License v. 1.0, which is available at
    http://www.eclipse.org/org/documents/edl-v10.php.

    SPDX-License-Identifier: BSD-3-Clause

-->

<!-- XML Schema -->
<grammar
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  ns="http://www.w3.org/2001/XMLSchema"
  xmlns:cc="http://www.xml.gr.jp/xmlns/relaxngcc"
  xmlns="http://relaxng.org/ns/structure/1.0"
  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
  
  cc:package="com.sun.xml.xsom.impl.parser.state"
  cc:runtime-type="com.sun.xml.xsom.impl.parser.NGCCRuntimeEx">
  
  <cc:java-import>
    import com.sun.xml.xsom.*;
    import com.sun.xml.xsom.parser.*;
    import com.sun.xml.xsom.impl.*;
    import com.sun.xml.xsom.impl.parser.*;
    import org.xml.sax.Locator;
    import org.xml.sax.ContentHandler;
    import org.xml.sax.helpers.*;
    import java.util.*;
    import java.math.BigInteger;
  </cc:java-import>
  
  
  <start cc:class="Schema" cc:access="public" cc:params="boolean includeMode,String expectedNamespace">
    <!--
      If a schema document of a particular namespace is expected,
      the 'expectedNamespace' parameter is not null.
    -->
    <cc:java-body>
      private String tns=null;  // it defaults to the no namespace.
      private Locator locator;
    </cc:java-body>
    
    <element name="schema">
    	Attributes test = $runtime.getCurrentAttributes();
    	String tns = test.getValue("targetNamespace");

      if(!includeMode) {
        // importing
        if(tns==null) tns=""; // if not present, then the empty namespace
        $runtime.currentSchema = $runtime.parser.schemaSet.createSchema(tns,$runtime.copyLocator());
        if(expectedNamespace!=null &amp;&amp; !expectedNamespace.equals(tns)) {
          $runtime.reportError(
            Messages.format("UnexpectedTargetnamespace.Import", tns, expectedNamespace, tns ),
            $runtime.getLocator());
        }
      } else {
        // including
        
        // check the consistency of @targetNamespace.
        // @targetNamespace must be null or equal to the target namespace of the schema
        if(tns!=null &amp;&amp; expectedNamespace!=null &amp;&amp; !expectedNamespace.equals(tns)) {
          $runtime.reportError(
            Messages.format("UnexpectedTargetnamespace.Include", tns, expectedNamespace, tns ) );
        }
        $runtime.chameleonMode = true;
      }
      
      // multiple inclusion test.
      if( $runtime.hasAlreadyBeenRead() ) {
          // skip this document
          $runtime.redirectSubtree(new DefaultHandler(),"","","" );
          return;
      }
        
      anno = (AnnotationImpl)$runtime.currentSchema.getAnnotation();
      $runtime.blockDefault = 0;
      $runtime.finalDefault = 0;
    	
    	
      <optional>
        <attribute name="targetNamespace">
          <data type="anyURI" />
        </attribute>
      </optional>
      
      <optional>
        <attribute name="attributeFormDefault">
          afd = <ref name="qualification"/>
          $runtime.attributeFormDefault = afd;
        </attribute>
      </optional>
      <optional>
        <attribute name="elementFormDefault">
          efd = <ref name="qualification"/>
          $runtime.elementFormDefault = efd;
        </attribute>
      </optional>
      <optional>
        <attribute name="blockDefault">
          <ref name="ersSet" cc:alias="blockDefault"/>
          <cc:java>$runtime.blockDefault=this.blockDefault.intValue();</cc:java>
        </attribute>
      </optional>
      <optional>
        <attribute name="finalDefault">
          <ref name="erSet" cc:alias="finalDefault"/>
          <cc:java>$runtime.finalDefault=this.finalDefault.intValue();</cc:java>
        </attribute>
      </optional>
      <!--optional>
        <attribute name="ID">
          <data type="ID"/>
        </attribute>
      </optional>
      <optional>
        <attribute name="version">
          <data type="token"/>
        </attribute>
      </optional-->
      <!-- here multiple annotations are handled in a bit different way than the others -->
      fa = <ref name="foreignAttributes"/>(null);
      $runtime.currentSchema.addForeignAttributes(fa);
      <zeroOrMore>
        <choice><!-- a bit relaxed content model to simply parsing table -->
          <group>
            anno = <ref name="annotation"/>(anno,AnnotationContext.SCHEMA);
            $runtime.currentSchema.setAnnotation(anno);
          </group>
            
          <ref name="includeDecl"/>
          <ref name="importDecl"/>
          <ref name="redefine"/>
          
          <element name="element">
            <cc:java>locator = $runtime.copyLocator();</cc:java>
            <ref name="elementDeclBody"
              cc:alias="e" cc:with-params="locator,true"/>

            $runtime.checkDoubleDefError( $runtime.currentSchema.getElementDecl(e.getName()) );
            $runtime.currentSchema.addElementDecl(e);
          </element>
          
          <group>
            <ref name="simpleType" cc:alias="st"/>
            
            $runtime.checkDoubleDefError( $runtime.currentSchema.getType(st.getName()) );
            $runtime.currentSchema.addSimpleType(st,false);
          </group>
          
          <group>
            <ref name="complexType" cc:alias="ct"/>
            
            $runtime.checkDoubleDefError( $runtime.currentSchema.getType(ct.getName()) );
            $runtime.currentSchema.addComplexType(ct,false);
          </group>
          
          <element name="attribute">
            locator = $runtime.copyLocator();
            defaultValue = null;
            fixedValue = null;
            
            <optional>
              <attribute name="default" cc:alias="defaultValue"/>
            </optional>
            <optional>
              <attribute name="fixed" cc:alias="fixedValue"/>
            </optional>
            <ref name="attributeDeclBody"
              cc:alias="ad" cc:with-params="locator,false,defaultValue,fixedValue"/>
              
            $runtime.checkDoubleDefError( $runtime.currentSchema.getAttributeDecl(ad.getName()) );
            $runtime.currentSchema.addAttributeDecl(ad);
          </element>
          
          <group>
            group = <ref name="group" />
            
            $runtime.checkDoubleDefError( $runtime.currentSchema.getModelGroupDecl(group.getName()) );
            $runtime.currentSchema.addModelGroupDecl(group,false);
          </group>
          
          <group>
            <ref name="notation" cc:alias="notation"/>
            $runtime.currentSchema.addNotation(notation);
          </group>
          
          <group>
            <ref name="attributeGroupDecl" cc:alias="ag" />
            
            $runtime.checkDoubleDefError( $runtime.currentSchema.getAttGroupDecl(ag.getName()) );
            $runtime.currentSchema.addAttGroupDecl(ag,false);
          </group>
        </choice>
      </zeroOrMore>
    </element>
    <!-- because of the way we handle multiple inclusions, this handler will receive
         endElement for xs:schema even if we are skipping. So don't add anything here -->
  </start>
  
  
  <include href="simpleType.rng"/>
  <include href="complexType.rng"/>
  <include href="attribute.rng"/>
  <include href="include.rng"/>
  <include href="element.rng"/>
  <include href="modelGroup.rng"/>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <define name="annotation" cc:params="AnnotationImpl existing, AnnotationContext context"
      cc:return-type="AnnotationImpl" cc:return-value="makeResult()">
    <cc:java-import>
      import com.sun.xml.xsom.parser.AnnotationParser;
    </cc:java-import>
    <cc:java-body>
      private AnnotationParser parser;
      private Locator locator;
      
      public AnnotationImpl makeResult() {
        Object e = null;
        if(existing!=null)  e=existing.getAnnotation();
        
        return new AnnotationImpl( parser.getResult(e),locator);
      }
    </cc:java-body>

    <!-- redirect this sub-tree to the annotation handler. -->
    <element name="annotation">
      <cc:java>
        locator = $runtime.copyLocator();
        parser = $runtime.createAnnotationParser();
        $runtime.redirectSubtree(parser.getContentHandler(
            context,
            $runtime.getAnnotationContextElementName(),
            $runtime.getErrorHandler(),
            $runtime.parser.getEntityResolver()
          ), $uri, $localName, $qname );
      </cc:java>
      <empty/>
    </element>
  </define>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <!--
    This block takes locator as a parameter
  -->
  <define name="wildcardBody"
    cc:params="Locator locator"
    cc:return-type="WildcardImpl" cc:return-value="makeResult()">
    <cc:java-import>
      import java.util.StringTokenizer;
      import java.util.HashSet;
    </cc:java-import>
    <cc:java-body>
      private WildcardImpl makeResult() {
        if(modeValue==null) modeValue="strict";
        
        int mode=-1;
        if(modeValue.equals("strict"))      mode = XSWildcard.STRTICT;
        if(modeValue.equals("lax"))         mode = XSWildcard.LAX;
        if(modeValue.equals("skip"))        mode = XSWildcard.SKIP;
        if(mode==-1)        throw new InternalError();
        
        if(ns==null || ns.equals("##any"))
            return new WildcardImpl.Any( $runtime.document, annotation,locator,fa,mode);
        
        if(ns.equals("##other"))
            return new WildcardImpl.Other( $runtime.document,
                annotation,locator,fa,
                $runtime.currentSchema.getTargetNamespace(),mode);
        
        StringTokenizer tokens = new StringTokenizer(ns);
        HashSet s = new HashSet();
        while(tokens.hasMoreTokens()) {
            String ns = tokens.nextToken();
            if(ns.equals("##local")) ns="";
            if(ns.equals("##targetNamespace")) ns=$runtime.currentSchema.getTargetNamespace();
            s.add(ns);
        }
        
        return new WildcardImpl.Finite( $runtime.document, annotation,locator,fa,s,mode);
      }
    </cc:java-body>
    
    <optional>
      <ref name="annotation" cc:alias="annotation"/>(null,AnnotationContext.WILDCARD);
    </optional>
    fa = <ref name="foreignAttributes"/>(null);
    <optional>
      <attribute name="processContents">
        <text cc:alias="modeValue"/>
      </attribute>
    </optional>
    <optional>
      <attribute name="namespace">
        <text cc:alias="ns"/>
      </attribute>
    </optional>
  </define>
  
  
  
  
  
  
  
  
  
  
  <define name="notation" cc:return-type="XSNotation" cc:return-value="makeResult()">
    <cc:java-body>
      private Locator loc;
      private XSNotation makeResult() {
        return new NotationImpl( $runtime.document,ann,loc,fa,name,pub,sys);
      }
    </cc:java-body>
    
    <element name="notation">
      loc = $runtime.copyLocator();
      fa = <ref name="foreignAttributes"/>(null);
      <attribute name="name" cc:alias="name"/>
      <optional>
          <attribute name="public" cc:alias="pub"/>
      </optional>
      <optional>
        <attribute name="system" cc:alias="sys"/>
      </optional>
      <optional>
        <ref name="annotation" cc:alias="ann"/>(null,AnnotationContext.NOTATION);
      </optional>
      
    </element>
  </define>
  
  
  
  
  <define name="qualification" cc:return-type="boolean"
    cc:return-value='text.trim().equals("qualified")'>
    <choice>
      text=<value>qualified</value>
      text=<value>unqualified</value>
    </choice>
  </define>
  
  
  
  <!--define name="anyElement">
    <element>
      <anyName>
        <except>
          <nsName/>
        </except>
      </anyName>
      <zeroOrMore>
        <attribute>
          <anyName/>
        </attribute>
      </zeroOrMore>
      <interleave>
        <text/>
        <zeroOrMore>
          <ref name="anyElement"/>
        </zeroOrMore>
      </interleave>
    </element>
  </define-->
  <define name="foreignAttributes"
    cc:return-value="makeResult()" cc:return-type="ForeignAttributesImpl"
    cc:params="ForeignAttributesImpl current">
    <cc:java-body>
      ForeignAttributesImpl makeResult() {
        return $runtime.parseForeignAttributes(current);
      }
    </cc:java-body>
    <empty />
    <!--zeroOrMore>
      <attribute>
        <anyName>
          <except>
            <nsName/>
            <nsName ns=""/>
          </except>
        </anyName>
      </attribute>
    </zeroOrMore-->
  </define>
  
  <define name="occurs">
    <cc:java-import>
    import java.math.BigInteger;
    </cc:java-import>
    <cc:java-body>
      BigInteger max = BigInteger.valueOf(1);
      BigInteger min = BigInteger.valueOf(1);
    </cc:java-body>
    <optional>
      <attribute name="maxOccurs">
        <empty/>
        <!-- to workaround a problem in RelaxNGCC, we need empty here -->
        <choice>
          <group>
            <data type="nonNegativeInteger" cc:alias="v"/>
            <cc:java>max = new BigInteger(v);</cc:java>
          </group>
          <group>
            <value>unbounded</value>
            <cc:java>max=BigInteger.valueOf(-1);</cc:java>
          </group>
        </choice>
      </attribute>
    </optional>
    <optional>
      <attribute name="minOccurs">
        <data type="nonNegativeInteger" cc:alias="v"/>
        <cc:java>min = new BigInteger(v);</cc:java>
      </attribute>
    </optional>
  </define>
  
  <define name="qname"
    cc:return-type="UName" cc:return-value="$runtime.parseUName(qvalue)">
    <!-- the return type will be UName -->
    
    <data type="QName" cc:alias="qvalue"/>
  </define>
</grammar>
