<!-- Examploforms: See http://brainattic.info/examploforms for details.

This stylesheet transforms an examploforms instance into a full XForms
+XHTML document suitable for running in an off-the-shelf XForms engine

Copyright (c) 2004 Brain Attic, L.L.C.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:

  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  * The name of the authors when specified in the source files shall be kept unmodified.

THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL BRAIN ATTIC, L.L.C. BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

History:

V0.1: creation

 -->

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:eg="http://examplotron.org/0/"
    xmlns:ex="http://examploforms.org/0/"
    xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns="http://www.w3.org/2002/06/xhtml2"
    exclude-result-prefixes="eg ex">
<xsl:output method="xml"
    doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
    doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
    indent="yes"
    encoding="UTF-8"/>

<!-- insert anything needed in the containing XHTML document below -->
<xsl:template match="/">
  <html xml:lang="en">
    <head>
      <title>Examploforms-generated document</title>
      <xf:model>
        <xf:instance>
          <!-- write instance data -->
          <xsl:apply-templates select="/*" mode="instance"/>
        </xf:instance>
        <xs:schema targetNamespace="http://examploforms/0/">
          <!-- write datatypes -->
          <xsl:apply-templates select="//*[contains(@eg:content, 'dt:')]" mode="datatypes"/>
        </xs:schema>
        <!-- write binding statements -->
        <xsl:apply-templates select="//*[count(*)=0]" mode="binding"/>
      </xf:model>
    </head>
    <body>
      <xf:group>
      <!-- write form controls -->
        <xsl:apply-templates select="//*[@eg:occurs='+' or (count(*)=0 and (eg:content or @eg:occurs or @ex:*) and not(ancestor::*[@eg:occurs='+']))]" mode="controls"/>
      </xf:group>
    </body>
  </html>
</xsl:template>


<!--
write the XForms instance.
Basically, copy the document, stripping out all eg: and ex: attributes
by matching them against empty templates.
-->
<xsl:template match="@*|node()" mode="instance"><xsl:copy><xsl:apply-templates select="@*|node()" mode="instance"/></xsl:copy></xsl:template>

<xsl:template match="@eg:*" mode="instance">
  <!-- intentionally blank -->
</xsl:template>

<xsl:template match="@ex:*" mode="instance">
  <!-- intentionally blank -->
</xsl:template>

<!--
write out XForms bind elements
-->
<xsl:template match="*" mode="binding">
  <xf:bind>
    <xsl:attribute name="nodeset">
      <xsl:call-template name="pathtohere"/>
    </xsl:attribute>
    <xsl:if test="@ex:constraint">
      <xsl:attribute name="constraint">
        <xsl:value-of select="@ex:constraint"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@ex:calculate">
      <xsl:attribute name="calculate">
        <xsl:value-of select="@ex:calculate"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@ex:required">
      <xsl:attribute name="required">
        <xsl:value-of select="@ex:required"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@ex:readonly">
      <xsl:attribute name="readonly">
        <xsl:value-of select="@ex:readonly"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:if test="@ex:relevant">
      <xsl:attribute name="relevant">
        <xsl:value-of select="@ex:relevant"/>
      </xsl:attribute>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="@eg:content">
        <xsl:attribute name="type">
          <xsl:value-of select="@eg:content"/>
        </xsl:attribute>
      </xsl:when>
      <xsl:when test=".='true' or .='false'">
        <xsl:attribute name="type">xs:boolean</xsl:attribute>
      </xsl:when>
      <xsl:when test="translate(substring(.,1,1), '0123456789+-', '') = '' and translate(substring(.,2), '0123456789', '') = ''">
        <xsl:attribute name="type">xs:integer</xsl:attribute>
      </xsl:when>
      <xsl:when test="translate(substring(.,1,1), '0123456789+-', '') = '' and translate(substring(.,2), '0123456789', '') = '.'">
        <xsl:attribute name="type">xs:decimal</xsl:attribute>
      </xsl:when>
      <xsl:when test="string(number(.))!='NaN'">
        <xsl:attribute name="type">xs:double</xsl:attribute>
      </xsl:when>
      <!-- for dates, only look at the first 10 characters,
       thus things like 1999-12-05+10:00 will still qualify -->
      <xsl:when test="translate(substring(.,1,10), '0123456789', '') = '--' and substring(.,5,1) = '-' and substring(.,8,1) = '-'">
        <xsl:attribute name="type">xs:date</xsl:attribute>
      </xsl:when>
    </xsl:choose>
  </xf:bind>
</xsl:template>

<!--
write out form controls
-->
<xsl:template match="*" mode="controls">
  <xsl:variable name="after1stdash" select="substring-after(@eg:content, '-')"/>
  <xsl:variable name="after2nddash" select="substring-after($after1stdash, '-')"/>
  <xsl:choose>
    <xsl:when test="@eg:occurs='+'">
      <xf:repeat>
        <xsl:attribute name="nodeset">
          <xsl:call-template name="pathtohere"/>
        </xsl:attribute>
        <xsl:apply-templates select="*[@eg:occurs='+' or (count(*)=0 and (eg:content or @eg:occurs or @ex:*))]" mode="controls"/>
      </xf:repeat>
    </xsl:when>
    <xsl:when test="starts-with(@eg:content, 'dt:enum')">
      <xf:select1>
        <xsl:call-template name="fccontents"/>
        <xsl:call-template name="choices">
          <xsl:with-param name="dtname" select="substring-after(@eg:content, 'enum-')"/>
        </xsl:call-template>
      </xf:select1>
    </xsl:when>
    <xsl:when test="starts-with(@eg:content, 'dt:string') and (($after2nddash='' and $after1stdash > 100) or ($after2nddash > 100))">
      <xf:textarea>
        <xsl:call-template name="fccontents"/>
      </xf:textarea>
    </xsl:when>
    <xsl:otherwise>
      <xf:input>
        <xsl:call-template name="fccontents"/>
      </xf:input>
    </xsl:otherwise>
  </xsl:choose>
  <br/>
