<?xml version="1.0" encoding="UTF-8"?>
<!-- TODO
  
* add support for atoms
  - similar mechanism to elements, since they must also be
    self-contained, but complex stuff around concur because
    they count as content
  
* add datatype support
  - buffer text to test against specified datatype

-->
<xsl:stylesheet version="2.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:test="http://www.jenitennison.com/xslt/unit-test"
                exclude-result-prefixes="xs test"
                xmlns:pat="http://www.lmnl.org/schema/pattern"
                xmlns:mem="http://www.lmnl.org/schema/pattern/memo"
                xmlns:ev="http://www.lmnl.org/event">
  
<xsl:import href="compile-creole.xsl" />  
  
<xsl:param name="trace" as="xs:boolean" select="false()" />  
  
<xsl:function name="pat:valid" as="xs:boolean">
  <xsl:param name="events" as="element()*" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:variable name="validated" as="element()+" 
    select="pat:validate($events, $patterns)" />
  <xsl:if test="not(pat:nullable($validated))">
    <xsl:message>
      final pattern: <xsl:value-of select="$patterns[1]/@name" />
      grammar: <xsl:copy-of select="$patterns" copy-namespaces="no" />
    </xsl:message>
  </xsl:if>
  <xsl:sequence select="pat:nullable($validated)" />
</xsl:function>  
  
<xsl:function name="pat:validate" as="element()+">
  <xsl:param name="events" as="element()*" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:choose>
    <xsl:when test="empty($events)">
      <xsl:choose>
        <xsl:when test="pat:nullable($patterns)">
          <xsl:if test="$trace">
            <xsl:message>
              grammar: <xsl:copy-of select="$patterns" copy-namespaces="no" />
            </xsl:message>
          </xsl:if>
          <xsl:sequence select="$patterns" />
        </xsl:when>
        <xsl:otherwise>
          <pat:error>
            <pat:grammar start="{$patterns[1]/@name}">
              <xsl:copy-of select="$patterns" />
            </pat:grammar>
          </pat:error>          
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="deriv" as="element()+" 
        select="pat:derivative($events[1], $patterns)" />
      <xsl:if test="$trace">
        <xsl:message>
          <xsl:for-each select="$events[1]">
            <xsl:copy copy-namespaces="no">
              <xsl:copy-of select="@*" />
              <xsl:attribute name="deriv" select="$deriv[1]/@name" />
            </xsl:copy>
          </xsl:for-each>
        <!--
          event:   <xsl:copy-of select="$events[1]" copy-namespaces="no" />
          pattern: <xsl:copy-of select="$patterns[1]" copy-namespaces="no" />
          deriv:   <xsl:copy-of select="$deriv[1]" copy-namespaces="no" />
          grammar: <xsl:copy-of select="$deriv" copy-namespaces="no" />
        -->
        </xsl:message>
      </xsl:if>
      <xsl:choose>
        <xsl:when test="$deriv[1]/@name = '#notAllowed'">
          <xsl:variable name="error">
            <pat:error>
              <xsl:copy-of select="$events[1]" />
              <pat:grammar start="{$patterns[1]/@name}">
                <xsl:copy-of select="$deriv" />
              </pat:grammar>
            </pat:error>
          </xsl:variable>
          <xsl:sequence select="$error/pat:error" />
          <!--
          <xsl:message>
            validation error occured at event:
            event:   <xsl:copy-of select="$events[1]" copy-namespaces="no" />
            pattern: <xsl:value-of select="$patterns[1]/@name" />
            deriv:   <xsl:value-of select="$deriv[1]/@name" />
            grammar: <xsl:copy-of select="$deriv" copy-namespaces="no" />
          </xsl:message>
          -->
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="pat:validate($events[position() > 1], $deriv)" />
        </xsl:otherwise>
      </xsl:choose>      
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>  

<!-- *** pat:derivative() *** -->  
  
<xsl:function name="pat:derivative" as="element()+">
  <xsl:param name="event" as="element()" />
  <xsl:param name="name" as="xs:string" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:param name="stickiness" as="xs:string" />
  <xsl:variable name="pattern" as="element()" select="pat:pattern($name, $patterns)" />
  <xsl:sequence select="pat:derivative($event, ($pattern, $patterns except $pattern), $stickiness)" />
</xsl:function>  
  
<xsl:function name="pat:derivative" as="element()+">
  <xsl:param name="event" as="element()" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:sequence select="pat:derivative($event, $patterns, 'none')" />
</xsl:function>  
  
<xsl:function name="pat:derivative" as="element()+">
  <xsl:param name="event" as="element()" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:param name="stickiness" as="xs:string" />
  <xsl:apply-templates select="$event" mode="pat:derivative">
    <xsl:with-param name="patterns" select="$patterns" />
    <xsl:with-param name="stickiness" select="$stickiness" />
  </xsl:apply-templates>
</xsl:function>  
  
<!-- ** Event-based derivatives ** -->  
  
