<?xml version="1.0" encoding="UTF-8"?>
<!--
  LEGAL INFORMATION
  
  Copyright (c) 2007 Syncro Soft Ltd  (http://www.oxygenxml.com)
  All rights reserved.
  
  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions are
  met:
  
  * Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer. 
  Altered source versions must be plainly marked as such, and must not be 
  misrepresented as being the original software.
  
  * Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in
  the documentation and/or other materials provided with the
  distribution.
  
  * Neither the name of the Syncro Soft nor
  the names of its contributors may be used to endorse or promote
  products derived from this software without specific prior written
  permission.
  
  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:nvdl="http://purl.oclc.org/dsdl/nvdl/ns/structure/1.0"
  xmlns:n="http://www.oxygenxml.com/nvdl"
  xmlns:xsl2="http://www.oxygenxml.com/xslt/alias" exclude-result-prefixes="nvdl">
  <xsl:namespace-alias stylesheet-prefix="xsl2" result-prefix="xsl"/>
  <xsl:output indent="yes"/>

  <xsl:key name="getMode" match="nvdl:rules|nvdl:mode" use="(@name,generate-id())[1]"/>

  <!-- Checks if the script uses any wildcards -->
  <xsl:variable name="enableWildcards" select="//nvdl:namespace[
    (contains(@ns, '*') and not(@wildcard)) 
    or 
    (@wildcard!='' and contains(@ns, @wildcard))
    ]"/>
  <!-- Put apostrophe in a variable -->
  <xsl:variable name="ap">'</xsl:variable>

  <xsl:template match="/">
    <xsl2:stylesheet  version="2.0" exclude-result-prefixes="n">
      <xsl2:output indent="yes"/>
      
      <!-- On document we get the dispatch interpretations and extract each intepretation as a child of the n:dispatch root element -->
      <xsl2:template match="/">
        <xsl:variable name="startMode"
          select="if (nvdl:rules/@startMode) then nvdl:rules/@startMode else generate-id(nvdl:rules)"/> 
        <n:dispatch>
          <xsl2:variable name="content">
            <xsl2:apply-templates mode="mode_{$startMode}"/>
          </xsl2:variable>
          <xsl2:apply-templates mode="flatten" select="$content"/>
        </n:dispatch>
      </xsl2:template>
      <!-- If wildcards are enabled then we output the functions we need to match on wildcards -->
      <xsl:if test="$enableWildcards">
        <xsl2:function name="n:matches" as="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xsl2:param name="uri"/>
          <xsl2:param name="ns"/>
          <xsl2:param name="wildcard"/>
          <xsl2:choose>
            <xsl2:when test="$wildcard='' or not(contains($ns, $wildcard))">
              <xsl2:value-of select="$uri=$ns"/>
            </xsl2:when>
            <xsl2:when test="starts-with($ns, $wildcard)">
              <xsl2:value-of select="n:matchLax($uri, substring-after($ns, $wildcard), $wildcard)"/>
            </xsl2:when>
            <xsl2:otherwise>
              <xsl2:variable name="constant" select="substring-before($ns, $wildcard)"/>
              <xsl2:choose>
                <xsl2:when test="starts-with($uri, $constant)">
                  <xsl2:value-of
                    select="n:matchLax(substring-after($uri, $constant), substring-after($ns, $wildcard), $wildcard)"
                  />
                </xsl2:when>
                <xsl2:otherwise>
                  <xsl2:value-of select="false()"/>
                </xsl2:otherwise>
              </xsl2:choose>
            </xsl2:otherwise>
          </xsl2:choose>
        </xsl2:function>
        
        <xsl2:function name="n:matchLax" as="xs:boolean" xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xsl2:param name="uri"/>
          <xsl2:param name="ns"/>
          <xsl2:param name="wildcard"/>
          <xsl2:choose>
            <xsl2:when test="$ns=''">
              <xsl2:value-of select="true()"/>
            </xsl2:when>
            <xsl2:when test="not(contains($ns, $wildcard))">
              <xsl2:value-of select="ends-with($uri, $ns)"/>
            </xsl2:when>
            <xsl2:otherwise>
              <xsl2:variable name="constant" select="substring-before($ns, $wildcard)"/>
              <xsl2:value-of
                select="
                if 
                (contains($uri, $constant)) 
                then 
                n:matchLax(substring-after($uri, $constant), substring-after($ns, $wildcard), $wildcard) 
                else 
                false()
                "
              />
            </xsl2:otherwise>
          </xsl2:choose>
        </xsl2:function>
      </xsl:if>
      <!-- Iterate all the modes and process their rules -->
      <xsl:for-each select="if (not(nvdl:rules/nvdl:mode)) then (nvdl:rules|//nvdl:mode) else //nvdl:mode">
        <xsl:variable name="currentMode" select="if (@name) then @name else generate-id(.)"/>
        <xsl:apply-templates mode="rules">
          <xsl:with-param name="currentMode" select="$currentMode"/>
        </xsl:apply-templates>
        <!-- The default any namespace reject rule on elements -->
        <xsl:if test="not(nvdl:anyNamespace[not(@match) or contains(@match, 'elements')])">
          <xsl2:template match="n:section" mode="mode_{$currentMode}">
            <n:reject>
              <xsl2:apply-templates mode="mode_{$currentMode}"/>
            </n:reject>
          </xsl2:template>
        </xsl:if>
      </xsl:for-each>
      <!-- Default rules for copying content and attributes. -->
      <xsl2:template match="*[namespace-uri()!='http://www.oxygenxml.com/nvdl']" mode="#all" priority="-100">
        <xsl2:copy>
          <xsl2:apply-templates select="@*[namespace-uri()!='http://www.oxygenxml.com/nvdl']" mode="#current"/>
          <xsl2:apply-templates select="@n:*" mode="#current"/>
          <xsl2:apply-templates mode="#current"/>
        </xsl2:copy>
      </xsl2:template>
      <xsl2:template match="@*[namespace-uri()!='http://www.oxygenxml.com/nvdl']" mode="#all" priority="-100">
        <xsl2:copy/>
      </xsl2:template>
      <xsl2:template match="@n:*" mode="#all" priority="-100"/>
      <!-- The templates to extract the interpretations as children of n:dispatch  -->
      <xsl2:template match="*" mode="flatten">
        <xsl2:apply-templates select="//n:*" mode="flatten1"/>
      </xsl2:template>
      <xsl2:template match="n:*" mode="flatten1">
        <xsl2:copy>
          <xsl2:copy-of select="@*"/>
          <xsl2:apply-templates select="text()|*[namespace-uri()!='http://www.oxygenxml.com/nvdl']"
            mode="flatten2"/>
        </xsl2:copy>
      </xsl2:template>
      <xsl2:template match="*[namespace-uri()!='http://www.oxygenxml.com/nvdl']" mode="flatten2">
        <xsl2:copy>
          <xsl2:copy-of select="@*[namespace-uri()!='http://www.oxygenxml.com/nvdl']"/>
          <xsl2:apply-templates select="text()|*[namespace-uri()!='http://www.oxygenxml.com/nvdl']"
            mode="flatten2"/>
        </xsl2:copy>
      </xsl2:template>
    </xsl2:stylesheet>
    
  </xsl:template>
  
  <!-- Process template rules -->
  <xsl:template match="*" mode="rules"/>
  <xsl:template match="nvdl:namespace[not(@match) or contains(@match, 'elements')]" mode="rules">
    <xsl:param name="currentMode"/>
    <!-- For each rule we have a template mathing it in the current mode -->
    <xsl2:template mode="mode_{$currentMode}">
      <xsl:attribute name="match">
        <xsl:choose>
          <xsl:when test="$enableWildcards and (
            (contains(@ns, '*') and not(@wildcard)) 
            or 
            (@wildcard!='' and contains(@ns, @wildcard))
            ) ">n:section[n:matches(@ns, '<xsl:value-of select="@ns"/>', '<xsl:value-of select="if (@wildcard) then @wildcard else '*'"/>')]</xsl:when>
          <xsl:otherwise>n:section[@ns='<xsl:value-of select="@ns"/>']</xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
      <!-- Apply processing on the actions inside the rule template match -->
      <xsl:apply-templates select="*" mode="actions">
        <xsl:with-param name="currentMode" select="$currentMode"/>
      </xsl:apply-templates>
    </xsl2:template>
    <!-- Apply processing on actions outside of the rule template match -->
    <xsl:apply-templates select="*" mode="actions2">
      <xsl:with-param name="currentMode" select="$currentMode"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="nvdl:anyNamespace[not(@match) or contains(@match, 'elements')]" mode="rules">
    <xsl:param name="currentMode"/>
    <xsl2:template match="n:section" mode="mode_{$currentMode}">
      <xsl:apply-templates select="*" mode="actions">
        <xsl:with-param name="currentMode" select="$currentMode"/>
      </xsl:apply-templates>
    </xsl2:template>
    <xsl:apply-templates select="*" mode="actions2">
      <xsl:with-param name="currentMode" select="$currentMode"/>
    </xsl:apply-templates>
  </xsl:template>
  <!-- Process template rules on attributes -->
  <xsl:template match="nvdl:namespace[contains(@match, 'attributes')]" mode="rules">
    <xsl:param name="currentMode"/>
    <!-- For each rule we have a template mathing it in the current mode -->
    <xsl2:template mode="mode_{$currentMode}">
      <xsl:attribute name="match">
        <xsl:choose>
          <xsl:when test="$enableWildcards and (
            (contains(@ns, '*') and not(@wildcard)) 
            or 
            (@wildcard!='' and contains(@ns, @wildcard))
            ) ">@*[n:matches(namespace-uri(), '<xsl:value-of select="@ns"/>', '<xsl:value-of select="if (@wildcard) then @wildcard else '*'"/>')]</xsl:when>
          <xsl:otherwise>@*[namespace-uri()='<xsl:value-of select="@ns"/>']</xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
          <xsl:if test="nvdl:attach"><xsl2:copy/></xsl:if>
    </xsl2:template>
    <xsl:apply-templates mode="attActions" select=".">
      <xsl:with-param name="currentMode" select="$currentMode"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="nvdl:anyNamespace[contains(@match, 'attributes')]" mode="rules">
    <xsl:param name="currentMode"/>
    <xsl2:template mode="mode_{$currentMode}" match="@*">
      <xsl:if test="nvdl:attach"><xsl2:copy/></xsl:if>
    </xsl2:template>
    <xsl:apply-templates mode="attActions" select=".">
      <xsl:with-param name="currentMode" select="$currentMode"/>
    </xsl:apply-templates>
  </xsl:template>
  
  <!-- Process actions inside the rule template macth -->
  <xsl:template match="*" mode="actions"/>
  <xsl:template match="nvdl:validate|nvdl:reject|nvdl:allow" mode="actions">
    <xsl:param name="currentMode"/>
    <xsl:variable name="useMode" select="if (@useMode) then @useMode else (if (nvdl:mode) then generate-id(nvdl:mode) else $currentMode)"/>
    <xsl:variable name="xslMode" select="string-join(('mode_',$currentMode,'_ns',generate-id(..),'_', local-name(),'_',generate-id(.)),'')"/>
    <xsl:element name="n:{local-name()}" namespace="http://www.oxygenxml.com/nvdl">
      <xsl:copy-of select="@*"/>
      <xsl2:apply-templates mode="{if (nvdl:context) then $xslMode else string-join(('mode_', $useMode), '')}"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="nvdl:attach" mode="actions">
    <xsl:param name="currentMode"/>
    <xsl:variable name="xslMode" select="string-join(('mode_',$currentMode,'_ns',generate-id(..),'_', local-name(),'_',generate-id(.)),'')"/>
    <xsl2:apply-templates mode="{$xslMode}"/>
  </xsl:template>
  <xsl:template match="nvdl:unwrap" mode="actions">
    <xsl:param name="currentMode"/>
    <xsl:variable name="xslMode" select="string-join(('mode_',$currentMode,'_ns',generate-id(..),'_', local-name(),'_',generate-id(.)),'')"/>
    <xsl2:variable name="thisSection" select="generate-id(.)"/>
    <xsl2:apply-templates select="//n:section[generate-id(ancestor::n:section[1])=$thisSection]" mode="{$xslMode}"/>
  </xsl:template>

  <!-- Process actions outside the rule template macth -->
  <xsl:template match="*" mode="actions2"/>
  <xsl:template match="nvdl:validate|nvdl:reject|nvdl:allow|nvdl:attach|nvdl:unwrap" mode="actions2">
    <xsl:param name="currentMode"/>
    <xsl:variable name="useMode" select="if (@useMode) then @useMode else (if (nvdl:mode) then generate-id(nvdl:mode) else $currentMode)"/>
    <xsl:variable name="xslMode" select="string-join(('mode_',$currentMode,'_ns',generate-id(..),'_', local-name(),'_',generate-id(.)),'')"/>
    <xsl:if test="nvdl:context">
      <xsl2:template match="n:section" mode="{$xslMode}">
        <xsl2:apply-templates mode="mode_{$useMode}" select="."/>
      </xsl2:template>
      <xsl:apply-templates select="key('getMode', $useMode)/(nvdl:namespace|nvdl:anyNamespace)[contains(@match, 'attributes')]" mode="rules">
        <xsl:with-param name="currentMode" select="substring-after($xslMode, 'mode_')"/>
      </xsl:apply-templates>
    </xsl:if>
    <xsl:apply-templates select="nvdl:context" mode="actions2">
      <xsl:with-param name="currentMode" select="$currentMode"/>
      <xsl:with-param name="xslMode" select="$xslMode"/>
    </xsl:apply-templates>
  </xsl:template>
  <xsl:template match="nvdl:context" mode="actions2">
    <xsl:param name="currentMode"/>
    <xsl:param name="xslMode"/>
    <xsl:variable name="useMode" select="if (@useMode) then @useMode else (if (nvdl:mode) then generate-id(nvdl:mode) else $currentMode)"/>    
    <xsl:variable name="path1" select="if (starts-with(@path, '/')) then '' else '/'"/>
    <xsl:variable name="path2" select="string-join((  for $i in tokenize(@path, '/') return if ($i='') then '' else string-join(('/*[local-name()=',$ap, $i,$ap,']'), '')  ),'')"/>
    <xsl2:template 
      match="n:section[generate-id(.) = generate-id(ancestor::n:section[1]{$path1}{$path2}/n:section)]" 
      mode="{$xslMode}">
      <xsl2:apply-templates mode="mode_{$useMode}" select="."/>
      </xsl2:template>
  </xsl:template>

  <!-- Process atttribute actions -->
  <xsl:template match="*|text()" mode="attActions"/>
  <xsl:template match="nvdl:namespace[nvdl:validate|nvdl:reject|nvdl:allow]" mode="attActions">
    <xsl:param name="currentMode"/>
    <xsl2:template mode="mode_{$currentMode}">
      <xsl:attribute name="match">
        <xsl:choose>
          <xsl:when test="$enableWildcards and (
            (contains(@ns, '*') and not(@wildcard)) 
            or 
            (@wildcard!='' and contains(@ns, @wildcard))
            ) ">@*[n:matches(., '<xsl:value-of select="@ns"/>', '<xsl:value-of select="if (@wildcard) then @wildcard else '*'"/>')]</xsl:when>
          <xsl:otherwise>@n:*[.='<xsl:value-of select="@ns"/>']</xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>
      <xsl:apply-templates mode="attActions"/>
    </xsl2:template>
  </xsl:template>
  <xsl:template match="nvdl:anyNamespace[nvdl:validate|nvdl:reject|nvdl:allow]" mode="attActions">
    <xsl:param name="currentMode"/>
    <xsl2:template mode="mode_{$currentMode}" match="@n:*[ends-with(name(), 'ns')]">
      <xsl:apply-templates mode="attActions"/>
    </xsl2:template>
    <xsl2:template mode="mode_{$currentMode}" match="@n:*[not(ends-with(name(), 'ns'))]"/>
  </xsl:template>
  <xsl:template match="nvdl:allow|nvdl:reject|nvdl:validate" mode="attActions">
    <xsl:element name="n:{local-name()}" namespace="http://www.oxygenxml.com/nvdl">
      <xsl:copy-of select="@*"/>
      <virtualElement xmlns="http://purl.oclc.org/dsdl/nvdl/ns/instance/1.0">
        <xsl2:copy-of select="../@*[namespace-uri()=current()]"/>
      </virtualElement>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>
