http://xml.apache.org/http://www.apache.org/http://www.w3.org/

Overview

Downloads
Getting Started

FAQs

Sample Apps
Command Line
Usage Patterns

API (Javadoc)

Xalan DTM
Extensions

Release Notes

Bug reporting

Introduction
 

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. An extension (a collection of elements and functions) inhabits a namespace, either a namespace you declare and designate as an extensions namespace, or the predefined java namespace 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 XSL 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 functionsYou 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.


Supported languages
 

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 Java and JavaScript. Other languages with BSF support appear in the table below.

BSF requires two JAR files on the class path: bsf.jar and bsfengines.jar. These two JAR files are shipped with Xalan-Java, and that is all that is required for Java extensions. The additional JAR files or DLLs required to support extensions 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

 
1.4 R3

 
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://www.alphaWorks.ibm.com/formula/bml

 
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/

 

The basic pattern
 

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 <timelapse multiplier="2"/> (in the stylesheet), the extension computes a deadline 6 days from now, and the stylesheet template transform the deadline element into a string along the lines of <p>We have received your enquiry and will respond by April 29, 2000 12:07:16 PM EST.</p>

NoteThe 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 both extension elements and extension functions.

As you review this stylesheet, please note the following:

  1. The declaration of the Xalan lxslt namespace, which provides support for the component and component/script elements:

    xmlns:lxslt="http://xml.apache.org/xslt"

  2. The declaration of a namespace for this extension:

    xmlns:my-ext="ext1"

  3. The designation of this namespace prefix as an extension prefix:

    extension-element-prefixes="my-ext"

  4. The lxslt:component with attributes designating the namespace prefix and the elements and functions this extension provides.

  5. The lxslt:script subelement with a JavaScript implementation of the extension. For Java extensions, the lxslt: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:lxslt="http://xml.apache.org/xslt"
    xmlns:my-ext="ext1"
    extension-element-prefixes="my-ext">
    
  <!--The component and its script are in the lxslt namespace and define the 
    implementation of the extension.-->
  <lxslt:component prefix="my-ext" elements="timelapse" functions="getdate">
    <lxslt: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();
      }
    </lxslt:script>
  </lxslt: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>

Setting up the runtime environment
 

To run the preceding example, bsf.jar, bsfengines.jar, and js.jar must be on the class path. Remember that bsf.jar and bsfengines.jar must be on the class path to run any extension. For extensions implemented in a scripting language, see the additional requirements in Supported languages.


Syntax
 

You can always use the pattern illustrated above to set up and use extension elements and extension functions. For extension functions implemented in Java, you can also use the java namespace, described in Alternative: using the predefined java extension namespace. Unless you are using the predefined java extension namespace, do the following:

1. Declare the lxslt namespace
 


xmlns:lxslt="http://xml.apache.org/xslt"

The lxslt namespace provides support for the lxslt:component element and lxslt:script subelement.

NoteYou may also use the LotusXSL alias for this namespace: "http://xsl.lotus.com/".

2. Declare a unique namespace for each extension prefix
 


xmlns:prefix=URI

The prefix identifies the namespace, and URI is one of the following:

  • An arbitrary (but unique) string that matches the prefix attribute of an lxslt:component element in the stylesheet.
    Example: xmlns:ext1="xyz"

  • [class:]FQCN
    where FQCN is a Java fully qualified class name. If the extension only involves static class method calls (no instance constructors or instance method calls) precede the class name with class:.
    Example: xmlns:ext2="java.util.Hashtable"

  • The file name or URL for another document that contains the lxslt:component element.
    Example: xmlns:ext3="my-component.txt"
NoteXalan-Java identifies the URI by working through the list above. In other words, if the URI does not match an lxslt:component element prefix in the stylesheet, Xalan-Java attempts to map the URI to a fully qualified class name on the class path, and so on.

If the stylesheet contains an lxslt:component element with a prefix attribute set to the extension prefix, the only function of the URI is to provide a unique namespace. If the stylesheet does not contain an lxslt:component, the URI must identify a Java class or a document containing the lxslt:component.


3. Designate the extension prefixes
 

In the stylesheet element:

extension-element-prefixes="prefix-1 prefix-2 ..."

In a literal result element or extension element include the xsl prefix:

xsl:extension-element-prefixes="prefix1 prefix2 ..."

Keep in mind that where you declare namespaces and designate extension prefixes determines the scope of those namespaces.To make your extensions available throughout the stylesheet, include these settings and attribute in the stylesheet element.

By default, namespace declarations are included in the transformation output. To exclude namespaces from the output, use

exclude-result-prefixes="prefix-1 prefix-2 ..."

in the stylesheet element or

xsl:exclude-result-prefixes="prefix-1 prefix-2 ..."

in a literal result element or extension element.


4. Set up an lxslt:component
 

In the scope of the xslt namespace declaration:

