Riding an XSLT Bike

I might be the only one but I've always found that XSLT is nothing like riding a bike; either I've been doing it lately and it's my friend or I haven't and it makes my head hurt.
I'm kinda into XML as a data transport format (JSON is cool too) right now. We've been writing transforms to produce some user friendly windows into our data warehouse CI process.
Not for the first time in my life, I find my self constrained to v1.0 but wanting to do some stuff that's much easier in v2.0. Hopefully the example below will help someone out one day (even if it's me in a year or two)...
I want to create a simple table from the following XML:
<things>
<thing value="Row 1" anotherValue="Hello Gary! Checkout [[myfile.txt]]"></thing>
<thing value="Row 2" anotherValue="Hello Bob! Checkout [[myfile.txt]]"></thing>
<thing value="Row 3" anotherValue="Hello Tracey! Checkout [[myotherfile.txt]]"></thing>
</things>
view raw bike.xml hosted with ❤ by GitHub
I want to replace "Gary" with "John" and I want to inject links to the (conveniently) wrapped file names.
Here's my transform:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" indent="yes" encoding="US-ASCII" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
<xsl:template match="things">
<html>
<head>
<title>Title</title>
</head>
<body onload="">
<xsl:call-template name="pageHeader"/>
</body>
</html>
</xsl:template>
<xsl:template name="pageHeader">
<h1>Heading</h1>
<table>
<tr valign="top">
<th>Column A</th>
<th>Column B</th>
</tr>
<xsl:for-each select="thing">
<tr valign="top">
<td><xsl:value-of select="@value"/></td>
<td>
<xsl:call-template name="tidyUp">
<xsl:with-param name="text" select="@anotherValue"/>
</xsl:call-template>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="tidyUp">
<xsl:param name="text"/>
<xsl:variable name="replacedName">
<xsl:call-template name="replaceThisWithThat">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="replaceThis" select="'Gary'"/>
<xsl:with-param name="withThat" select="'John'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="injectedLinks">
<xsl:call-template name="injectLinks">
<xsl:with-param name="text" select="$replacedName"/>
</xsl:call-template>
</xsl:variable>
<xsl:copy-of select="$injectedLinks"/>
</xsl:template>
<xsl:template name="replaceThisWithThat">
<xsl:param name="text"/>
<xsl:param name="replaceThis"/>
<xsl:param name="withThat"/>
<xsl:choose>
<xsl:when test="contains($text, $replaceThis)">
<xsl:value-of select="substring-before($text, $replaceThis)"/>
<xsl:value-of select="$withThat"/>
<xsl:call-template name="replaceThisWithThat">
<xsl:with-param name="text" select="substring-after($text, $replaceThis)"/>
<xsl:with-param name="replaceThis" select="$replaceThis"/>
<xsl:with-param name="withThat" select="$withThat"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="injectLinks">
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($text, '[[') and contains($text, ']]')">
<xsl:variable name="linkPath">
<xsl:copy-of select="substring-before(substring-after($text,'[['),']]')"/>
</xsl:variable>
<xsl:value-of select="substring-before($text, '[[')"/>
<a target="_blank"><xsl:attribute name="href">http://server/<xsl:copy-of select="$linkPath"/></xsl:attribute><xsl:copy-of select="$linkPath"/></a>
<xsl:call-template name="injectLinks">
<xsl:with-param name="text" select="substring-after($text, ']]')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
view raw bike.xsl hosted with ❤ by GitHub
Here's (a screenshot of) the output:
The following are worth pointing out:
1. The use of "copy-of" (instead of "value-of") whenever my "thing" contains markup that I want to keep as markup
2. The use of "xsl:attribute name=" to allow me to inject values into node attributes
3. The use of recursion for multiple replacements
4. The use of variables (in "tidyUp") to stack transformations