[xsl] test for ancestral attribute returning variant results

Subject: [xsl] test for ancestral attribute returning variant results
From: "Trevor Nicholls trevor@xxxxxxxxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Sun, 24 Oct 2021 17:42:30 -0000
Hi

 

(What follows is XSL 2.0 and the processor I am using is Saxon, I have tried
with Saxon versions 8.7.3, 9.9 and 10.0 and all exhibit the same behaviour.)

 

I am at a complete loss to explain why this is happening.

 

My XML documents conform to a schema which allows many elements to have a
'version' attribute. An element's version serves as a default for all
descendants, although it can be overridden on any descendant subtree by
another version on the descendant node. In many cases an element will have
an unnecessary version specified (because it is the same as the nearest
ancestral version). 

 

I have a named template (versiontest) which is called on elements to
construct a version callout in the margin. The version should appear if it
is different to that of the parent, or if the ancestral element with the
same version appears on an earlier page.

 

My problem is that this template is generating different output depending on
the size of the containing document.

 

Here's the pertinent XML fragment:

 

<note version="8.00">

  <para version="8.00">blah blah blah.</para>

  <para version="8.00">blah blah blah.</para>

</note>

 

The para versions are superfluous, and their presence triggers the problem,
but (a) these documents are not created by me, (b) my code should cope with
superfluous versions anyway, and (c) my code works in smaller documents.

 

This is the versiontest template, it should either generate nothing or
output a version string. I've added a few debug lines which I hoped would
clarify the situation (spoiler alert, they don't). 

 

<xsl:template name="versiontest">

<!--

  -- @version is this node's version

  -- $ancvers is the nearest ancestral version, found on node $ancv

  -- the nearest ancestral page break is on node $ancpg

  -- $versdepth is the depth of $ancv from the root

  -- $pgdepth is the depth of $ancpg from the root

  -- We output the version IF @version is different to $ancvers,

  -- OR IF @version matches $ancvers but $versdepth is less than $pgdepth

  -- Note: if versdepth > pgdepth then the version will have appeared
earlier in the page,

  -- if versdepth < pgdepth then it will have appeared on a previous page,

  -- while if versdepth = pgdepth then the version will appear at the top of
the current page

-- >

<!-- DEBUG 1

  <xsl:message select="concat('1 element=', local-name(), ', version=',
@version)" />

END DEBUG 1 -- >

  <xsl:variable name="ancv" select=ancestor::*[@version][1]" />

  <xsl:variable name="ancvers" select="$ancv/@version" />

  <xsl:variable name="ancpg" select="ancestor::*[local-name() = 'section'
and @break='Y'][1]" />

  <xsl:variable name="versdepth">

    <xsl:for-each select="$ancv">

      <xsl:value-of select="count(ancestor-or-self::*)" />

    </xsl:for-each>

  </xsl:variable>

  <xsl:variable name="pgdepth">

    <xsl:for-each select="$ancpg">

      <xsl:value-of select="count(ancestor-or-self::*)" />

    </xsl:for-each>

  </xsl:variable>

<!-- DEBUG 2

   <xsl:message>

     <xsl:value-of select="concat('2 ancvers=', $ancvers, ', versdepth=',
$versdepth, ', pgdepth=', $pgdepth)" />

     <xsl:value-of select="concat('[', count(ancestor::*[@version]), ': ')"
/>

    <xsl:for-each select="ancestor::*[@version]">

    <xsl:value-of select="concat('(', ./@version, ')')" />

   </xsl:for-each>

   <xsl:text>]</xsl:text>

  </xsl:message>

END DEBUG 2 -- >

  <xsl:if test="@version != ''">

    <xsl:if test="@version != $ancvers or not($versdepth > $pgdepth)">

<!-- DEBUG 3

  <xsl:message select="concat('3 output version=', @version)" />

END DEBUG 3 -->

      <xsl:value-of select="@version" />

    </xsl:if>

  </xsl:if>

<xsl:template>

 

In a smallish document, the versiontest template outputs "8.00" for the note
element and nothing for the para elements. In a large document it outputs
"8.00" for all three elements.

 

With the debug statements included, the small document generates these
messages

 

1 element=note, version=8.00

2 ancvers=, versdepth=, pgdepth=3 [0:]

3 output version=8.00

1 element=para, version=8.00

2 ancvers=8.00, versdepth=5, pgdepth=3 [1: (8.00)]

1 element=para, version=8.00

2 ancvers=8.00, versdepth=5, pgdepth=3 [1: (8.00)]

 

I wondered if the larger document might include earlier "versioned" subtrees
that triggered a flaw in my logic, but evidently not. If this was the cause,
there would be more versions listed in the message labelled DEBUG 2.

The large document generates these messages

 

1 element=note, version=8.00

2 ancvers=, versdepth=, pgdepth=8 [0:]

3 output version=8.00

1 element=para, version=8.00

2 ancvers=8.00, versdepth=10, pgdepth=8 [1: (8.00)]

3 output version=8.00

1 element=para, version=8.00

2 ancvers=8.00, versdepth=10, pgdepth=8 [1: (8.00)]

3 output version=8.00

 

I can't see where the tests between DEBUG 2 and DEBUG 3 are going wrong;
certainly if you plug in the values reported in the messages then the
results should be those reported in the smaller documents, not what I'm
actually seeing.

 

I'm hoping there's a flaw in my logic which is obvious to a more experienced
eye, but I'm afraid I can't see it myself.

 

You're going to ask for a small failing test case; unfortunately there isn't
one :-(

 

Cheers

T

Current Thread