xsl-list
[Top] [All Lists]

bug in LibXSLT???

2003-03-05 20:46:27
Hi there, I think I've uncovered a bug in LibXSLT. I have the following versions installed of LibXSLT and sablotron.

% xsltproc --version
Using libxml 20425, libxslt 10019 and libexslt 710
xsltproc was compiled against libxml 20425, libxslt 10019 and libexslt 710
libxslt 10019 was compiled against libxml 20425
libexslt 710 was compiled against libxml 20425
% sabcmd --version
sabcmd 0.96 (Sep 5, 2002)
copyright (C) 2000 - 2002 Ginger Alliance (www.gingerall.com)

My styleSheet is intended to produce dynamic HTML forms from an RNG (relax NG) grammar. What I have here is a small snippet of RNG, and one of the XSLT in my pipeline. This xslt is intended to translate RNG into "Mid" which is just a custom intermediate xml dialect that I'm making up, the results of which will be transformed into HTML by a later xslt. It's part of a roundtripping solution to generate XML instances from an RNG grammar.

In many places I print out the RNG path of the context node. That's the path made up of the @name attributes of the ancestors of the context node, in reverse document order. I have a simple template to do it named "RNGPathToSelf". The problem is that in certain places I get the wrong order ... it's not reverse or forward, it's half and half. See for yourself ... :-\

I was killing myself trying to figure out what was wrong, and then I decided to try sablotron (I prefer libxslt normally because it has support for EXSLT dyn:evaluate) and it didn't display the problem. Is this a bug in libxslt or am I doing something wonky?


Here's the input, the XSLT, and the output from LibXSLT that's incorrect marked with [[[BUG HERE]]], then the output from Sablotron with the output marked [[[CORRECT HERE]]] . Apologies for the length of the email, but the FAQ recommends providing full examples. I hope someone will still help!

TIA,

simon

=============================================
=============================================
file: input.xml
=============================================
=============================================
<?xml version="1.0"?>
<grammar xmlns:dyn="http://exslt.org/dynamic";>
  <start>
    <element name="resume">
      <optional>
        <attribute name="id">
          <data type="ID"/>
        </attribute>
      </optional>
      <empty/>
      <empty/>
      <optional>
        <attribute name="xsi:schemaLocation"/>
      </optional>
      <optional/>
      <optional>
        <element name="header">
          <optional>
            <element name="address">
              <optional>
                <attribute name="format">
                  <choice>
                    <value>
                      standard
                    </value>
                    <value>
                      european
                    </value>
                    <value>
                      italian
                    </value>
                  </choice>
                </attribute>
              </optional>
            </element>
          </optional>
        </element>
      </optional>
    </element>
  </start>
</grammar>


=============================================
=============================================
file: rng2mid.xsl
=============================================
=============================================
<?xml version="1.0"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
  version="1.0"
  xmlns:dyn="http://exslt.org/dynamic";
  extension-element-prefixes="dyn"
  >

  <xsl:output method="xml" indent="yes"/>

<!--[[xsl:strip-space elements="*" /]]-->
<!--apparently that doesn't play well with the linenumber finding scheme I have here-->

  <xsl:param name="StartPointStr">//grammar</xsl:param>

  <xsl:param name="AddXPath">DEFAULT</xsl:param>
  <xsl:param name="AddType">DEFAULT</xsl:param>
  <xsl:param name="AddContent">DEFAULT</xsl:param>

  <xsl:variable name="instance" select="document('inst.xml')"/>

  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--Normal templates              -->
  <!--******************************-->

  <!--******************************-->
  <!--Root block-->

  <xsl:template match="grammar">
    <dummy>
    <xsl:element name="rngform" namespace="mynms">
      <title>RNG Form</title>
      <xsl:if test="$StartPointStr != '//grammar'">
        <startpoint>
          <xsl:value-of select="$StartPointStr"/>
        </startpoint>
      </xsl:if>
      <xsl:if test="$AddXPath != 'DEFAULT'">
        <add>
          <value><xsl:value-of select="$AddContent"/></value>
          <type><xsl:value-of select="$AddType"/></type>
          <into><xsl:value-of select="$AddXPath"/></into>
        </add>
        <!--<p>
          <textarea rows="5" cols="40">
