xsl-list
[Top] [All Lists]

Re: Dealing without global counters

2003-11-12 00:11:10
Hi Robert,

Output (this output is a javascript array for an expandable TOC)
db[1]=new dbRecord(chapter stuff)
db[2]=new dbRecord(first para stuff)
db[3]=new dbRecord(second para stuff)
db[4]=new dbRecord(third para stuff)
db[5]=new dbRecord(fourth para stuff)
db[6]=new dbRecord(first image stuff)
db[7]=new dbRecord(second image stuff)
db[8]=new dbRecord(first table stuff)

What I want to do is output a chapter, all text in a chapter, then
all images for a chapter, and lastly all tables for a chapter. This
is done repetitively for each chapter.

A simple-minded solution might be the following. You can calculate the
number for a chapter using <xsl:number> with level="any" (counting
elements at any level in the document) and with the count attribute
matching those elements you're interested in counting: chapter, paras,
tables and images.

Once you've output the line for the chapter, you can go on to process
its <para> children, then its <image> children, then its <table>
children. When processing the <para> elements, you want to start
counting from the number for the chapter; when processing the <image>
elements, you want to start counting from the number for the chapter
plus the number of <para> children the chapter has; similarly, when
processing the <table> children, you want to start counting from the
number for the chapter plus the number of <para> or <image> children
the chapter has:

<xsl:template match="chapter">
  <xsl:variable name="number">
    <xsl:number level="any" count="chapter | para | table | image" />
  </xsl:variable>

  <xsl:value-of
    select="concat('db[', $number, ']=new dbRecord(chapter stuff)&#xA;')" />
  
  <xsl:apply-templates select="para">
    <xsl:with-param name="start" select="$number" />
  </xsl:apply-templates>
  <xsl:apply-templates select="image">
    <xsl:with-param name="start" select="$number + count(para)" />
  </xsl:apply-templates>
  <xsl:apply-templates select="table">
    <xsl:with-param name="start" select="$number + count(para | image)" />
  </xsl:apply-templates>
</xsl:template>

When it comes to processing the <para>, <image> and <table> elements,
the $start parameter gives you the starting number. The number for the
<para>, <image> or <table> is this starting number plus the number for
the particular <para>, <image> or <table>, which you can again get via
the <xsl:number> instruction:

<xsl:template match="para | image | table">
  <xsl:param name="start" select="0" />
  <xsl:variable name="number"><xsl:number /></xsl:variable>
  <xsl:value-of
    select="concat('db[', $start + $number,
                   ']=new dbRecord(', name(), ' stuff)&#xA;')" />
</xsl:template>


This approach isn't very efficient -- numbering with <xsl:number>
seldom is, especially with level="any" -- but that might not be a
problem for you.

In XSLT 2.0, you can create a sequence that contains the elements that
you want to process in the correct order, and then iterate over that
sequence, using the position() function to provide the relevant
numbers. The code would look something like:

  <xsl:variable name="elements" as="element()*">
    <xsl:for-each select="chapter">
      <xsl:sequence select="(., para, image, table)" />
    </xsl:for-each>
  </xsl:variable>
  <xsl:for-each select="$elements">
    <xsl:value-of
      select="concat('db[', xs:string(position()),
                     ']=new dbRecord(', name(), ' stuff)&#xA;')" />
  </xsl:for-each>

Cheers,

Jeni

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


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