<xsl:template match="ev:text" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="not(pat:allows-text($patterns))">
      <xsl:choose>
        <xsl:when test="@ws = 'true'">
          <xsl:sequence select="$patterns" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
          <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
        </xsl:otherwise>
      </xsl:choose>      
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="$patterns[1]" mode="pat:text-derivative">
        <xsl:with-param name="event" select="." />
        <xsl:with-param name="patterns" select="$patterns" />
        <xsl:with-param name="stickiness" select="$stickiness" />
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  
<xsl:template match="ev:start-tag-open" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:apply-templates select="$patterns[1]" mode="pat:start-tag-derivative">
    <xsl:with-param name="event" select="." />
    <xsl:with-param name="patterns" select="$patterns" />
    <xsl:with-param name="stickiness" select="$stickiness" />
  </xsl:apply-templates>
</xsl:template>
  
<xsl:template match="ev:end-tag-close" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:apply-templates select="$patterns[1]" mode="pat:end-tag-derivative">
    <xsl:with-param name="event" select="." />
    <xsl:with-param name="patterns" select="$patterns" />
    <xsl:with-param name="stickiness" select="$stickiness" />
  </xsl:apply-templates>
</xsl:template>
  
<xsl:template match="ev:start-annotation-open" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:apply-templates select="$patterns[1]" mode="pat:start-annotation-derivative">
    <xsl:with-param name="event" select="." />
    <xsl:with-param name="patterns" select="$patterns" />
  </xsl:apply-templates>
</xsl:template>  
  
<xsl:template match="ev:end-annotation-close" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:apply-templates select="$patterns[1]" mode="pat:end-annotation-derivative">
    <xsl:with-param name="event" select="." />
    <xsl:with-param name="patterns" select="$patterns" />
  </xsl:apply-templates>
</xsl:template>  
  
<!-- ignore other kinds of events -->
<xsl:template match="ev:*" mode="pat:derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select="$patterns" />
</xsl:template>
  
<!-- ** Pattern-based derivatives ** -->  

<xsl:template match="pat:define" as="element()+"
              mode="pat:text-derivative
                    pat:start-tag-derivative 
                    pat:end-tag-derivative
                    pat:start-annotation-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" select="'none'" />
  <xsl:variable name="mem-deriv" as="element(mem:deriv)?" 
    select="mem:deriv[@event = $event/@hash and @stickiness = $stickiness]" />
  <xsl:choose>
    <xsl:when test="exists($mem-deriv)">
      <xsl:variable name="deriv" as="element()" select="pat:pattern($mem-deriv/@pattern, $patterns)" />
      <xsl:sequence select="$deriv, $patterns except $deriv" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="deriv" as="element()+">
        <xsl:apply-templates select="pat:*[1]" mode="#current">
          <xsl:with-param name="event" select="$event" />
          <xsl:with-param name="patterns" select="$patterns" />
          <xsl:with-param name="stickiness" select="$stickiness" />
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="new-define" as="element()">
        <pat:define>
          <xsl:copy-of select="@*" />
          <xsl:copy-of select="pat:*" />
          <xsl:copy-of select="mem:*" />
          <mem:deriv event="{$event/@hash}" stickiness="{$stickiness}" pattern="{$deriv[1]/@name}" />
        </pat:define>
      </xsl:variable>

      <xsl:choose>
        <xsl:when test="$deriv[1]/@name = @name">
          <xsl:sequence select="$new-define, $deriv[position() > 1]" />          
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$deriv[1], $deriv[position() > 1][@name != current()/@name], $new-define" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<xsl:template match="pat:ref" as="element()+"
              mode="pat:text-derivative
                    pat:start-tag-derivative 
                    pat:end-tag-derivative
                    pat:start-annotation-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:apply-templates select="pat:pattern(@name, $patterns)" mode="#current">
    <xsl:with-param name="event" select="$event" />
    <xsl:with-param name="patterns" select="$patterns" />
    <xsl:with-param name="stickiness" select="$stickiness" />
  </xsl:apply-templates>
</xsl:template>  
  
<!-- *** Generic behaviour for constructs *** -->  
  
<xsl:template match="pat:choice" as="element()+" 
              mode="pat:text-derivative
                    pat:start-tag-derivative 
                    pat:end-tag-derivative
                    pat:start-annotation-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv1" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:variable name="deriv2" as="element()+"
    select="pat:derivative($event, pat:ref[2]/@name, $deriv1, $stickiness)" />
  <xsl:call-template name="pat:choice">
    <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
    <xsl:with-param name="pattern2" select="$deriv2[1]/@name" />
    <xsl:with-param name="patterns" select="$deriv2" />
  </xsl:call-template>
</xsl:template>  
  
