Good code documentation is as important for XSLT style sheets as for any other programming system. You can document style sheets simply by adding appropriate XML comments. You can even make the comments structured -- similar to the Javadoc approach -- so that you can generate documentation automatically.
Unfortunately, there are disadvantages to using comments as documentation. For one thing, comments may not survive XML processing since the default XSLT action for handling comments and processing instructions in the source file is to ignore them. Also, if you decide to use structured comments to generate documentation, create interactive cross-references, code browsers, and so on, you must invent a syntax for those structured comments. In XML, which is after all a data-format language, you could use XML for the structured-comment format, as long as you could implement it without causing confusion.
Luckily, XSLT has mechanisms for foreign markup, which in some cases must be ignored by the processor. In other cases, these mechanisms can be treated specially by certain processors and ignored by others. The key requirement is that the special markup be in a defined namespace that differs from that of XSLT (and in practice, from any other namespace used in the transform input or result).
So all that remains is to pick a namespace and vocabulary for markup-based comments. Resource Description Framework (RDF) is a standard mechanism provided by the W3C for just this purpose: describing metadata related to Web resources. Code comments are metadata, and RDF is an expressive and well-supported system that makes it easier to put the commentary to solid use.
The most straightforward approach is to have documentation only at the top level of the transform. XSLT processors are required to ignore elements in a non-XSLT namespace at a transform's top level.
The example in Listing 1 generates the Fibonacci series: 1,1,2,3,5,8,13,21,34,55,89,144.... (For those who have not encountered this workhorse of computer-science examples, it is a series in which each number is computed as the sum of the two preceding numbers, starting with 1 and 1.) The transform takes a top-level parameter that sets an upper bound on the generated numbers. It is also commented using RDF and a vocabulary of my choosing.
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1"
xmlns:doc="http://schemas.uche.ogbuji.net/rdfexample/"
version="1.0"
>
<rdf:Description
about="http://uche.ogbuji.net/articles/xsldoctip/fibonacci.xslt">
<dc:Creator>Uche Ogbuji</dc:Creator>
<dc:Date>2001-01-27</dc:Date>
<dc:Description>A Fibonacci series generator</dc:Description>
<dc:Subject>Fibonacci</dc:Subject>
<doc:OutputMethod>text</doc:OutputMethod>
<doc:Param>
<doc:Name>upper-bound</doc:Name>
<doc:Default>200</doc:Default>
<doc:Description>The highest number generated
will be lesser than this value.</doc:Description>
</doc:Param>
</rdf:Description>
<xsl:output method="text"/>
<xsl:param name="upper-bound" select="200"/>
<rdf:description ID="tpl1" doc:construct="template">
<doc:Match>/</doc:Match>
<doc:Description>Initiates the fibonacci generation,
producing the initial 1 and 1 of the series.</doc:Description>
</rdf:description>
<xsl:template match="/">
<xsl:text>1 1</xsl:text>
<xsl:call-template name="fibonacci-next">
<xsl:with-param name="n1" select="1"/>
<xsl:with-param name="n2" select="1"/>
</xsl:call-template>
</xsl:template>
<rdf:description ID="fibonacci-next" doc:construct="template">
<doc:Name>fibonacci-next</doc:Name>
<doc:Description>
Print the next number in the fibonacci series.
</doc:Description>
<doc:Param>
<doc:Name>n1</doc:Name>
<doc:Description>The second to last number generated.</doc:Description>
</doc:Param>
<doc:Param>
<doc:Name>n2</doc:Name>
<doc:Description>The last number generated.</doc:Description>
</doc
</rdf:description>
<xsl:template name="fibonacci-next">
<xsl:param name="n1"/>
<xsl:param name="n2"/>
<xsl:variable name="next" select="$n1 + $n2"/>
<xsl:if test="$next < $upper-bound">
<xsl:text> </xsl:text>
<xsl:value-of select="$next"/>
<xsl:call-template name="fibonacci-next">
<xsl:with-param name="n1" select="$n2"/>
<xsl:with-param name="n2" select="$next"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:transform> |
The first thing to notice is the added namespaces on the transform root element (highlighted with red text in the listing). There is the RDF namespace, the Dublin Core namespace, and a custom namespace for transform documentation (see Resources).
I also added an RDF description as a heading of the transform (highlighted with blue text in the listing). The RDF description is set up to make statements
about the transform file itself, assuming that this file is available at
the URL http://uche.ogbuji.net/articles/xsldoctip/fibonacci.xslt.
The first few statements in the description use the Dublin Core (DC) vocabulary,
addressing the DC domain of expressing authorship and ownership. This has
the advantage of allowing the transform to be cataloged and classified
uniformly along with other documents that are marked up with the popular
DC metadata element set.
Then come several statements in a custom vocabulary for transform documentations.
One defines the transform as generating text output. The next comments
the top-level upper-bound parameter by its name and default value.
A stanza of documentation (highlighted with green text in the listing) comes
before each template. I mark each with an RDF ID so that the documentation
itself becomes a first-class resource. If the template being documented
is a named template, I use the name as the ID. If not, I create a generic
unique ID, in this case tpl1. The doc:construct="template"
notation indicates the particular construct I am documenting. You can tie
the documentation stanzas to the transform elements in other ways as well.
For instance, you can use RDF types to indicate the type, and you can
use XPointer to refer to the actual XML element representing the template.
The documentation stanza contains either the name or the match attribute
of the template and a description element, which gives a prose explanation
of the template. It also specifies parameters, if any, with descriptions
of their own.
This entire scheme is just one possibility. RDF's flexibility means you can use an infinite variety of schemes for placing the documentation, tying documentation stanzas to transform elements, and so on. You can make the documentation more verbose or leaner (perhaps by placing all of the individual template comments in the main transform description element).
So far, I've shown only how to place documentation at the top level. If you want to place documentation within templates and in other places where only XSLT instructions or extension elements are allowed, you can specify the documentation namespaces as extension namespaces.
In the example in Listing 2, I've omitted some of the top-level documentation for brevity.
<?xml version="1.0"?>
<xsl:transform
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1"
xmlns:doc="http://schemas.uche.ogbuji.net/rdfexample/"
version="1.0"
extension-element-prefixes="doc"
>
<xsl:output method="text"/>
<xsl:param name="upper-bound" select="200"/>
<xsl:template match="/">
<xsl:text>1 1</xsl:text>
<xsl:call-template name="fibonacci-next">
<xsl:with-param name="n1" select="1"/>
<xsl:with-param name="n2" select="1"/>
</xsl:call-template>
</xsl:template>
<rdf:description ID="fibonacci-next" doc:construct="template">
<doc:Name>fibonacci-next</doc:Name>
<doc:Description>
Prints the next number in the Fibonacci series.
</doc:Description>
</rdf:description>
<xsl:template name="fibonacci-next">
<xsl:param name="n1" select="1"/>
<xsl:param name="n2" select="1"/>
<doc:InlineComment>$next is the number
generated from the parameters by addition</doc:InlineComment>
<xsl:variable name="next" select="$n1 + $n2"/>
<doc:InlineComment>The termination condition:
stop when $next exceeds the upper bound</doc:InlineComment>
<xsl:if test="$next < $upper-bound">
<xsl:text> </xsl:text>
<xsl:value-of select="$next"/>
<doc:InlineComment>The recursive call</doc:InlineComment>
<xsl:call-template name="fibonacci-next">
<xsl:with-param name="n1" select="$n2"/>
<xsl:with-param name="n2" select="$next"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:transform> |
In the second example in Listing 2, notice the extension-elements-prefixes
attribute on the root element, which sets aside instruction elements in
the custom documentation namespace as extension elements. That has the
useful effect: XSLT processors ignore the extension elements if they don't
know how to handle them (unless a fallback is specified).
The added documentation occurs in-line within the fibonacci-next
template. Note that this time it's not in RDF form; RDF would probably
be overkill for in-line comments, and could be very verbose. However, Listing 2
does use the same custom namespace I used in the RDF. This allows rich documentation systems to easily manage the in-line documentation the same way as the top-level documentation, perhaps by highlighting both in the same way.
Using top-level elements for documentation helps ensure that the documentation is well-structured and that it can be maintained by general XML processing. Using RDF as the documentation scheme allows the programmer or code repository to take advantage of the many tools emerging for querying, visualizing, and otherwise manipulating RDF. True, it might be somewhat verbose, but to be frank, what about XML isn't verbose?
Probably the best thing about this approach to XSLT documentation is that the scheme I used as an example is just the point of departure. With the flexibility of XSLT and RDF, you can pretty much adapt it to your needs until it looks nothing like the examples.
-
The W3C's XSL page with many
useful links to XSLT-related resources, including the specifications themselves,
tutorials, articles and implementations.
-
You can find my article, Introduction
to RDF on developerWorks; there is also an RDF
tutorial on xml.com.
-
The style sheet processor I used in examples is 4XSLT, available in my firm's
open-source XML tool set for Python developers, 4Suite.
-
If you'd like to learn more about the Fibonacci series, you can't beat
Fibonacci
Numbers and the Golden Section, a comprehensive page on the subject.
-
IBM trial software: Build your next development project with trial software available for download directly from developerWorks.
- Find more XML resources on the developerWorks XML zone. For a complete list of XML tips to date, check out the tips summary page.

Uche Ogbuji is a consultant and co-founder of Fourthought, Inc., a consulting firm specializing in XML solutions for enterprise knowledge management applications. Fourthought develops 4Suite, the open source platform for XML middleware. Mr. Ogbuji is a computer engineer and writer born in Nigeria, living and working in Boulder, Colorado, USA. You can reach him at uche@ogbuji.net.
Comments (Undergoing maintenance)





