Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining the context for position() in a template with multiple predicates

I'm having some trouble determining the correct context for a node set. I have a template match that looks a bit like this (using XSL 2.0):

<xsl:template match="//chapter/body/*[matches(name(), '^toc')][crossref][not(crossref/@idref='cip' or crossref/@idref='copy')]">
  <xsl:variable name="curr_id" select="crossref/@idref"/>
  <xsl:element name="location">
    <xsl:attribute name="id"><xsl:value-of select="$curr_id"/></xsl:attribute>
    <xsl:attribute name="order"><xsl:value-of select="position()"/></xsl:attribute>
    <xsl:element name="label">
        <text><xsl:value-of select="."/></text>
    </xsl:element>
  </xsl:element>
</xsl:template>

The XML looks a bit like this:

<chapter id="toc">
  <body>
    <title>Contents</title>
    <tocfm><crossref idref="cip">Catalog</crossref></tocfm>
    <tocfm><crossref idref="copy">Copyright</crossref></tocfm>
    <tocfm><crossref idref="ded">Dedication</crossref></tocfm>
    <toc><crossref idref="prologue">Prologue</crossref></toc>
    <toc><crossref idref="pt1">Book One</crossref></toc>
    <toc><crossref idref="pt2">Book Two</crossref></toc>
    <toc><crossref idref="pt3">Book Three</crossref></toc>
  </body>
</chapter>

My expectation is that the predicate will generate a node set that contains:

<tocfm><crossref idref="ded">Dedication</crossref></tocfm>
<toc><crossref idref="prologue">Prologue</crossref></toc>
<toc><crossref idref="pt1">Book One</crossref></toc>
<toc><crossref idref="pt2">Book Two</crossref></toc>
<toc><crossref idref="pt3">Book Three</crossref></toc>

In other words, all of the toc-like elements that contain a crossref whose idref isn't cip or copy. The template does this in terms of output, but the position function doesn't seem to be working on that node set. Instead, it generates a position of '3' for Dedication. However, if I output the value of the node found by following the predicate with [1], I do get Dedication as that value. So, I'm stumped as to what position is working on. Can anyone enlighten me?

like image 968
Christina Avatar asked Jul 10 '12 22:07

Christina


1 Answers

You expectation is wrong because it is based on a common misconception about how the context sequence is defined. The context sequence is NOT determined by the template match pattern, but rather by something outside of the template.

At some point xsl:apply-templates is invoked with a select expression. This expression is evaluated to produces an item sequence. The current context sequences is pushed onto a stack. This item sequence becomes the context sequence. The context sequence is iterated through in sequence order. At each item is visited, this item becomes the context item, and the position() function reflects the position of the context item within the context sequence. At this point the appropriate template is found based on the match expression and priorities. Match expressions DO NOT return sequences. They are simply a boolean test on the the context item - either the context item matches the template rule or it does not. If matched and highest priority, then the template sequence constructor is entered. At this point context sequence, context item and position() have all been defined by the client xsl:apply-templates, NOT by the match condition. After exiting the sequence constructor, control returns back to the client xsl:apply-templates. It will increment the position() number, update the context item, and again find the appropriate template. This may be the same template as the previous item or a different one.

Illustration

This style-sheet illustrates that it is the client instruction (apply-templates, for-each etc) that determines the context sequence and position(), not the template match pattern.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
 <t>
   <note message ="apply-templates on EVEN items">
    <xsl:apply-templates select="*/item[ (@id mod 2) = 0]"/>
   </note>  
   <note message ="apply-templates on ODD items">
    <xsl:apply-templates select="*/item[ (@id mod 2) = 1]">
     <xsl:sort select="position()" data-type="number" order="descending"/>
    </xsl:apply-templates>
   </note>  
 </t>
</xsl:template>
      
<xsl:template match="item[@colour='blue']">
 <note message='Brought to you by the Blue template!' positon="{position()}"> 
  <xsl:copy-of select='self::*'/>
  </note>  
</xsl:template>
      
<xsl:template match="item[@colour='red']">
 <note message='Brought to you by the Red template!' positon="{position()}"> 
  <xsl:copy-of select='self::*'/>
  </note>  
</xsl:template>
      
</xsl:stylesheet>
    

... applied on this document ...

<t>
 <item id="1" colour="blue" /> 
 <item id="2" colour="blue" /> 
 <item id="3" colour="blue" /> 
 <item id="4" colour="red" /> 
 <item id="5" colour="red" /> 
 <item id="6" colour="red" /> 
</t>

... yields this output ...

<t>
  <note message="apply-templates on EVEN items">
    <note message="Brought to you by the Blue template!" positon="1">
      <item id="2" colour="blue" />
    </note>
    <note message="Brought to you by the Red template!" positon="2">
      <item id="4" colour="red" />
    </note>
    <note message="Brought to you by the Red template!" positon="3">
      <item id="6" colour="red" />
    </note>
  </note>
  <note message="apply-templates on ODD items">
    <note message="Brought to you by the Red template!" positon="1">
      <item id="5" colour="red" />
    </note>
    <note message="Brought to you by the Blue template!" positon="2">
      <item id="3" colour="blue" />
    </note>
    <note message="Brought to you by the Blue template!" positon="3">
      <item id="1" colour="blue" />
    </note>
  </note>
</t>
like image 85
Sean B. Durkin Avatar answered Sep 28 '22 20:09

Sean B. Durkin