<xsl:template match="pat:group | pat:after" as="element()+" 
              mode="pat:text-derivative
                    pat:start-tag-derivative
                    pat:end-tag-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="not(pat:only-annotations(pat:ref[1], $patterns))">
      <xsl:variable name="deriv1" as="element()+"
        select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />  
      <xsl:variable name="group" as="element()+">
        <xsl:choose>
          <xsl:when test="self::pat:group">
            <xsl:call-template name="pat:group">
              <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
              <xsl:with-param name="pattern2" select="pat:ref[2]/@name" /> 
              <xsl:with-param name="patterns" select="$deriv1" />
            </xsl:call-template>
          </xsl:when>
          <xsl:when test="self::pat:after">
            <xsl:call-template name="pat:after">
              <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
              <xsl:with-param name="pattern2" select="pat:ref[2]/@name" /> 
              <xsl:with-param name="patterns" select="$deriv1" />
            </xsl:call-template>
          </xsl:when>
        </xsl:choose>
      </xsl:variable>
      
      <xsl:choose>
        <xsl:when test="pat:nullable(pat:ref[1], $deriv1)">
          <xsl:variable name="deriv2" as="element()+"
            select="pat:derivative($event, pat:ref[2]/@name, $group, $stickiness)" />      
          <xsl:call-template name="pat:choice">
            <xsl:with-param name="pattern1" select="$group[1]/@name" />
            <xsl:with-param name="pattern2" select="$deriv2[1]/@name" />
            <xsl:with-param name="patterns" select="$deriv2" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$group" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:when test="self::pat:group">
      <xsl:variable name="deriv2" as="element()+"
        select="pat:derivative($event, pat:ref[2]/@name, $patterns, $stickiness)" />
      <xsl:call-template name="pat:group">
        <xsl:with-param name="pattern1" select="pat:ref[1]/@name" />
        <xsl:with-param name="pattern2" select="$deriv2[1]/@name" /> 
        <xsl:with-param name="patterns" select="$deriv2" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
      <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="pat:interleave" as="element()+" 
              mode="pat:text-derivative
                    pat:start-tag-derivative
                    pat:end-tag-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv1" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:variable name="interleave1" as="element()+">
    <xsl:call-template name="pat:interleave">
      <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
      <xsl:with-param name="pattern2" select="pat:ref[2]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv1" />
    </xsl:call-template>
  </xsl:variable>
      
  <xsl:variable name="deriv2" as="element()+"
    select="pat:derivative($event, pat:ref[2]/@name, $interleave1, $stickiness)" />
  <xsl:variable name="interleave2" as="element()+">
    <xsl:call-template name="pat:interleave">
      <xsl:with-param name="pattern1" select="pat:ref[1]/@name" />
      <xsl:with-param name="pattern2" select="$deriv2[1]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv2" />
    </xsl:call-template>
  </xsl:variable>
  
  <xsl:call-template name="pat:choice">
    <xsl:with-param name="pattern1" select="$interleave1[1]/@name" />
    <xsl:with-param name="pattern2" select="$interleave2[1]/@name" />
    <xsl:with-param name="patterns" select="$interleave2" />
  </xsl:call-template>
</xsl:template>  
  
<xsl:template match="pat:concur" as="element()+" 
              mode="pat:start-tag-derivative
                    pat:end-tag-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv1" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:variable name="concur1" as="element()+">
    <xsl:call-template name="pat:concur">
      <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
      <xsl:with-param name="pattern2" select="pat:ref[2]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv1" />
    </xsl:call-template>
  </xsl:variable>
      
  <xsl:variable name="deriv2" as="element()+"
    select="pat:derivative($event, pat:ref[2]/@name, $concur1, $stickiness)" />
  <xsl:variable name="concur2" as="element()+">
    <xsl:call-template name="pat:concur">
      <xsl:with-param name="pattern1" select="pat:ref[1]/@name" />
      <xsl:with-param name="pattern2" select="$deriv2[1]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv2" />
    </xsl:call-template>
  </xsl:variable>
  
  <xsl:variable name="either" as="element()+">
    <xsl:call-template name="pat:choice">
      <xsl:with-param name="pattern1" select="$concur1[1]/@name" />
      <xsl:with-param name="pattern2" select="$concur2[1]/@name" />
      <xsl:with-param name="patterns" select="$concur2" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:sequence select="$either" />
</xsl:template>  

<xsl:template match="pat:concur" as="element()+" 
              mode="pat:text-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv1" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
      
  <xsl:variable name="deriv2" as="element()+"
    select="pat:derivative($event, pat:ref[2]/@name, $deriv1, $stickiness)" />
  
  <xsl:call-template name="pat:concur">
    <xsl:with-param name="pattern1" select="$deriv1[1]/@name" />
    <xsl:with-param name="pattern2" select="$deriv2[1]/@name" />
    <xsl:with-param name="patterns" select="$deriv2" />
  </xsl:call-template>
</xsl:template>
  
<xsl:template match="pat:oneOrMore
                     | pat:concurOneOrMore" as="element()+"
              mode="pat:text-derivative
                    pat:start-tag-derivative
                    pat:end-tag-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:variable name="choice" as="element()+">
    <xsl:call-template name="pat:choice">
      <xsl:with-param name="pattern1" select="parent::pat:define/@name" />
      <xsl:with-param name="pattern2" select="if (self::pat:oneOrMore) then '#empty' else '#text'" />
      <xsl:with-param name="patterns" select="$deriv" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:choose>
    <xsl:when test="self::pat:oneOrMore">
      <xsl:call-template name="pat:group">
        <xsl:with-param name="pattern1" select="$deriv[1]/@name" />
        <xsl:with-param name="pattern2" select="$choice[1]/@name" />
        <xsl:with-param name="patterns" select="$choice" />    
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:call-template name="pat:concur">
        <xsl:with-param name="pattern1" select="$deriv[1]/@name" />
        <xsl:with-param name="pattern2" select="$choice[1]/@name" />
        <xsl:with-param name="patterns" select="$choice" />    
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>  
</xsl:template>  
  
<xsl:template match="pat:empty | pat:notAllowed" as="element()+"
              mode="pat:text-derivative
                    pat:start-tag-derivative
                    pat:end-tag-derivative
                    pat:end-annotation-derivative">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>  
  