<lxslt:component prefix="prefix"
    functions="func-1 func-2 ...func-n"
    elements="elem-1 elem-2 ...elem-n">
  <!--See lxslt:script below-->
</lxslt:component>

where func-1 func-2 ... func-n and elem-1 elem-2 ... elem-n designate the functions and elements the extension provides and the stylesheet uses. You can use the function-available and element-available functions to determine at run time whether a function or element designated in the lxslt:component is actually available.

NoteIf your extension namespace is a fully qualified class name, you do not need to include the lxslt:component. If you do not include it, you cannot use the function-available and element-available functions to determine whether a given element or function is actually available at runtime.

5. Set up the lxslt:script element
 

In each lxslt:component, you must include an lxslt:script element. If the extension is implemented in Java:

<lxslt:script lang="javaclass" src="[class:]FQCN"/>

where FQCN is the fully qualified class name. If the extension only involves static class method calls (no instance constructors or instance method calls) precede the class name with class:.
Example: <lxslt:script lang="javaclass"
          src="java.util.Hashtable"/>

If the extension is implemented in JavaScript:

<lxslt:script lang="javascript" >
  <!--The implementation script-->
</lxslt:script>

For other scripting languages supported by BSF, use the same approach as for JavaScript. Xalan-Java plans to add support for using the src attribute to identify another document that contains the implementation script; this feature is not yet supported.


Implicit DTD for lxslt:component
 
<!ELEMENT lxslt:component (lxslt:script)>
<!ATTLIST lxslt:component
  prefix CDATA #IMPLIED
  namespace-uri CDATA #IMPLIED
  elements NMTOKENS #REQUIRED
  functions NMTOKENS #REQUIRED>

<!ELEMENT lxslt:script EMPTY)>
<!ATTLIST lxslt:script
  lang CDATA #REQUIRED
  src CDATA #IMPLIED>


Using an extension element
 

Extension elements pass the extension two objects:

  • org.apache.xalan.xslt.XSLProcessorContext, which provides access to the XSL processor, the XML source tree, the stylesheet tree, the current context node, and the current mode (if any).

  • org.w3c.dom.Element, which provides the API for interacting with the extension element.

You can use the Element getAttribute(String name) method, for example, to read element attributes.

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.xslt.XSLProcessorContext,
             org.w3c.dom.Element 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).

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.xslt.XSLProcessorContext,
          org.w3c.dom.Element extensionElement)

JavaScript example: function myElement(xslProcContext, element)


The Redirect extension
 

The Redirect extension (org.apache.xalan.xslt.extensions.Redirect) is shipped with Xalan-Java (more extensions are on the way!).

A standard XSL transformation involves three parameters: the XML source tree, an XSL stylesheet, and the transformation result tree. Whether the result tree is output to a file, a character stream, a byte stream, a DOM, or a SAX document handler, the initial transformation sends the entire result to a single target, represented by the XSLTResultTarget class.

The Redirect extension supplies three extension elements that you can use to redirect portions of your transformation output to multiple files: <open>, <write>, and <close>. If you use the <write> element alone, the extension opens a file, writes to it, and closes the file immediately. If you want explicit control over the opening and closing of files, use <write> in conjunction with the <open> and <close> elements.

Each of these elements includes a file attribute and/or a select attribute to designate the output file. The file attribute takes a string, so you can use it to directly specify the output file name; The select attribute takes an XPath expression, so you can use it to dynamically generate the output file name. If you include both attributes, the Redirect extension first evaluates the select attribute, and falls back to the file attribute if the select attribute expression does not return a valid file name.


Example with the Redirect extension
 

Suppose you are outputting the bulk of your result tree to one file, but you want to output the transformation of all <foo> elements and their children to another file. The following example illustrates the basic structure of the XML source:

<?xml version="1.0"?> 
<doc>
  <foo file="foo.out">
    Testing Redirect extension:
      <bar>A foo subelement text node</bar>
  </foo>
  <main>
    Everything else
  </main>  
</doc>

This stylesheet redirects part of the output to a secondary file:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0"
    xmlns:lxslt="http://xml.apache.org/xslt"
    xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
    extension-element-prefixes="redirect">

  <xsl:template match="/">
    <standard-out>
      Standard output:
      <xsl:apply-templates/>
    </standard-out>
  </xsl:template>
  
  <xsl:template match="main">
    <main>
      <xsl:apply-templates/>
    </main>
  </xsl:template>
  
  <xsl:template match="/doc/foo">
    <redirect:write select="@file">
      <foo-out>
        <xsl:apply-templates/>
      </foo-out>
    </redirect:write>
  </xsl:template>
  
  <xsl:template match="bar">
    <foobar-out>
      <xsl:apply-templates/>
    </foobar-out>
  </xsl:template>
  
</xsl:stylesheet>

The standard output is:

<?xml version="1.0" encoding="UTF-8"?>
<standard-out>
  Standard output:
  <main>
    Everything else.
  </main>