<![CDATA[<xupdate:modifications version="1.0" xmlns:xupdate="http://www.xmldb.org/xupdate";>]]>
          <xsl:text>&#xA;with: </xsl:text>
          <xsl:text>update: </xsl:text>
          <xsl:value-of select="$AddXPath"/>
          <xsl:text>&#xA;with: </xsl:text>
          <xsl:value-of select="$AddContent"/>
          </textarea>
        </p>-->
      </xsl:if>
      <xsl:apply-templates/>
    </xsl:element> <!-- rngform -->
    </dummy>
  </xsl:template>

  <xsl:template match="start">
    <xsl:apply-templates/>
  </xsl:template>


  <!--******************************-->
  <!--Dynamic blocks-->

  <xsl:template match="zeroOrMore">
    <zeroormore>
      <xsl:call-template name="SimplyRecurse"/>
    </zeroormore>
  </xsl:template>

  <xsl:template match="oneOrMore">
    <oneormore>
      <xsl:call-template name="SimplyRecurse"/>
    </oneormore>
  </xsl:template>


  <!--******************************-->
  <!--Conditional blocks-->

  <xsl:template match="choice">
    <multi-choice>
      <xsl:call-template name="SimplyRecurse"/>
    </multi-choice>
  </xsl:template>

  <xsl:template match="choice/asdf">
    <choice>
      <node><xsl:value-of select="name()"/></node>
      <path>
        <xsl:call-template name="RNGPathToSelf"/>
      </path>
      <ident><xsl:value-of select="@name"/></ident>
      <!--<xsl:apply-templates/>-->
      <xsl:call-template name="SimplyRecurse"/>
    </choice>
  </xsl:template>

  <xsl:template match="value">
    <value>
      <path>
        <xsl:call-template name="RNGPathToSelf"/>
     </path>
      <xsl:call-template name="SimplyRecurse"/>
    </value>
  </xsl:template>

  <xsl:template match="optional">
    <optional>
      <path>
        <xsl:call-template name="RNGPathToSelf"/>
        <!--<xsl:call-template name="RNGNameOfNearestChild"/>-->
      </path>
      <xsl:call-template name="SimplyRecurse"/>
    </optional>
  </xsl:template>


  <!--******************************-->
  <!--Basic blocks-->

  <xsl:template match="rngsub_stopped">
    <prune-point>
      <insert-into> <xsl:value-of select="@location"/> </insert-into>
      <i><xsl:value-of select="@location"/></i>
    </prune-point>
  </xsl:template>

  <xsl:template match="element">
    <xsl:if test="not(text)">
      <xsl:call-template name="RNGPathToSelf"/>
    </xsl:if>
    <xsl:call-template name="SimplyRecurse"/>
  </xsl:template>



  <!--******************************-->
  <!--Terminating blocks-->

  <xsl:template name="TextEntryArea">
    <xsl:element name="text-entry-area"><!-- namespace="mynms"> -->
      <add-info>
        <path>
          <xsl:call-template name="RNGPathToSelf"/>
        </path>
        <type>
          <xsl:choose>
            <xsl:when test="@type">
              <xsl:value-of select="@type"/>
            </xsl:when>
            <xsl:otherwise>text</xsl:otherwise>
          </xsl:choose>
        </type>
      </add-info>
      <prefilled-value>
        <xsl:call-template name="RNGNameOfAncestorOrSelfTemplate"/>
        <!-- ???!!! <xsl:call-template name="instanceDataTemplate"/> -->
      </prefilled-value >
      <description>
        <label> <xsl:call-template name="RNGPathToSelf"/> </label>
        <type>
          <xsl:choose>
<xsl:when test="name()!='data'"><xsl:value-of select="name()"/></xsl:when> <xsl:otherwise><xsl:value-of select="@type"/></xsl:otherwise>
          </xsl:choose>
        </type>
      </description>
    </xsl:element>
  </xsl:template>

  <xsl:template match="text">
    <xsl:call-template name="TextEntryArea"/>
  </xsl:template>

  <xsl:template match="attribute">
    <xsl:choose>
      <xsl:when test="not(./*) or ./empty"> <!--no children-->
        <xsl:call-template name="TextEntryArea"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="SimplyRecurse"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="data">
    <xsl:call-template name="TextEntryArea"/>
    <!--should check for param children-->
  </xsl:template>

  <!--TODO-->
  <xsl:template match="group"/>



  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--Named templates               -->
  <!--******************************-->

  <!--******************************-->
  <!--HTML Forms labels and inputs-->


  <!--******************************-->
  <!--Names of ancestors/descendants-->

  <xsl:template name="RNGNameOfAncestorOrSelfTemplate">
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of
      select="ancestor-or-self::*
        [self::element or self::attribute][(_at_)name][1]/@name"/>
    <xsl:text>&gt;</xsl:text>
  </xsl:template>

  <xsl:template name="RNGPathToSelf">
    <xsl:for-each select="ancestor-or-self::*/@name">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>

  <xsl:template name="RNGNameOfNearestChild">
    <xsl:text>/</xsl:text>
    <xsl:value-of
      select="descendant-or-self::*
        [self::element or self::attribute][(_at_)name][1]/@name"/>
  </xsl:template>

  <xsl:template name="RNGPathToParent">
    <xsl:for-each select="ancestor::*/@name">
      <xsl:text>/</xsl:text>
      <xsl:value-of select="."/>
    </xsl:for-each>
  </xsl:template>


  <!--******************************-->
  <!--Misc-->

 <xsl:template name="sourceLineNumberTemplate">
    <!--For debugging purposes-->
    <xsl:text> </xsl:text>
    <xsl:text>{{{</xsl:text>
<!--[[xsl:value-of select="(count(preceding::* | ancestor::*) * 2 * 9 div 10 + 1" /]]-->
    <!--works (less well) even if whitespace stripping is on-->
<xsl:value-of select="count(preceding::text()[contains(., '&#xA;')]) + 1+1"/>
    <xsl:text>}}}</xsl:text>
  </xsl:template>

  <xsl:template name="SimplyRecurse">
