XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition (176 page)

BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition
4.71Mb size Format: txt, pdf, ePub

You can find a square root function implemented in XSLT on Dimitre Novatchev's FXSL site at
http://fxsl.sourceforge.net/
. It's faster than you might think. Nor is it a purely academic exercise. XSLT can be used to create graphical renditions of your data in SVG format, and this will often require such computations.

Usage and Examples

In this section I will first outline a few ways in which stylesheet functions can be used. I will then look more specifically at the differences between stylesheet functions and named templates. Then I will discuss the use of recursive functions, which provide an extremely powerful programming tool.

Example: Calculating Annual Leave

Stylesheet functions can be used in many different ways. Here is a simple function, which can be applied to an

element to calculate the employee's annual leave entitlement in days. In turn it calls another function, which calculates the duration in months between two dates. The full stylesheet is in
annual-leave.xsl

Source

This example uses two very simple source documents
employees.xml
and
departments.xml
, both governed by a schema
employees.xsd
. The stylesheet is schema-aware. This means that to run it using AltovaXML 2008, you will need to modify the outermost element of the two source documents by adding the attributes:

xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=“http://ns.megacorp.com/hr employee.xsd”

Stylesheet

              xpath-default-namespace=“http://ns.megacorp.com/hr”>

  

  

                as=“xs:yearMonthDuration”

                 select=“pers:monthDifference(

                            current-date(),

                            $emp/date-of-joining)”/>

  

    

      

    

    

      

    

    

      

    




  

  

  

       select=“(year-from-date($arg1) - year-from-date($arg2))*12

                + (month-from-date($arg1) - month-from-date($arg2))”/>

  


This function can now be called from any XPath expression. For example, you can process all the employees with more than 16 days’ annual leave by writing:


Or you could process the employees sorted according to the number of days they are entitled to:


  


This function could be packaged in a library module with other similar functions, allowing reuse of the code, and allowing the algorithms to be changed in one place rather than having them scattered around many different stylesheets. For calculating properties of nodes, functions are much more flexible than named templates because of the way they can be called.

In the previous edition of this book, which was based on drafts of the XSLT 2.0 Recommendation, I was able to make use of a system function
subtract-dates-yielding-yearMonthDuration
, thus avoiding the need to write the
pers:monthDifference
function myself. This function was dropped from the final specification, not because of its unwieldy name, but because no-one could quite agree on the specification: how many months are there, for example, between Feb 29, 2008 and Jan 31, 2008? In my version of the function, the answer is 1.

As well as encapsulating properties of elements, functions can be used to encapsulate relationships. For example, the following function determines the responsible line manager for an employee:

       xpath-default-namespace=“http://ns.megacorp.com/hr”>

  

  

                select=“doc(‘departments.xml’)

                          /departments

                          /department[@dept-nr = $emp/department]

                          /manager-nr”/>

  

                          /key(‘emp’, $mgr-nr)”/>


Users of this function do not need to know how the relationship between employees and their line manager is actually represented in the XML source documents, only that the information is obtainable. This function can then be used in a path expression, rather like a virtual axis:


   Manager: 

   


In these examples I have declared the types of the parameters and the result by reference to types defined in a schema. This helps to document what the function is intended for, and it ensures that you will get an error message (rather than garbage output) if you call the function with incorrect parameters, for example a department rather than an employee element.

I have used the
xpath-default-namespace
attribute, which can be used on any XSLT element to define the namespace that is used for unprefixed element and type names occurring in XPath expressions. For details of this attribute, see the entry for

on page 465.

I also chose to put the functions in the same namespace as the elements that they operate on. This is not the only approach possible, but to my mind it establishes clearly that there is a close relationship between the types (such as
pers:employee
) and the functions designed to operate on those types—the functions act like methods on a class.

Functions versus Named Templates

XSLT offers two very similar constructs: named templates and stylesheet functions. This section discusses the differences between them and suggests when they might be used.

The main difference between named templates and stylesheet functions is the way they are called. Templates are called from the XSLT level using the

instruction, while stylesheet functions are called from XPath expressions using a function call. Another difference is that when a template is called, the caller's context is retained, which is not the case for a function call. The content model for the

and

elements is identical, and there is no difference in the way they are evaluated to produce a result, or in the kinds of result they can return. If you need to invoke the same functionality from both the XSLT and the XPath levels, it is very easy to define a named template as a wrapper for a stylesheet function:


   

   

   


or to define a stylesheet function as a wrapper for a named template:


   

   

    

      

      

   


My own preference is to use stylesheet functions when I want to compute a value or to select existing nodes, and to use a named template when I want to construct new nodes. This reflects the fact that, in general, the role of XSLT instructions is to construct nodes in the result tree, while the role of XPath expressions is to select nodes in the source tree and compute values derived from their content. It's dangerous to speculate about performance of products in general terms, but it seems to me quite likely that XPath engines will be optimized for navigating around source trees, while XSLT engines will be optimized for constructing result trees, and this difference may be reflected in the way the two call mechanisms work.

The fact that stylesheet functions do not have access to the context item may seem at first to be an inconvenience. But I think that the fact that all parameters to the function are explicit greatly helps programming discipline, and produces code that is easier to maintain. It also makes life much easier for the optimizer, which brings another benefit in terms of faster execution.

Functions with side effects can cause some surprises at the XPath level, and creating a new node is a kind of a side effect. For example, one might expect that the result of the expression:

my:f($x) is my:f($x)

is always true. But if the function
my:f()
creates a new node, this is not the case. It is no longer a pure function, because it returns different results on different invocations. An optimizer has to recognize this possibility when rearranging such an expression: it must make sure that the function is actually called twice.

Another example of this effect is the (admittedly rather perverse) expression:

count(//a/../my:f(.))

Normally when evaluating a path expression, the processor can first find all the nodes that the path expression locates, then sort them into document order and eliminate duplicates. But with the expression above, if
my:f()
creates new nodes then the result of the final
count()
depends critically on how many times the function
my:f()
is called, and the correct answer is that it must be called exactly once for each distinct parent of an

element in the source document.

Other books

Bachelor's Wife by Jessica Steele
A Sister's Secret by Wanda E. Brunstetter
Gift of the Gab by Morris Gleitzman
Deep Harbor by Lisa T. Bergren
Holly Lane by Toni Blake
Dead Man's Secret by Simon Beaufort