<standard-out>

The output redirected to foo.out is:

<?xml version="1.0" encoding="UTF-8"?>
<foo-out>
    Testing Redirect extension:
    <foobar-out>foo subelement text node</foobar-out>
  </foo-out>

For more information on using the Redirect extension to send output to multiple files, see Redirect class.



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
 

The XSLT data types map to Java data types as follows:

XSLT Type  Java Type 
Node-Set  org.w3c.dom.NodeList 
String  java.lang.String 
Boolean  boolean or Boolean 
Number  double or Double 
Result Tree Fragment  org.w3c.dom.DocumentFragment 

For the XSLT boolean and number types, Xalan-Java first looks for a method with the corresponding Java primitive type. If it does not find such a method, it looks for a method with the object type. In the case of the foo:bar(10) static method call, for example, Xalan-Java first looks for bar(double). If it does not find such a method, it looks for for bar(java.lang.Double).

When an extension function is invoked, the arguments are converted as indicated above, and sent to the extension. No conversion takes place for arguments of non-XSLT types.


Extension function Java calls
 

Use the following syntax to instantiate Java objects and to call methods:

prefix:FQCN.new (args)

where prefix is the extension namespace prefix and FQCN is the fully qualified class name of which a new instance is to be created with the args constructor arguments (if any).
Example: variable myHash
       select"my-ext:java.util.Hashtable.new()"

prefix:FQCN.methodName (args)

where FQCN is the fully qualified class name whose static method methodName is to be invoked using the args arguments.
Example: variable new-pop
        select="my-ext:java.lang.Integer.valueOf(string(@population))"

prefix:methodName (object, args)

where methodName is the name of the method to invoke on object with the args arguments.
Example: variable old-pop
       select="my-ext:put($myHash, string(@region), $new-pop)"


Passing Nodes to java
 

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.xalan.xpath.MutableNodeList, which extends 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.


Implementing extension functions
 

For each extension function in a namespace, the implementation must include a Java method with the following signature, or the scripting language equivalent:

public object function(args)

where object is the return type, function is the local part of the extension function name (the function name without the namespace prefix), and args correspond to the arguments in the function call.



Alternative: using the predefined java extension namespace
 

For extension functions implemented in Java, Xalan provides a java namespace. When you declare and use the java namespace, you do not use an lxslt:component to designate the functions.

The java namespace supports the use of extension functions implemented in Java. You cannot use this syntax with extension elements or with extensions implemented in JavaScript or another scripting language.

Declare the Xalan java namespace
 

xmlns:lxslt="http://xml.apache.org/xslt/java"

NoteYou may also use the LotusXSL alias for this namespace: "http://xsl.lotus.com/java".

Use the java namespace when you make extension function calls
 

Use "java" as the prefix with the syntax described in Extension function Java calls.

That is all. You do not need to set an extension-element-prefixes attribute, and you do not include an lxslt:component element. Given the absence of the lxslt:component element, you cannot use the function-available method to determine at runtime whether a Java method call is actually available.

Using the java namespace clearly involves less setup than using your own namespace, as long as these restrictions are not a problem. Remember that you always have the option of setting up your own namespace and extra overhead is really minimal.


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/xslt/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.

Java implementation
 

MyCounter.java

Import java.util.*;

public class MyCounter {
  Hashtable counters = new Hashtable ();

  public MyCounter () 
  {}

  public void init(org.apache.xalan.xslt.XSLProcessorContext context, 
                   org.apache.xalan.xslt.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.xslt.XSLProcessorContext context, 
                   org.apache.xalan.xslt.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:lxslt="http://xml.apache.org/xslt"
                xmlns:counter="MyCounter"
                extension-element-prefixes="counter"
                version="1.0">

  <lxslt:component prefix="counter"
                   elements="init incr" functions="read">
    <lxslt:script lang="javaclass" src="MyCounter"/>
  </lxslt: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>

JavaScript implementation
 

<?xml version="1.0"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:lxslt="http://xml.apache.org/xslt"
                xmlns:counter="MyCounter"
                extension-element-prefixes="counter"
                version="1.0">

  <lxslt:component prefix="counter"
                   elements="init incr" functions="read">
    <lxslt:script lang="javascript">
      var counters = new Array();

      function init (xslproc, elem) {
        name = elem.getAttribute ("name");
        value = parseInt(elem.getAttribute ("value"));
        counters[name] = value;
        return null;
      }

      function read (name) {
        // Return a string.
        return "" + (counters[name]);
      }

      function incr (xslproc, elem)
      {
        name = elem.getAttribute ("name");
        counters[name]++;
        return null;
      }
    </lxslt:script>
  </lxslt:component>

  <xsl:template match="/">
    <HTML>
      <H1>Names in alphatebical 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>

This stylesheet produces the same output as the preceding example with the Java extension.




Copyright © 2000 The Apache Software Foundation. All Rights Reserved.