



































|
 |
Unless otherwise
specified, the Xalan-Java extensions discussed in this section
refers to the Xalan-Java Interpretive processor. See Extensions for XSLTC for more
information. |
|
|
For those
situations where you would like to augment the functionality of
XSLT with calls to a procedural language, Xalan-Java supports the
creation and use of extension elements and extension functions.
Xalan-Java also provides a growing extensions library available for your
use. An extension (a collection of elements and functions) inhabits
a namespace, either a namespace you declare and designate as an
extensions namespace, or one of the predefined namespaces that
Xalan-Java provides. For information about XML namespaces, see Namespaces in
XML.
Extension elements: Unlike a
literal result element, which the stylesheet simply transfers to
the result tree, an extension element performs an action. For
example, you can use the Redirect extension elements shipped with
Xalan-Java to redirect portions of your transformation output to
one or more files. Extension elements may contain attributes, text
nodes, other elements, basically any valid XML. Extension elements
may perform quite sophisticated actions, given that the extension
routine (the implementation) has direct access to the XSLT
processor context object and to the element. In many cases the
implementation returns void or null; if it does return a value,
that value is placed in the transformation result tree.
Extension functions: You can
think of extension functions as extending the core library of
functions that XPath provides. An extension function passes
arguments to the extension implementation and returns a value. You
can use extension functions to return values that XSLT can interact
with directly (node-set, result tree fragment, string, boolean, and
number) as well as values (of any type) that you pass in turn to
other extension functions. Extension functions written in Java can
also access certain items in the XSLT execution environment through
an
ExpressionContext interface.
XSLT
extensions are specified in the XSLT Recommendation.
This document focuses on the Xalan-Java implementation of those
requirements, not on XSLT extensions in general. For additional
information on extensions, consult the Recommendation or the other
resources listed in Getting up to
speed with XSLT.
|
|
|
Extensions
written in Java are directly supported by Xalan-Java. For
extensions written in languages other than Java, Xalan-Java uses
the Bean Scripting Framework (BSF), an architecture
for incorporating scripting into Java applications and applets. BSF
allows an application to take advantage of scripting while being
independent of any specific scripting language. To date, we have
tested extensions implemented in JavaScript. Other languages with
BSF support appear in the table below.
BSF
requires bsf.jar on the classpath. This JAR file is shipped with
Xalan-Java and is required only if you have extensions written in
languages other than Java. The additional JAR files or DLLs
required to support extensions written in other languages are
listed in the table below. These files are available from the
sources indicated and are not shipped with Xalan-Java.
Language |
Version |
Requirements |
Mozilla Rhino (JavaScript)
|
1.5
|
js.jar available from http://www.mozilla.org/rhino
|
NetRexx
|
1.148 up
|
NetRexxC.zip available from
http://www2.hursley.ibm.com/netrexx
|
BML
|
2.4
|
bmlall.jar available from
http://oss.software.ibm.com/developerworks/projects/bsf
|
JPython
|
1.1-beta3
|
python.jar available from http://www.jpython.org/
|
Jacl
|
1.1.1
|
jacl.jar and tcljava.jar from
http://www.scriptics.com/java
|
Win32 ActiveScript langs JScript, VBScript
|
|
MSVCP60.DLL from Microsoft, appropriate language DLLs
from Microsoft http://msdn.microsoft.com/scripting
|
PerlScript
|
|
ActivePerl from http://www.activestate.com/
|
|
|
|
Let's
examine a simple example. The stylesheet below uses an extension
element and an extension function to transform an element in the
XML source into a statement in the output indicating the date by
which a customer can expect a response to a given
enquiry.
The source
element contains a numdays attribute. The extension element
contains a multiplier attribute, which is used to set a variable in
the extension. The extension function computes the deadline, that
is the current date plus numdays * multiplier. So for <deadline
numdays="3"/> (in the XML source) and <my-ext:timelapse
multiplier="2"/> (in the stylesheet), the extension computes a
deadline 6 days from now, and the stylesheet template transforms
the deadline element into a string along the lines of <p>We have logged your enquiry and
will respond by April 29, 2000 12:07:16 PM
EST.</p>
 |
