Re: [xsl] multi-levels counter

Subject: Re: [xsl] multi-levels counter
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Mon, 10 Jun 2002 16:46:18 +0100
Hi Allen,

> How can I keep a counter for each <text> segment? Thanks in
> advance!!

The method that most closely resembles your description is to turn the
natural rule-based processing model of XSLT into a procedural one by
stepping through the elements one by one, passing a parameter down
through each call.

Doing that is quite ugly and can make it difficult to get a structured
result, so it's probably easier to rephrase what you want to do to
"How can I assign a number to each text segment based on its position
relative to all the other text segments in the document?"

There's a natural solution to that in XSLT, namely xsl:number. Doing:

<xsl:template match="text">
  <xsl:number level="any" format="1." />
  <xsl:value-of select="." />
</xsl:template>

will produce output with numbers like '1.', '2.' for each text element
in the document.

xsl:number actually works by, for each text element, looking at all
the preceding nodes in the tree, finding text elements amongst them,
and counting them. That can be quite a lot of node visits in a large
document, which would be quite slow. If speed is an issue, then you
could try a partial version of the step-by-step method mentioned
above, which would be something like:

<xsl:template match="*">
  <!-- $ntext keeps track of the number of text elements so far -->
  <xsl:param name="ntext" select="0" />
  <!-- create a copy of this element -->
  <xsl:copy>
    <xsl:copy-of select="@*" />
    <!-- pass $ntext on to the first child of this element -->
    <xsl:apply-templates select="*[1]">
      <xsl:with-param name="ntext" select="$ntext" />
    </xsl:apply-templates>
  </xsl:copy>
  <!-- move on to the next sibling of this element, adding the number
       of text element children of this one to the count -->
  <xsl:apply-templates select="following-sibling::*[1]">
    <xsl:with-param name="ntext" select="$ntext + count(.//text)" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="text">
  <xsl:param name="ntext" select="0" />
  <!-- give the number, followed by the text segment -->
  <xsl:value-of select="$ntext" />. <xsl:value-of select="." />
  <!-- move on to the next sibling of this element, adding one to the
       count -->
  <xsl:apply-templates select="following-sibling::*[1]">
    <xsl:with-param name="ntext" select="$ntext + 1" />
  </xsl:apply-templates>
</xsl:template>

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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


Current Thread