<!-- *** pat:sticky-derivative mode *** -->
  
<xsl:template match="pat:group" as="element()+"
              mode="pat:sticky-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="not(pat:only-annotations(pat:ref[1], $patterns))">
      <xsl:variable name="deriv" as="element()+"
        select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />  
      <xsl:variable name="after" as="element()+">
        <xsl:call-template name="pat:apply-after">
          <xsl:with-param name="construct" select="'group'" />
          <xsl:with-param name="arg2" select="pat:ref[2]/@name" /> 
          <xsl:with-param name="pattern" select="$deriv[1]/@name" />
          <xsl:with-param name="patterns" select="$deriv" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="pat:nullable(pat:ref[1], $after)">
          <xsl:variable name="deriv2" as="element()+"
            select="pat:derivative($event, pat:ref[2]/@name, $after, $stickiness)" />      
          <xsl:call-template name="pat:choice">
            <xsl:with-param name="pattern1" select="$deriv[1]/@name" />
            <xsl:with-param name="pattern2" select="$after[1]/@name" />
            <xsl:with-param name="patterns" select="$after" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$after" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="deriv2" as="element()+"
        select="pat:derivative($event, pat:ref[2]/@name, $patterns, $stickiness)" />      
      <xsl:call-template name="pat:apply-after">
        <xsl:with-param name="construct" select="'group'" />
        <xsl:with-param name="arg1" select="pat:ref[1]/@name" /> 
        <xsl:with-param name="pattern" select="$deriv2[1]/@name" />
        <xsl:with-param name="patterns" select="$deriv2" />
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  
<xsl:template match="pat:interleave
                     | pat:concur" as="element()+" 
              mode="pat:sticky-derivative
                    pat:start-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv1" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:variable name="after1" as="element()+">
    <xsl:call-template name="pat:apply-after">
      <xsl:with-param name="construct" select="local-name(.)" />
      <xsl:with-param name="pattern" select="$deriv1[1]/@name" />
      <xsl:with-param name="arg2" select="pat:ref[2]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv1" />
    </xsl:call-template>
  </xsl:variable>
      
  <xsl:variable name="deriv2" as="element()+"
    select="pat:derivative($event, pat:ref[2]/@name, $after1, $stickiness)" />
  <xsl:variable name="after2" as="element()+">
    <xsl:call-template name="pat:apply-after">
      <xsl:with-param name="construct" select="local-name(.)" />
      <xsl:with-param name="arg1" select="pat:ref[1]/@name" />
      <xsl:with-param name="pattern" select="$deriv2[1]/@name" /> 
      <xsl:with-param name="patterns" select="$deriv2" />
    </xsl:call-template>
  </xsl:variable>
  
  <xsl:call-template name="pat:choice">
    <xsl:with-param name="pattern1" select="$after1[1]/@name" />
    <xsl:with-param name="pattern2" select="$after2[1]/@name" />
    <xsl:with-param name="patterns" select="$after2" />
  </xsl:call-template>
</xsl:template>  
    
<xsl:template match="pat:oneOrMore
                     | pat:concurOneOrMore" as="element()+"
              mode="pat:sticky-derivative
                    pat:start-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv" as="element()+"
    select="pat:derivative($event, pat:ref/@name, $patterns, $stickiness)" />
  <xsl:variable name="choice" as="element()+">
    <xsl:call-template name="pat:choice">
      <xsl:with-param name="pattern1" select="parent::pat:define/@name" />
      <xsl:with-param name="pattern2" select="if (self::oneOrMore) then '#empty' else '#text'" />
      <xsl:with-param name="patterns" select="$deriv" />
    </xsl:call-template>
  </xsl:variable>
  <xsl:call-template name="pat:apply-after">
    <xsl:with-param name="construct" select="if (self::oneOrMore) then 'group' else 'concur'" />
    <xsl:with-param name="arg2" select="$choice[1]/@name" />
    <xsl:with-param name="pattern" select="$deriv[1]/@name" />
    <xsl:with-param name="patterns" select="$choice" />
  </xsl:call-template>
</xsl:template>
  
<xsl:template match="pat:after" as="element()+"
              mode="pat:sticky-derivative
                    pat:start-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv" as="element()+"
    select="pat:derivative($event, pat:ref[1]/@name, $patterns, $stickiness)" />
  <xsl:call-template name="pat:apply-after">
    <xsl:with-param name="construct" select="'after'" />
    <xsl:with-param name="arg2" select="pat:ref[2]/@name" />
    <xsl:with-param name="pattern" select="$deriv[1]/@name" />
    <xsl:with-param name="patterns" select="$deriv" />
  </xsl:call-template>
</xsl:template>
  
<xsl:template match="pat:partition" as="element()+"
              mode="pat:sticky-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:variable name="deriv" as="element()+" 
    select="pat:derivative($event, pat:ref/@name, $patterns, 'both')" />
  <xsl:call-template name="pat:after">
    <xsl:with-param name="pattern1" select="$deriv[1]/@name" />
    <xsl:with-param name="pattern2" select="'#empty'" />
    <xsl:with-param name="patterns" select="$deriv" />
  </xsl:call-template>
</xsl:template>
  
<xsl:template match="pat:*" mode="pat:sticky-derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>  

<!-- *** pat:start-tag-derivative mode *** -->
  