The extension function
could include both numdays and multiplier as arguments, thus
bypassing the need for the extension element, but the purpose here
is to illustrate the usage pattern for extension
elements. |
As you
review this stylesheet, please note the following:
- The
declaration of the xalan namespace, which provides support for the
component and component/script elements:
xmlns:xalan="http://xml.apache.org/xalan"
- The
declaration of a namespace for this extension:
xmlns:my-ext="ext1"
- The
designation of this namespace prefix as an extension prefix. This
causes any element in the namespace associated with this prefix to
be treated as an extension element rather than a literal result
element.
extension-element-prefixes="my-ext"
- The
xalan:component with attributes designating the namespace prefix
and the elements and functions this extension provides.
- The
xalan:script subelement with a JavaScript implementation of the
extension. For Java extensions, the xalan:script element has a src
attribute that you set to identify the Java class.
 |
 |
 |
 |
<?xml version="1.0"?>
<!--Namespaces are global if you set them in the stylesheet element-->
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:my-ext="ext1"
extension-element-prefixes="my-ext">
<!--The component and its script are in the xalan namespace and define the
implementation of the extension.-->
<xalan:component prefix="my-ext" elements="timelapse" functions="getdate">
<xalan:script lang="javascript">
var multiplier=1;
// The methods or functions that implement extension elements always take 2
// arguments. The first argument is the XSL Processor context; the second
// argument is the element node.
function timelapse(xslProcessorContext, elem)
{
multiplier=parseInt(elem.getAttribute("multiplier"));
// The element return value is placed in the result tree.
// If you do not want a return value, return null.
return null;
}
function getdate(numdays)
{
var d = new Date();
var totalDays = parseInt(numdays) * multiplier;
d.setDate(d.getDate() + totalDays);
return d.toLocaleString();
}
</xalan:script>
</xalan:component>
<xsl:template match="deadline">
<p><my-ext:timelapse multiplier="2"/>We have logged your enquiry and will
respond by <xsl:value-of select="my-ext:getdate(string(@numdays))"/>.</p>
</xsl:template>
</xsl:stylesheet>
|
 |
 |
 |
 |
|
 |
 |
 |
 |
Using an extension
element |
 |
 |
 |
 |
|
|
Extension
elements pass the extension two objects:
You can
use the ElemExtensionCall getAttribute(String name) method, for
example, to read element attributes in their raw form. Use the
getAttribute(String name, Node sourceNode, XSLTEngineImpl
processor) method to evaluate the attribute as an attribute value
template. Note that the method names are the same but the method
signatures are different. For full details, see the
Javadoc for the ElemExtensionCall class.
 |
 |
 |
 |
Implementing an extension
element |
 |
 |
 |
 |
|
|
For each extension element in a namespace, the
implementation must be a Java method with the following signature,
or the scripting language equivalent:
Type
element(org.apache.xalan.extensions.XSLProcessorContext,
org.apache.xalan.templates.ElemExtensionCall
extensionElement)
where Type designates the return type and
element is the local part of the extension element name (the
element name without the namespace prefix). In the method
signature, you may also use superclasses of the indicated
types.
If the extension element is implemented in a loosely
typed scripting language, such as JavaScript, the arguments and
return value are untyped.
Caution: The value returned by an extension
element is placed in the transformation result. If you are not
interested in a return value, use a public void Java method or
return null from a scripting language function.
Java example: public void
myElement
(org.apache.xalan.extensions.XSLProcessorContext,
org.apache.xalan.templates.ElemExtensionCall
extensionElement)
JavaScript example: function myElement(xslProcContext,
element)
The Redirect
extension in the extensions library contains three extension
elements.
|
|
 |
 |
 |
 |
Using extension
functions |
 |
 |
 |
 |
|
|
Extension
functions may include arguments of any type and return a value of
any type.
XSLT
recognizes five data types: node-set, result-tree-fragment, string,
boolean, and number. You can use XPath expressions to set variables
with values of these types. You can also pass literals for string,
boolean, and number arguments. If you want to pass an argument of a
type that XSLT does not recognize, use another extension function
to return an object of that type. The stylesheet that appears in Formatting a
date, for example uses extension functions to return a Date
object and a SimpleDateFormat object, and then uses these objects
to call another extension function.
 |
 |
 |
 |
Data type mapping and method
selection |
 |
 |
 |
 |
