page 153
We still need the
Redirect
class, so that prefix is still with us. The other two prefixes are
jython-extension
, associated with the URL of the Jython home page though the value could be anything, and
lxslt
. Xalan uses this prefix to implement scripting languages. Our next step is to actually write the Jython code. With Xalan, this code goes inside an
lxslt:component
element:
lxslt:component prefix=jython-extension functions=cos sin toRadians lxslt:script lang=jpython
import math def cosd:
return math.cosd def sind:
return math.sind def toRadiansd:
return d 180 math.pi lxslt:script
lxslt:component
The
prefix
attribute associates this
lxslt:component
with the
jython-extension
prefix, and the
functions
attribute lists all of the functions supported by this script. The
lxslt:script lang=jpython
tells Xalan to use the Jython interpreter the current version of BSF requires us to use
lang= jpython
, the languages former name whenever these functions are invoked. Now that weve set everything up, all we have to do is invoke the extension
functions:
xsl:variable name=currentAngle select=jython-extension:toRadiansregionSales div
totalSales 360.0
Other than the
jython-extension
extension before the function call, the rest of our stylesheet is exactly the same. Notice that the Python
math
library does not define a
toRadians
function, so we had to define that function ourselves. The other two functions are part of the library, so
all we had to do was invoke them. One final point: when we invoke these extension functions written in other languages, the
Java
CLASSPATH
must be set up correctly. If the class libraries for Jython or Javascript or whatever scripting language youre using cant be found, the extension functions will fail. Our
example here uses jython.jar, available at http:www.jython.org
. We promised wed look at extensions in JavaScript, as well. Heres how the
lxslt:component
element looks when we write the extension functions in JavaScript:
lxslt:component prefix=javascript-extension functions=cos sin toRadians lxslt:script lang=javascript
function cosd {
return Math.cosd; }
function sind {
return Math.sind; }
function toRadiansd {
return d Math.PI 180; }
lxslt:script lxslt:component
page 154
Here is the
lxslt:component
element with the extension functions written in Jacl:
lxslt:component prefix=jacl-extension functions=cosine sine toRadians
lxslt:script lang=jacl proc cosine {d} {expr cosd}
proc sine {d} {expr sind} proc toRadians {d} {expr d 3.1415926535897932384626433832795 180.0}
lxslt:script lxslt:component
Again, most of our task is to use existing features of the language. In the JavaScript and Jacl code, the
cos
and
sin
functions are part of the language, and we wrote our own versions of the
toRadians
function. Jacl doesnt define a constant for
pi
, so we hardcoded the first 32 digits into the Jacl version of
toRadians
.
8.1.3 Fallback Processing
If the code that implements a given extension element cant be found, we need some relatively graceful way for the stylesheet to handle the situation. XSLT defines the
xsl:fallback
element to handle this case. In an earlier stylesheet, we used the
element- available
function to determine whether a given function is available. In this case, well use the
xsl:fallback
to transform our document if the
Redirect
extension cant be found:
?xml version=1.0? xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform
xmlns:redirect=org.apache.xalan.xslt.extensions.Redirect extension-element-prefixes=redirect
xsl:output method=html xsl:template match=
xsl:for-each select=bookchapter redirect:write
select=concatchapter, position, .html html
head titlexsl:value-of select=titletitle
head body
h1xsl:value-of select=titleh1 xsl:apply-templates select=para
xsl:if test=notposition=1 pa href=chapter{position-1}.htmlPreviousap
xsl:if xsl:if test=notposition=last
p a
xsl:attribute name=href xsl:value-of
select=concatchapter, position+1, .html xsl:attribute
Next a
p xsl:if
body html
xsl:fallback xsl:if test=position=1
html head
titlexsl:value-of select=booktitletitle head
body
page 155 xsl:for-each select=bookchapter
h1xsl:value-of select=titleh1 xsl:apply-templates select=para
xsl:for-each body
html xsl:if
xsl:fallback redirect:write
xsl:for-each xsl:template
xsl:template match=para pxsl:apply-templates select=|textp
xsl:template xsl:stylesheet
In our example, we only invoke the fallback processing once. This approach assumes that if somethings wrong with the extension, it will fail the first time and be completely
inaccessible. Using
xsl:fallback
, we know that the contents of the
xsl:fallback
element will be invoked if anything goes wrong when the stylesheet processor attempts to use an extension element. If youd like more complete control over fallback processing, you can
use the
element-available
and
function-available
functions as we did in our earlier example.
8.2 Extending the Saxon Processor
Michael Kays excellent Saxon processor also provides an extension mechanism. One of the nice features of Saxons extensibility mechanism is that you can implement your own sort
functions. When we discussed the
xsl:sort
element a couple of chapters ago, we mentioned that it has a
lang
attribute that defines the language of the things being sorted. While Xalan doesnt currently support this attribute although by the time youre reading this,
it might, Saxon lets you create your own extension function to handle the sorting. Your extension function must extend the
com.icl.saxon.sort.TextComparer
class. Heres a sample XML document well use to illustrate this function:
?xml version=1.0? wordlist
wordcampoword wordlunaword
wordciudadword wordllavesword
wordchihuahuaword wordarrozword
wordlimonadaword wordlist
This document contains Spanish words that are sorted differently than they would be in English. In Spanish, ch and ll are separate letters that sort after c and l, respectively.
Well write a stylesheet that uses three
xsl:template
s to illustrate how our extension function works. Heres the stylesheet:
?xml version=1.0? xsl:stylesheet version=1.0 xmlns:xsl=http:www.w3.org1999XSLTransform
xsl:output method=text indent=no xsl:strip-space elements=
xsl:variable name=newline xsl:text
xsl:text xsl:variable
xsl:template match= xsl:value-of select=newline
xsl:apply-templates select=wordlist mode=unsorted