<xsl:template match="pat:oneOrMore
                     | pat:concurOneOrMore
                     | pat:group
                     | pat:interleave
                     | pat:concur
                     | pat:after" as="element()+"
              mode="pat:start-tag-derivative
                    pat:text-derivative
                    pat:end-tag-derivative"
              priority="5">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="$stickiness = 'none'">
      <!--
      <xsl:choose>
        <xsl:when test="pat:allows-partition(., $patterns)">
          <xsl:variable name="sticky-deriv" as="element()+">
            <xsl:apply-templates select="." mode="pat:sticky-derivative">
              <xsl:with-param name="event" select="$event" />
              <xsl:with-param name="patterns" select="$patterns" />
              <xsl:with-param name="stickiness" select="'sticky'" />
            </xsl:apply-templates>
          </xsl:variable>
          <xsl:variable name="non-sticky-deriv" as="element()+">
            <xsl:next-match>
              <xsl:with-param name="event" select="$event" />
              <xsl:with-param name="patterns" select="$sticky-deriv" />
              <xsl:with-param name="stickiness" select="'non-sticky'" />
            </xsl:next-match>
          </xsl:variable>
          <xsl:call-template name="pat:choice">
            <xsl:with-param name="pattern1" select="$sticky-deriv[1]/@name" />
            <xsl:with-param name="pattern2" select="$non-sticky-deriv[1]/@name" />
            <xsl:with-param name="patterns" select="$non-sticky-deriv" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:next-match>
            <xsl:with-param name="event" select="$event" />
            <xsl:with-param name="patterns" select="$patterns" />
            <xsl:with-param name="stickiness" select="'none'" />
          </xsl:next-match>
        </xsl:otherwise>
      </xsl:choose>
      -->
      <xsl:variable name="sticky-deriv" as="element()+">
        <xsl:apply-templates select="." mode="pat:sticky-derivative">
          <xsl:with-param name="event" select="$event" />
          <xsl:with-param name="patterns" select="$patterns" />
          <xsl:with-param name="stickiness" select="'sticky'" />
        </xsl:apply-templates>
      </xsl:variable>
      <xsl:variable name="non-sticky-deriv" as="element()+">
        <xsl:next-match>
          <xsl:with-param name="event" select="$event" />
          <xsl:with-param name="patterns" select="$sticky-deriv" />
          <xsl:with-param name="stickiness" select="'non-sticky'" />
        </xsl:next-match>
      </xsl:variable>
      <xsl:call-template name="pat:choice">
        <xsl:with-param name="pattern1" select="$sticky-deriv[1]/@name" />
        <xsl:with-param name="pattern2" select="$non-sticky-deriv[1]/@name" />
        <xsl:with-param name="patterns" select="$non-sticky-deriv" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$stickiness = 'sticky'">
      <xsl:apply-templates select="." mode="pat:sticky-derivative">
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
        <xsl:with-param name="stickiness" select="'sticky'" />
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
        <xsl:with-param name="stickiness" select="$stickiness" />
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
  
<xsl:template match="pat:partition" as="element()+"
              mode="pat:start-tag-derivative
                    pat:text-derivative
                    pat:end-tag-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="$stickiness = 'non-sticky'">
      <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
      <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
    </xsl:when>
    <xsl:when test="$stickiness = ('none', 'sticky')">
      <xsl:apply-templates select="." mode="pat:sticky-derivative">
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
        <xsl:with-param name="stickiness" select="'sticky'" />
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="deriv" as="element()+"
        select="pat:derivative($event, pat:ref[1]/@name, $patterns, 'both')" />
      <xsl:call-template name="pat:partition">
        <xsl:with-param name="patterns" select="$deriv" />
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>  
</xsl:template>  
  