|
|
When calling an extension function written in a language
other than Java, objects of the following Java classes will always
be passed to the extension function:
XSLT
Type |
Java
Type |
Node-Set |
org.w3c.dom.traversal.NodeIterator |
String |
java.lang.String |
Boolean |
java.lang.Boolean |
Number |
java.lang.Double |
Result Tree Fragment |
org.w3c.dom.DocumentFragment |
Any non-XSLT type is passed without
conversion.
When calling an extension function written in Java, the
extension function signature can specify any of the indicated Java
types, as explained below:
XSLT
Type |
Java
Types |
Node-Set |
org.w3c.dom.traversal.NodeIterator, org.w3c.dom.NodeList,
org.w3c.dom.Node or its subclasses, java.lang.String,
java.lang.Object, char, [double, float, long, int, short, byte,]
boolean |
String |
java.lang.String, java.lang.Object, char, [double, float,
long, int, short, byte,] boolean |
Boolean |
boolean, java.lang.Boolean, java.lang.Object,
java.lang.String |
Number |
double, java.lang.Double, float, long, int, short,char,
byte, boolean, java.lang.String, java.lang.Object
|
Result Tree Fragment |
org.w3c.dom.traversal.NodeIterator, org.w3c.dom.NodeList,
org.w3c.dom.Node or its subclasses, java.lang.String,
java.lang.Object, char, [double, float, long, int, short, byte,]
boolean |
Non-XSLT Type |
the native type or any of its superclasses, double,
float, long, int, short, char, byte, java.lang.String
|
When calling extension functions written in Java,
Xalan-Java selects the method to call as follows:
- Xalan-Java selects all methods whose name matches the
extension function name as specified in Extension function Java
calls.
- From this list of methods, Xalan-Java determines which
methods are qualified.
- Each qualified method is assigned a score based on the
table shown above. To assign the score to a given method,
Xalan-Java examines each of the XSLT argument types in the function
invocation in the stylesheet. For each argument, the appropriate
row in the table above is selected. Then, the corresponding Java
parameter type in the method signature in the Java program is
scored. Types which appear earlier in the list are given a higher
score. That is, the list appears in order of scoring preference
from highest to lowest. Types shown in square brackets have equal
priority.
- The method with the highest score is invoked after the
arguments are converted to the appropriate type. If more than one
method has the highest score, an exception is thrown.
Any extension function written in Java can have a first
parameter of type org.apache.xalan.extensions.ExpressionContext .
Any method with an ExpressionContext as the first parameter will
score higher than any method which does not have an
ExpressionContext as a first parameter.
|
|
|
The Xalan-Java extension mechanism examines the class of
the value returned from a function and converts the value into an
XSLT type according to the following table:
Java
Types |
XSLT
Type |
org.w3c.dom.traversal.NodeIterator
org.apache.xml.dtm.DTM
org.apache.xml.dtm.DTMAxisIterator
org.apache.xml.dtm.DTMIterator
org.w3c.dom.Node |
Node-Set |
java.lang.String |
String |
java.lang.Boolean |
Boolean |
java.lang.Number |
Number |
org.w3c.dom.DocumentFragment |
Result Tree Fragment |
org.apache.xpath.objects.XObject |
Specified XSLT Type |
All other classes |
Non-XSLT Type |
Note that the above test is applied using instanceof so that any
subclasses of the classes listed above will be treated the same as
the listed superclass. For example, a java.lang.Double will be
treated the same as a java.lang.Number and will
be converted to an XSLT Number.
 |
The return value
conversion can somtimes cause confusion. For example, if your
extension function returns an object that is a subclass of
NodeIterator, that object will be converted by Xalan-Java to an
XSLT Node-Set. If you later attempt to treat that object as a
non-XSLT object of your original class and try to invoke a method
on that object, your method invocation will fail. |
|
 |
 |
 |
 |
Extension function Java
calls |
 |
 |
 |
 |
