[xsl] Maximum "child-depth" of current node?

Subject: [xsl] Maximum "child-depth" of current node?
From: "Scott Trenda" <Scott.Trenda@xxxxxxxx>
Date: Tue, 16 Oct 2007 10:31:02 -0500
Hey XSL-List. Got a bugger of a problem that started to drive me nuts
yesterday, figured I'd ask here first thing today to avoid pulling my
hair out over this. :P

I just found out yesterday that the nest-hierarchy system I set up for a
recent big project has to essentially be done in _reverse_ for a
different format, but this one isn't as simple. I'm basically making a
list header hierarchy into a nested HTML table header in one format, and
I have to make it into similar-looking CSV in the other format. Take
this example data:

<x>
  <c>col1</c>
  <c>col2</c>
  <g n='grp1'>
    <c>col3</c>
    <c>col4</c>
    <c>col5</c>
  </g>
  <g n='grp2'>
    <c>col6</c>
    <g n='grp3'>
      <c>col7</c>
      <c>col8</c>
    </g>
    <c>col9</c>
  </g>
  <c>col10</c>
  <c>col11</c>
</x>

HTML output:
------------------------------------------------------------------------
--------
|      |      |        grp1        |            grp2           |       |
|
|      |      |--------------------|---------------------------|       |
|
|      |      |      |      |      |      |     grp3    |      |       |
|
|      |      |      |      |      |      |-------------|      |       |
|
| col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 | col9 | col10 |
col11 |
------------------------------------------------------------------------
--------

CSV output:
    ,    ,    ,    ,    ,grp2,    ,    ,    ,     ,
    ,    ,grp1,    ,    ,    ,grp3,    ,    ,     ,
col1,col2,col3,col4,col5,col6,col7,col8,col9,col10,col11


For HTML (this part is done already), I have a key that gets the <c> or
<g> elements at a specified depth - since col1, col2, col10, and col11
actually exist in the first <tr> of the table, they belong with grp1 and
grp2 at the top level. But those cells are bottom-valigned because
there's a data table beneath it, and it makes sense to have the label
sitting directly above it. In the CSV output, I need to alter the
structure so they actually appear there in the result document.

With that, here's the key I'm using for the HTML version:
    <key name="cols-at-depth" match="c|g[.//c]" use="count(ancestor::g)
+ 1"/>

Later in the stylesheet, I find the max column depth
($total-header-levels), start processing with key('cols-at-depth', 1)
and loop until I'm at key('cols-at-depth', $total-header-levels). But
for the CSV version, I essentially need to go the opposite way - rather
than counting the node's depth from its farthest <g> ancestor, I need to
count the depth of its deepest child branch. If I could do this with a
key, it would definitely be best, but just finding the algorithm to get
it in the first place would be a good start. My strategy from there is
to do a loop similar to the HTML cols-at-depth algorithm above, but the
CSV version would hold off on making the parent group entries until
absolutely necessary (at the point where the output is on the
nth-to-last output row, and parent group has at least one branch n
levels deep). Any ideas on XPath trickery I could use here?

I've included a trimmed-down version of the HTML-output stylesheet at
the bottom.


... and I know we've had this discussion before - I'm stuck with XSLT
1.0. Thanks in advance!

~ Scott



<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>

    <xsl:output method="html" encoding="utf-8"/>

    <xsl:key name="columns-at-depth" match="c|g[.//c]"
use="count(ancestor::g) + 1"/>

    <xsl:variable name="total-header-levels">
        <xsl:for-each select="//c">
            <xsl:sort select="count(ancestor::g)" data-type="number"/>
            <xsl:if test="position() = last()">
                <xsl:value-of select="count(ancestor::g) + 1"/>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>


    <xsl:template match="/">
        <table>
            <xsl:call-template name="loop-rows"/>
        </table>
    </xsl:template>

    <xsl:template name="loop-rows">
        <xsl:param name="row" select="1"/>
        <tr>
            <xsl:apply-templates select="key('columns-at-depth',
$row)"/>
        </tr>
        <xsl:if test="$row &lt; $total-header-levels">
            <xsl:call-templates name="loop-rows">
                <xsl:with-param name="row" select="$row + 1"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>


    <xsl:template match="c">
        <th rowspan="{$total-header-levels - count(ancestor::g)}">
            <xsl:value-of select="."/>
        </th>
    </xsl:template>

    <xsl:template match="g">
        <th colspan="{count(.//c)}">
            <xsl:value-of select="@n"/>
        </th>
    </xsl:template>


</xsl:stylesheet>

Current Thread