Re: [xsl] ordering and iteration problem

Subject: Re: [xsl] ordering and iteration problem
From: Mark Nahabedian <naha@xxxxxxxxxx>
Date: Tue, 1 May 2001 13:44:31 -0400 (Eastern Daylight Time)
Thanks Jeni.  This was a big help.  My transform is now generating
HTML output which I'm happy with (modulo having to edit out the
namespace prefixes to get InternetExplorer to understand it).

I'm curious why one typically initializes "random-nodes" from the
stylesheet document rather than the input document.  It seems to me
that the input document is guaranteed to have enough nodes, while the
XSLT transform document might not if the input document is big enough.

Instead of

    <xsl:variable name="random-nodes" select="document('')//node()" />

I tried

    <xsl:variable name="random-nodes" select="//node()" />

to get the set of all nodes from the input document.  This seems to
work for me.  I was wondering if there is any reason why this is not
what is typically done.



Jeni Tennison writes:
 > Hi Mark,
 > 
 > > My thinking is that I need to do something like
 > >
 > >    for each row
 > >        for each column
 > >            ooutput the <circuit-breaker> with that row and column
 > 
 > I'd probably do this using the Piez Method/Hack of having an
 > xsl:for-each iterate over the correct number of random nodes and using
 > the position of the node to give the row/column number.
 > 
 > You need to define some random nodes - I usually use nodes from the
 > stylesheet:
 > 
 > <xsl:variable name="random-nodes" select="document('')//node()" />
 > 
 > And since you'll be iterating over them, you need some way of getting
 > back to the data:
 > 
 > <xsl:variable name="data" select="/" />
 > 
 > I've used two keys to get the relevant circuit breakers quickly. One
 > just indexes them by column (this is so you can work out whether you
 > need to add a cell or whether there's a circuit breaker from higher in
 > the column that covers it). The other indexes them by row and column.
 > 
 > <xsl:key name="breakers-by-column" match="b:circuit-breaker"
 >          use="@column" />
 > <xsl:key name="breakers" match="b:circuit-breaker"
 >          use="concat(@row, ':', @column)" />
 > 
 > I've assumed that you've stored the maximum number of rows in a
 > global variable called $max-row and the maximum number of columns in a
 > global variable called $max-col.  Here's the template that does the
 > work:
 >          
 > <xsl:template match="/">
 >    <!-- store the right number of nodes for the rows in a variable -->
 >    <xsl:variable name="rows"
 >                  select="$random-nodes[position() &lt;= $max-row]" />
 >    <!-- store the right number of nodes for the columns in a variable
 >         -->
 >    <xsl:variable name="columns"
 >                  select="$random-nodes[position() &lt;= $max-col]" />
 >    <!-- create the table -->
 >    <table>
 >       <!-- iterate over the right set of nodes to get the rows -->
 >       <xsl:for-each select="$rows">
 >          <!-- store the row number -->
 >          <xsl:variable name="row" select="position()" />
 >          <!-- create the row -->
 >          <tr>
 >             <!-- iterate over the right set of nodes to get the
 >                  columns -->
 >             <xsl:for-each select="$columns">
 >                <!-- store the column number -->
 >                <xsl:variable name="col" select="position()" />
 >                <!-- change the current node so that the key works -->
 >                <xsl:for-each select="$data">
 >                   <!-- identify the relevant circuit breaker -->
 >                   <xsl:variable name="breaker"
 >                                 select="key('breakers',
 >                                             concat($row, ':', $col))" />
 >                   <xsl:choose>
 >                      <!-- if there is one, apply templates to get the
 >                           table cell -->
 >                      <xsl:when test="$breaker">
 >                         <xsl:apply-templates select="$breaker" />
 >                      </xsl:when>
 >                      <xsl:otherwise>
 >                         <!-- find other breakers that start higher in
 >                              the column -->
 >                         <xsl:variable name="column-breakers"
 >                                       select="key('breakers-by-column', $col)
 >                                                  [@row &lt; $row]" />
 >                         <!-- output an empty cell if there isn't one
 >                              that overlaps -->
 >                         <xsl:if test="not($column-breakers
 >                                              [@row + @height &gt; $row])">
 >                            <td />
 >                         </xsl:if>
 >                      </xsl:otherwise>
 >                   </xsl:choose>
 >                </xsl:for-each>
 >             </xsl:for-each>
 >          </tr>
 >       </xsl:for-each>
 >    </table>
 > </xsl:template>
 > 
 > <!-- template to give the cell for the circuit breaker -->
 > <xsl:template match="b:circuit-breaker">
 >    <td rowspan="{@height}">
 >       <xsl:value-of select="b:amps" />
 >    </td>
 > </xsl:template>
 > 
 > If you don't like the 'one big template' approach, then you could
 > split it down by applying templates to the row and column nodes in
 > 'row' and 'column' modes to distinguish between the two.
 > 
 > I hope that helps,
 > 
 > Jeni
 > 
 > ---
 > Jeni Tennison
 > http://www.jenitennison.com/
 > 
 > 
 > 


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


Current Thread