Donnerstag, 24. Mai 2012





April 2006
aus XML & Web Services Magazin Ausgabe: 4.2004
Play the Saxon
Praktische Einführung in XSLT 2.0 und Saxon 8.0
von Martin Szugat

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 1
Auszug 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 2
Auszug 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 3
Auszug 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 4
Auszug 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 5
Auszug 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


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das Entwickler Magazin direkt über unser Online-Formular.

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen




Software & Support Verlag GmbH