Re: [xsl] recursive template grabbing nested elements...

Subject: Re: [xsl] recursive template grabbing nested elements...
From: Jeni Tennison <mail@xxxxxxxxxxxxxxxx>
Date: Sun, 15 Jul 2001 09:30:18 +0100
Hi Anthony,

> i want to have a recursive template that finds the deepest <tab>
> with state="on", and then grabs the preceding sibling and the
> following siblings' (or parent's sibling) "id" attribute.

Once you've found the deepest tab element, you can output whatever you
want within a template for it, e.g.:

<xsl:template match="tab">
  current = <xsl:value-of select="@id" />
  prev = <xsl:value-of select="preceding-sibling::tab/@id" />
  next = <xsl:value-of
           select="following-sibling::tab/@id |
                   ../following-sibling::tab
                        [not(current()/following-sibling::tab)]/@id" />
</xsl:template>

[Note that this doesn't get the parent's following

You can work out the depth of an element by counting how many
ancestors it has:

  count(ancestor::*)

So finding the deepest tab element involves finding a calculated
maximum and applying templates to the tab element that has that
maximum. I would use a template like the following, which works
through the tab elements by applying templates to them. If there's a
following tab with a state of on that's more deeply nested, then the
template moves on to that tab. Otherwise, it applies the template
above to this tab element:

<xsl:template match="tab" mode="deepest">
  <xsl:variable name="depth" select="count(ancestor::*)" />
  <xsl:variable name="next"
                select="following::tab[@state = 'on']
                          [count(ancestor::*) > $depth]" />
  <xsl:choose>
    <xsl:when test="$next">
      <xsl:apply-templates select="$next" mode="deepest" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="." />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

You would call this template by applying templates to the first
descendant tab element with a state of 'on', in 'deepest' mode:

  <xsl:apply-templates select=".//tab[@state = 'on'][1]"
                       mode="deepest" />

Alternatively, you could use a more traditionally recursive template
into which you pass all the tab elements that you might be interested
in:

  <xsl:call-template name="deepest">
    <xsl:with-param name="tabs" select=".//tab[@state = 'on']" />
  </xsl:call-template>

The template to handle this would keep track of the deepest tab found
thus far and compare it to the first in the $tabs list. The new
deepest tab is passed in to the next recursion of the template, if
there are more tab elements left in the list to go through, or has
templates applied to it if not.

<xsl:template name="deepest">
  <xsl:param name="tabs" />
  <xsl:param name="deepest" select="/.." />
  <xsl:variable name="new-deepest"
                select="count($tabs[1]/ancestor::*) >
                        count($deepest/ancestor::*)" />
  <xsl:choose>
    <xsl:when test="$tabs[2]">
      <xsl:call-template name="deepest">
        <xsl:with-param name="tabs" select="$tabs[position() > 1]" />
        <xsl:with-param name="deepest"
                        select="$tabs[1][$new-deepest] |
                                $deepest[not($new-deepest)]" />
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="$new-deepest">
      <xsl:apply-templates select="$tabs[1]" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="$deepest" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

I should think Dimitre will show you an alternative that is not as
closely coupled with your source XML, using one of his generic
templates.

Cheers,

Jeni

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


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


Current Thread