<test:tests>
  <test:title>Validating Elements</test:title>
  <test:test>
    <test:title>Start tag against element</test:title>
    <test:context>
      <pat:element>
        <pat:ref name="d32e0"/>
        <pat:ref name="#text"/>
      </pat:element>
    </test:context>
    <test:param name="event">
      <ev:start-tag-open ns="" name="foo" hash="start-tag('','foo','')" />            
    </test:param>
    <test:param name="patterns">
      <pat:define name="#notAllowed" hash="#notAllowed" nullable="false" allows-text="false">
        <pat:notAllowed/>
      </pat:define>
      <pat:define name="#empty" hash="#empty" nullable="true" allows-text="false">
        <pat:empty/>
      </pat:define>
      <pat:define name="#text" hash="#text" nullable="true" allows-text="true">
        <pat:text/>
      </pat:define>
      <pat:define name="d32e0" hash="{}foo" nullable="false" allows-text="false">
        <pat:name name="foo" ns=""/>
      </pat:define>
      <pat:define name="d36e3" hash="(#text,d36e0)" nullable="false" allows-text="true">
        <pat:group>
          <pat:ref name="#text"/>
          <pat:ref name="d36e0"/>
        </pat:group>
      </pat:define>
      <pat:define name="d36e0" hash="end-tag d32e0=" nullable="false" allows-text="false">
        <pat:end-tag id="">
          <pat:ref name="d32e0"/>
        </pat:end-tag>
      </pat:define>
      <pat:define name="d38e0" hash="after{d36e3,#empty}" nullable="false" allows-text="true">
        <pat:after>
          <pat:ref name="d36e3"/>
          <pat:ref name="#empty"/>
        </pat:after>
      </pat:define>
      <pat:define name="d33e0" hash="element d32e0 {#text}" nullable="false" allows-text="false">
        <pat:element>
          <pat:ref name="d32e0"/>
          <pat:ref name="#text"/>
        </pat:element>
      </pat:define>
    </test:param>
    <test:expect>
      <pat:define name="d38e0" hash="after{d36e3,#empty}" nullable="false" allows-text="true">
        <pat:after>
          <pat:ref name="d36e3"/>
          <pat:ref name="#empty"/>
        </pat:after>
      </pat:define>
      <pat:define name="#notAllowed" hash="#notAllowed" nullable="false" allows-text="false">
        <pat:notAllowed/>
      </pat:define>
      <pat:define name="#empty" hash="#empty" nullable="true" allows-text="false">
        <pat:empty/>
      </pat:define>
      <pat:define name="#text" hash="#text" nullable="true" allows-text="true">
        <pat:text/>
      </pat:define>
      <pat:define name="d32e0" hash="{}foo" nullable="false" allows-text="false">
        <pat:name name="foo" ns=""/>
      </pat:define>
      <pat:define name="d36e3" hash="(#text,d36e0)" nullable="false" allows-text="true">
        <pat:group>
          <pat:ref name="#text"/>
          <pat:ref name="d36e0"/>
        </pat:group>
      </pat:define>
      <pat:define name="d36e0" hash="end-tag d32e0=" nullable="false" allows-text="false">
        <pat:end-tag id="">
          <pat:ref name="d32e0"/>
        </pat:end-tag>
      </pat:define>
      <pat:define name="d33e0" hash="element d32e0 {#text}" nullable="false" allows-text="false">
        <pat:element>
          <pat:ref name="d32e0"/>
          <pat:ref name="#text"/>
        </pat:element>
      </pat:define>
    </test:expect>
  </test:test>
</test:tests>
<xsl:template match="pat:range" as="element()+" 
              mode="pat:start-tag-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="$stickiness = 'sticky'">
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:when>
    <xsl:when test="pat:contains(pat:ref[1], QName($event/@ns, $event/@name), $patterns)">
      <xsl:variable name="end-tag" as="element()">
        <pat:end-tag id="{$event/@id}">
          <pat:name ns="{$event/@ns}">
            <xsl:value-of select="$event/@name" />
          </pat:name>
        </pat:end-tag>
      </xsl:variable>

      <xsl:variable name="compiled-end-tag" as="element()+">
        <xsl:apply-templates select="$end-tag" mode="pat:compile">
          <xsl:with-param name="patterns" select="$patterns" />
        </xsl:apply-templates>
      </xsl:variable>      
      <xsl:call-template name="pat:group">
        <xsl:with-param name="pattern1" select="pat:ref[2]/@name" />
        <xsl:with-param name="pattern2" select="$compiled-end-tag[1]/@name" />
        <xsl:with-param name="patterns" select="$compiled-end-tag" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template> 
  
<xsl:template match="pat:*" as="element()+" 
              mode="pat:start-tag-derivative">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>
  
<!-- *** pat:end-tag-derivative mode *** -->
  
<xsl:template match="pat:end-tag" mode="pat:end-tag-derivative" as="element()+">
  <xsl:param name="event" as="element(ev:end-tag-close)" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:param name="nc" as="element(pat:name)"
    select="pat:pattern(pat:ref[1]/@name, $patterns)/pat:name" />
  <xsl:choose>
    <xsl:when test="$stickiness = 'sticky'">
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:when>
    <xsl:when test="string($nc) eq $event/@name and
                    $nc/@ns eq $event/@ns and
                    @id eq $event/@id">
      <xsl:variable name="empty" as="element()" select="$patterns[@name = '#empty']" />
      <xsl:sequence select="$empty, $patterns except $empty" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<xsl:template match="pat:*" mode="pat:end-tag-derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>

<!-- *** pat:start-annotation-derivative mode *** -->
  