</xsl:template>

<xsl:template name="fccontents">
  <xsl:attribute name="ref">
    <xsl:if test="not(ancestor::*[@eg:occurs='+'])">
      <xsl:call-template name="pathtohere"/>
    </xsl:if>
    <xsl:if test="ancestor::*[@eg:occurs='+']">
      <xsl:value-of select="name()"/>
    </xsl:if>
  </xsl:attribute>
  <xsl:if test="@ex:label">
    <xf:label><xsl:value-of select="@ex:label"/></xf:label>
  </xsl:if>
  <xsl:if test="not(@ex:label)">
    <xf:label><xsl:value-of select="translate(local-name(), '_', ' ')"/></xf:label>
  </xsl:if>
</xsl:template>

<!--
given a current node, spit out the path to it.
For example, if the current node is the c element here:
<a>
 <b>
  <c/>
 </b>
</a>
It will output /a/b/c
-->
<xsl:template name="pathtohere">
  <xsl:for-each select="ancestor-or-self::*">
    <xsl:text>/</xsl:text><xsl:value-of select="name()"/>
  </xsl:for-each>
</xsl:template>

<!--
for a given node, spit out the XML Schema datatype definition
eliminating duplicates
-->
<xsl:template match="*" mode="datatypes">
  <xsl:variable name="dtname" select="substring-after(@eg:content, 'dt:')"/>
  <xsl:if test="not(@eg:content=preceding::*/@eg:content)">
    <xs:simpleType name="{$dtname}">
      <xsl:if test="starts-with($dtname, 'enum')">
        <xs:restriction base="xs:string">
          <xsl:call-template name="enumdt">
            <xsl:with-param name="dtname" select="substring-after($dtname, 'enum-')"/>
          </xsl:call-template>
        </xs:restriction>
      </xsl:if>
      <xsl:if test="not(starts-with($dtname, 'enum'))">
        <xsl:call-template name="limitdt">
          <xsl:with-param name="dtname" select="$dtname"/>
        </xsl:call-template>
      </xsl:if>
    </xs:simpleType>
  </xsl:if>
</xsl:template>

<!--
implement limited-length datatype library
Given a name like string-20 or string-10-30,
spit out the XML Schema to define the datatype
-->
<xsl:template name="limitdt">
  <xsl:param name="dtname"/>
  <xs:restriction base="xs:{substring-before($dtname, '-')}">
    <xsl:choose>
      <xsl:when test="string-length($dtname) - string-length(translate($dtname, '-', '')) = 1">
        <xs:maxLength value="{substring-after($dtname, '-')}"/>
      </xsl:when>
      <xsl:when test="string-length($dtname) - string-length(translate($dtname, '-', '')) = 2">
        <xsl:variable name="minmax" select="substring-after($dtname, '-')"/>
        <!-- $minmax will be a string like "10-20" -->
        <xs:minLength value="{substring-before($minmax, '-')}"/>
        <xs:maxLength value="{substring-after($minmax, '-')}"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:message terminate="yes">Invalid limited-length datatype specification: <xsl:value-of select="$dtname"/></xsl:message>
      </xsl:otherwise>
    </xsl:choose>
  </xs:restriction>
</xsl:template>

<!--
implement enumerated value datatype library
Given a name like a-b-c-d-e-f, (the prefix enum- already having been stripped off)
spit out the XML Schema facets to define the datatype
recursive
-->
<xsl:template name="enumdt">
  <xsl:param name="dtname"/>
  <xsl:if test="string-length($dtname) > 0">
    <xsl:if test="contains($dtname, '-')">
      <xs:enumeration value="{substring-before($dtname, '-')}"/>
    </xsl:if>
    <xsl:if test="not(contains($dtname, '-'))">
      <xs:enumeration value="{$dtname}"/>
    </xsl:if>
    <xsl:call-template name="enumdt">
      <xsl:with-param name="dtname" select="substring-after($dtname, '-')"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

<!-- same as above, but write out selection choices -->
<xsl:template name="choices">
  <xsl:param name="dtname"/>
  <xsl:if test="string-length($dtname) > 0">
    <xsl:if test="contains($dtname, '-')">
      <xf:item>
        <xf:label><xsl:value-of select="substring-before($dtname, '-')"/></xf:label>
        <xf:value><xsl:value-of select="substring-before($dtname, '-')"/></xf:value>
      </xf:item>
    </xsl:if>
    <xsl:if test="not(contains($dtname, '-'))">
      <xf:item>
        <xf:label><xsl:value-of select="$dtname"/></xf:label>
        <xf:value><xsl:value-of select="$dtname"/></xf:value>
      </xf:item>
    </xsl:if>
    <xsl:call-template name="choices">
      <xsl:with-param name="dtname" select="substring-after($dtname, '-')"/>
    </xsl:call-template>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>
