<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns:f="http://nwalsh.com/xslt/functions"
		xmlns:rng="http://relaxng.org/ns/structure/1.0"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
		exclude-result-prefixes="xs f rng"
                version="2.0">

<xsl:param name="rngfile" select="''"/>
<xsl:param name="keeptext" select="1"/>

<xsl:output method="xml" encoding="utf-8" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:key name="element" match="rng:element" use="@name"/>
<xsl:key name="define" match="rng:define" use="@name"/>

<xsl:variable name="rng" as="document-node()">
  <xsl:choose>
    <xsl:when test="$rngfile = ''">
      <xsl:message terminate="yes">
	<xsl:text>You must specify the $rngfile, </xsl:text>
	<xsl:text>the URI of the RNG grammar.</xsl:text>
      </xsl:message>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="document($rngfile)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

<xsl:variable name="classes" as="xs:string+">
  <xsl:for-each select="$rng//rng:element">
    <xsl:value-of select="@name"/>
  </xsl:for-each>
</xsl:variable>

<xsl:template match="/">
  <xsl:if test="count($rng//rng:start/*) &gt; 1
		or not($rng//rng:start/rng:ref)">
    <xsl:message terminate="yes">
      <xsl:text>Unexpected RNG format in start pattern.</xsl:text>
    </xsl:message>
  </xsl:if>

  <xsl:variable name="patn"
		select="key('define',$rng//rng:start/rng:ref/@name,$rng)"/>

  <xsl:variable name="root"
		select="$patn/rng:element/@name"/>

  <xsl:element name="{$root}">
    <xsl:apply-templates>
      <xsl:with-param name="inelem" select="false()"/>
      <xsl:with-param name="parent" select="$root"/>
    </xsl:apply-templates>
  </xsl:element>
</xsl:template>

<xsl:template match="*[@class]">
  <xsl:param name="inelem" as="xs:boolean"/>
  <xsl:param name="parent" as="xs:string" select="''"/>

  <xsl:variable name="this" select="."/>

  <xsl:for-each select="tokenize(normalize-space(@class),' ')">
    <xsl:variable name="allowed" select="f:allowed-in(.,$parent)"/>
    <xsl:choose>
      <xsl:when test=". = $classes and $allowed">
	<xsl:element name="{.}">
	  <xsl:if test="$this/@title
			and key('element',.,$rng)//rng:attribute[@name='title']">
	    <xsl:attribute name="title" select="$this/@title"/>
	  </xsl:if>
	  <xsl:apply-templates select="$this/node()">
	    <xsl:with-param name="inelem" select="true()"/>
	    <xsl:with-param name="parent" select="."/>
	  </xsl:apply-templates>
	</xsl:element>
      </xsl:when>
      <xsl:otherwise>
	<xsl:apply-templates select="$this/node()">
	  <xsl:with-param name="inelem" select="false()"/>
	  <xsl:with-param name="parent" select="$parent"/>
	</xsl:apply-templates>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

<xsl:template match="*">
  <xsl:param name="inelem" as="xs:boolean"/>
  <xsl:param name="parent" as="xs:string" select="''"/>

  <xsl:apply-templates>
    <xsl:with-param name="inelem" select="$inelem"/>
    <xsl:with-param name="parent" select="$parent"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="comment()|processing-instruction()">
  <xsl:param name="inelem" as="xs:boolean"/>
  <!-- suppress -->
</xsl:template>

<xsl:template match="text()">
  <xsl:param name="inelem" as="xs:boolean"/>
  <xsl:if test="$inelem and $keeptext != 0">
    <xsl:value-of select="."/>
  </xsl:if>
</xsl:template>

<xsl:function name="f:allowed-in" as="xs:boolean">
  <xsl:param name="child" as="xs:string"/>
  <xsl:param name="parent" as="xs:string"/>
 
  <xsl:variable name="element" select="key('element',$parent,$rng)"/>

  <!--
  <xsl:message>
    <xsl:value-of select="$child"/>
    <xsl:text> in </xsl:text>
    <xsl:value-of select="$parent"/>
    <xsl:text>?</xsl:text>
  </xsl:message>
  -->

  <xsl:variable name="found" as="xs:boolean*">
    <xsl:for-each select="$element//rng:ref">
      <xsl:variable name="defn" select="key('define',@name,$rng)"/>
      <xsl:if test="$defn/rng:element/@name = $child">
	<xsl:sequence select="true()"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:value-of select="true() = $found"/>
</xsl:function>

</xsl:stylesheet>