<xsl:template match="pat:annotation" as="element()+" 
              mode="pat:start-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:choose>
    <xsl:when test="pat:contains(pat:ref[1], QName($event/@ns, $event/@name), $patterns)">
      <xsl:variable name="end-annotation" as="element()">
        <pat:end-annotation>
          <pat:name ns="{$event/@ns}">
            <xsl:value-of select="$event/@name" />
          </pat:name>
        </pat:end-annotation>
      </xsl:variable>

      <xsl:variable name="compiled-end-annotation" as="element()+">
        <xsl:apply-templates select="$end-annotation" mode="pat:compile">
          <xsl:with-param name="patterns" select="$patterns" />
        </xsl:apply-templates>
      </xsl:variable>      
      <xsl:variable name="group" as="element()+">
        <xsl:call-template name="pat:group">
          <xsl:with-param name="pattern1" select="pat:ref[2]/@name" />
          <xsl:with-param name="pattern2" select="$compiled-end-annotation[1]/@name" />
          <xsl:with-param name="patterns" select="$compiled-end-annotation" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="after" as="element()+">
        <xsl:call-template name="pat:after">
          <xsl:with-param name="pattern1" select="$group[1]/@name" />
          <xsl:with-param name="pattern2" select="'#empty'" />
          <xsl:with-param name="patterns" select="$group" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:sequence select="$after" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
      <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<xsl:template match="pat:group" as="element()+"
              mode="pat:start-annotation-derivative">
  <xsl:param name="event" as="element()" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:choose>
    <xsl:when test="not(pat:allows-annotations(pat:ref[1], $patterns))">
      <xsl:variable name="deriv" as="element()+" 
        select="pat:derivative($event, pat:ref[2]/@name, $patterns, 'none')" />
      <xsl:variable name="after" as="element()+">
        <xsl:call-template name="pat:apply-after">
          <xsl:with-param name="construct" select="'group'" />
          <xsl:with-param name="arg1" select="pat:ref[1]/@name" />
          <xsl:with-param name="pattern" select="$deriv[1]/@name" />
          <xsl:with-param name="patterns" select="$deriv" />
        </xsl:call-template>
      </xsl:variable>
      <!--
      <xsl:message>
        event: <xsl:copy-of select="$event" copy-namespaces="no" />
        group: <xsl:copy-of select="." copy-namespaces="no" />
        deriv: <xsl:copy-of select="$deriv[1]" copy-namespaces="no" />
        after: <xsl:copy-of select="$after[1]" copy-namespaces="no" />
        grammar: <xsl:copy-of select="$deriv" copy-namespaces="no" />
      </xsl:message>
      -->
      <xsl:sequence select="$after" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="deriv" as="element()+"
        select="pat:derivative($event, pat:ref[1]/@name, $patterns, 'none')" />  
      <xsl:variable name="after" as="element()+">
        <xsl:call-template name="pat:apply-after">
          <xsl:with-param name="construct" select="'group'" />
          <xsl:with-param name="arg2" select="pat:ref[2]/@name" /> 
          <xsl:with-param name="pattern" select="$deriv[1]/@name" />
          <xsl:with-param name="patterns" select="$deriv" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="pat:nullable(pat:ref[1], $after)">
          <xsl:variable name="deriv2" as="element()+"
            select="pat:derivative($event, pat:ref[2]/@name, $after, 'none')" />      
          <xsl:call-template name="pat:choice">
            <xsl:with-param name="pattern1" select="$deriv[1]/@name" />
            <xsl:with-param name="pattern2" select="$after[1]/@name" />
            <xsl:with-param name="patterns" select="$after" />
          </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
          <xsl:sequence select="$after" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="pat:notAllowed | pat:empty" as="element()+"
              mode="pat:start-annotation-derivative">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>
  
<xsl:template match="pat:*" as="element()+" mode="pat:start-annotation-derivative">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select=".., $patterns except .." />
</xsl:template>

<!-- *** pat:end-annotation-derivative mode *** -->
  
<xsl:template match="pat:end-annotation" mode="pat:end-annotation-derivative" as="element()+">
  <xsl:param name="event" as="element(ev:end-annotation-close)" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="nc" as="element(pat:name)"
    select="pat:pattern(pat:ref[1]/@name, $patterns)/pat:name" />
  <xsl:choose>
    <xsl:when test="string($nc) eq $event/@name and
                    $nc/@ns eq $event/@ns">
      <xsl:variable name="empty" as="element()" select="$patterns[@name = '#empty']" />
      <xsl:sequence select="$empty, $patterns except $empty" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:next-match>
        <xsl:with-param name="event" select="$event" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<xsl:template match="pat:*" mode="pat:end-annotation-derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>  
  
<!-- *** pat:text-derivative mode *** -->
  
<xsl:template match="pat:text" mode="pat:text-derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:param name="stickiness" as="xs:string" required="yes" />
  <xsl:choose>
    <xsl:when test="$stickiness = 'sticky'">
      <xsl:next-match>
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:next-match>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select=".., $patterns except .." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<xsl:template match="pat:*" mode="pat:text-derivative" as="element()+">
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
  <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
</xsl:template>
      
<!-- *** pat:apply-after *** -->  
  
