page 70
In the recursive approach, the function calls itself whenever theres at least one more occurrence of the substring. Each time the function calls itself, the
originalString
parameter is a little smaller, until eventually weve processed the complete string. Heres the complete
template:
xsl:template name=replace-substring xsl:param name=original
xsl:param name=substring xsl:param name=replacement select=
xsl:variable name=first xsl:choose
xsl:when test=containsoriginal, substring xsl:value-of select=substring-beforeoriginal, substring
xsl:when xsl:otherwise
xsl:value-of select=original xsl:otherwise
xsl:choose xsl:variable
xsl:variable name=middle xsl:choose
xsl:when test=containsoriginal, substring xsl:value-of select=replacement
xsl:when xsl:otherwise
xsl:textxsl:text xsl:otherwise
xsl:choose xsl:variable
xsl:variable name=last xsl:choose
xsl:when test=containsoriginal, substring xsl:choose
xsl:when test=containssubstring-afteroriginal, substring, substring
xsl:call-template name=replace-substring xsl:with-param name=original
xsl:value-of select=substring-afteroriginal, substring xsl:with-param
xsl:with-param name=substring xsl:value-of select=substring
xsl:with-param xsl:with-param name=replacement
xsl:value-of select=replacement xsl:with-param
xsl:call-template xsl:when
xsl:otherwise xsl:value-of select=substring-afteroriginal, substring
xsl:otherwise xsl:choose
xsl:when xsl:otherwise
xsl:textxsl:text xsl:otherwise
xsl:choose xsl:variable
xsl:value-of select=concatfirst, middle, last xsl:template
This style of programming takes some getting used to, but whatever you want to do can usually be done. Our example here is a good illustration of the techniques weve discussed in
this chapter, including branching statements, variables, invoking templates by name, and passing parameters.
page 71
4.7 A Stylesheet That Emulates a for Loop
We stressed earlier that the
xsl:for-each
element is not a
for
loop; its merely an iterator across a group of nodes. However, if you simply must implement a
for
loop, theres a way to do it. Get ready to use recursion, though.
4.7.1 Template Design
Our design here is to create a named template that will take some arguments, then act as a
for
loop processor. If you think about a traditional
for
loop, it has several properties:
•
One or more initialization statements. These statements are processed before the
for
loop begins. Typically the initialization statements refer to an index variable that is used to determine whether the loop should continue.
•
An increment statement. This statement specifies how the index variable should be updated after each pass through the loop.
•
A boolean expression. If the expression is
true
, the loop continues; if it is ever
false
, the loop exits.
Lets take a sample from the world of Java and C++:
for int i=0; ilength; i++
In this scintillating example, the initialization statement is
i=0
, the index variable the variable whose value determines whether were done or not is
i
, the boolean expression we use to test whether the loop should continue is
ilength
, and the increment statement is
i++
. For our purposes here, were going to make several simplifying assumptions. Feel free, dear
reader, to make the example as complicated as you wish. Here are the shortcuts well take:
•
Rather than use an initialization statement, well require the caller to set the value of the local variable
i
when it invokes our
for
loop processor.
•
Rather than specify an increment statement such as
i++
, well require the caller to set the value of the local variable
increment
. The default value for this variable is
1
; it can be any negative or positive integer, however. The value of this variable will be added
to the current value of
i
after each iteration through our loop.
•
Rather than allow any conceivable boolean expression, well require the caller to pass in two parameters;
operator
and
testValue
. The allowable values for the
operator
variable are
=
, coded as
lt;
, coded as
gt;
, coded as
lt;gt;
,
=
coded as
lt;=
, and
=
coded as
gt;=
. Were doing things this way because there isnt a way to ask the XSLT processor to evaluate a literal such as
ilength
as if it were part of the stylesheet.
4.7.2 Implementation
Lets look at the parameters for our
for
loop template:
xsl:param name=i select=1 xsl:param name=increment select=1
xsl:param name=operator select== xsl:param name=testValue select=1
page 72
Our
for
template uses four parameters: the index variable, the increment, the comparison operator, and the test value. To emulate this C++ statement:
for int i=1; i=10; i++
Youd use this markup:
xsl:call-template name=for-loop xsl:with-param name=i select=1
xsl:with-param name=increment select=1 xsl:with-param name=operator select=lt;=
xsl:with-param name=testValue select=10 xsl:call-template
To demonstrate our stylesheet, our first version simply prints out the value of our index variable each time through the loop:
Transforming... Iteration 1: i=1
Iteration 2: i=2 Iteration 3: i=3
Iteration 4: i=4 Iteration 5: i=5
Iteration 6: i=6 Iteration 7: i=7
Iteration 8: i=8 Iteration 9: i=9
Iteration 10: i=10 transform took 260 milliseconds
XSLProcessor: done
Heres the markup youd use to emulate the Java statement
for int i=10; i0; i-=2
:
xsl:call-template name=for-loop xsl:with-param name=i select=10
xsl:with-param name=increment select=-2 xsl:with-param name=operator select=gt;
xsl:with-param name=testValue select=0 xsl:call-template
In this case, the values of
i
decrease from
10
to :
Transforming... Iteration 1: i=10
Iteration 2: i=8 Iteration 3: i=6
Iteration 4: i=4 Iteration 5: i=2
transform took 110 milliseconds XSLProcessor: done
4.7.3 The Complete Example
Heres our complete stylesheet:
?xml version=1.0? xsl:stylesheet xmlns:xsl=http:www.w3.org1999XSLTransform version=1.0
xsl:output method=text xsl:variable name=newline
xsl:text xsl:text
xsl:variable xsl:template name=for-loop
xsl:param name=i select=1 xsl:param name=increment select=1
xsl:param name=operator select== xsl:param name=testValue select=1
xsl:param name=iteration select=1