### Extending XSLT with EXSLT

January 5, 2005

In an earlier column titled XSLT Extensions, I described how the Extensions section of the XSLT 1.0 Recommendation provides a way to identify the use of extensions to the XSLT 1.0 language. If your XSLT processor implements specialized new elements and functions, this gives your stylesheet a standard way to declare them and to check whether the processor in use implements them. The good news is that, for example, a Java programmer using a Java-based XSLT implementation can add and use customized XPath functions in his or her stylesheets. The bad news is that this customization can kill the stylesheet's portability across XSLT processors.

Unless... some hardworking XSLT devotees coordinate a collection of extensions to be implemented the same way by different XSLT processors. In early 2001, Leigh Dodds wrote in XML.com about how Jeni Tennison, Uche Ogbuji, David Carlisle, and others laid the ground work for EXSLT, a "a community initiative to provide extensions to XSLT." They've led this community effort to define a wide variety of extension elements and functions, and many useful functions are implemented identically in three of my four favorite XSLT processors: Saxon, Xalan Java, and libxslt. (My other favorite, XSLT C++, is working on it.) In this month's column, we'll look at two stylesheets that demonstrate a collection of EXSLT functions that work identically in Saxon 8.1, Xalan Java 2.4.1, and libxslt 10106.

### Date and Time Functions

The first stylesheet has two groups of function calls. The calls under the line "right now" demonstrate what happens when you call a selection of functions from EXSLT's dates and times module without passing any parameter: the function assumes that you want this information about the point in time right now.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:date="http://exslt.org/dates-and-times" extension-element-prefixes="date" version="1.0"> <xsl:template match="/"> right now: date:date: <xsl:value-of select="date:date()"/> date:date-time: <xsl:value-of select="date:date-time()"/> date:day-name: <xsl:value-of select="date:day-name()"/> date:day-abbreviation: <xsl:value-of select="date:day-abbreviation()"/> date:day-in-week: <xsl:value-of select="date:day-in-week()"/> date:day-in-month: <xsl:value-of select="date:day-in-month()"/> date:day-in-year: <xsl:value-of select="date:day-in-year()"/> date:day-of-week-in-month: <xsl:value-of select="date:day-of-week-in-month()"/> date:hour-in-day: <xsl:value-of select="date:hour-in-day()"/> date:leap-year: <xsl:value-of select="date:leap-year()"/> date:minute-in-hour: <xsl:value-of select="date:minute-in-hour()"/> date:month-abbreviation: <xsl:value-of select="date:month-abbreviation()"/> date:month-in-year: <xsl:value-of select="date:month-in-year()"/> date:month-name: <xsl:value-of select="date:month-name()"/> date:second-in-minute: <xsl:value-of select="date:second-in-minute()"/> date:time: <xsl:value-of select="date:time()"/> date:week-in-year: <xsl:value-of select="date:week-in-year()"/> date:year: <xsl:value-of select="date:year()"/> with parameter of '2003-07-11' passed: date:day-name: <xsl:value-of select="date:day-name('2003-07-11')"/> date:day-abbreviation: <xsl:value-of select="date:day-abbreviation ('2003-07-11')"/> date:day-in-week: <xsl:value-of select="date:day-in-week('2003-07-11')"/> date:day-in-year: <xsl:value-of select="date:day-in-year('2003-07-11')"/> date:day-of-week-in-month: <xsl:value-of select="date:day-of-week-in-month ('2003-07-11')"/> date:leap-year: <xsl:value-of select="date:leap-year('2003-07-11')"/> date:month-abbreviation: <xsl:value-of select="date:month-abbreviation ('2003-07-11')"/> date:month-name: <xsl:value-of select="date:month-name('2003-07-11')"/> date:week-in-year: <xsl:value-of select="date:week-in-year('2003-07-11')"/> </xsl:template> </xsl:stylesheet>