<!-- This follows James Clark's logic. -->
<xsl:template name="pat:apply-after" as="element()+">
  <xsl:param name="construct" as="xs:string" required="yes" />
  <xsl:param name="arg1" as="xs:string?" select="()" />
  <xsl:param name="arg2" as="xs:string?" select="()" />
  <xsl:param name="pattern" as="xs:string" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:variable name="pat" as="element()" select="pat:pattern($pattern, $patterns)" />
  <!--
  <xsl:message>
    construct: <xsl:value-of select="$construct" />
    arg1: <xsl:value-of select="$arg1" />
    arg2: <xsl:value-of select="$arg2" />
    pattern: <xsl:value-of select="$pattern" />
    patterns: <xsl:copy-of select="$patterns" copy-namespaces="no" />
  </xsl:message>
  -->
  <xsl:choose>
    <xsl:when test="$pat/pat:after">
      <xsl:variable name="group" as="element()+">
        <xsl:variable name="pat1" as="xs:string" 
          select="if (exists($arg1)) then $arg1 else $pat/pat:after/pat:ref[2]/@name"/>
        <xsl:variable name="pat2" as="xs:string" 
          select="if (exists($arg2)) then $arg2 else $pat/pat:after/pat:ref[2]/@name" />
        <xsl:choose>
          <xsl:when test="$construct = 'group'">
            <xsl:call-template name="pat:group">
              <xsl:with-param name="pattern1" select="$pat1" />
              <xsl:with-param name="pattern2" select="$pat2" />
              <xsl:with-param name="patterns" select="$patterns" />
            </xsl:call-template>
          </xsl:when>
          <xsl:when test="$construct = 'interleave'">
            <xsl:call-template name="pat:interleave">
              <xsl:with-param name="pattern1" select="$pat1" />
              <xsl:with-param name="pattern2" select="$pat2" />
              <xsl:with-param name="patterns" select="$patterns" />
            </xsl:call-template>
          </xsl:when>
          <xsl:when test="$construct = 'concur'">
            <xsl:call-template name="pat:concur">
              <xsl:with-param name="pattern1" select="$pat1" />
              <xsl:with-param name="pattern2" select="$pat2" />
              <xsl:with-param name="patterns" select="$patterns" />
            </xsl:call-template>
          </xsl:when>
          <xsl:when test="$construct = 'after'">
            <xsl:call-template name="pat:after">
              <xsl:with-param name="pattern1" select="$pat1" />
              <xsl:with-param name="pattern2" select="$pat2" />
              <xsl:with-param name="patterns" select="$patterns" />
            </xsl:call-template>
          </xsl:when>
        </xsl:choose>
      </xsl:variable>      
      <xsl:call-template name="pat:after">
        <xsl:with-param name="pattern1" select="$pat/pat:after/pat:ref[1]/@name" />
        <xsl:with-param name="pattern2" select="$group[1]/@name" />
        <xsl:with-param name="patterns" select="$group" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$pat/pat:choice">
      <xsl:variable name="after1" as="element()+">
        <xsl:call-template name="pat:apply-after">
          <xsl:with-param name="construct" select="$construct" />
          <xsl:with-param name="arg1" select="$arg1" />
          <xsl:with-param name="arg2" select="$arg2" />
          <xsl:with-param name="pattern" select="$pat/pat:choice/pat:ref[1]/@name" />
          <xsl:with-param name="patterns" select="$patterns" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="after2" as="element()+">
        <xsl:call-template name="pat:apply-after">
          <xsl:with-param name="construct" select="$construct" />
          <xsl:with-param name="arg1" select="$arg1" />
          <xsl:with-param name="arg2" select="$arg2" />
          <xsl:with-param name="pattern" select="$pat/pat:choice/pat:ref[2]/@name" />
          <xsl:with-param name="patterns" select="$after1" />
        </xsl:call-template>
      </xsl:variable>      
      <xsl:call-template name="pat:choice">
        <xsl:with-param name="pattern1" select="$after1[1]/@name" />
        <xsl:with-param name="pattern2" select="$after2[1]/@name" />
        <xsl:with-param name="patterns" select="$after2" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$pat/pat:ref">
      <xsl:call-template name="pat:apply-after">
        <xsl:with-param name="construct" select="$construct" />
        <xsl:with-param name="arg1" select="$arg1" />
        <xsl:with-param name="arg2" select="$arg2" />
        <xsl:with-param name="pattern" select="$pat/pat:ref/@name" />
        <xsl:with-param name="patterns" select="$patterns" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="notAllowed" as="element()" select="$patterns[@name = '#notAllowed']" />
      <xsl:sequence select="$notAllowed, $patterns except $notAllowed" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>  
  
<!-- *** pat:contains() *** -->
  
<xsl:function name="pat:contains" as="xs:boolean">
  <xsl:param name="nc" as="element()" />
  <xsl:param name="qname" as="xs:QName" />
  <xsl:param name="patterns" as="element()+" />
  <xsl:apply-templates select="$nc" mode="pat:contains">
    <xsl:with-param name="qname" select="$qname" />
    <xsl:with-param name="patterns" select="$patterns" />
  </xsl:apply-templates>
</xsl:function>
  
<xsl:template match="pat:ref" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence 
    select="pat:contains(pat:pattern(@name, $patterns), $qname, $patterns)" />
</xsl:template>  

<xsl:template match="pat:define" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select="pat:contains(pat:*, $qname, $patterns)" />
</xsl:template>  
  
<xsl:template match="pat:name" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:sequence select="string(.) = local-name-from-QName($qname) and
                        @ns = namespace-uri-from-QName($qname)" />
</xsl:template>
  
<xsl:template match="pat:nsName" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:sequence select="@ns = namespace-uri-from-QName($qname)" />
</xsl:template>  

<xsl:template match="pat:nsNameExcept" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select="@ns = namespace-uri-from-QName($qname) and
                        not(pat:contains(pat:*, $qname, $patterns))" />
</xsl:template>
  
<xsl:template match="pat:anyName" mode="pat:contains" as="xs:boolean">
  <xsl:sequence select="true()" />
</xsl:template>
  
<xsl:template match="pat:anyNameExcept" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select="not(pat:contains(pat:*, $qname, $patterns))" />
</xsl:template>
  
<xsl:template match="pat:choice" mode="pat:contains" as="xs:boolean">
  <xsl:param name="qname" as="xs:QName" required="yes" />
  <xsl:param name="patterns" as="element()+" required="yes" />
  <xsl:sequence select="pat:contains(pat:*[1], $qname, $patterns) or
                        pat:contains(pat:*[2], $qname, $patterns)" />
</xsl:template>
    
</xsl:stylesheet>
