Writing XSLT extension instructions
Saxon implements the element extensibility feature defined in the XSLT standard. This feature
allows you to define your own instruction types for use in the stylesheet. These instructions
can be used anywhere within a sequence constructor, for example as a child of
xsl:template
, xsl:if
, xsl:variable
, or of a literal
result element.
To implement and use extension instructions, three steps are necessary:
-
There must be a class that implements the interface ExtensionElementFactory, which recognizes all the extension elements in a particular namespace and provides the Java code to implement them.
-
This factory class must be associated with a namespace URI and registered with the Configuration, which can be done either by calling the method setExtensionElementNamespace(namespace, classname), or by means of an entry in the configuration file.
-
Within the stylesheet, there must be a namespace declaration that binds a prefix to this namespace URI, and the prefix must be declared as an extension namespace by means of the
extension-element-prefixes
attribute, typically on thexsl:stylesheet
element. (A rarely-used alternative is to declare it in thexsl:extension-element-prefixes
attribute of an enclosing literal result element.)
Saxon itself provides a number of stylesheet elements beyond those defined in the XSLT
specification, including saxon:assign
, saxon:entity-ref
, and
saxon:while
(see the Saxon
Extension Instructions section). To enable these, use the standard XSLT extension mechanism: define
extension-element-prefixes="saxon"
on the xsl:stylesheet element
, or
xsl:extension-element-prefixes="saxon"
on any enclosing literal result
element.
Any element whose prefix matches a namespace listed in the
extension-element-prefixes
attribute of an enclosing element is treated as an
extension element. If no class can be instantiated for the element (for example, because no ExtensionElementFactory has been registered for the relevant namespace, or because the
ExtensionElementFactory
doesn't recognise the local name), then fallback action
is taken as follows:
-
If the element has one or more xsl:fallback children, they are processed.
-
Otherwise, an error is reported.
When xsl:fallback
is used in any
other context, it and its children are ignored.
Within the stylesheet it is possible to test whether an extension element is implemented by using the system function element-available(). This returns true if the namespace of the element identifies it as an extension element (or indeed as a standard XSLT instruction) and if a class can be instantiated to represent it. If the namespace is not that of an extension element, or if no class can be instantiated, it returns false.
The ExtensionElementFactory interface defines a single method,
getExtensionClass()
, which takes the local name of the element (that is, the
name without its namespace prefix) as a parameter, and returns the Java class used to
implement this extension element (for example, return SQLConnect.class
). The
class returned must be a subclass of
net.sf.saxon.style.StyleElement, and the easiest way to implement it is as a subclass of
net.sf.saxon.style.ExtensionInstruction.
Implementing extension instructions
The best way to see how to implement an extension element is by looking at the example, for
SQL extension elements, provided in package net.sf.saxon.option.sql
, and at the
sample stylesheet books-sql.xsl which uses these extension elements. Start
with the class net.sf.saxon.option.sql.SQLElementFactory.
The StyleElement class represents an element node in the stylesheet document. Saxon calls methods on this class to validate and type-check the element, and to generate a node in the expression tree that is evaluated at run-time. Assuming that the class is written to extend ExtensionInstruction, the methods it should provide are:
prepareAttributes() |
This is called while the stylesheet tree is still being built, so it should not attempt
to navigate the tree. Its task is to validate the attributes of the stylesheet element
and perform any preprocessing necessary. For example, if the attribute is an attribute
value template, this includes creating an |
validate() |
This is called once the tree has been built, and its task is to check that the
stylesheet element is valid "in context": that is, it may navigate the tree and check
the validity of the element in relation to other elements in the stylesheet module, or
in the stylesheet as a whole. By convention, a parent element contains checks on its
children, rather than the other way around: this allows child elements to be reused in a
new context without changing their code. The system will automatically call the method
|
compile() |
This is called to create an |
isInstruction() |
This should return true, to ensure that the element is allowed to appear within a template body. |
mayContainSequenceConstructor() |
This should return true, to ensure that the element can contain instructions. Even if
it can't contain anything else, extension elements should allow an |
The StyleElement class has
access to many services supplied either via its superclasses or via the XPathContext
object.
For details, see the API documentation of the individual classes.
The simplest way to implement the compile()
method is to return an instance of a
class that is defined as a subclass of SimpleExpression.
However, in principle
any Expression object can be
returned, either an expression class that already exists within Saxon, or a user-written
implementation. The following notes assume that SimpleExpression
is being used.
At compile time, the method SimpleExpression.setArguments()
must be called to
give a list of sub-expressions that act as operands for the extension instruction. These might
derive from XPath expressions in attributes of the instruction (including attribute value templates,
which can be compiled to expressions), or from a contained sequence constructor (which can also be
compiled to an expression).
At run-time, Saxon will call the SimpleExpression.call()
method to evaluate the
extension instruction, supplying the values of these sub-expressions, each in the form of a
Sequence object. The implementation
of this method should return the result of the extension instruction, also as a Sequence
.
Saxon also supplies an XPathContext
object which contains details of the dynamic context.
This should not be modified, but can be used as the basis for creating a new dynamic context if required.