The `xsl:stylesheet` element's `extension-element-prefixes` attribute lists
the prefixes associated with any namespaces that were declared to let the stylesheet
use
extensions from those namespaces. Usually, this means URLs associated with the XSLT
processor in use (for example, http://xml.apache.org/xalan and http://saxon.sf.net/)
because
the extensions are added features provided by the processor's developer. The
http://exslt.org/dates-and-times URL shown above is used by all XSLT processors that
implement any of the EXSLT date functions, making this a stylesheet that uses extensions
while remaining fairly portable—a very nice combination.

Once you've seen the output (use any well-formed XML file as the source document),
most
functions in the stylesheet above are self-explanatory, although I had to check the
documentation to see what `date:date-of-week-in-month` meant: "the
day-of-the-week in a month of a date as a number (e.g. 3 for the 3rd Tuesday in May)."

right now: date:date: 2004-12-09 date:date-time: 2004-12-09T18:30:07-05:00 date:day-name: Thursday date:day-abbreviation: Thu date:day-in-week: 5 date:day-in-month: 9 date:day-in-year: 344 date:day-of-week-in-month: 2 date:hour-in-day: 18 date:leap-year: true date:minute-in-hour: 30 date:month-abbreviation: Dec date:month-in-year: 12 date:month-name: December date:second-in-minute: 7 date:time: 18:30:07-05:00 date:week-in-year: 50 date:year: 2004 with parameter of '2003-07-11' passed: date:day-name: Friday date:day-abbreviation: Fri date:day-in-week: 6 date:day-in-year: 192 date:day-of-week-in-month: 2 date:leap-year: false date:month-abbreviation: Jul date:month-name: July date:week-in-year: 28

The above stylesheet's second group of function calls, beginning with the phrase "with
parameter of '2003-07-11' passed," demonstrates many of the functions from the first
group
with an ISO 8601 representation of a specific date passed. It only demonstrates the
functions whose implementation required more than pulling a substring out of the passed
date. For example, the `date:day-name` function shows that 2003-07-11 falls on a
Friday.

### Other EXSLT Functions

The following stylesheet, which also works on the three XSLT processors mentioned above, demonstrates several more handy EXSLT functions.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:math="http://exslt.org/math" xmlns:set="http://exslt.org/sets" extension-element-prefixes="exsl math set" version="1.0"> <xsl:variable name="var1"> <a> <b fn="mick" ln="taylor">5</b> <b fn="mick" ln="jones">2</b> <b fn="dick" ln="taylor">8</b> <b fn="steve" ln="jones">2</b> <b fn="roger" ln="taylor">8</b> <b fn="roger" ln="waters">3</b> </a> </xsl:variable> <xsl:template match="/"> exsl:object-type(3): <xsl:value-of select="exsl:object-type(3)"/> exsl:object-type('potrzebie'): <xsl:value-of select="exsl:object-type ('potrzebie')"/> exsl:object-type('$var1'): <xsl:value-of select="exsl:object-type ($var1)"/> exsl:object-type(2 > 1): <xsl:value-of select="exsl:object-type (2 > 1)"/> exsl:object-type(..): <xsl:value-of select="exsl:object-type(..)"/> math:constant('PI',4): <xsl:value-of select="math:constant('PI',4)"/> math:constant('PI',12): <xsl:value-of select="math:constant('PI',12)"/> math:constant('E',12): <xsl:value-of select="math:constant('E',12)"/> math:power(2,10): <xsl:value-of select="math:power(2,10)"/> math:power(16,.25): <xsl:value-of select="math:power(16,.25)"/> math:sqrt(256): <xsl:value-of select="math:sqrt(256)"/> math:sin(30 div 57.2957795): <xsl:value-of select="math:sin (30 div 57.2957795)"/> math:cos(120 div 57.2957795): <xsl:value-of select="math:cos (120 div 57.2957795)"/> math:tan(45 div 57.2957795): <xsl:value-of select="math:tan (45 div 57.2957795)"/> set:distinct(exsl:node-set($var1)/a/b/text()): <xsl:for-each select="set:distinct(exsl:node-set($var1)/a/b/text())"> <xsl:apply-templates select="."/> <xsl:text> </xsl:text> </xsl:for-each> set:difference(): <xsl:for-each select="set:difference(exsl:node-set($var1)/a/b[@fn='mick'], exsl:node-set($var1)/a/b[@ln='jones'])"> <xsl:apply-templates select="."/> </xsl:for-each> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>

It has two template rules: the first, which gets triggered when the XSLT processor sees the root of the source tree, demonstrates our second batch of EXSLT functions, and the second template rule copies nodes not covered by the first one. Like the second template rule, the variable declaration that starts the stylesheet makes some of the functions easier to demonstrate. Before looking at the first template rule's individual function calls, let's look at the result of the stylesheet, which you can run with any well-formed XML file as its source:

exsl:object-type(3): number exsl:object-type('potrzebie'): string exsl:object-type('$var1'): node-set exsl:object-type(2 > 1): boolean exsl:object-type(..): node-set math:constant('PI',4): 3.1415 math:constant('PI',12): 3.141592653589 math:constant('E',12): 2.718281828459 math:power(2,10): 1024 math:power(16,.25): 2 math:sqrt(256): 16 math:sin(30 div 57.2957795): 0.500000000103536 math:cos(120 div 57.2957795): -0.500000000414144 math:tan(45 div 57.2957795): 1.0000000003586593 set:distinct(exsl:node-set($var1)/a/b/text()): 5 2 8 3 set:difference(): <b fn="mick" ln="taylor">5</b>

The main template rule begins with five calls to the `exsl:object-type` function, which returns a string showing the type of the
object passed. This can be useful when debugging stylesheets.

The next group of function calls demonstrate several of EXSLT's math functions. In the first three, the `math:constant`
function gets the values of the named constants to the precision specified in the
second
parameter. The first call to `math:constant` above gets pi with 4 digits of
precision, and the second gets it with 12 digits of precision; the third gets the
natural
logarithmic base e to 12 digits of precision. The `math:constant` function can also
accept SQRRT2, LN2, LN10, LOG2E, and SQRT1_2 as its first parameter.

The next two lines call the math:power function, which calculates the first parameter to the power specified by
the second. The first call calculates 2^{10}, giving us 1024, and the second
calculates 16^{1/4}, giving us 2. The line after those calculates the square root of
256, giving us 16.

The last three math function calls perform a little trigonometry. Because they expect their arguments in radians, and I think about trigonometry in degrees, I specified arguments in degrees and divided each by 57.2957795 to pass the function the radians figure it wanted. The results show that the sine of 30 degrees is .5, the cosine of 120 is -.5, and the tangent of 45 degrees is 1.0. (Actually, the figures returned by the function calls are a tiny bit off from these, because I didn't pick a particularly precise value to convert the degrees to radians. And remember, XSLT is not the best tool for doing precise math.)

The last two function calls demonstrate EXSLT set manipulation functions. For the first, the stylesheet passes the
`set:distinct` function the set of all text node children of the $var1 variable's
`b` elements. The function returns a list of the nodes with the duplicates (an
extra 2 and an extra 8) removed. To see the returned nodes individually, the stylesheet
loops through them with an `xsl:for-each` element, putting a single space after each
one.

The `set:difference` function takes two node set arguments and returns the nodes
from the first set that aren't in the second. For the first argument, the stylesheet
passes
it the `b` elements from the $var1 variable that have "mick" as their `fn`
attribute value; for the second, the stylesheet passes the `b` nodes from the same
variable that have "jones" as the `ln` attribute value. (The combination of the
`xsl:apply-templates` element and the stylesheet's second template rule ensure that
any elements grabbed by the function get added to the result tree in their entirety.)
The
only `b` element from the "mick" set that's not in the "jones" set is the mick taylor
one, which displays in the result.

The calls to the set manipulation functions also demonstrate `exsl:node-set`,
an EXSLT function that converts a Result Tree Fragment into a node set. For functions
such
as `set:distinct` and `set:difference` that require a node-set argument, this
function is obviously useful.

In an earlier column titled Trees,
Temporarily, I described how the idea of Result Tree Fragments, which the
`exsl:node-set` function is designed to work around, will no longer be in our way
in XSLT 2.0. This brings up an important point about these extension functions: if
XSLT 2.0
is on the way, why bother with these extensions? For one thing, XSLT 2.0 is only on
the way;
the specification is still a Working Draft. And remember, all the functions demonstrated
in
this column work in Saxon, Xalan Java, and libxslt, while Saxon remains the only XSLT
processor to support XSLT 2.0. If you're designing a production application, you should
still think about how an XSLT 2.0 version will look, so that you can migrate with
minimal
trouble when the time comes. Meanwhile, check out the handy extras that the EXSLT
project's
influence has inspired in these three free, popular, robust XSLT processors.