Item 39. Parameterize XSLT Stylesheets

There's an old programmer's adage that the only numbers in programs should be 0, 1, and infinity. Everything else should be a named constant. The same can be said for styles in XSLT stylesheets. Sooner or later somebody is going to want to customize anything they can customize. To enable this, use named variables (an XSLT variable is much like a C++ const) instead of hard-coding styles like 12pt or bold.

The key to this is top-level xsl:variable and xsl:param elements. These are direct children of the root xsl:stylesheet element. Each such element has a name attribute that sets the name of the variable (or parameter) and a select attribute that sets the value of the variable (or parameter) using an XPath expression. For example, the following xsl:variable and xsl:param elements set various font properties.

 <xsl:variable name="body-font-name" select="'Times New Roman'"/> <xsl:variable name="body-font-size" select="'10pt'"/> <xsl:param name="code-font-name" select="'Courier'"/> <xsl:param name="code-font-size" select="'12pt'"/> 

The value of each of these variables/parameters is a literal string. A value can also be a number.

 <xsl:variable name="start-page" select="25"/> <xsl:variable name="columns" select="2"/> 

A variable can even be set to a node-set. For example, the code below would set the variable headings to all the level 1 and level 2 headings in a typical DocBook document.

 <xsl:variable name="headings" select="//sect1/title  //sect2/title"/> <xsl:variable name="columns" select="2"/> 

However, for variables and parameters that define constants, it's normally more plausible to use simple string, numeric, and perhaps boolean values.

Variables and parameters can also be set to result tree fragments by omitting the select attribute and using a template inside the xsl:variable or xsl:param element instead, as demonstrated below.

 <xsl:variable name="body-font-name">Times</xsl:variable> <xsl:variable name="body-font-size">10pt</xsl:variable> <xsl:param name="code-font-name">Courier</xsl:variable> <xsl:param name="code-font-size">12pt</xsl:variable> <xsl:variable name="columns">2<xsl:variable/> 

The result tree fragments are normally converted to the necessary type automatically. Using these result tree fragments like node-sets is more useful for purposes other than stylesheet parameterization. Here, they're mostly being used as an alternate, slightly more convenient syntax for strings.

Variables and parameters are dereferenced within other XPath expressions in the XSLT stylesheet by placing a dollar sign in front of the variable name, for example, $body-font-name and $body-font-size . These two templates transform DocBook paragraphs and program listings into XSL-FO fo:block elements. The styles for the paragraph are determined by the body-font-family and body-font-size variables. The styles for the program listing are given by the code-font-family and code-font-size parameters.

 <xsl:template match="para">   <fo:block font-family="{$body-font-family}"             font-size="{$body-font-size}">     <xsl:apply-templates/>   </fo:block> </xsl:template> <xsl:template match="programlisting">   <fo:block font-family="{$code-font-family}"             font-size="{$code-font-size}"             white-space-collapse="false">     <xsl:apply-templates/>   </fo:block> </xsl:template> 

In this example, since the font-family and font-size attributes are part of the output vocabulary, rather than XSLT-defined attributes, it's necessary to put the variable reference inside curly braces to indicate that the value is an XPath expression that should be evaluated. In an XSLT attribute that expects an XPath expression, such as the test attribute of xsl:if or the select attribute of xsl:copy-of , this isn't necessary.

Variables and parameters can also be used to direct the flow of control in the stylesheet, as well as to select particular characteristics in the output. For example, a boolean parameter could be used to specify whether to include a table of contents, a list of figures, a list of tables, an index, and so forth. These could all be set as shown below.

 <xsl:param name="include-table-of-contents" select="true()"/> <xsl:param name="include-list-of-figures" select="false()"/> <xsl:param name="include-list-of-tables" select="false()"/> <xsl:param name="include-index" select="true()"/> 