|
|
The technique for instantiating Java objects and calling
Java methods depends on the format of the extension namespace that
was declared. See Declare the
namespace for the three different formats of namespace
declarations for Java extensions. For each namespace format, the
section below describes how to instantiate an object, how to invoke
an instance method, and how to invoke a static method. The sections
below explain, for each syntax, which methods are qualified
for method selection as described in the preceeding
section.
|
|
To create an instance of an object:
prefix:new
(args)
where prefix is the extension namespace prefix. A
new instance is to be created with the args constructor
arguments (if any). All constructor methods are qualified for
method selection.
Example: <xsl:variable
name="myType"
select="my-class:new()">
To invoke an instance method on a specified
object:
prefix:methodName
(object, args)
where prefix is the extension namespace prefix and
methodName is the name of the method to invoke on
object with the args arguments. object must be
an object of the class indicated by the namespace declaration.
Otherwise, the case shown immediately below will apply. Only
instance methods with the name methodName are qualified
methods. If a matching method is found, object will be used
to identify the object instance and args will be passed to
the invoked method.
Example: <xsl:variable
name="new-pop"
select="my-class:valueOf($myType,
string(@population))">
To invoke an instance method on a default
object:
prefix:methodName
(args)
where prefix is the extension namespace prefix and
methodName is the name of the method to invoke with the
args arguments. The first arg, if any, must not be an
object of the class indicated by the namespace declaration.
Otherwise, the case shown immediately above will apply. Only
instance methods with the name methodName are qualified
methods. If a matching method is found, a default instance of the
class will be created if it does not already exist.
Example: <xsl:variable
name="new-pop"
select="my-class:valueOf(string(@population))">
To invoke a static method:
prefix:methodName
(args)
where prefix is the extension namespace prefix and
methodName is the name of the method to invoke with the
args arguments. Only static methods with the name
methodName are qualified methods. If a matching method is
found, args will be passed to the invoked static
method.
Example: <xsl:variable
name="new-pop"
select="my-class:printit(string(@population))">
|
|
|
To create an instance of an object:
prefix:subpackage.class.new
(args)
where prefix is the extension namespace prefix,
subpackage is the rest of the package name (the beginning of
the package name was in the namespace declaration), and
class is the name of the class. A new instance is to be
created with the args constructor arguments (if any). All
constructor methods are qualified for method selection.
Example: <xsl:variable
name="myType"
select="my-package:extclass.new()">
To invoke an instance method on a specified
instance:
prefix:methodName
(object, args)
where prefix is the extension namespace prefix and
methodName is the name of the method to invoke on
object with the args arguments. Only instance methods
of the object with the name methodName are qualified
methods. If a matching method is found, object will be used
to identify the object instance and args will be passed to
the invoked method.
Example: <xsl:variable
name="new-pop"
select="my-package:valueOf($myType,
string(@population))">
To invoke a static method:
prefix:subpackage.class.
methodName (args)
where prefix is the extension namespace prefix,
subpackage is the rest of the package name (the beginning of
the package name was in the namespace declaration), class is
the name of the class, and methodName is the name of the
method to invoke with the args arguments. Only static
methods with the name methodName are qualified methods. If a
matching method is found, args will be passed to the invoked
static method.
Example: <xsl:variable
name="new-pop"
select="my-package:extclass.printit(string(@population))">
 |
Unlike the class
format namespace, there is no concept of a default object since the
namespace declaration does not identify a unique
class. |
|
|
|
To create an instance of an object:
prefix:FQCN.new
(args)
where prefix is the extension namespace prefix for
the Java namespace and FQCN is the fully qualified class
name of the class whose constructor is to be called. A new instance
is to be created with the args constructor arguments (if
any). All constructor methods are qualified for method
selection.
Example: <xsl:variable
name="myHash"
select="java:java.util.Hashtable.new()">
To invoke an instance method on a specified
instance:
prefix:methodName
(object, args)
where prefix is the extension namespace prefix and
methodName is the name of the method to invoke on
object with the args arguments. Only instance methods
of the object with the name methodName are qualified
methods. If a matching method is found, object will be used
to identify the object instance and args will be passed to
the invoked method.
Example: <xsl:variable
name="new-pop"
select="java:put($myHash,
string(@region), $newpop)">
To invoke a static method:
prefix:FQCN.methodName
(args)
where prefix is the extension namespace prefix,
FQCN is the fully qualified class name of the class whose
static method is to be called, and methodName is the name of
the method to invoke with the args arguments. Only static
methods with the name methodName are qualified methods. If a
matching method is found, args will be passed to the invoked
static method.
Example: <xsl:variable
name="new-pop"
select="java:java.lang.Integer.valueOf(string(@population))">
 |
