[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] backwards tree-traversal algorithm?


Subject: Re: [xsl] backwards tree-traversal algorithm?
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Wed, 23 Oct 2002 18:48:18 +0100

Hi John,

> since i am doing (i believe it is called) "push-based" processing
> rather than pull, how would i specify which node is my starting
> node?

Your starting node is automatically the current node:

  <xsl:for-each select="ancestor::*">
    ...
  </xsl:for-each>

is the same as:

  <xsl:for-each select="current()/ancestor::*">
    ...
  </xsl:for-each>

If you have a node held in a variable, then use that instead of
current():

  <xsl:for-each select="$current/ancestor::*">
    ...
  </xsl:for-each>

> i make document() calls to an xml file "dirs.xml" containing an xml
> representation of the directory structure where the source document
> is located. the reason that i want to iterate over the ancestor
> nodes is that i am doing a "breadcrumbs" sort of thing, where i want
> to show what the progression of parent nodes was in the tree.
>
> at transformation time, the stylesheet gets a set of parameters from
> the command line: $parent0, $parent1, $parent2, etc. which contain
> the directory names of the preceding directories, and a $depth param
> which contains the depth of the file in the structure, eg.
>
> sab sheet.xsl /foo/bar/baz/file.xml $depth=3 $parent0=foo $parent1=bar $parent3=baz $depth=3
>
> i then use those parameters to create a nodeset variable pointing to
> the node within dirs.xml which corresponds to the location of the
> source document in the filesystem.
>
> eg. i have a variable called $current, defined as follows:
>
> <xsl:variable name="current">
>   <xsl:choose>
>     <xsl:when test="$depth=1"><xsl:copy-of select="document($d)/root/section" /></xsl:when>
>     <xsl:when test="$depth=2"><xsl:copy-of select="document($d)/root/section[@id=$parent0]/section" 
>     <xsl:when test="$depth=3"><xsl:copy-of select="document($d)/root/section[@id=$parent0]/section[@id=$parent1]/section" 
>                 ...
>                 <xsl:otherwise> no logic coded for this level of hierarchy </xsl:otherwise>
>   </xsl:choose>
> </xsl:variable>
>
> this obviously seems very crude because it puts a strict upper bound
> on how deep the hierarchy can go, and it's really redundant. but
> when i initially passed in the location path as a string, eg.
>
> sab sheet.xsl /foo/bar/baz/file.xml $location=/foo/bar/baz/file.xml
>
> and tried to turn $location into an xpath expression, i fell on my
> face because it's not possible to construct xpath on fly in XSL.

I'm not sure I'm following all the details, but you can use recursion
to work through the $location parameter as you work through the tree.
Something like:

<xsl:template match="/">
  <xsl:apply-templates select="document($d)/root" mode="find">
    <xsl:with-param name="loc"
                    select="substring-after($location, '/')" />
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="*" mode="find">
  <xsl:param name="loc" />
  <xsl:choose>
    <xsl:when test="contains($loc, '/')">
      <xsl:apply-templates mode="find"
          select="section[@id = substring-before($loc, '/')]">
        <xsl:with-param name="loc"
                        select="substring-after($loc, '/')" />
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <!-- got to the file name -->
      <xsl:apply-templates select="section" mode="process" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="section" mode="process">
  <xsl:copy-of select="." />
</xsl:template>

If you want to do something other than copy the section elements that
are children of the one representing the directory in which the file
is held, replace the content of the process-mode template.

Cheers,

Jeni

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


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



Current Thread
Keywords