You are viewing a plain text version of this content. The canonical link for it is here.
Posted to general@xml.apache.org by dl...@locus.apache.org on 2000/01/18 23:12:52 UTC
cvs commit: xml-site/sources/xalan extensions.xml
dleslie 00/01/18 14:12:52
Added: sources/xalan extensions.xml
Log:
Added document on support for XSLT extensions.
Revision Changes Path
1.1 xml-site/sources/xalan/extensions.xml
Index: extensions.xml
===================================================================
<?xml version="1.0" standalone="no"?>
<!--
* Copyright (c) 1999 Lotus Development Corporation, Inc. All Rights Reserved.
* This software is provided without a warranty of any kind.
*
-->
<!DOCTYPE s1 SYSTEM "sbk:/style/dtd/document.dtd">
<s1 title="&xslt4j; Extensions">
<s2 title="Introduction">
<p>For those situations where you would like to augment the functionality of XSLT with calls to a procedural language, &xslt4j; supports the creation and use of extension elements and extension functions.</p>
<p>You can set up &xslt4j; components to define extension elements and extension functions that call Java classes and Javascript scripts. In your XSL stylesheets, you can then use extension components to set script properties, for example, and extension functions to return values. &xslt4j; also provides a predefined "java" extension namespace that you can use to interact directly with Java objects.</p>
<p>Furthermore, &xslt4j; ships with a propriatary extension that you can use to redirect the output of an XSL transformation to multiple files. This extension is implemented in the org.apache.xalan.xslt.extensions.Redirect class. Suggestions for extensions to add in future releases are more than welcome.</p>
</s2>
<s2 title="Supported languages">
<p>&xslt4j; uses he Bean Scripting Framework (BSF), an architecture for incorporating scripting into Java
applications and applets. Using BSF allows an application to take advantage of scripting while being
independent of any specific scripting language. </p>
<p>To use BSF, you must put two JAR files on the classpath: bsf.jar and bsfengine.jar. For JavaScript support, BSF has been tested with Mozilla Rhino 1.4 R3 js.jar, available from http://www.mozilla.org/rhino. We do not ship js.jar.</p>
<table>
<tr>
<td><em>Language</em></td>
<td><em>Version</em></td>
<td><em>Dependencies</em></td>
</tr>
<tr>
<td><p>Mozilla Rhino</p></td>
<td><p>1.4 R3</p></td>
<td><p>js.jar available from
http://www.mozilla.org/rhino</p></td>
</tr>
<tr>
<td><p>NetRexx</p></td>
<td><p>1.148 up </p></td>
<td><p>NetRexxC.zip available from http://www2.hursley.ibm.com/netrexx</p></td>
</tr>
<tr>
<td><p>BML</p></td>
<td><p>2.4</p></td>
<td><p>bmlall.jar available from http://www.alphaWorks.ibm.com/formula/bml</p></td>
</tr>
<tr>
<td><p>JPython</p></td>
<td><p>1.1-beta3</p></td>
<td><p>python.jar available from http://www.jpython.org/</p></td>
</tr>
<tr>
<td><p>Jacl</p></td>
<td><p>1.1.1</p></td>
<td><p> jacl.jar and tcljava.jar from http://www.scriptics.com/java</p></td>
</tr>
<tr>
<td><p>Win32 ActiveScript langs
JScript, VBScript</p></td>
<td><p></p></td>
<td><p>MSVCP60.DLL from Microsoft, appropriate language DLLs from Microsoft
http://msdn.microsoft.com/scripting</p></td>
</tr>
<tr>
<td><p>PerlScript</p></td>
<td><p></p></td>
<td><p>ActivePerl from http://www.activestate.com/</p></td>
</tr>
</table>
</s2>
<s2 title="&xslt4j; components">
<p>To set up extensions, do the following in an XSL stylesheet:</p>
<ol>
<li>Declare the &xslt4j; xslt namespace</li>
<li>Declare a namespace for at least one extension prefix</li>
<li>Use the extension-element-prefixes attribute on the xsl:stylesheet element, a literal result
element, or another extension element to declare the extension namespace as an extension</li>
</ol>
<p>Then you can utilize the extension elements and associated extension functions in your stylesheet
templates.</p>
<p>To implement the extension, define a &xslt4j; component in the xslt namespace. A &xslt4j; component may be embedded within the stylesheet or maintained separately. The component contains the code that is called when the extension elements and functions are used.</p>
<s3 title="&xslt4j; Component Syntax">
<source><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:lxslt="http://xml.apache.org.xslt"
xmlns:extn1="URI of extension namespace1"
extension-element-prefixes="extn1 extn2">
<xsl:template match="...">
...
<extn1:element1 att1="value1" att2="value2" ...>
<whatever-the-contents/>
<including-xsl-elements/>
</extn1>
...
<xsl:value-of select="extn1:function1(arg1,arg2,...,argn)"/>
...
</xsl:template>
<lxslt:component prefix="extn1" elements="element1" functions="function1">
<lxslt:script lang="javascript">
function element1 (xslProcContext, exensionElement) {
code to do element1, including possibly evaluating the
contents of the element by calling back to the processor
}
function function1 (arg1, arg2, ..., argn) {
code to do function1
}
</lxslt:script>
</lxslt:component>
</xsl:stylesheet></source>
</s3>
<s3 title="DTD for &xslt4j; Components">
<source><!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></source>
</s3>
<p>For extension namespaces implemented using Java, a special
convenience is supported which allows the programmer to skip the
component specification. This is described later in the semantics
of how a component description is located and loaded.</p>
</s2>
<s2 title="Locating &xslt4j; Components">
<p>When &xslt4j; encounters the extension-element-prefixes attribute on a stylesheet, it uses the
following algorithm to find and load the components to handle those extension namespaces:</p>
<ol>
<li> For each extension namespace, search for an <<em>lxslt:component</em>>
element with a <em>prefix</em> attribute whose
value matches the extension namespace prefix. If none is found, proceed to
step 2.</li>
<li>Treat the URI of the namespace as a fully
qualified class name and attempt to
load that class. If the URI starts with
"class:", strip that prefix
before attempting to load the class. If the class
loads, assume the following component description:
<source> <lxslt:component prefix="prefix-being-searched-for">
<lxslt:script lang="javaclass" src="namespaceURI"/>
</lxslt:component></source>
<p>Assume, that is, a component implementation in Java
with a source URL value of the namespace URI
as the fully qualified class name. No elements and functions are defined -
which means that if one were to test this
namespace (via extension-element-available or
extension-function-available) then the tests
would fail. However, if a function or element is
invoked on this extension namespace the call
succeeds or fails depending on whether or not the
requisite functions (as defined below) were
defined by the class.</p>
</li>
<li>Treat the URI of the namespace as a URL and
attempt to load the component description
from this URL. If found and if the
lxslt:component has a "namespace-uri"
attribute present, it <em>must</em> match the
namespace URI of the extension specified in the
stylesheet. If no "namespace-uri"
attribute is present, the component is assumed to be the
component specification for this namespace. [NOT
FULLY IMPLEMENTED YET - NAMESPACE USAGE ISN'T
CORRECT.]<br/>
</li>
</ol>
<p>The component specification identifies the valid local
parts of the extension namespace. The element and
function local parts are identified separately using the <em>elements</em>
and <em>functions</em> attributes, respectively. The
component specification presents the code that implements
these local parts using a <<em>lxslt:script</em>>
element. The script element can inline the code or refer
to an external source for the code using the <em>src</em>
attribute.</p>
<p>The currently supported languages are:
"javaclass" and "javascript", where
"javaclass" is used to work with an
implementation in Java and "javascript" refers
to Netscape Rhino.</p>
<p>Special case for lang="javaclass": The <em>src</em>
attribute is treated as the fully qualified classname of
the class which handles the extension namespace. If the
name starts with "<em>class:</em>",
then the class is not instantiated and the methods
invoked to handle various local parts (see below) are
assumed to be static. Otherwise, a new instance is
created and used for invoking the appropriate methods as
defined below.</p>
<p>The <em>src</em> attribute is not yet implemented for
languages other than "javaclass".</p>
<p>For each extension element within the namespace, a
function with the following signature must be defined in
the implementation of the component:</p>
<source>Object <localPart> (org.apache.xalan.xslt.XSLProcessorContext,
org.w3c.dom.Element extensionElement)</source>
<p>where <localPart> is the local part of the
extension element name. If the component is implemented
in a loosely typed scripting language such as JavaScript,
then these are untyped arguments to the function and the
return type is unspecified as well. Note that for
lang=="javaclass", methods may be static or
not-static and must be public in order to be accessible.</p>
<p>When an extension element is invoked, the appropriate
function (as identified above) is invoked to handle the
invocation. The entire extension element is given as the
second argument to the function with content, if any,
as-is. Thus, if within an extension element one were to
place elements from the xsl: namespace, then those would
have to be processed by an explicit evaluation request from
the extension element handler. Such invocations are done
via the XSLProcessorContext object. [CALLING BACK INTO
XSLProcessor.java NOT IMPLEMENTED YET.]</p>
<p>For each extension function within the namespace, a
function with the following signature must be defined in
the implementation of the component:</p>
<source> Object <localPart> (Type1 arg1, Type2 arg2, ..., Typen argn)</source>
<p>where <localPart> is the local part of the
extension function name. The type of each argument
depends on what was given within XSLT in the invocation
of the function. Here is the mapping from an XSLT data
type to the Java data types that are used in invoking the
methods:</p>
<table>
<tr>
<th>XSLT Type</th>
<th>Java Type</th>
</tr>
<tr>
<td>Node-Set</td>
<td>org.w3c.dom.NodeList</td>
</tr>
<tr>
<td>String</td>
<td>java.lang.String</td>
</tr>
<tr>
<td>Boolean</td>
<td>boolean or Boolean</td>
</tr>
<tr>
<td>Number</td>
<td>double or Double</td>
</tr>
<tr>
<td>Result Tree Fragment</td>
<td>org.w3c.dom.DocumentFragment</td>
</tr>
<tr>
<td>variable</td>
<td>one of above based on <br/>
type of variable's value</td>
</tr>
</table>
<p>For boolean and number XSLT types,
first all such types are converted to the corresponding
primitive type in Java and the method searched for. If
the method is not found, then a method with the object
types is searched for. For example foo:bar('a'='b', 1,
'Hello', 2) would first look for a method
bar(boolean,double,String,boolean) and then for
bar(Boolean,Double,String,Double).</p>
<p>If the component is implemented in a loosely typed
scripting language such as JavaScript, then these are
untyped arguments to the function and the return type is
unspecified as well.</p>
<p>When an extension function is invoked, the appropriate
function (as identified above) is invoked to handle the
invocation. Any arguments to the function are first
evaluated and then the resulting values are passed to the
function by first converting them according to the table
listed above.</p>
<p>To control how a stylesheet behaves if an extension is not available, create an xsl:fallback element with
xsl:choose or xsl:if instructions and the element-available and extension-available functions.</p>
</s2>
<s2 title="Special Handling for Extensions into Java">
<p>When a qualified function name is invoked, if the
namespace is not already registered as an extension namespace (by
the above mechanism), &xslt4j; treats the URI of the namespace as a fully qualified class
name and attempts to load that class. If the URI starts
with "class:", &xslt4j; strips that prefix before
attempting to load the class. If class loading succeeds, then an
extension namespace implemented in "javaclass" is
assumed with the URI as the value of the src attribute. Note that
no functions are defined - which means that if one were to test
this namespace (via extension-function-available) then the tests
would fail. However, if a function is invoked on this extension
namespace, the call succeeds or fails depending on whether or not the
requisite functions (as defined above) were defined by the class.</p>
</s2>
<s2 title="Predefined Extension Namespace for Java Access">
<p>&xslt4j; supports a special namespace for convenient
interaction with Java objects. The namespace URI is "http://xsl.lotus.com/java".
This namespace allows the stylesheet author to create new
instances of Java classes and to invoke static and non-static
methods on them. Assuming, the namespace declaration "<em>xmlns:java='http://xsl.lotus.com/java'</em>"
is in scope, the following are supported:</p>
<p>java:<ref>FQCN</ref>.<em>new</em> (<ref>args</ref>)</p>
<p>where FQCN is the fully qualified class name of which a new
instance is to be created with the given constructor arguments
(if any).</p>
<p>java:<ref>FQCN.methodName</ref> (<ref>args</ref>)</p>
<p>where <ref>FQCN</ref> is the fully qualified class name whose
static method "<ref>methodName</ref>" is to be invoked
using the given arguments. </p>
<p>java:<ref>methodName</ref> (<ref>object</ref>, <ref>args</ref>)</p>
<p>where <ref>methodName</ref> is the name of the (static or
non-static) method to invoke on <ref>object</ref> with the given
arguments.</p>
</s2>
<s2 title="Passing Nodes to java">
<p>If you want to pass one or more Nodes 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).</p>
<p>When you call an extension function with an argument that constitutes a pattern match for one or more Nodes, the &xslt4j; extension mechanism evaluates this argument as a NodeList, even if only one Node is involved.</p>
<p>Suppose, for example, you have a myExtensions.ProcessNodes class with the following doSomething method:</p>
<p><code>doSomething(org.w3c.dom.NodeList nList)</code></p>
<p>Any of the following extension calls in a stylesheet are syntatically possible:</p>
<p><code>java:MyExtensions.ProcessNodes.doSomething(.) <!-- current node --></code></p>
<p><code>java:MyExtensions.ProcessNodes.doSomething(*) <!-- all nodes in current context--></code></p>
<p><code>java:MyExtensions.ProcessNodes.doSomething(/*) <!-- all nodes --></code></p>
<p><code>java:MyExtensions.ProcessNodes.doSomething(foo/baz) <!-- foo/baz nodes in current context --></code></p>
<p><code>java:MyExtensions.ProcessNodes.doSomething(/foo/baz | /bar/saz) <!-- /foo/baz and /bar/saz nodes --></code></p>
<p>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, wich may not be the next Node in the NodeList.</p>
</s2>
<s2 title="Example 1: Named Counter Component in Java">
<p>MyCounter.java:</p>
<source> import java.util.*;
public class MyCounter {
Hashtable counters = new Hashtable ();
public MyCounter () {
}
// the context arg is actually a org.apache.xalan.xslt.XSLProcessorContext
public void init (Object context, org.w3c.dom.Element elem) {
String name = elem.getAttribute ("name");
String value = elem.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(Object context, org.w3c.dom.Element elem) {
String name = elem.getAttribute ("name");
Integer cval = (Integer) counters.get (name);
int nval = (cval == null) ? 0 : (cval.intValue () + 1);
counters.put (name, new Integer (nval));
}
}</source>
<p>MyFamily.xml:</p>
<source><?xml version="1.0"?>
<doc>
<name first="Sanjiva" last="Weerawarana"/>
<name first="Shahani" last="Weerawarana"/>
<name first="Rukmal" last="Weerawarana"/>
<name first="Sashi" last="Weerawarana"/>
<name first="Kamal" last="Fernando"/>
<name first="Ruby" last="Fernando"/>
</doc></source>
<p>Counter.xsl:</p>
<source><?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:lxslt="http://xsl.lotus.com/"
xmlns:counter="MyCounter"
extension-element-prefixes="counter">
<!-- note that the component definition is optional because I
used the fully qualified class name of the implementation
as the URI for this namespace. -->
<lxslt:component prefix="counter"
elements="init incr" functions="read">
<lxslt:script lang="javaclass" src="MyCounter"/>
</lxslt:component>
<xsl:template match="/">
<HTML>
<H1>Test for namespace specified Java extension.</H1>
<counter:init name="index" value="1"/>
<p>Here are the names in alphabetical order by last name:</p>
<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></source>
<p>Output:</p>
<source><HTML>
<H1>Test for namespace specified Java extension.</H1>
<p>Here are the names in alphabetical order by last name, first name:</p>
<p>[1]. Fernando, Kamal</p>
<p>[2]. Fernando, Ruby</p>
<p>[3]. Weerawarana, Rukmal</p>
<p>[4]. Weerawarana, Sanjiva</p>
<p>[5]. Weerawarana, Sashi</p>
<p>[6]. Weerawarana, Shahani</p>
</HTML></source>
</s2>
<s2 title="Example 2: Named Counter Component Implemented Using Java
XPath Extension Namespace">
<p>Counter.xsl:</p>
<source><?xml version="1.0"?>
<!-- named counter functionality done using java: -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:java="http://xsl.lotus.com/java">
<xsl:template match="/">
<HTML>
<H1>Test for namespace specified Java extension.</H1>
<xsl:variable name="counter-table"
select="java:java.util.Hashtable.new ()"/>
<!-- do following for side-effect -->
<xsl:if test="java:put ($counter-table, 'index', 1)"/>
<p>Here are the names in alphabetical order by last name, first name:</p>
<xsl:for-each select="doc/name">
<xsl:sort select="@last"/>
<xsl:sort select="@first"/>
<p>
<xsl:text>[</xsl:text>
<xsl:value-of select="java:get ($counter-table, 'index')"/>
<xsl:text>]. </xsl:text>
<xsl:value-of select="@last"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="@first"/>
</p>
<!-- do following for side-effect -->
<xsl:if test="java:put ($counter-table, 'index',
java:get($counter-table,'index')+1)"/>
</xsl:for-each>
</HTML>
</xsl:template>
</xsl:stylesheet></source>
<p>Produces the same results.</p>
</s2>
<s2 title="Example 3: Redirecting transformation output to multiple files">
<p>For information on using the Redirect extension to send output to mulitple files, see <resource-ref idref="RedirectDoc"/>.</p>
</s2>
</s1>