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>
with: </xsl:text>
<xsl:text>update: </xsl:text>
<xsl:value-of select="$AddXPath"/>
<xsl:text>
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><</xsl:text>
<xsl:value-of
select="ancestor-or-self::*
[self::element or self::attribute][(_at_)name][1]/@name"/>
<xsl:text>></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(.,
'
')]) + 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><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><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>/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><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><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