Inside Xslt
The <xsl:param> and <xsl: with-param > Elements:Creating Parameters
Parameters are much like variables , except theyre usually used with named templates. Parameters enable you to pass values into templates. You create parameters with the <xsl:param> element, which has two attributes:
-
name (mandatory). The name of the variable, set to a QName.
-
select (optional). The default value of the parameter. Set to an XPath expression.
Like <xsl:variable> , this element can either be a top-level element or be used inside a template body. Parameters created with top-level <xsl:param> elements are global parameters, and those created inside templates are local parameters. If you create a parameter inside a template body, the <xsl:param> element should come before any other child elements. This element also can contain an optional template body, which creates a result tree fragment, which is no longer allowed in XSLT 1.1. If this element does have a body, you should not use the select attribute.
After declaring a parameter with <xsl:param> , you can refer to its value in a template the same way as you would a variable: by prefixing its name with a $.
When you call a named template with <xsl:call-template> or apply templates with <xsl:apply-template> , you can use the <xsl:with-param> element to set the value of the parameters used in the template. If the parameter itself was assigned a value with the <xsl:param> elements select element when it was declared, that value acts as a default value for the parameter. This default value is overridden if you specify a new value for the parameter with the <xsl:with-param> element.
The <xsl:with-param> element has two attributes:
-
name (mandatory). The name of the variable; set to a QName.
-
select (optional). An XPath expression that specifies the value of the parameter. If you omit this attribute, the value of the variable is specified by the contents of <xsl:with-param> .
This element could contain an optional template body, which created a result tree fragment, but thats no longer allowed in XSLT 1.1.
In the following example, I create a named template called COLORS that adds color to a planets data in the HTML result document. This named template uses one parameter, COLOR , which you set to the color you want to use. Heres how it looks in practice, where Im setting the COLOR parameter to various colors for the different planets, using <xsl:with-param> and calling the COLORS template:
Listing 9.3 Using Stylesheet Parameters
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:if test="NAME='Mercury'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'RED'"/> </xsl:call-template> </xsl:if> <xsl:if test="NAME='Venus'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'GREEN'"/> </xsl:call-template> </xsl:if> <xsl:if test="NAME='Earth'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'BLUE'"/> </xsl:call-template> </xsl:if> </xsl:template> . . .
This calls the COLORS template with various different values in the COLOR parameter. I can make use of those colors when I format each planets data. Note that I declare the COLOR parameter with <xsl:param> in the very beginning of the COLORS template, as follows :
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> . . . </HTML> </xsl:template> <xsl:template match="PLANET"> <xsl:if test="NAME='Mercury'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'RED'"/> </xsl:call-template> </xsl:if> <xsl:if test="NAME='Venus'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'GREEN'"/> </xsl:call-template> </xsl:if> <xsl:if test="NAME='Earth'"> <xsl:call-template name="COLORS"> <xsl:with-param name="COLOR" select="'BLUE'"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="COLORS"> <xsl:param name="COLOR"/> <TR> <TD><FONT COLOR="{$COLOR}"><xsl:value-of select="NAME"/></FONT></TD> <TD><FONT COLOR="{$COLOR}"><xsl:apply-templates select="MASS"/></FONT></TD> <TD><FONT COLOR="{$COLOR}"><xsl:apply-templates select="RADIUS"/></FONT></TD> <TD><FONT COLOR="{$COLOR}"><xsl:apply-templates select="DAY"/></FONT></TD> </TR> </xsl:template> <xsl:template match="MASS"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> . . . <xsl:template match="DAY"> <xsl:value-of select="."/> <xsl:text> </xsl:text> <xsl:value-of select="@UNITS"/> </xsl:template> </xsl:stylesheet>
And heres the result:
<HTML> <HEAD> <TITLE> The Colorful Planets Table </TITLE> </HEAD> <BODY> <H1> The Colorful Planets Table </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <TR> <TD><FONT COLOR="RED">Mercury</FONT></TD> <TD><FONT COLOR="RED">.0553 (Earth = 1)</FONT></TD> <TD><FONT COLOR="RED">1516 miles</FONT></TD> <TD><FONT COLOR="RED">58.65 days</FONT></TD> </TR> <TR> <TD><FONT COLOR="GREEN">Venus</FONT></TD> <TD><FONT COLOR="GREEN">.815 (Earth = 1)</FONT></TD> <TD><FONT COLOR="GREEN">3716 miles</FONT></TD> <TD><FONT COLOR="GREEN">116.75 days</FONT></TD> </TR> <TR> <TD><FONT COLOR="BLUE">Earth</FONT></TD> <TD><FONT COLOR="BLUE">1 (Earth = 1)</FONT></TD> <TD><FONT COLOR="BLUE">2107 miles</FONT></TD> <TD><FONT COLOR="BLUE">1 days</FONT></TD> </TR> </TABLE> </BODY> </HTML>
You can see this result document in Figure 9.2 (even if only in black and white).
Figure 9.2. Calling a named template with parameters.
In the following example, I use parameters to localize the language used in a template. I create a new template called localize that uses a parameter named language . If language is set to en for English, the result document will be labeled Planets; if language is de for German, the result document will be labeled Planeten; and if fr for French, the result document will be labeled Plantes.
Heres how I call the localize template, setting language to fr:
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> <xsl:call-template name="localize"> <xsl:with-param name="language" select="'fr'"/> </xsl:call-template> </TITLE> </HEAD> <BODY> <H1> <xsl:call-template name="localize"> <xsl:with-param name="language" select="'fr'"/> </xsl:call-template> . . .
And heres what the parameterized template COLORS looks like. Note that I declare the COLOR parameter in the template using an <xsl:param> element (and note that the HTML 4.01 character entity for the in Plantes is , which I use here):
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/PLANETS"> <HTML> <HEAD> <TITLE> <xsl:call-template name="localize"> <xsl:with-param name="language" select="'fr'"/> </xsl:call-template> </TITLE> </HEAD> <BODY> <H1> <xsl:call-template name="localize"> <xsl:with-param name="language" select="'fr'"/> </xsl:call-template> </H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> <xsl:apply-templates/> </TABLE> </BODY> </HTML> </xsl:template> <xsl:template name="localize"> <xsl:param name="language"/> <xsl:if test="$language='en'"> <xsl:text>Planets</xsl:text> </xsl:if> <xsl:if test="$language='de'"> <xsl:text>Planten</xsl:text> </xsl:if> <xsl:if test="$language='fr'"> <xsl:text>Planètes</xsl:text> </xsl:if> </xsl:template> . . .
And heres the resulting localized document:
<HTML> <HEAD> <TITLE>Planètes</TITLE> </HEAD> <BODY> <H1>Planètes</H1> <TABLE BORDER="2"> <TR> <TD>Name</TD> <TD>Mass</TD> <TD>Radius</TD> <TD>Day</TD> </TR> . . .
You can see this result document in Figure 9.3.
Figure 9.3. Calling a named template with parameters to set languages.
Calling a template is much like calling a function of the kind you saw in Chapter 8, and the capability to pass data using parameters enhances the similarity. However, in the absence of an assignment operator of the kind supported by programming languages, it seems that you cant assign a value returned from a named template to a variablebut you can, if youre clever. In fact, you can also do something else that you can do with functions: call them recursively .
Calling Templates Recursively
This topic is mostly for programmers, because I make XSLT act like a programming language here. One of the things I do here is make a named template call itself , which is called recursion . The classic example of recursion is to write a factorial programfor example, 6 factorial, written 6!, is equal to 6 * 5 * 4 * 3 * 2 * 1, or 720.
To implement recursion in a true programming language, you write a function named factorial that you call with the value 6: factorial(6) . The factorial of 6 is 6 * factorial(5) , so all this function needs to do is to multiply the results of calling itself with a value of 5that is, factorial(5) by 6. On the other hand, factorial(5) is just 5 * factorial(4) , so the function can call itself again to find out what factorial(4) is. This process continues all the way down to factorial(1) , and we know that 1! is just 1, so factorial(1) returns 1. From that point, control returns through all the previous stages, giving us 1 * 2 * 3 * 4 * 5 * 6, or 720, which is 6!
This sounds like a pretty tall order to implement in a style language such as XSLT. However, it can be donein XSLT 1.0, anyway. The key is to realize that you can store the return value of a called template in a variable if you call the template inside the <xsl:variable> element that declares the variable. For example, if I have a named template called factorial and want to compute 6!, I can pass it a value of 6 using <xsl:with-param> and assign the string value of the result to a variable named result , which I then display:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:variable name="result"> <xsl:call-template name="factorial"> <xsl:with-param name="value" select="6"/> </xsl:call-template> </xsl:variable> 6! = <xsl:value-of select="$result"/> </xsl:template> . . .
The following example shows how you can implement the factorial template so that it calls itself to compute the factorial. In a programming language, I could write each stage of the recursion as n ! = n * factorial( n - 1) , but theres no assignment operator here, so when I calculate factorial( n - 1) I store its value in a new variable, temp , and write each stage to return the value n * $temp instead:
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:variable name="result"> <xsl:call-template name="factorial"> <xsl:with-param name="value" select="6"/> </xsl:call-template> </xsl:variable> 6! = <xsl:value-of select="$result"/> </xsl:template> <xsl:template name="factorial"> <xsl:param name="value"/> <xsl:choose> <xsl:when test="$value=1"> <xsl:value-of select="1"/> </xsl:when> <xsl:otherwise> <xsl:variable name="temp"> <xsl:call-template name="factorial"> <xsl:with-param name="value" select="$value - 1"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="$temp * $value"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
Heres the result document:
<?xml version="1.0" encoding="utf-8"?> 6! = 720
As you see, it can be done, at least in XSLT 1.0, which allows the result tree fragments Ive used here.