When processing the book the stylesheet would use the values of these variables to determine whether or not to call particular templates.

 <xsl:template match="book">   <xsl:if test="$include-table-of-contents">     <xsl:call-template name="make-table-of-contents"/>   </xsl:if>   <xsl:if test="$include-list-of-figures">     <xsl:call-template name="make-list-of-figures"/>   </xsl:if>   <xsl:if test="$include-list-of-tables">     <xsl:call-template name="make-list-of-tables"/>   </xsl:if>   <xsl:if test="$include-index">     <xsl:call-template name="generate-index"/>   </xsl:if> </xsl:template> 

Variables and parameters enable customizers to make small changes to the final output without modifying or overriding the templates, a much more difficult operation. XSLT stylesheets are programs, and they require the skills of a programmer to write. If you give typical print designers an XSLT stylesheet and ask them to change it to suit their needs, you're lucky if the results are well- formed , much less correct. However, such designers are normally capable of selecting the names and sizes of fonts, the number of columns, the widths of pages, and so on. In fact, they can probably do a much more attractive job of that than the typical programmer can. Each field has its own strengths. The XSLT programmer's task is to enable the designer to do his or her job without requiring XSLT expertise.

There are several ways the values of a top-level variable or a parameter can be adjusted.

  • The user can edit the stylesheet directly to type in a new value of the select attribute that sets the variable or parameter.

  • An xsl:variable or xsl:param element inside a template can override the value of the global variable or parameter with the same name.

  • When stylesheet A imports stylesheet B, the definitions in the importing stylesheet A take precedence over the definitions in the imported stylesheet B.

  • Parameters (but not variables) can be modified from the environment that invokes the stylesheet, for instance, by a command line flag or an API call.

Which of these is used to customize a stylesheet depends on who is doing the customizing and why. For instance, developers who are importing the stylesheet into their own stylesheets would be likely to simply override a value with their own xsl:param element. However, people who are applying your stylesheet to their own documents might just set command line flags.

Note

A variable or a parameter can be shadowed by a different binding, but it cannot be changed. That is, its value is fixed. This sounds a little strange . After all, a variable that can't change is more like a constant. The terminology here is taken more from the mathematical notion of a variable than the common programming notion of a variable.


Large stylesheets can and should collect related variable definitions into their own files, which can be imported or included from the master stylesheets. This centralizes them in one place where any customizer can look to adjust the output. This is similar to using a .h header file in C.

A parameter (but not a variable) can be adjusted when the stylesheet is invoked from the environment. Exactly how this is done varies from processor to processor. However, it's generally accomplished via a system property or command line argument passed to the processor when invoking it. For example, in xsltproc the --stringparam flag is followed by the name and the value of the parameter to set. I use xsltproc to generate the RSS feeds for my Cafe au Lait and Cafe con Leche web sites. The default parameter values are set up for Cafe con Leche as follows .

 <xsl:param name="url">http://www.cafeconleche.org/ </xsl:param> <xsl:param name="sitename">Cafe con Leche</xsl:param> 

However, I can use the same stylesheet for Cafe au Lait by passing the appropriate command line options from the cron job that drives the transform.

 xsltproc cafe.xsl http://www.cafeconleche.org/ xsltproc --stringparam url http://www.cafeaulait.org/   --stringparam sitename "Cafe au Lait"   cafe.xsl http://www.cafeaulait.org/ 

In this case, the parameter is used to feed in external information (the name and base URL of the site) that is not available in the documents themselves .

In an XSLT API, there will normally be a method call for setting the values of the parameters before using the stylesheet. For instance, in TrAX the javax.xml.transform.Transformer class has setParameter() and getParameter() methods .

 transformer.setParameter("url", "http://www.cafeaulait.org/"); transformer.setParameter("sitename", "Cafe au Lait"); 

The exact API calls or command line options will vary from one processor to the next . However they're set, though, external values take precedence over the local definitions in the stylesheet. Whether set internally or externally, using parameters instead of hard-coded values makes stylesheets much more maintainable and extensible.



Effective XML. 50 Specific Ways to Improve Your XML
Effective XML: 50 Specific Ways to Improve Your XML
ISBN: 0321150406
EAN: 2147483647
Year: 2002
Pages: 144

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net