Re: [xsl] Dealing without global counters

Subject: Re: [xsl] Dealing without global counters
From: "M. David Peterson" <m.david@xxxxxxxxxx>
Date: Wed, 12 Nov 2003 00:59:45 -0700
A solution that might add a bit of efficiency as well as give you the
ability to use the textual representation of the numbers (the way you had it
in your desired output) is as follows...

the following template outputs:

db[1]=new dbRecord(Chapter 1)
db[2]=new dbRecord( First Paragraph testPara_one )
db[3]=new dbRecord( Second Paragraph testPara_two )
db[4]=new dbRecord( Third Paragraph testPara_three )
db[5]=new dbRecord( Fourth Paragraph testPara_four )
db[6]=new dbRecord( First  Image testImage_one )
db[7]=new dbRecord( Second  Image testImage_two )
db[8]=new dbRecord( First  Table testTable )

when using the input:

<chapters>
<chapter>
    <para>testPara_one
    </para>
    <para>testPara_two
    </para>
    <image>testImage_one
    </image>
    <para>testPara_three
    </para>
    <image>testImage_two
    </image>
    <table>testTable
    </table>
    <para>testPara_four
    </para>
</chapter>
</chapters>

The template is as follows...

<xsl:template match="/">
  <xsl:apply-templates/>
 </xsl:template>

 <xsl:template match="chapters/chapter">

 db[1]=new dbRecord(Chapter <xsl:number/>)<br/>

 <xsl:for-each select="para">
 <xsl:variable name="count"><xsl:number/></xsl:variable>
  db[<xsl:value-of select="$count + 1"/>]=new dbRecord(
  <xsl:call-template name="textValueofNumber">
   <xsl:with-param name="position">
    <xsl:number/>
   </xsl:with-param>
  </xsl:call-template>
  Paragraph&#160;<xsl:value-of select="."/>)<br/>

 </xsl:for-each>

 <xsl:for-each select="image">
 <xsl:variable name="count"><xsl:number/></xsl:variable>
 <xsl:variable name="paraCount"><xsl:value-of
select="count(../child::para)"/></xsl:variable>
  db[<xsl:value-of select="$count + $paraCount + 1"/>]=new dbRecord(
  <xsl:call-template name="textValueofNumber">
   <xsl:with-param name="position">
    <xsl:number/>
   </xsl:with-param>
  </xsl:call-template>
  &#160;Image&#160;<xsl:value-of select="."/>)<br/>
 </xsl:for-each>


 <xsl:for-each select="table">
 <xsl:variable name="count"><xsl:number/></xsl:variable>
 <xsl:variable name="childCount"><xsl:value-of
select="count(../child::*)"/></xsl:variable>
  db[<xsl:value-of select="$count + $childCount"/>]=new dbRecord(
  <xsl:call-template name="textValueofNumber">
   <xsl:with-param name="position">
    <xsl:number/>
   </xsl:with-param>
  </xsl:call-template>
  &#160;Table&#160;<xsl:value-of select="."/>)<br/>
 </xsl:for-each>

 </xsl:template>

 <xsl:template name="textValueofNumber">

  <xsl:param name="position"/>

  <xsl:choose>
   <xsl:when test="$position = '1'">
    First
   </xsl:when>
   <xsl:when test="$position = '2'">
    Second
   </xsl:when>
   <xsl:when test="$position = '3'">
    Third
   </xsl:when>
   <xsl:when test="$position = '4'">
    Fourth
   </xsl:when>

    ... etc... through the reach of numbers you think you will need.

  </xsl:choose>

 </xsl:template>

Best of luck!

M.

----- Original Message ----- 
From: "Jeni Tennison" <jeni@xxxxxxxxxxxxxxxx>
To: "Robert Ogden" <Robert.Ogden@xxxxxxxx>
Cc: <xsl-list@xxxxxxxxxxxxxxxxxxxxxx>
Sent: Wednesday, November 12, 2003 12:11 AM
Subject: Re: [xsl] Dealing without global counters


> 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
>


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


Current Thread