>Refactoring XSL Stylesheets

>One of the most difficult things for people new to XSL is learning to think in a functional language instead of a procedural language. XSL’s power comes from the fact that it works best off of templates. While there is a looping construct <xsl:for-each>, it isn’t necessarily the best solution. Unfortunately, this is what may be used most often when XSL is first being used, as it seems a natural fit to procedural way of thinking. Almost anything that can be done with for-each can be done with templates and apply-templates.

There is also a tendency, to put everything into one big giant template. For anybody that has used the PDE Site tools, you realize that it will also create a site.xsl file in the web directory. The site.xsl file can be used to have a web browser dynamically create an html page, or it can be used in a batch environment during a build to generate a static html page to be served.

Unfortunately, this is an XSLT that was written from a procedural point of view instead of a functional template view. It does the very common mistake, everything is in one giant XSL Template (template match=”/”). I’m not going to post the entire xsl that is generated here, but will show a few refactorings that can be done, and have been submitted as bug 240597. All code was refactored and tested using the tools provided by the XSL Tools project.

1. Refactor to Templates

The first step is to refactor as many of the for-each statements to templates. As stated before, XSL is a functional templating language, so it works best this way. It also makes for easier code maintance and re-usable code as well, helping to reduce the overall size of the application.


<xsl:template match="/">
<xsl:apply-templates select="site" />
</xsl:template>

This replaces a for-each in the original which looked something like:


<xsl:for-each select="site">
.
.
.
</xsl:for-each>

The thing here is that this is a really needless xsl:for-each as a site.xml file will only ever have one site element. The apply-templates accomplishes the same thing, by applying any template that is set to match the site element.

The for-each itself is made into a template that then contains the necessary code to handle the processing of site.xml file:


<xsl:template match="site">
.
.
.
</xsl:template>

The same is done for each of the for-each statements. This helps to break up the xsl into more manageable templates instead of having all code in one big giant template.

2. Refactor to Named Template

Think of the named template in XSLT 1.0 and XSLT 2.0 as reusable methods in any other programming language. XSLT 2.0 makes it possible to also create xsl:functions which can be used within XPATH 2.0 statements but that is beyond this blog posting. Also, since this xslt is ment to run within a browser, no browser currently supports XSLT 2.0.

Anyways, there are a couple of sections that have been split out to named templates.


<xsl:template name="row-coloring">
<xsl:param name="nodeposition" select="position()" />
<xsl:choose>
<xsl:when test="($nodeposition mod 2 = 1)">
<xsl:attribute name="class">dark-row</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class">light-row</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSLT 1.0 does not have the If-Then-Else construct, this is accomplished with the Choose-When-Otherwise. Choose is more of a case statement construct but it accomplishes the same thing as if-then-else. The above named template accepts a parameter called nodeposition, if the no parameter is passed to it, it will get the value returned by the position XPath function.

The above can be executed from with in a match template by calling it:


<xsl:call-template name="row-coloring">
<xsl:with-param name="nodeposition"
select="count(preceding-sibling::feature[not(category)])"/>
</xsl:call-template>

3. Eliminate platform specific functionality

Unless you absolutely need it, in order to have the most portable XSLT try to avoid platform specific functionality. If you need to use extension functions for XSLT 1.0, then make sure to try and use the EXSLT functions. Most XSLT 1.0 parsers do support this. In the site.xsl file, there is a call to a microsoft specific function to return a Nodeset. XSLT 1.0 will not return a complete nodeset from variables it returns a result-tree-fragment, so some functions that require a nodeset won’t work. When this functionality is necessary use the exslt:node-set() function instead. It will convert a result-tree-fragment into a true node-set.

The EXSLT library has many useful extensions to XSLT 1.0, many of which did make it into XSLT 2.0. The Microsoft extension wasn’t needed as the choice statement that was being used had code to handle the formatting if the function wasn’t found. So these lines were eliminated.

4. Refactor Long Templates

This is much like the Long Method refactoring for Object Orient languages. The idea is to break up an overly long method, into smaller, maintainable, and reusable pieces. Again this is where named templates can come into play. By breaking up long templates, into smaller named templates, common reuses and elimination of duplicate code can occur. Smaller sections of properly named code are much easier to maintain than long complicated sections.

The Results:

Overall, after applying the four refactoring patterns, the overall size of the site.xsl was reduced by 93 lines of code. It makes better use of the XSLT language, and it is much easier to maintain and has more reusable pieces of code. Further tweaking of the site.xsl could be done, so that parameters for the project name and the title of the html page could be passed to it as well.

Advertisements
This entry was posted in eclipse, pde, refactoring, xml, xslt. Bookmark the permalink.

2 Responses to >Refactoring XSL Stylesheets

  1. Ray says:

    >Good article. I can’t wait to start using the XSL tooling.You are missing a few closing tags in the row-coloring template though…

  2. David Carver says:

    >Good catch. I’ve corrected the examples.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s