|
Summary
This is an example of a coding technique, necessary to simulate in XSLT the "break" (out of loop) statement of traditional programming languages.
Imagine you're inside an xsl:for-each loop and the node-list to be processed is very long (hundreds or thousands of nodes).
At the same time you know that during some point of the processing you'll be able to determine that all output generation has been already
performed and there's absolutely no need to repeat the loop for the rest of the nodes.
This could happen even when we have processed just the first node from the node-list.
In conventional programming languages there's the C-like break;
statement, which terminates and jumps out of the loop immediately.
The question everybody seems to ask is:
What is the XSLT element, which is equivalent to the C 'break;' statement?.
The answer is a discouraging:
None
This snippet presents a way to simulate break; functionality in XSLT.
Let's start with a simple problem (submitted by Michael Lee in Januaryin the xslt-list
http://sources.redhat.com/ml/xsl-list/2001-01/msg00151.html):
I am writing a XSLT stylesheet to transform a simple table from HTML to
WML. However, the columns attribute is required for the table element in the
latter format. Therefore, I must be able to determine the maximum number
of cells in the rows and use it as the value for the columns attribute.
A simple approach is to sort all rows according to the descending number
of their cells and then take the first node in the sorted nodelist -- its number of cells is the value of the columns attribute.
The following code does exactly this: <xsl:template name=maxCols> <xsl:for-each
select=//tr> <xsl:sort select=count(td) order=descending /> <xsl:if
test=position() = 1> <xsl:value-of select=. /> </xsl:if> </xsl:for-each> </xsl:template>
This is a classical case where a break statement would be very useful -- the repeated processing produces the necessary
result on the very first iteration -- then the time for the rest is just wasted.
Because there isn't a xsl:break element in XSLT, we will have to simulate it.
The solution, as originally described by Jeni Tennison, is not to use an xsl:for-each construct at all, but to replace
it with a (recursively applied) template:
<xsl:template name=maxCols> <xsl:apply-templates
select=//tr[1] mode=maxCols /> </xsl:template>
<xsl:template
match=tr mode=maxCols> <xsl:variablename=next select=following-sibling::tr[count(td)
>count(current()/td)][1] /> <xsl:choose> <xsl:when
test=$next> <xsl:apply-templates select=$next mode=maxCols /> </xsl:when> <xsl:otherwise><xsl:value-of
select=count(td) /></xsl:otherwise> </xsl:choose> </xsl:template>
What is the underlying logic here?
The template with mode=maxCols is applied initially only on the first
tr element in the xml document. It constructs $next -- a node-set of all remaining tr elements that still have a greater number of td children than the current node.
The (same) template is re-applied only in case $next is not empty and
there really is still work to be done. In case there are no tr elements with greater number of td
children, then the result is immediately output and this is the end of processing.
Exactly like break, isn't it?
|