This version:
exslt-functions-010310.html
Most Recent version:
http://www.jenitennison.com/xslt/exslt/functions/
Previous versions:
exslt-functions-010306.html
exslt-common-010302.html
exslt-common-010225.html
Author: Jeni Tennison
This document describes EXSLT 1.0 - Functions. EXSLT 1.0 is a set of extension elements and functions that XSLT authors may find helpful when creating stylesheets. EXSLT 1.0 - Functions are those extension elements and functions that allow users to define their own functions for use in expressions and patterns in XSLT.
Other parts of EXSLT 1.0 include:
This document is a third draft for review by the implementers of XSLT processors and the XSLT stylesheet authors. It is based on discussions on XSL-List. Comments on this document should be sent to XSL-List.
This document has no official standing and has not been considered nor approved by any organization.
1. Introduction
2. Namespace
3. Defining Extension Functions
3.1 Function Arguments
3.2 Function Results
3.2.1 Result Values
A. References
B. Sample Functions
B.1 Common Extension Functions
B.2 Set Functions
B.3 Numerical Functions
B.4 Generative Functions
B.5 Sorting Functions
B.6 Other Document Functions
C. Acknowledgements
D. Changes from previous version
This document describes EXSLT 1.0 - Functions. EXSLT 1.0 is a set of extension elements and functions that XSLT authors may find helpful when creating stylesheets. EXSLT 1.0 - Functions are those extension elements and functions that allow users to define their own functions for use in expressions and patterns in XSLT.
The extension elements and functions defined within this document are governed by the general rules about extensions to XSLT covered in [14. Extensions] in [XSLT 1.0].
XSLT processors are free to support any number of the extension elements and functions described in this document. However, an XSLT processor must not claim to support EXSLT 1.0 - Common unless all the extensions described within this document are implemented by the processor. An implementation of an extension element or function in an EXSLT namespace must conform to the behaviour described in this document.
The namespace for the extension elements and functions described in this document is:
http://xmlns.opentechnology.org/xslt-extensions/functions
Throughout this document, the prefix exsl
is used to refer to this namespace. Any other prefix can be used within a particular stylesheet (though a prefix must be specified to enable the extension functions to be recognised as extensions).
XPath contains a number of functions that allow you to perform manipulation of strings, numbers, node sets and so on. While these cover basic functionality, there are often situations where stylesheet authors either need to or want to do more within an XPath.
In the majority of cases, sophisticated manipulation of objects in XSLT can be done within a template, stored in a variable and then used in an XPath. However, there are situations in which expressions and patterns are evaluated without this ability, in particular in the match
attribute of xsl:template
, the match
and use
attributes of xsl:key
and the select
attribute of xsl:sort
. This limits the sophistication of template matching, the definition of keys and the things that a node set can be sorted on.
Most XSLT applications offer a range of extension functions. However, using only implementation's extension functions limits the stylesheet author to those thought of and implemented by a particular vendor. It also means that the stylesheet itself is limited to that vendor. Allowing users to define their own extension functions enables them to create the functions that they need for their particular application and enhances the portability of their stylesheets.
Stylesheet authors need to have a ways of defining their own functions. These definitions may be in any programming language, but it is likely that different XSLT processors will support different languages. The one language that all XSLT processors support is XSLT. It therefore makes sense to allow stylesheet authors to define extension functions using XSLT instructions - the implementation may not be as efficient as it would be in, say, Java, but at least it can be supported across platforms and implementations, and limits the number of langauges that stylesheet authors have to learn.
One way of defining extension functions using XSLT instructions would be to simply use named templates (see [6. Named Templates]). However, named templates can only be used to generate result tree fragments and many useful functions involve returning other value types. Result tree fragments can be coerced into strings, numbers and booleans, and with the exsl:node-set function into node sets, but the node sets produced through coercion of result tree fragment consist of newly-generated nodes rather than nodes that already exist. Node sets generated via result tree fragments can only contain copies of nodes in the source document; these copies have different properties - different ancestry, preceding and following nodes, base URIs, languages and so on.
Thus, an extension element is required to allow users to define functions that involve returning node sets consisting of existing nodes.
<-- Category: top-level-element --> <exsl:function name = QName> <!-- Content: (xsl:param*, template) --> </exsl:function>
Issue: exsl:function
content - should the content of exsl:function
be a template? Templates are instantiated to create RTFs, but exsl:function
has an 'escape' in exsl:result
that means that the result of instantiating a function body can be something other than an RTF. This is a departure from the XSLT 1.0 processing model. An alternative is described in [FXPath].
Issue: exsl:function
return type - should the RTF returned by a function (if exsl:return
isn't used) be automatically converted to other object types, as indicated by an attriute? This option is used in fx:template-function
[FXPath].
The exsl:function
element can only occur at the top level of the stylesheet. The exsl:function
element declares an extension function that is visible everywhere: the extension function is added to the function library available to the expressions and patterns used in the XSLT stylesheet.
An exsl:function
element must have a name
attribute, indicating the name of the function. The value of the name
attribute is a QName, which is expanded as described in Section 2.4 [Qualified Names] in the XSLT 1.0 Recommendation. It is an error if the namespace URI of the expanded name of the function is null - extension functions must not be in a null namespace.
Note: the rules on resolving qualified names entail that if no prefix is defined, the namespace URI resolves to the null namespace. Thus, it is an error if the qualified name specified does not have a prefix.
It is an error if a stylesheet contains more than one exsl:function
element with the same name and the same import precedence. An XSLT processor may signal the error; if it does not signal the error, it must recover by using the function definition that occurs last in the stylesheet.
When an extension function defined with exsl:function
is called, the content of the exsl:function
is instantiated to give the result of the function (see [3.2 Function Results]).
Arguments for functions are defined with the xsl:param
element, as specified in [11. Variables and Parameters] of [XSLT].
Issue: exsl:arg
- should arguments be specified with an extension element (e.g. exsl:arg
or exsl:argument
) rather than xsl:param
? xsl:param
is fairly overloaded as it is.
When an extension function is called, the values passed as arguments are assigned to parameters according to the position of the xsl:param
. The first argument is assigned to the first parameter, the second to the second and so on. The presence of an xsl:param
indicates that an argument is expected for the function but does not imply that an argument has to be passed to the function.
An XSLT processor must not signal an error if an extension function is called with fewer arguments than there are parameters defined for the extension function. It is an error to call a function with more arguments than there are parameters defined for the extension function. An XSLT processor may signal the error; if it does not signal the error, then it must recover by ignoring the extra arguments.
As an example, take the following function definition:
<exsl:function name="my:func"> <xsl:param name="foo" /> <xsl:param name="bar" select="false()" /> ... </exsl:function>
All the following function calls are legal:
my:func() my:func('Fred') my:func('Fred', true()) my:func('Fred', 'Barney')
The following function call is illegal:
my:func('Fred', true(), 'Barney')
The value specified by an xsl:param
indicates the default value for an argument if that argument is not given in a function call, but does not indicate the acceptable value types for the function. The type of the value passed into the function can be tested with the exsl:object-type function.
The content of the exsl:function
element is a template. When the function is called, the template is instantiated to give the result of the function. The template is instantiated with the context node from the expression in which the function was called as the current node, and with the context node list from the expression in which the function was called as the current node list.
If the instantiation of the template results in the generation of result nodes, then the result of the function is a result tree fragment consisting of those nodes. For example a call to my:func
as below will generate a result tree fragment consisting of a root node with a single foo
element child.
<exsl:function name="my:func"> <foo /> </exsl:function>
It is an error if a member of the sequence of nodes created by instantiating the template is an attribute node or a namespace node, since a root node cannot have an attribute node or a namespace node as a child. An XSLT processor may signal the error; if it does not signal the error, it must recover by not adding the attribute node or namespace node.
The result tree fragment that is the result of the function may be converted to a node set using the exsl:node-set function, to a string using the string function, or to a number using the number function.
Note: Applying the boolean function to the result tree fragment will always evaluate to true.
As discussed earlier in this section, generating result tree fragments only is not sufficient for many functions that involve manipulation of node sets. To enable functions to return node sets (and booleans), the instantiation of the content of the exsl:function
element may involve the instantiation of an exsl:result
element.
<exsl:result select = expression> <!-- Content: template --> </exsl:result>
When an exsl:result
element is instantiated, it sets the result of the function to its value. The value of the exsl:result
element is determined in a similar way to variable-binding elements as described in [11.2 Values of Variables and Parameters] of [XSLT] (see [3.2.1 Result Values]).
Issue: exsl:result
name - should exsl:result
be called exsl:return instead?
Issue: exsl:reference-of
- should it be possible to generate node set results by building them up gradually through an extension element such as exsl:reference-of
(or exsl:append
or multiple exsl:result
elements)? It would help give results consisting of multiple nodes from a function. This is achievable by building an RTF with IDs pointing to the relevant nodes instead.
Issue: exsl:result
parent - should the use of exsl:result
be restricted to within exsl:function
?
It is an error if an exsl:result
element is instantiated if result nodes have been generated prior to its instantiation. Thus, the following is an unrecoverable error:
<exsl:function name="my:func"> <foo /> <exsl:result select="'foo'" /> </exsl:function>
It is an error if result nodes are generated following the instantiation of an exsl:result
element. An XSLT processor may signal the error; if it does not signal the error, then it must recover by ignoring the result nodes. Thus, the following is an error:
<exsl:function name="my:func"> <exsl:result select="'foo'" /> <foo /> </exsl:function>
If an XSLT processor recovers from the error, the function is equivalent to:
<exsl:function name="my:func"> <exsl:result select="'foo'" /> </exsl:function>
Issue: RTF after exsl:result
error - should generating result nodes after instantiating an exsl:result
be an unrecoverable error, or a recoverable one?
It is an error if instantiating the content of the exsl:function
element results in the instantion of more than one exsl:result
elements. An XSLT processor may signal the error; if it does not signal the error, it must recover by ignoring all exsl:result
elements after the first.
Issue: Multiple exsl:result
error - should instantiating exsl:result
more than once be an unrecoverable error, or a recoverable one?
The following is an error if the value of the context node when the function is called is equal to the string 'yes'
, as two exsl:result
elements are instantiated: one within the xsl:if
and one directly within the exsl:function
:
<exsl:function name="my:func1">
<xsl:if test=". = 'yes'">
<exsl:result select="true()" />
</xsl:if
>
<exsl:result select="false()" />
</exsl:function>
If an XSLT processor recovers from this error, the above function is equivalent to:
<exsl:function name="my:func1"> <xsl:choose> <xsl:when test=". = 'yes'"> <exsl:result select="true()" /> </xsl:when> <xsl:otherwise> <exsl:result select="false()" /> </xsl:otherwise> </xsl:choose> </exsl:function>
It is an error if an exsl:result
element occurs within an exsl:result
element. Thus the following is an error:
<exsl:function name="my:func2"> <exsl:result> <exsl:result select="." /> </exsl:result> </exsl:function>
It is an error if instantiating the content of a variable-binding element (i.e. xsl:variable
, xsl:param
) results in the instantiation of an exsl:result
element. Thus the following is an error:
<exsl:function name="my:func3"> <xsl:variable name="foo"> <exsl:result select="." /> </xsl:variable> </exsl:function>
If no result nodes are generated during the instantiation of the function content, and no exsl:result
element is instantiated, then the result of the function is an empty result tree fragment.
The exsl:result
element can specify the value of the variable in three alternative ways.
If the exsl:result
element has a select
attribute, then the value of the attribute must be an expression and the returned value is the object that results from evaluating the expression. In this case, the content must be empty.
If the exsl:result
element does not have a select
attribute and has non-empty content (i.e. the exsl:result
element has one or more child nodes), then the content of the exsl:result
element specifies the value. The content of the exsl:result
element is a template, which is instantiated to give the returned value. The value is a result tree fragment equivalent to a node-set containing just a single root node having as children the sequence of nodes produced by instantiating the template. The base URI of the nodes in the result tree fragment is the base URI of the exsl:result
element.
It is an error if a member of the sequence of nodes created by instantiating the template is an attribute node or a namespace node, since a root node cannot have an attribute node or a namespace node as a child. An XSLT processor may signal the error; if it does not signal the error, it must recover by not adding the attribute node or namespace node.
If the exsl:result
element has empty content and does not have a select
attribute, then the returned value is an empty string. Thus
<exsl:result />
is equivalent to
<exsl:result select="''"/>
This appendix holds example implementations of several extension functions.
Function: object com:if(boolean, object, object)
The com:if function returns the second argument if the first argument is true and the third argument if the first argument is false. Both the second and third arguments are evaluated whether the first argument is true or false.
<exsl:function name="com:if"> <xsl:param name="test" select="true()" /> <xsl:param name="true" /> <xsl:param name="false" /> <xsl:choose> <xsl:when test="$test"> <exsl:result select="$true" /> </xsl:when> <xsl:otherwise> <exsl:result select="$false" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: node-set set:difference(node-set, node-set)
The set:difference function returns the difference between two node sets - those nodes that are in the node set passed as the first argument that are not in the node set passed as the second argument.
<exsl:function name="set:difference"> <xsl:param name="node-set1" select="/.." /> <xsl:param name="node-set2" select="/.." /> <exsl:result select="$node-set1[count(.|$node-set2) != count($node-set2)]" /> </exsl:function>
Function: boolean set:has-same-node(node-set, node-set)
The set:has-same-node function returns true if the node set passed as the first argument shares any nodes with the node set passed as the second argument. If there are no nodes that are in both node sets, then it returns false.
<exsl:function name="set:has-same-node"> <xsl:param name="node-set1" select="/.." /> <xsl:param name="node-set2" select="/.." /> <exsl:result select="boolean($node-set1[count(.|$node-set2) = count($node-set2)])" /> </exsl:function>
Function: node-set set:intersection(node-set, node-set)
The set:intersection function returns a node set comprising the nodes that are within both the node sets passed as arguments to it.
<exsl:function name="set:intersection"> <xsl:param name="node-set1" select="/.." /> <xsl:param name="node-set2" select="/.." /> <exsl:result select="$node-set1[count(.|$node-set2) = count($node-set2)]" /> </exsl:function>
Function: node-set set:distinct(node-set)
The set:distinct function returns the nodes within the node set passed as the first argument that are the first nodes in document order whose string value is their string value.
<exsl:function name="set:distinct"> <xsl:param name="node-set" select="/.." /> <xsl:param name="distinct" select="/.." /> <xsl:choose> <xsl:when test="not($node-set)"> <exsl:result select="/.." /> </xsl:when> <xsl:otherwise> <xsl:variable name="distinct" select="set:distinct($node-set[position() > 1])" /> <exsl:result select="$distinct | $node-set[1][. != $distinct]" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: node-set set:leading(node-set, node-set)
The set:leading function returns the nodes in the node set passed as the first argument that precede, in document order, the first node in the node set passed as the second argument. If the first node in the second node set is not contained in the first node set, then an empty node set is returned. If the second node set is empty, then the first node set is returned.
<exsl:function name="set:leading"> <xsl:param name="node-set" select="/.." /> <xsl:param name="end-node-set" select="/.." /> <xsl:variable name="end-node" select="$end-node-set[1]" /> <xsl:choose> <xsl:when test="not($end-node) or not($node-set)"> <exsl:result select="$node-set" /> </xsl:when> <xsl:when test="not(set:has-same-node($node-set, $end-node)) or count($node-set[1] | $end-node) = 1"> <exsl:result select="/.." /> </xsl:when> <xsl:otherwise> <exsl:result select="$node-set[1] | set:leading($node-set[position() > 1], $end-node)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: node-set set:trailing(node-set, node-set)
The set:trailing function returns the nodes in the node set passed as the first argument that follow, in document order, the first node in the node set passed as the second argument. If the first node in the second node set is not contained in the first node set, then an empty node set is returned. If the second node set is empty, then the first node set is returned.
<exsl:function name="set:trailing"> <xsl:param name="node-set" select="/.." /> <xsl:param name="end-node-set" select="/.." /> <xsl:variable name="end-node" select="$end-node-set[1]" /> <xsl:choose> <xsl:when test="not($end-node) or not($node-set)"> <exsl:result select="$node-set" /> </xsl:when> <xsl:when test="not(set:has-same-node($node-set, $end-node))"> <exsl:result select="/.." /> </xsl:when> <xsl:when test="count($node-set[1] | $end-node) = 1"> <exsl:result select="$node-set[position() > 1]" /> </xsl:when> <xsl:otherwise> <exsl:result select="set:trailing($node-set[position() > 1], $end-node)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: number math:max(node-set)
The math:max function returns the maximum, for each node in the argument node-set, of the result of converting the string-values of the node to a number using the number function. The numbers are compared as with the >
operator. If the node set is empty, NaN
is returned.
<exsl:function name="math:max"> <xsl:param name="node-set" select="/.." /> <exsl:result select="number($node-set[not($node-set > .)])" /> </exsl:function>
Function: number math:min(node-set)
The math:min function returns the maximum, for each node in the argument node-set, of the result of converting the string-values of the node to a number using the number function. The numbers are compared as with the <
operator. If the node set is empty, NaN
is returned.
<exsl:function name="math:min"> <xsl:param name="node-set" select="/.." /> <exsl:result select="number($node-set[not($node-set < .)])" /> </exsl:function>
Function: node-set math:highest(node-set)
The math:highest function returns the nodes for which the result of converting the string-value of the node to a number is maximum value for the node set, as calculated with math:max.
<exsl:function name="math:highest"> <xsl:param name="node-set" select="/.." /> <exsl:result select="$node-set[not($node-set > .)]" /> </exsl:function>
Function: node-set math:lowest(node-set)
The math:lowest function returns the nodes for which the result of converting the string-value of the node to a number is minimum value for the node set, as calculated with math:min.
<exsl:function name="math:lowest"> <xsl:param name="node-set" select="/.." /> <exsl:result select="$node-set[not($node-set < .)]" /> </exsl:function>
Function: node-set gen:range(number, number)
The gen:range function generates a node set in which the string values of the nodes are numbers between the number passed as the first argument and the number passed as the second argument.
<exsl:function name="gen:range"> <xsl:param name="start" select="0" /> <xsl:param name="end" select="0" /> <xsl:choose> <xsl:when test="number($start) > number($end)"> <exsl:result select="/.." /> </xsl:when> <xsl:when test="number($start) = number($end)"> <xsl:variable name="rtf"> <node><xsl:value-of select="$start" /></node> </xsl:variable> <exsl:result select="exsl:node-set($rtf)" /> </xsl:when> <xsl:otherwise> <xsl:variable name="mid-point" select="$start + floor(($end - $start) div 2)" /> <xsl:variable name="rtf"> <xsl:copy-of select="gen:range($start, $midpoint)" /> <xsl:copy-of select="gen:range($midpoint + 1, $end)" /> </xsl:variable> <exsl:result select="exsl:node-set($rtf)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: string gen:padding(number, string?)
The gen:padding function generates a string generated by repeating the string given as the second argument (or a space [' '
] if there is no second argument) repeated the number of times specified by the first argument.
<exsl:function name="gen:padding"> <xsl:param name="repeat" select="1" /> <xsl:param name="string" select="' '" /> <xsl:variable name="padding"> <xsl:for-each select="gen:range(1, $repeat)"> <xsl:value-of select="$string" /> </xsl:for-each> </xsl:variable> <exsl:result select="string($padding)" /> </exsl:function>
Function: node-set gen:tokens(string, string?)
The gen:tokens function tokenises the string passed as the first argument by splitting it at each character in the string passed as the second argument. If no second argument is specified, then a space (' '
) is assumed.
<exsl:function name="gen:tokens"> <xsl:param name="string" /> <xsl:param name="delimiters" select="' '" /> <xsl:variable name="char" select="substring($delimiters, 1, 1)" /> <xsl:choose> <xsl:when test="string-length($delimiters) > 1"> <xsl:variable name="replacement" select="gen:padding(string-length($delimiters) - 1, $char)" /> <xsl:variable name="string-tokens" select="translate($string, substring($delimiters, 2), $replacement)" /> <exsl:result select="gen:tokens($string, $char)" /> </xsl:when> <xsl:otherwise> <xsl:variable name="rtf"> <xsl:choose> <xsl:when test="contains($string, $delimiters)"> <node> <xsl:value-of select="substring-before($string, $char)" /> </node> <xsl:copy-of select="gen:tokens(substring-after($string, $char), $char)" /> </xsl:when> <xsl:otherwise> <node><xsl:value-of select="$string" /></node> </xsl:otherwise> </xsl:choose> </xsl:variable> <exsl:result select="exsl:node-set($rtf)"> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: number sort:position(node-set, string?, string?)
The sort:position function gives the position of the current node within the node-set passed as the first argument when the nodes in it are sorted according to a sort value. If the third argument is 'number'
then the sort value is the string value of each node converted to a number as by the number function. Otherwise, the sort value of a node is its string value.
The second argument gives the order in which the nodes should be sorted. It should either be 'ascending'
or 'descending'
. The default is 'ascending'
.
<exsl:function name="sort:position">
<xsl:param name="node-set" select="/.." />
<xsl:param name="order" select="'ascending'" />
<xsl:param name="data-type" select="'text'" />
<xsl:variable name="current" select="." />
<xsl:choose>
<xsl:when test="count($current | $node-set) = count($node-set)">
<xsl:for-each select="$node-set">
<xsl:sort order="{$order}"
data-type="{$data-type}" />
<xsl:if test="count(.|$current) = 1">
<exsl:result select="position()" />
</xsl:if
>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<exsl:result select="number('NaN')" />
</xsl:otherwise>
</xsl:choose>
</exsl:function>
Function: node-set doc:key(string, object, object, node-set?)
The doc:key function is an extension of the XSLT key function that retrieves nodes in documents aside from the one the current node is in.
The first two arguments operate in the same way as the two arguments to key. The third and fourth arguments operate in the same way as the two arguments to document.
<exsl:function name="doc:key"> <xsl:param name="key-name" /> <xsl:param name="key-value" /> <xsl:param name="documents" select="/.." /> <xsl:param name="base-URI" select="document('')" /> <xsl:choose> <xsl:when test="exsl:object-type($documents) = 'string' or count($documents) = 1"> <xsl:for-each select="document($documents, $base-URI)"> <exsl:result select="key($key-name, $key-value)" /> </xsl:for-each> </xsl:when> <xsl:otherwise> <exsl:result select="doc:key($key-name, $key-value, $documents[1], $base-URI) | doc:key($key-name, $key-value, $documents[position() != 1], $base-URI)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: node-set doc:id(object, object, node-set?)
The doc:id function is an extension of the XPath id function that retrieves nodes in documents aside from the one the context node is in.
The first argument operates in the same way as the argument to id. The second and third arguments operate in the same way as the two arguments to document.
<exsl:function name="doc:id"> <xsl:param name="id" /> <xsl:param name="documents" select="/.." /> <xsl:param name="base-URI" select="document('')" /> <xsl:choose> <xsl:when test="exsl:object-type($documents) = 'string' or count($documents) = 1"> <xsl:for-each select="document($documents, $base-URI)"> <exsl:result select="id($id)" /> </xsl:for-each> </xsl:when> <xsl:otherwise> <exsl:result select="doc:id($id, $documents[1], $base-URI) | doc:id($id, $documents[position() != 1], $base-URI)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
Function: string doc:unparsed-entity-uri(string, object, node-set?)
The doc:unparsed-entity-uri function is an extension of the XSLT unparsed-entity-uri function that retrieves the URL of an entity in documents aside from the one the context node is in. This function can be used to search in documents other than the one holding the context node, but will return the first URL that it finds; if the entity is defined with different URLs in different documents, then only the first document given in the node set specified as the second argument (when considered in document order) will be returned.
The first argument operates in the same way as the argument to unparsed-entity-uri. The second and third arguments operate in the same way as the two arguments to document.
<exsl:function name="doc:unparsed-entity-uri"> <xsl:param name="entity-name" /> <xsl:param name="documents" select="/.." /> <xsl:param name="base-URI" select="document('')" /> <xsl:variable name="entity-URI"> <xsl:for-each select="document($documents[1], $base-URI)"> <xsl:value-of select="unparsed-entity-uri($entity-name)" /> </xsl:for-each> </xsl:variable> <xsl:choose> <xsl:when test="string($entity-URI)"> <exsl:result select="string($entity-URI)" /> </xsl:when> <xsl:otherwise> <exsl:result select="doc:unparsed-entity-uri($entity-name, $documents[position() != 1], $base-URI)" /> </xsl:otherwise> </xsl:choose> </exsl:function>
This has been informed and inspired by discussions on XSL-List with:
David Carlisle
Jarno Elovirta
Joe English
Clark C. Evans
Jim Fuller
Dave Gomboc
Dave Hartnoll
Kevin Jones
Yevgeniy (Eugene) Kaganovich
Mike Kay
Steve Muench
Miloslav Nic
Francis Norton
Dimitre Novatchev
Uche Ogbuji
David Rosenborg