Die Spannung steigt. Bald dürfte es soweit sein, und das W3C verabschiedet den derzeitigen Entwurf von XSLT 2.0 [1] als so genannte Recommendation (zu deutsch Empfehlung). Damit erhielte die eXtensible Stylesheet Language for Transformations auch in ihrer zweiten Version den Status eines Standards des W3C. Das wäre wiederum der Startschuss für eine Vielzahl von Firmen und Open-Source-Gemeinschaften, ihre XSLT-Engines auf den aktuellen Stand der Dinge zu bringen. Zumindest eine der bekannteren Engines, namentlich Saxon, ist diesem Ziel schon sehr nahe.
Dies ist auch nicht weiter verwunderlich, denn der Entwickler dieser überaus umfangreichen Java-Bibliothek ist Michael Kay, der Editor des XSLT 2.0 Working Drafts und Autor mehrerer Bücher zu XSLT (erschienen bei Wrox Press). Dem nicht genug, ist er seit kurzem auch Firmengründer und Chef der britischen Firma Saxonica Limited [2].
Open Business
War Saxon bisher, also vor der Version 8.0, ausschließlich als Open-Source-Software verfügbar [3], gibt es nunmehr zwei Versionen der Java-Bibliothek: Saxon 8.0B und Saxon 8.0SA. Erstere ist die weiterhin kostenlose Variante, wohingegen die mit SA (für schema-aware) betitelte Fassung Geld kostet, dafür jedoch in der Lage ist, die Eingabedokumente anhand von Schemata auf ihre Gültigkeit hin zu überprüfen. Für die Evaluierung von Saxon 8.0SA steht eine auf 30 Tage begrenzte Probeversion zur Verfügung.Saxon ist in der aktuellen Version jedoch mehr als nur ein XSLT-2.0-konformer Parser. Neben XSLT 2.0 werden auch XQuery 1.0 und XPath 2.0 unterstützt, sowohl über entsprechende Kommandozeilenbefehle als auch über die jeweiligen Java-Klassen. Zudem kennt Saxon eine Reihe von nützlichen Erweiterungen, darunter zusätzliche Elemente, Funktionen und Attribute, und unterstützt Teile des EXSLT-Projektes [4]. Schließlich können derartige Erweiterungen auch selbst (in Java) programmiert und in Saxon integriert werden.Wie diese eingesetzt werden, aber auch wie eigene entwickelt werden, wird im Folgenden anhand einiger einfacher Beispiele demonstriert werden. Hierfür wird das Saxon 8.0B-Paket von [3] benötigt und ein geeigneter XSLT-Editor (siehe hierzu gleichnamiger Kasten). Eine aufwendige Prozedur für die Installation von Saxon ist nicht notwendig: ZIP-Datei komprimieren und das JAR-Archiv
saxon8.jar gegebenenfalls in den Klassenpfad aufnehmen.
version="2.0"
Die wichtigste, aber zugleich unscheinbarste Neuerung in XSLT 2.0 betrifft den Wert für das
version-Attribut des
stylesheet-Elements. Anstelle der 1.0 hat hier 2.0 zu stehen. Andernfalls ist der Parser angewiesen, selbst wenn er XSLT 2.0 versteht, sich auf den Sprachschatz von XSLT 1.0 zu beschränken und das Stylesheet entsprechend zu verarbeiten.Der Wert für den XSL-Namensraum ist dagegen gleich geblieben:
www.w3.org/1999/XSL/Transform. Für die neuen XPath-Funktionen gibt es einen eigenen Namensraum ebenso wie für die Saxon-Erweiterungen. Letztere müssen zusätzlich über das Attribut
extension-element-prefixes und den Wert saxon kenntlich gemacht werden. Schließlich werden auch die Schema-Typen benötigt und, sofern die Ausgabe XHTML-konform sein soll, ebenso dieser entsprechende Namensraum.Die notwendigen Deklarationen zeigt Listing 1. Besonders wichtige Stellen, die zudem im Text ausdrücklich besprochen werden, sind mit fetter Schrift hervorgehoben. Das Listing demonstriert auch eine erste Saxon-spezifische Erweiterung namens
call-template. Schließlich können über dieses Hauptstylesheet sämtliche exemplarischen Stylesheets zur Anzeige gebracht werden. Hierfür wird dem Stylesheet
saxon.xsl ein Parameter
examples übergeben und mit einer Liste von Template-Namen belegt.
Listing 1Auszug aus der Datei saxon.xsl <?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2003/11/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
extension-element-prefixes="saxon"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xm="urn:xml-magazin:saxon"
>
<xsl:import href="examples.xsl" />
<xsl:param name="examples" as="xs:string" select="'all'"
saxon:assignable="yes" />
<xsl:output method="html" indent="yes" encoding="utf-8" />
<xsl:template match="/">
<html>
<head>
<title>Saxon 8.0 and XSLT 2.0 demo stylesheet</title>
</head>
<body>
<h1>Saxon 8.0 and XSLT 2.0 demo stylesheet</h1>
<xsl:if test="$examples = 'all'">
<saxon:assign name="examples"
select="fn:string-join(xm:list-all(),',')" />
</xsl:if>
<xsl:for-each select="fn:tokenize($examples, ',')">
<hr />
<h2>
<xsl:text>example: </xsl:text>
<xsl:value-of select="." />
</h2>
<saxon:call-template name="{.}" /><b> </b>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Dynamisch modular
Die benannten Templates sind andernorts definiert, und zwar in separaten XSL-Dateien. Diese werden über die Datei
examples.xsl zusammengefasst, die schließlich selbst über eine
import-Anweisung in das gezeigte Hauptstylesheet geladen wird. Der Aufruf der Templates erfolgt in dem Stylesheet
saxon.xsl über die Anweisung
saxon:call-template. Im Unterschied zu
xsl:call-template kann der Name des aufzurufenden Templates bei dieser Variante dynamisch gewählt werden. Im vorliegenden Fall entstammt der Name der Variablen
$examples, die mittels der XPath-Funktion
fn:tokenize in ihre Bestandteile zerlegt wird. Hierbei wird der Funktion eine Zeichenfolge und ein Trennzeichen übergeben. Als Ausgabe liefert die Funktion eine Sequenz von Zeichenfolgen: die Template-Namen.Dann werden die einzelnen Einträge in einer
for-each-Schleife durchlaufen und jeweils
call-template aufgerufen. Die Parameterübergabe erfolgt dabei über den Punktoperator ., welcher den aktuellen Eintrag referenziert. Um die Auswertung innerhalb des
name-Attributes zu erzwingen, muss dieser noch in geschweifte Klammern gesetzt werden. Die Möglichkeit, in einer
for-each-Schleife über die Elemente einer Sequenz anstelle von Knoten zu iterieren, ist ebenfalls neu in XSLT 2.0.Der Aufruf des Stylesheets
saxon.xsl mithilfe von Saxon 8.0 auf einem beliebigen XML-Dokument
data.xml erfolgt in der Kommandozeile durch den nachstehenden Befehl:
java net.sf.saxon.Transform -o saxon.html data.xml saxon.xsl examples=all
Bei diesem Aufruf wird der Parameter
examples mit dem Wert all belegt, womit das Stylesheet veranlasst wird, sämtliche Beispiele auszuführen. Dies ist zugleich der Standardwert für den Parameter, sodass hier die Angabe entfallen könnte.Die Option
-o bewirkt, dass das resultierende HTML-Dokument nicht in die Standardausgabe, sondern in eine Datei namens
saxon.html geschrieben wird. Eine vollständige Auflistung der möglichen Optionen und eine Beschreibung der Aufrufssyntax liefert der folgende Befehl:
java net.sf.saxon.Transform
Saxon hält eine weitere Verbesserung bereit, deren Anwendung ebenfalls in Listing 1 gezeigt wird. Wird der Parameter
examples mit dem Wert all besetzt oder auf diesem Standardwert belassen, so wird dies innerhalb einer
xsl:if-Anweisung erkannt, und der Parameterwert wird in einem
saxon:assign-Element durch die vollständige Liste der Template-Namen ersetzt. Wie diese Liste ermittelt wird, wird im nachfolgenden Listing 2 gezeigt.XSLT 2.0 erlaubt zwar keine nachträgliche Änderung von Variablen oder Parameterwerten, doch unter Saxon ist dies dank des Attributs
saxon:assignable möglich. Indem das Attribut innerhalb eines
xsl:variable- oder
xsl:param-Elements auf yes gesetzt wird, kann die Variable beziehungsweise der Parameter auch nach der Deklaration mit einem neuen Wert belegt werden. Hierfür ist die
saxon:assign-Anweisung zuständig.
Reflektion
Mit der Version 2.0 findet ein lang ersehntes Feature Einzug in XSLT: die Definition eigener Funktionen. Einige XSLT-Engines, darunter Saxon und ebenso das EXSLT-Projekt, hatten hierfür bereits eigene Erweiterungen definiert - meist in Form eines
function-Elements. In der kommenden XSLT-Version gehört die
function-Anweisung zum regulären Sprachumfang. Als einziges Attribut namens
name benötigt sie den Namen der Funktion inklusive eines Namensraumpräfixes. Dieses Präfix muss wie gewohnt im
stylesheet-Element deklariert werden. Für die hier vorgestellten Beispiele wird der Namensraum wie folgt angegeben:
xmlns:xm="urn:xml-magazin:saxon"
Ein Beispiel für die Anwendung einer benutzerdefinierten Funktion wurde bereits in Listing 1 mit der
xm:list-all-Funktion gezeigt, um alle möglichen Beispiele aufzulisten. Diese Funktion ist in der Datei
examples.xsl definiert. Ihr Quellcode ist in Listing 2 abgebildet. Zudem enthält die Datei
examples.xsl eine Reihe von
import-Anweisungen, um die Stylesheets mit den Beispielen zu laden.Eben diese
import-Anweisungen sind es, die von der
list-all-Funktion dazu genutzt werden, sämtliche Stylesheets einzulesen und die darin befindlichen benannten Templates zu suchen, um schließlich deren Namen als Funktionswert zurückzugeben. Hierfür öffnet die Funktion das eigene Stylesheet
examples.xsl und führt eine XPath-Abfrage nach den
href-Attributen der
import-Elemente aus. Das Ergebnis wird schließlich in der lokalen Variablen
example-files abgelegt. Letztere wird noch um den Eintrag für die Datei
examples.xsl selbst ergänzt.Für das Einlesen von zusätzlichen XML-Dokumenten stellt XPath 2.0 die Funktion
fn:doc zur Verfügung, die als einzigen Parameter den absoluten oder relativen URI des Dokuments verlangt. Das Ergebnis ist ein
document-node, der wiederum als Ausgangspunkt für eine XPath-Abfrage dienen kann.In früheren Versionen von XSLT war dies nicht möglich, zum einen weil eine Funktion wie
fn:doc fehlte, zum anderen jedoch weil so genannte result tree fragments - wie sie zum Beispiel in folgendem Code gezeigt werden - für XPath-Abfragen nicht zugänglich waren.
<variable name="temp-tree">
<node><node /></node>
<node>text</node>
</variable>
Einige XSLT-Engines halfen dem Anwender mit einer Funktion
node-set, die ein solches Fragment - hier die Variable
temp-tree - in eine erlaubte Knotenmenge überführt. Mit XSLT 2.0 ist die Funktion
node-set, die ohnehin weder in die erste noch in die zweite Version von XSLT offiziell Eingang fand, obsolet.Um die benannten Templates in den exemplarischen Stylesheets zu finden, verwendet die
list-all-Funktion erneut die
fn:doc-Funktion. Nur entstammen in diesem Fall die Pfade zu den XML-Dokumenten, sprich den Stylesheets, der Variablen
example-files. In einer
for-Schleife, ein neues Konstrukt aus XPath 2.0, werden die einzelnen Einträge durchlaufen, und in den jeweiligen Stylesheets wird nach den
name-Attributen der
template-Elemente gesucht. Um sie aus der Schleife nach außen zu befördern, wird die
return-Anweisung benutzt. Schließlich sammelt ein
sequence-Element, welches zugleich die letzte Anweisung im
function-Element ist und somit den Funktionswert bestimmt, die Namen der Templates ein und fasst sie zu einer Sequenz zusammen.Aufseiten des Stylesheets
saxon.xsl werden die Einträge mit der Funktion
fn:string-join zu einer einzigen Zeichenkette zusammengefasst und innerhalb der Zeichenkette durch ein Komma getrennt. Im Falle des benannten Templates
list-all (siehe folgenden Code) werden die Einträge hingegen in einer
for-each-Schleife durchlaufen, alphabetisch sortiert und als HTML ausgegeben.
<xsl:template name="list-all">
<xsl:for-each select="xm:list-all()">
<xsl:sort />
<xsl:value-of select="." />
<br />
</xsl:for-each>
</xsl:template>
Listing 2Auszug aus der Datei examples.xsl <xsl:import href="fibonacci.xsl" />
<xsl:import href="exslt.xsl" />
...
<xsl:function name="xm:list-all">
<xsl:variable name="example-files"
select="
fn:doc('examples.xsl')/xsl:stylesheet/xsl:import/@href,
'examples.xsl'" />
<xsl:sequence select="
for $f in $example-files
return fn:doc($f)//xsl:template/@name" />
</xsl:function>
Funktionen mit Hirn
Listing 3 zeigt eine weitere benutzerdefinierte Funktion, diesmal aus dem Stylesheet
fibonacci.xsl. Sie implementiert, wie der Name bereits andeutet, die Fibonacci-Funktion: Eine rekursive Funktion auf den ganzen Zahlen, deren Wert sich aus der Summe der Funktionswerte für die beiden nächst kleineren Argumente zusammensetzt. Mit Ausnahme der Werte 1 und 2, für welche die Funktion eins liefert, wird für ein gegebenes
n sowohl der Funktionswert für
n-1 als auch für
n-2 berechnet. Für
n-1 wird wiederum der Funktionswert für
n-2 und
n-3 benötigt, kurzum, einige Funktionswerte müssen mehrfach berechnet werden.Es wäre also sinnvoll, bereits berechnete Werte für gegebene Argumente zwischenzuspeichern und sie bei Bedarf unmittelbar zurückzugeben anstatt sie erneut zu berechnen. Eben diese Aufgabe leistet das Attribut
saxon:memo-function. Wird es auf yes gesetzt, speichert Saxon alle berechneten Werte und gibt diese bei einem gleichwertigen Funktionsaufruf zurück. Dies ist natürlich nur dann zu empfehlen, wenn die Funktion für die Berechnung ausschließlich auf die eigenen Parameter zurückgreift, nicht jedoch wenn die Funktion intern globale Variablen oder gar Zufallszahlen (mehr hierzu gleich) benutzt.Bei der
fibonacci-Funktion ist Letzteres jedoch nicht der Fall, und da die Berechnung der Fibonacci-Zahlen für große
n überaus rechenintensiv ist, bringt der Einsatz von
saxon:memo-function einen echten Performancegewinn: Bereits für die ersten 25 Zahlen beschleunigt sich die Berechnung um den Faktor 3, bei den ersten 50 Zahlen ist es gar das Tausendfache.Jedoch hat das Attribut
saxon:memo-function nicht bei jeder Funktion die gewünschte Wirkung. Ein erster Entwurf der
fibonacci-Funktion sah beispielsweise folgendermaßen aus:
<xsl:function name="xm:fibonacci"
saxon:memo-function="yes" as="xs:integer">
<xsl:param name="n" as="xs:integer" />
<xsl:sequence select="
if ($n = 1) then 1
else if ($n = 2) then 1
else xm:fibonacci($n - 2) + xm:fibonacci($n - 1)" />
</xsl:function>
Da hier die Berechnung durch einen komplexen XPath-Ausdruck erfolgt, kann Saxon (in der derzeitigen Version) die so genannte Memorisierung nicht durchführen. Der genaue Umstand wird von Michael Kay im Saxon-Forum auf Sourceforge erläutert [5].
Listing 3Auszug aus der Datei fibonacci.xsl <xsl:function name="xm:fibonacci"
saxon:memo-function="yes" as="xs:integer">
<xsl:param name="n" as="xs:integer" />
<xsl:choose>
<xsl:when test="$n = 1">
<xsl:sequence select="1" />
</xsl:when>
<xsl:when test="$n = 2">
<xsl:sequence select="1" />
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="
xm:fibonacci($n - 2) + xm:fibonacci($n - 1)" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
Zufällig Zufall
Eine mathematische Größe wird dann als zufällig erachtet, im intuitiven Sinn, wenn ihr Wert für einen gegebenen Zeitpunkt nicht berechenbar ist und wenn sie zu verschiedenen Zeiten unterschiedliche Werte annimmt. Gemeinhin wird noch angenommen, dass alle möglichen Werte gleich oft auftreten. Für das Erzeugen von Zufallszahlen sieht die XSLT-2.0-Spezifikation keine entsprechende Funktion vor, doch das hier mehrmals erwähnte Enhanced-XSLT-Projekt (siehe [4]) spezifiziert eine solche Funktion namens
math:random, die auch von Saxon implementiert wird. Sie liefert gleich verteilte Zufallszahlen im Bereich von 0 bis 1. Die Funktion hat allerdings einen kleinen Haken, der durch das folgende Codefragment aufgedeckt wird:
<xsl:sequence select="for $i in 1 to $n return math:random()" />
Hierbei werden
n Zufallszahlen erzeugt, wobei - wegen der oben genannten Annahme - davon ausgegangen wird, dass die Zahlen unterschiedlich sind - zumindest ein beachtlicher Teil. Doch dies ist nicht der Fall. Es werden
n gleiche Zufallszahlen erzeugt. Die Ursache hierfür ist die Optimierungsarbeit, die Saxon leistet. Da die Engine davon ausgeht, dass eine Funktion für dieselben Argumente (in diesem Fall ist die Argumentliste leer) dieselben Werte liefert, kann der obige XPath-Ausdruck erheblich vereinfacht werden. Hierfür wird die Funktion
math:random nur ein einziges Mal aufgerufen. Schließlich wird diese einmalig erzeugte Zufallszahl
n-mal in die Sequenz geschrieben.Einen Ausweg bietet die nachfolgend gezeigte Funktion
random-sequence. Diese Variante funktioniert, weil Saxon bisher keine
for-each-Schleifen optimiert. Doch auch diese Lösung wird nicht lange Bestand haben, denn, wie Michael Kay in einem entsprechenden Beitrag auf Sourceforge [6] angekündigte, wird auch dieses Konstrukt optimiert werden.
<xsl:function name="xm:random-sequence" as="xs:double *">
<xsl:param name="count" as="xs:integer" />
<xsl:for-each select="1 to $count">
<xsl:sequence select="math:random()" />
</xsl:for-each>
</xsl:function>
Die endgültige Lösung liegt wieder im EXSLT-Projekt, welches ausdrücklich für solche Zwecke eine Funktion
random:sequence definiert. Allerdings ist diese in Saxon 8.0 nicht implementiert, sodass sie selbst implementiert werden muss - und zwar in Java.
Infektion mit Java
Die Funktion
random:sequence erzeugt, so schreibt es die EXSLT-Spezifikation vor, ein oder mehrere Zufallszahlen im Bereich von null bis eins. Die Anzahl der Zufallszahlen wird durch den ersten und optionalen Parameter
numberOfItems bestimmt. Sein Standardwert ist eins. Der zweite, ebenfalls optionale Parameter,
seed genannt, bestimmt den Initialisierungswert für den Zufallszahlengenerator. Fehlt diese Angabe, so ist der Wert mit der aktuellen Zeit zu belegen.Da beide Parameter optional sind, gibt es zumindest rein rechnerisch vier verschiedene Möglichkeiten, die Funktion aufzurufen. Für den Fall, dass die
random-sequence-Funktion mit einem Argument aufgerufen wird, kann die XSLT-Engine jedoch nicht entscheiden, ob hiermit die Anzahl an Zufallszahlen oder der Initialisierungswert gemeint ist. In der in Listing 4 gezeigten Implementierung fiel die Wahl auf die erst genannte Variante. Die Funktion ohne Parameter ist hier aus Platzgründen nicht gezeigt, sie ist aber ebenfalls in der Datei
Random.java implementiert.Die Funktionen sind als öffentliche und statische Methoden der Klasse
random.Random implementiert. Hierbei bezeichnet
random den Paket- und
Random den Klassennamen. Die Kompilierung in eine
.class-Datei erfolgt somit durch:
javac random/Random.java
In einem Saxon-spezifischen Stylesheet wird die Klasse durch die folgende Namensraumdeklaration eingebunden:
xmlns:random="java:random.Random"
Hierbei ist
java: ein spezieller Bezeichner, der die Saxon-Engine anweist, im Klassenpfad nach einer Klasse
random.Random zu suchen und ihre statischen Methoden unter dem Präfix
random zur Verfügung zu stellen. Da der Konstruktor einer Klasse ebenfalls eine statische Methode ist, ließen sich auf diese Weise auch Objektinstanzen erzeugen, deren Methoden und öffentliche Variablen sich ebenfalls im Stylesheet verwenden ließen. Dies wird allerdings von dem hier gezeigten Beispiel nicht demonstriert, um die Darstellung nicht unnötig zu verkomplizieren. Die in Listing 4 gezeigten Methoden mit dem Namen
randomSequence sind innerhalb des Stylesheets unter dem Bezeichner
random-sequence verfügbar. Saxon führt hierzu eine Abbildung von Java-konformen Namen auf XSLT-konforme Namen durch. Je nach Anzahl der Argumente wird die entsprechende Implementierung aufgerufen. Fehlt beispielsweise der Initialisierungswert, so wird die zuletzt gezeigte Funktion aufgerufen. Sie setzt für den fehlenden Parameter den Wert der Eigenschaft
System.currentTimeMillis ein
und übergibt ihn an eine private Implementierung der
randomSequence-Funktion. Diese erwartet im Unterschied zur öffentlichen
randomSequence-Funktion den Initialisierungswert als
long-Typ. Die öffentliche Funktion akzeptiert hingegen einen
double-Wert und konvertiert diesen mittels
Double.doubleToLong in einen
long-Wert.Die genannte interne Implementierung verwendet wiederum die
Random-Klasse aus dem
java.util-Paket und deren
nextDouble()-Methode, um die Zufallszahlen zu generieren. Da das Ergebnis als Sequenz geliefert werden muss, werden die
double-Werte in
DoubleValue-Objekte verpackt und diese schließlich in einem
ArrayIterator-Objekt abgelegt, welches dann als Funktionswert zurückgegeben wird. Beide Objektklassen entstammen der Saxon-Bibliothek
saxon.jar, so dass diese bei der Kompilierung der Datei
Random.java in den Klassenpfad aufgenommen werden muss.Bei Erscheinen dieses Artikels könnte die Saxon-Engine bereits die
random:random-sequence-Funktion unterstützen, denn die hier vorgestellte Lösung wurde - in abgewandelter Form - dem Saxon-Projekt zur Verfügung gestellt. In diesem Fall könnte auf die
Random-Klasse verzichtet werden und anstelle der eben gezeigten Namensraumdeklaration die folgende verwendet werden:
xmlns:random="http://exslt.org/random"
Listing 4Auszug aus der Datei Random.java public static ArrayIterator randomSequence(
int numberOfItems, double seed)
throws IllegalArgumentException {
return randomSequence(numberOfItems,
doubleToLong(seed));
}
/* Converts a double to a long value. */
private static long doubleToLong(double value) {
return Double.doubleToLongBits(value);
}
/* Internal implementation of randomSequence */
private static ArrayIterator randomSequence(
int numberOfItems, long seed)
throws IllegalArgumentException {
if (numberOfItems < 1) {
throw new IllegalArgumentException(
"numberOfItems must be positive.");
}
java.util.Random random =
new java.util.Random(seed);
DoubleValue[] items =
new DoubleValue[numberOfItems];
for (int i = 0; i < items.length; i++) {
items[i] = new DoubleValue(
random.nextDouble());
}
return new ArrayIterator(items);
}
public static ArrayIterator randomSequence(
int numberOfItems)
throws IllegalArgumentException {
return randomSequence(numberOfItems,
System.currentTimeMillis());
}
Reine Theorie
Zu guter Letzt soll überprüft werden, wie gut die von den beiden
random-sequence-Funktionen (
xm: und
random:) erzeugten Zufallszahlen tatsächlich sind. Hierfür werden pro Funktion 100000 Zahlen zwischen null und eins generiert und anschließend der Mittelwert und die Standardabweichung berechnet. Hierbei helfen wieder die Funktionen von XPath 2.0 und die des
math-Paketes aus dem EXSLT-Projekt.Für die Durchschnittswertberechnung findet die Funktion
fn:avg Anwendung. Bei der Berechnung der Standardabweichung werden zusätzlich die Funktionen
math:power und
math:sqrt benötigt, um das Quadrat und die Quadratwurzel zu berechnen. Entsprechende Funktionen sind leider weder in XSLT 2.0 noch in XPath 2.0 vorgesehen, sodass hier erneut auf das EXSLT-Projekt zurückgegriffen werden muss.Das Ergebnis der Berechnungen zeigt, dass die generierten Zufallszahlen durchaus der Prüfung Stand halten: Der Mittelwert ist bei beiden Funktionen nahe des theoretisch ermittelten Wertes von 0.5 und auch die Standardabweichung, die sich aus der Wurzel von ein Zwölftel berechnet (entspricht in etwa 0,2887), liegt in beiden Fällen in diesem Bereich.
Listing 5Auszug aus der Datei exslt.xsl <xsl:function name="xm:dev">
<xsl:param name="randoms" as="xs:double*" />
<xsl:param name="avg" as="xs:double" />
<xsl:sequence select="
<b>math:sqrt(fn:avg(</b>
for $r in $randoms
return math:power($r - $avg, 2)))" />
</xsl:function>
<xsl:template name="random">
<xsl:variable name="xm_randoms"
select="<b>xm:random-sequence(100000)</b>" />
<xsl:variable name="rnd_randoms"
select="<b>random:random-sequence(100000)</b>" />
<xsl:variable name="xm_avg"
select="<b>fn:avg($xm_randoms)</b>" />
<xsl:variable name="rnd_avg"
select="fn<b>:avg($rnd_randoms)</b>" />
<xsl:variable name="xm_dev"
select="<b>xm:dev($xm_randoms, $xm_avg)</b>" />
<xsl:variable name="rnd_dev"
select="<b>xm:dev($rnd_randoms, $rnd_avg)</b>" />
...
</xsl:template>
Fazit
Bei der Fülle an Neuheiten und gleichermaßen Möglichkeiten, die Saxon in der achten Version bietet, konnten die hier gezeigten Beispiele nur einen kleinen Ausschnitt darstellen. Doch bereits diese einfachen Beispiele zeigen, welches Potenzial in XSLT 2.0 & Co. steckt: Sei es die Programmierung eigener Funktionen oder das reichhaltige Angebot an vordefinierten Funktionen, XSLT 2.0 vereinfacht nicht nur die Entwicklung von Stylesheets, es macht auch bisher Unmögliches möglich. In Kombination mit Saxon wird XSLT zudem zur mächtigen Programmiersprache, die kaum noch Wünsche offen lässt. Und selbst wenn, Saxon bietet auch die Option an, eigene Erweiterungen zu integrieren. Damit steht der Weg für die Programmierung von leistungsfähigen Anwendungen auf Basis von XSLT 2.0 und Saxon 8.0 offen. Mit der Lektüre des vorliegenden Artikels haben Sie den ersten Schritt hierzu bereits getan. In diesem Sinne wünsche ich Ihnen viel Erfolg und ein gutes Gelingen mit Saxon 8.0 und XSLT 2.0.
Martin Szugat ist seit mehreren Jahren als freischaffender Fachautor im Bereich der Softwareentwicklung mit den Schwerpunkten XML und .NET tätig. Bedingt durch sein Studium (der Bioinformatik) beschäftigt er sich zunehmend auch mit Java und Linux. In XML, und insbesondere in Web Services, sieht er die optimale Technologie, um das Beste aus beiden Welten (Java und .NET) zu verbinden. Sie erreichen ihn unter Martin.Szugat@GMX.net.
Literatur und Links