Unlike the class
format namespace, there is no concept of a default object since the
namespace declaration does not identify a unique
class. |
|
|
|
|
Please keep in mind that all LocationPath
expressions return a node-set, even if the expression only returns
a single attribute or a text node (node-sets with one member). You
can use the XSLT string() function (as in the syntax examples
above) to convert a node-set value to string, and the number()
function to convert a node-set value to number (a
double).
If you want to pass a node-set to an extension function,
set up a Java method to accept an org.w3c.dom.NodeList (or an
org.apache.xpath.NodeSet, which implements NodeList, if you want to
modify the nodes).
Suppose, for example, you have a
myExtensions.ProcessNodes class with the following doSomething
method:
public static
boolean doSomething(org.w3c.dom.NodeList
nList)
Assuming you set up this extension in the node-ext
namespace, any of the following extension calls from a stylesheet
are syntactically possible:
<!--Process the
current node-->
<xsl:variable
name="success"
select="node-ext:MyExtensions.ProcessNodes.doSomething(.)"/>
<!--Process all
nodes in current context-->
<xsl:variable
name="success"
select="node-ext:MyExtensions.ProcessNodes.doSomething(*)"/>
<!-- Process
all nodes -->
<xsl:variable
name="success"
select="node-ext:MyExtensions.ProcessNodes.doSomething(/*)"/>
<!--Process the
foo/baz nodes in current context -->
<xsl:variable
name="success"
select="node-ext:MyExtensions.ProcessNodes.doSomething(foo/baz)"/>
<!--Process
the/foo/baz and /bar/saz nodes -->
<xsl:variable
name="success"
select="node-ext:MyExtensions.ProcessNodes.doSomething(/foo/baz |
/bar/saz)"/>
The NodeList is in fact a list of references into the XML
document, so keep in mind that getNextSibling(), for example, gets
you the next sibling in the document, which may not be the next
Node in the NodeList.
|
|
 |
 |
 |
 |
Alternative: using the abbreviated
syntax for extensions implemented in Java |
 |
 |
 |
 |
|
|
For
extension functions and extension elements implemented in Java,
Xalan-Java permits an abbreviated syntax. When you use the
abbreviated syntax, you do not use an xalan:component to designate
the functions.
The
abbreviated syntax supports the use of extension functions and
extension elements implemented in Java. You cannot use this syntax
with extensions implemented in JavaScript or another scripting
language.
|
|
Declare the namespace for your extensions using one of
the following three formats. The technique for invoking an
extension for each format is explained in Extension function Java
calls.
class format: xmlns:my-class="xalan://FQCN"
where FQCN is the fully qualified class
name.
Examples: xmlns:my-class="xalan://java.util.Hashtable"
xmlns:my-class="xalan://mypackage.myclass"
package format: xmlns:my-class="xalan://PJPN"
where PJPN is a partial java package name. That
is, it is the beginning of or the complete name of a java
package.
Examples: xmlns:my-package="xalan://java.util"
xmlns:my-package="xalan://mypackage"
Java format: xmlns:java="http://xml.apache.org/xalan/java"
 |
The old namespace
http://xml.apache.org/xslt/java is still supported for backward
compatibility. |
 |
Although the namespace
declarations for the class and package formats are shown with the
xalan:// prefix, the current implementation for those formats will
simply use the string to the right of the rightmost forward slash
as the Java class name. This format, however, is the preferred
format for extension namespace declarations. |
 |
The class: prefix
which was sometimes required in earlier versions of Xalan-Java is
no longer required. |
|
 |
 |
 |
 |
Example: Formatting a
date |
 |
 |
 |
 |
|
|
This example uses extension functions to call the
SimpleDateFormat class and the IntDate class. IntDate uses String
arguments to set up a Date object:
 |
 |
 |
 |
import java.util.Date;
import java.util.Calendar;
public class IntDate
{
public static Date getDate(String year, String month, String day)
{
// Date(int, int, int) has been deprecated, so use Calendar to
// set the year, month, and day.
Calendar c = Calendar.getInstance();
// Convert each argument to int.
c.set ( Integer.parseInt(year),
Integer.parseInt(month),
Integer.parseInt(day) );
return c.getTime();
}
}
|
 |
 |
 |
 |
The template transforms date elements with four
attributes. For example, it transforms <date format="EEEE, MMM dd, yyyy"
year="2000" month="4" day="27"/> into
<p>Date: Thursday, April 27, 2000.</p>.
As you review this stylesheet, please keep the following
in mind:
- The exclude-result-prefixes stylesheet attribute prevents
the java namespace declaration from appearing in the
output.
- The XSLT type returned by any LocationPath expression is
node-set, so the XSLT string function is used to convert the
format, year, month, and day attribute values from node-sets to
strings.
- The format attribute provides a String argument for
constructing a java.text.SimpleDateFormat object.
- The IntDate class uses String values provided by the
year, month, and day attributes, to set the date. XSLT can pass
number values, but these are converted into doubles.
- The formatter variable holds a SimpleDateFormat object,
and the date variable holds a Date object. XSLT does not understand
either of these types, but they are used to call the
SimpleDateFormat format method. In that call, $formatter is the
object, and $date is the argument. The syntax for calling Java
constructors and methods is described above in Extension function Java
calls.
 |
 |
 |
 |