<xsl:if test="normalize-space($instance//*[boolean(name()=current()/@name)]/ text())">
      <value-under-here>
<xsl:value-of select="name($instance//*[boolean(name()=current()/@name)])"/>
        <xsl:text>===</xsl:text>
<xsl:value-of select="$instance//*[boolean(name()=current()/@name)]/text()"/>
      </value-under-here>
    </xsl:if>
    <xsl:call-template name="sourceLineNumberTemplate"/>
    <xsl:apply-templates/>
  </xsl:template>


  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--******************************-->
  <!--Discard                       -->
  <!--******************************-->
  <xsl:template match="define"/>
</xsl:stylesheet>


=============================================
=============================================
OUTPUT from LibXSLT (with error)
=============================================
=============================================

% xsltproc rng2mid.xsl input.xml
<?xml version="1.0"?>
<dummy xmlns:dyn="http://exslt.org/dynamic";>
  <rngform xmlns="mynms"><title>RNG Form</title>

    /resume {{{4}}}
      <optional><path>/resume</path> {{{5}}}
         {{{6}}}
<text-entry-area><add-info><path>/resume/id</path><type>ID</type></add- info><prefilled-value>&lt;id&gt;</prefilled-value><description><label>/ resume/id</label><type>ID</type></description></text-entry-area>

      </optional>


      <optional><path>/resume</path> {{{12}}}
<text-entry-area><add-info><path>/resume/xsi:schemaLocation</ path><type>text</type></add-info><prefilled- value>&lt;xsi:schemaLocation&gt;</prefilled-value><description><label>/ resume/xsi:schemaLocation</label><type>attribute</type></description></ text-entry-area>
      </optional>
      <optional><path>/resume</path> {{{15}}}</optional>
      <optional><path>/resume</path> {{{16}}}
        /resume/header {{{17}}}
          <optional><path>/resume/header</path> {{{18}}}
            /resume/header/address {{{19}}}
              <optional><path>/resume/header/address</path> {{{20}}}
                 {{{21}}}
                  <multi-choice> {{{22}}}
<value><path>/address/format/resume/header</path> {{{23}}} [[[ERROR HERE]]]
                      standard
                    </value>
<value><path>/address/format/resume/header</path> {{{25}}} [[[ERROR HERE]]]
                      european
                    </value>
<value><path>/address/format/resume/header</path> {{{27}}} [[[ERROR HERE]]]
                      italian
                    </value>
                  </multi-choice>

              </optional>

          </optional>

      </optional>


</rngform>
</dummy>

=============================================
=============================================
OUTPUT from Sablotron (correct)
=============================================
=============================================
% sabcmd rng2mid.xsl 2.xml
<?xml version="1.0" encoding="UTF-8"?>
<dummy>
  <ns_1:rngform xmlns:ns_1="mynms">
    <title>RNG Form</title>

    /resume {{{4}}}
      <optional>
      <path>/resume</path> {{{5}}}
         {{{6}}}
          <text-entry-area>
        <add-info>
          <path>/resume/id</path>
          <type>ID</type>
        </add-info>
        <prefilled-value>&lt;id></prefilled-value>
        <description>
          <label>/resume/id</label>
          <type>ID</type>
        </description>
      </text-entry-area>

      </optional>


      <optional>
      <path>/resume</path> {{{12}}}
        <text-entry-area>
        <add-info>
          <path>/resume/xsi:schemaLocation</path>
          <type>text</type>
        </add-info>
        <prefilled-value>&lt;xsi:schemaLocation></prefilled-value>
        <description>
          <label>/resume/xsi:schemaLocation</label>
          <type>attribute</type>
        </description>
      </text-entry-area>
      </optional>
      <optional>
      <path>/resume</path> {{{15}}}</optional>
      <optional>
      <path>/resume</path> {{{16}}}
        /resume/header {{{17}}}
          <optional>
        <path>/resume/header</path> {{{18}}}
            /resume/header/address {{{19}}}
              <optional>
          <path>/resume/header/address</path> {{{20}}}
                 {{{21}}}
                  <multi-choice> {{{22}}}
                    <value>
<path>/resume/header/address/format</path> {{{23}}} [[[CORRECT HERE]]]
                      standard
                    </value>
                    <value>
<path>/resume/header/address/format</path> {{{25}}} [[[CORRECT HERE]]]
                      european
                    </value>
                    <value>
<path>/resume/header/address/format</path> {{{27}}} [[[CORRECT HERE]]]
                      italian
                    </value>
                  </multi-choice>

              </optional>

          </optional>

      </optional>


</ns_1:rngform>
</dummy>


XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



<Prev in Thread] Current Thread [Next in Thread>