RE: [xsl] Traversing the tree

Subject: RE: [xsl] Traversing the tree
From: "Michael Kay" <mike@xxxxxxxxxxxx>
Date: Sat, 10 Sep 2005 17:17:19 +0100
This kind of problem is best tackled using recursion. It's difficult to
write your first recursive stylesheet, but it's a very powerful technique to
have under your belt. 

Basically you need to write a template that processes one of the siblings,
passing it the value of your "counter" or "running-total" as a parameter.
The template needs to make whatever changes are necessary to the counter or
running total, then make a recursive call to process the next element if
there is one, passing the new value of the counter or running total as a
parameter. When the final sibling is reached, you return (or write to the
output) the final value of the counter.

You can do this either with call-template or apply-templates (perhaps using
a special mode). I find it easier to use apply-templates, for two reasons:
(a) the current node is passed as an implicit parameter, and (b) the
recursion often terminates naturally when there are no more siblings. 

Here's an example that totals @price*@qty over a set of sibling elements:

<xsl:template match="order">
    <xsl:apply-template select="order-item[1]">
      <xsl:with-param name="running-total" select="0"/>

<xsl:template match="order-item">
  <xsl:param name="running-total"/>
    <xsl:when test="following-sibling::order-item">
    <xsl:apply-template select="following-sibling::order-item[1]">
      <xsl:with-param name="running-total" select="$running-total +
      <xsl:value-of select="$running-total + (@price*@qty)"/>

I hope you can see how to adapt this technique to your problem.

Michael Kay

> -----Original Message-----
> From: Agnisys Technology (P) Ltd. [mailto:agnisys@xxxxxxxxx] 
> Sent: 09 September 2005 21:31
> To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: Re: [xsl] Traversing the tree
> David,
>   My real problem is a little more complex as it turns out, 
> and I'm stuck again!
>   What if instead of using the count or sum functions, I need 
> to use my own function for each of
> the nodes after the one with an "offset"? Can I still use 
> some creative Xpath operation?
>   For example: 
> Input:
> <top>
>     <a>
>         <reg                > A1 </reg>
>         <reg     offset="5" > A2 </reg>
>         <reg                > A3 </reg>
>         <section>
>             section_foo
>         </section>
>         <section>
>             section_bar
>         </section>
>         <reg                > A4 </reg>
>      </a>
>  </top>
> Assume there is a "sizeNode" function that returns a "size" 
> of "section".
> sizeNode(section_foo) =  10
> sizeNode(section_bar) = 100
> So, the output of the above should be :
> A1          :   0
> A2          :   5
> A3          :   6
> section_foo :   7
> section_bar :  17
> A4          : 117
> So now I cannot use the following because the "size" of each 
> "section" could be different. 
> sum($x/@offset) + 
> (count(preceding-sibling::reg|preceding-sibling::section) -
>  count($x/preceding-sibling::reg|preceding-sibling::section))
> I need some way to add up "size"s of all the nodes after (and 
> including) the node with an "offset"
> attribute.
> Once again I'm tempted to use a for loop and have a running 
> counter, but I'm sure there is a
> better way.
> I would appreciate any help/pointer in this regard. 
> Anupam.
> P.S: Jay thanks for your effort, for me it was more food for thought.
> --- "Agnisys Technology (P) Ltd." <agnisys@xxxxxxxxx> wrote:
> > David,
> >   Your alternate strategy works well and solves the problem.
> >   Although, I do need to study it carefully to understand 
> how it is doing it!
> > Thanks,
> > Anupam.
> > 
> ______________________________________________________
> Click here to donate to the Hurricane Katrina relief effort.

Current Thread