<?xml version="1.0"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0"
xmlns:java="http://xml.apache.org/xalan/java"
exclude-result-prefixes="java">
<!--Other templates for transforming the rest of
the XML source documents-->
<xsl:template match="date">
<xsl:variable name="year" select="string(./@year)"/>
<xsl:variable name="month" select="string(./@month)"/>
<xsl:variable name="day" select="string(./@day)"/>
<xsl:variable name="format" select="string(./@format)"/>
<xsl:variable name="formatter"
select="java:java.text.SimpleDateFormat.new($format)"/>
<xsl:variable name="date"
select="java:IntDate.getDate($year, $month, $day)"/>
<p>Date: <xsl:value-of select="java:format($formatter, $date)"/></p>
</xsl:template>
</xsl:stylesheet>
|
 |
 |
 |
 |
|
|
 |
 |
 |
 |
Examples: using Java and JavaScript to
implement the same extension |
 |
 |
 |
 |
|
|
This
section contains two examples. The first example uses a Java
extension to transform a set of name elements into an alphabetical
and numbered list. The second example uses a JavaScript script to
do the same. Both examples include equivalent extension elements
and an extension function.
|
|
MyCounter.java
 |
 |
 |
 |
Import java.util.*;
public class MyCounter {
Hashtable counters = new Hashtable ();
public MyCounter ()
{}
public void init
( org.apache.xalan.extensions.XSLProcessorContext context,
org.apache.xalan.templates.ElemExtensionCall extElem )
{
String name = extElem.getAttribute("name");
String value = extElem.getAttribute("value");
int val;
try
{
val = Integer.parseInt (value);
}
catch (NumberFormatException e)
{
e.printStackTrace ();
val = 0;
}
counters.put (name, new Integer (val));
}
public int read(String name)
{
Integer cval = (Integer) counters.get (name);
return (cval == null) ? 0 : cval.intValue ();
}
public void incr
( org.apache.xalan.extensions.XSLProcessorContext context,
org.apache.xalan.templates.ElemExtensionCall extElem)
{
String name = extElem.getAttribute("name");
Integer cval = (Integer) counters.get(name);
int nval = (cval == null) ? 0 : (cval.intValue () + 1);
counters.put (name, new Integer (nval));
}
}
|
 |
 |
 |
 |
An XML source document:
 |
 |
 |
 |
<?xml version="1.0"?>
<doc>
<name first="David" last="Marston"/>
<name first="David" last="Bertoni"/>
<name first="Donald" last="Leslie"/>
<name first="Emily" last="Farmer"/>
<name first="Jack" last="Donohue"/>
<name first="Myriam" last="Midy"/>
<name first="Paul" last="Dick"/>
<name first="Robert" last="Weir"/>
<name first="Scott" last="Boag"/>
<name first="Shane" last="Curcuru"/>
</doc>
|
 |
 |
 |
 |
The stylesheet:
 |
 |
 |
 |
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:counter="MyCounter"
extension-element-prefixes="counter"
version="1.0">
<xalan:component prefix="counter"
elements="init incr" functions="read">
<xalan:script lang="javaclass" src="xalan://MyCounter"/>
</xalan:component>
<xsl:template match="/">
<HTML>
<H1>Names in alphabetical order</H1>
<counter:init name="index" value="1"/>
<xsl:for-each select="doc/name">
<xsl:sort select="@last"/>
<xsl:sort select="@first"/>
<p>
<xsl:text>[</xsl:text>
<xsl:value-of select="counter:read('index')"/>
<xsl:text>]. </xsl:text>
<xsl:value-of select="@last"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@first"/>
</p>
<counter:incr name="index"/>
</xsl:for-each>
</HTML>
</xsl:template>
</xsl:stylesheet>
|
 |
 |
 |
 |
Transformation output:
 |
 |
 |
 |
<HTML>
<H1>Names in alphabetical order</H1>
<p>[1]. Bertoni, David</p>
<p>[2]. Boag, Scott</p>
<p>[3]. Curcuru, Shane</p>
<p>[4]. Dick, Paul</p>
<p>[5]. Donohue, Jack</p>
<p>[6]. Farmer, Emily</p>
<p>[7]. Leslie, Donald</p>
<p>[8]. Marston, David</p>
<p>[9]. Midy, Myriam</p>
<p>[10]. Weir, Robert</p>
</HTML>
|
 |
 |
 |
 |
|
|
|
|