xsl-list
[Top] [All Lists]

Re: backwards tree-traversal algorithm?

2002-10-23 10:48:18
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[(_at_)id=$parent0]/section" 
    <xsl:when test="$depth=3"><xsl:copy-of 
select="document($d)/root/section[(_at_)id=$parent0]/section[(_at_)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[(_at_)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



<Prev in Thread] Current Thread [Next in Thread>