Die Erweiterungsmöglichkeiten des XSLT-Prozessors lassen sich grob in drei Gruppen einteilen. Die erste Gruppe dient der Steuerung der Ausgaben. In einer Zweiten lassen sich die bereits in Xalan enthaltenen Erweiterungselemente und -funktionen zusammenfassen. Ein mächtiges Programmierwerkzeug pictureet die Möglichkeit, eigene Erweiterungen zu implementieren. Unabhängig von Xalan - aber dennoch zum Projekt gehörend - ist der XSLT-Compiler.
Ausgabesteuerung
In der XSLT-Empfehlung sind insgesamt zehn Attribute für das Element xsl:output vorgesehen. Offensichtlich reichen diese Steuerungsmöglichkeiten für die Ausgabe aber nicht aus. Es besteht immer der Bedarf nach weiteren. Um diese zu nutzen, muss zunächst der entsprechende Namespace im xsl:stylesheet Element gesetzt werden: xmlns:xalan = "http://xml.apache.org/xslt". Die Bezeichnung xalan ist nicht entscheidend für das Funktionieren, ein spartanisches x wäre völlig ausreichend. Das Stylesheet wird auf diese Weise aber übersichtlicher, da sofort sichtbar wird, welcher URL und damit welche Erweiterungen referenziert werden. Je nach verwendeter Ausgabemethode (xml, html, text) können jetzt weitere Attribute zum Element xsl:output hinzugefügt werden. Bei der Verwendung von xml steht xalan:indent-amount zur Verfügung. Ursprünglich bestand nur die Möglichkeit, festzulegen, ob eine Einrückung der Ausgabe erfolgen soll. Über diese Erweiterung lässt sich festlegen, um wie viele Leerzeichen die Kindelemente jeweils eingerückt werden sollen. In den Quellen zum Artikel befindet sich dazu ein Beispiel. Das Ergebnis lässt sich über den Kommandozeilenaufruf java org.apache.xalan.xslt.Process -in foo1.xml -xsl foo1.xsl -out foo_out1.xml betrachten. Sind die Quelldateien nicht im aktuellen Verzeichnis, muss die URL-Schreibweise verwendet werden ( file:///). Standardmäßig ist der Wert für indent-amount auf 0 gesetzt, so dass seine interne Verwendung gar nicht bemerkt wird. Auch bei der HTML-Ausgabe besteht die Möglichkeit, das Ausmaß der Einrückung festzulegen. Eine weitere Möglichkeit ist das Unterdrücken des standardmäßig eingefügten Meta-Tags über xalan:omit-meta-tags="yes". Über xalan:use-url-escaping wird gesteuert, ob Attributwerte (z.B. von href, action, codebase,) so umgewandelt werden, dass ein korrekter URL entsteht (URL escaping). Diese Umwandlung ist standardmäßig eingeschaltet, ist aber nicht immer sinnvoll (siehe Beispiel foo2.xsl). Andere Prozessoren führen diese Umwandlung nicht automatisch durch, sodass eine generelle Abschaltung dieses Features durchaus sinnvoll sein kann. Für XSLT-Einsteiger ist nicht immer nachvollziehbar, warum innerhalb eines Stylesheets die Angabe von zu einem Fehler führt. Die Angabe von aber zu einer korrekten Ausgabe von im HTML-Text führt. Die Ursache liegt darin, dass Stylesheets zunächst einmal XML-Dokumente sind. In diesen sind nur die vier Entitäten < > & und " zulässig. Wenn der XSLT-Prozessor die HTML-Ausgabedatei erzeugt, werden die Zahlenangaben in entsprechende HTML-Entität umgewandelt. Xalan verwendet dazu eine Liste (src/org/apache/xalan/serialize/HTMLEntities.res), die als Default-Wert dem Attribut xalan:entities zugewiesen ist. Sollte für spezielle Anwendungen eine Entität fehlen, kann sie entweder zu dieser Liste hinzugefügt werden oder es wird eine eigene Liste erstellt und diese dem Attribut xalan:entities zugewiesen. Mit diesen Möglichkeiten lässt sich schon einiges an Chaos stiften. Wem das nicht reicht, der kann auch noch einen Schritt weiter gehen und die gesamte Ausgabe durch eine eigene Klasse realisieren lassen. Dazu muss dem Attribut xalan:content-handler diese Methode als Wert übergeben werden. Standardmäßig verwendet Xalan die Klasse org.apache.xalan.serialize.SerializerToXML für die Ausgabe von XML-Dokumenten. Für die anderen Ausgabemöglichkeiten existieren jeweils andere Klassen, die von dieser abgeleitet sind. Mit einem Blick in den Quelltext ist es sicher kein Problem eine völlig neue Ausgabemethode zu schreiben, die sämtliche Wünsche umsetzt.Erweiterungen in XSLT
Bevor wir einen Blick auf die Erweiterung werfen, soll dargestellt werden, wie Stylesheets trotz der Nutzung propriäterer Erweiterungen Standardkonform entwickelt werden können. Dazu dienen die Funktionen element-available, function-available sowie das Element xsl:fallback. Mit Hilfe der Funktion element-available kann vor der Verwendung eines bestimmten Elements überprüft werden, ob der Prozessor ein solches Element unterstützt bzw. ob ein solches Element über einen Erweiterungsmechanismus verfügbar gemacht wurde. Die Funktion liefert für das Argument xsl:template mit Sicherheit bei jedem XSLT-Prozessor true zurück. Für ein imaginäres Element xsl:foo, aber sicher false. Ähnlich arbeitet die Funktion function-available. Nur, dass diese die Verfügbarkeit von Funktionen, wie z.B. format-number, überprüft. Folgendes Codebeispiel demonstriert die Verwendung:<xsl:if test="not(element-available('test:elem1'))">Das Element test:elem1 ist nicht verfügbar!</xsl:if><xsl:if test="not(function-available('xsl:anyFunction'))">Die Funktion xsl:anyFunction ist nicht verfügbar!</xsl:if><xsl:if test="function-available('number')">Die Funktion number() ist verfügbar!</xsl:if>
<test:elem2>eigentliche Transformation<xsl:fallback>Alternative Transformation</xsl:fallback></test:elem2>
Erweiterungselemente
Normalerweise fügt der XSLT-Prozessor alle Element, die nicht zum Namensraum xsl gehören in das Ausgabedokument ein. Werden Erweiterungselemente verwendet, führt der Prozessor eine bestimmte Aktion aus. Liefert die Aktion einen Ergebniswert zurück, wird dieser an Stelle des Elements in die Ausgabe übernommen. Dabei ist es durchaus zulässig, dass kein Ergebnis zurückgegeben wird. Dem Prozessor muss als Erstes mitgeteilt werden, welche Elemente er ausführen soll. Dazu sieht der Standard das Attribut extension-element-prefixes vor. Der innerhalb dieses Attributs angegebene Wert enthält eine durch Leerzeichen getrennte Liste aller Namensräume, die als Erweiterungselemente behandelt werden sollen. Daraus folgt natürlich, dass ein solcher Namensraum zunächst definiert worden sein muss. Um z.B. die Erweiterungselemente zur Ausgabeumleitung nutzen zu können, muss also zuerst ein Namespace deklariert werden: xmlns:redirect = "org.apache.xalan.xslt.extensions.Redirect". Und anschließend dieser als Namespace von Erweiterungselementen gekennzeichnet werden: extension-element-prefixes="redirect". Jetzt stehen drei weitere Elemente zur Verfügung (open, write, close), mit denen die Ausgabe einer Transformation in Dateien umgeleitet werden kann. Jedes Element kann über die Attribute file und select gesteuert werden. Mit file wird ein statischer Ausdruck angegeben, der den Dateinamen angibt. Über select ist die Auswertung eines XPath-Ausdrucks möglich, so dass die Dateinamen zur Laufzeit ermittelt werden können. Im einfachsten Fall ist das Element write ausreichend. Trifft der Prozessor auf dieses Element, öffnet er die angegebene Datei, schreibt das Ergebnis der Transformation in diese und schließt die Datei wieder. Wenn die vollständige Kontrolle über diesen Mechanismus gewünscht wird, muss die Datei explizit mit open geöffnet und mit close wieder geschlossen werden. Die redirect-Erweiterung lässt sich sehr gut einsetzen, um große XML-Dokumente (z.B. eine Dokumentation) für die HTML-Ausgabe in kleinere Dokumente zu zerlegen und bei einer PDF-Ausgabe das gesamte Dokument in einem Stück zu belassen. Eine interessante Erweiterung stellt das Element pipeDocument dar. Das Element ist über den Namespace xmlns:pipe="xalan://PipeDocument" erreichbar. Diese Abkürzung kann verwendet werden, da Xalan die entsprechenden Klassen u.a. im Package org.apache.xalan.lib sucht. Die vollständige Bezeichnung der Klasse lautet demzufolge org.apache.xalan.lib.PipeDocument. Wie der Name vermuten lässt, können mit Hilfe dieses Elements mehrere Stylesheets nacheinander für eine Transformation benutzt werden. Dazu wird den Attributen source und target jeweils ein Dateiname übergeben und die zu prozessierenden Stylesheets als Kindelement notiert (Listing 1
<xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"verison="1.0"xmlns:lxslt="http://xml.apache.org/xslt"xmlns:my-ext="extension1"extension-element-prefixes="my-ext"/><lxslt:component prefix="my-ext" elements="elem1" functions="func1"><lxslt:script lang="javascript">function elem1(xslProcessorContext, elem){return null;}function func1(param1,pram2){return result;}</lxslt:script></lxslt:component>
Erweiterungsfunktionen
In der Klasse org.apache.xalan.lib.Extensions sind eine Reihe von Erweiterungsfunktionen zusammengefasst, die über den Namespace http://xml.apache.org/xalan erreichbar sind. Mit Hilfe der Funktion evaluate können XPath-Ausdrücke zur Laufzeit berechnet werden, obwohl an der entsprechenden Stelle vom XSLT-Standard keine Auswertung vorgesehen ist. Ein Beispiel (Verzeichnis ExtensionLib ) macht das Problem deutlich. Normalerweise besteht keine Möglichkeit, für das Element xsl:sort zur Laufzeit festzulegen, nach welchem Element/Attribut sortiert werden soll. Dabei wäre es natürlich ideal, einen Parameter an das Stylesheet zu übergeben, der festlegt, wonach sortiert werden soll. An dieser Stelle hilft die Funktion evaluate. Ihr kann der Parameter übergeben werden. Sie berechnet den XPath-Ausdruck und fügt das Ergebnis an der entsprechenden Stelle ein. Der XSLT-Standard definiert eine Möglichkeit, einer Variablen einen Teilbaum zuzuweisen. Das funktioniert aber nur über einen entsprechenden select-Ausdruck. Wenn der Variablen reiner Text übergeben wird, auch wenn er ein wohlgeformtes Teildokument darstellt, besteht keine Möglichkeit, über XPath-Ausdrücke auf bestimmte Knoten zuzugreifen. Dazu muss zuerst eine Umwandlung in eine Knotenmenge erfolgen. Diese Aufgabe übernimmt die Erweiterungsfunktion nodeset. Funktionen, die ebenfalls im Zusammenhang mit Knotenmengen stehen, sind intersection (Schnittmenge), difference (Durchschnitt), distinct (entfernt mehrfach auftretende Knoten) und hasSameNodes (überprüft, ob zwei NodeSets die gleichen Koten enthalten). Interessant dürfte auch die Funktion tokenize sein, die eine Zeichenkette zerlegt und ein NodeSet zurück liefert. Das Begrenzungszeichen kann festgelegt werden. Als Standard werden die Whitespace-Zeichen verwendet - Tabulator, Zeilenumbruch, Wagenrücklauf und Leerzeichen.SQL-Unterstützung
Inzwischen unterstützen fast alle großen Datenbankhersteller in irgend einer Form die Generierung von XML auf der Grundlage von Datenbankabfragen. Für einfache Anwendungen ist es aber nicht immer erforderlich auf diese Möglichkeit zurückzugreifen. Es kann natürlich auch sein, dass die bevorzugte Datenbank eine solche Funktion (noch) nicht zur Verfügung stellt. In solchen Fällen bietet die in Xalan eingebaute SQL- Bibliothek trotzdem eine gute Möglichkeit, mit relativ wenig Aufwand, aus SQL-Datenbanken XML-Dokumente zu generieren (Verzeichnis SQL). JDBC leistet an dieser Stelle gute Dienste. Am Deutlichsten wird die Verwendung der SQL-Erweiterung mit Sicherheit an einem Beispiel, das das generierte XML-Dokument ausgibt (siehe Listing 2).Listing 2
<?xml version="1.0" encoding="ISO-8859-1" ?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:sql="org.apache.xalan.lib.sql.XConnection"extension-element-prefixes="sql"><xsl:output method="xml" encoding="ISO-8859-1" indent="yes"/><xsl:param name="driver" select="'sun.jdbc.odbc.JdbcOdbcDriver'"/><xsl:param name="datasource" select="'jdbc:odbc:JavaMagDSN'"/><xsl:param name="query" select="'SELECT * FROM db.txt'"/><xsl:template match="root"><xsl:variable name="connection" select="sql:new($driver, $datasource)"/><xsl:variable name="table" select="sql:query($connection, $query)"/><xsl:copy-of select="$table"/><xsl:value-of select="sql:close($connection)"/></xsl:template></xsl:stylesheet>
<sql><metadata><column-header column-name="HEADLINE"/><column-header column-name="KATEGORIE"/><column-header column-name="AUTOR"/></metadata><row-set><row><col column-name="HEADLINE">Projektverträge in der Praxis</col><col column-name="KATEGORIE">recht</col><col column-name="AUTOR"/></row>...</row-set></sql>
XSLTC
Dass die Verarbeitung von Stylesheets nicht gerade zu einem Geschwindigkeitsrausch führt, ist schon durch das Prinzip der Interpretation zur Laufzeit bedingt. Wenn die Implementierungen eines XSLT-Prozessors dann auch noch eine Performance-Optimierung vermissen lässt, kann es schon zu Zweifeln am Sinn einer Transformationssprache kommen. Ursprünglich stammt die Idee von SUN, ein XSLT-Stylesheet in Javabyte-Code zu übersetzen und diesen, anstelle der eigentlichen XSLT-Dokumente, für die Transformation zu verwenden. Um ein Stylesheet zu kompilieren, müssen sich die Klassenbibliotheken xsltc.jar, runtime.jar und BCEL.jar im Klassensuchpfad befinden. Der Kommandozeilenaufruf java org.apache.xalan.xsltc.compiler.XSLTC stylesheet.xsl übersetzt dann das Stylesheet und erzeugt eine stylesheet.class-Datei. Im Sprachgebrauch von XSLTC ist diese Klasse ein Translet. Leider können die oben beschriebenen Erweiterungen nicht benutzt werden, da der XSLT-Compiler völlig unabhängig vom eigentlichen Xalan arbeitet. Zur Verwendung des Translets muss sich die Klassenbibliothek xsltc.jar und das Translet selbst im Klassenpfad befinden. Über den Kommandozeilenaufruf java org.apache.xalan.xsltc.runtime.DefaultRun foo.xml stylesheet erfolgt dann die Transformation des XML-Dokumentes foo.xml. Parameter werden übergeben, in dem der Name, gefolgt von einem Gleichheitszeichen und dem Parameterwert, angehängt wird. Mehrere Parameter werden durch Leerzeichen getrennt. Diese Aufrufvariante ist natürlich etwas umständlich. Sie kann aber auch über JAXP erfolgen. Es muss nur die gewünschte Implementierung für die TransformerFactory eingestellt werden. Das erfolgt über System.setProperty. Als key wird javax.xml.transform.TransformerFactory und als value org.apache.xalan.xsltc.trax.TranformerFactoryImpl übergeben. Die Verwendung ist dann völlig identisch mit jedem normalen Aufruf einer Transformation. Entscheidend ist aber, dass der Aufruf von Templates translet = tFactory.newTemplates(new StreamSource(xslInUrI)) dazu führt, dass das Stylesheet kompiliert wird und so für die spätere Verwendung zur Verfügung steht. Der Vorteil ergibt sich natürlich erst, wenn mehrere Transformationen nacheinander mit dem gleichen Translet durchgeführt werden. Es ist aber auch problemlos möglich, ein bereits kompiliertes Stylesheet zu benutzen. Die Geschwindigkeitsvorteile können erheblich sein. Insbesondere der erste Aufruf kann die Zeit für die Transformation eines Dokuments gut um die Hälfte verkürzen. Das ist natürlich von einigen Nebenbedingungen abhängig. So ist sicher nachvollziehbar, dass das Größenverhältnis und die Komplexität von Stylesheet und XML-Dokument einen relativ großen Einfluss auf die Transformationsgeschwindigkeit haben. Problematisch bei der Verwendung von Translets ist die noch nicht vollständige Umsetzung des XSLT-Standard.Warum nicht?
Die Verwendung propriäterer Erweiterungen ist immer etwas problematisch. Da eine durchgehende Standardisierung fehlt, können Stylesheets, die auf diese Art von Erweiterungen bauen, im Allgemeinen. nicht mit anderen Prozessoren verwendet werden. Trotzdem bieten sie sehr gute Möglichkeiten, den Umgang mit Stylesheets zu vereinfachen, für etwas mehr Geschwindigkeit zu sorgen, die XSLT-Dokumente übersichtlicher zu gestalten und fehlende Funktionen sehr einfach hinzuzufügen. Wenn die Entscheidung für Xalan aber gefallen ist, gibt es keinen Grund, auf diese Vorteile zu verzichten.Entwicklung eigener Erweiterungen
Wenn XML-Dokumente in HTML-Formulare transformiert werden, sollen auch Auswahlfelder (≶select name="select">) nicht fehlen. Die zur Verfügung stehenden Optionen sollen natürlich nicht statisch im Stylesheet angegeben, sondern u.U. aus einem anderen XML-Dokument ermittelt werden. Eventuell muss sogar ein bestimmter Wert vorselektiert werden. In einem ersten Lösungsansatz könnte dieses Problem durch ein Template gelöst werden, dass mit entsprechenden Parametern aufgerufen wird (SpeedTest1.xsl). Diese Variante wäre insofern von Vorteil, da sie mit hoher Wahrscheinlichkeit von allen XSLT-Prozessoren unterstützt wird. Es geht aber eleganter. Ein weiterer Lösungsansatz (Methode optionList der Klasse HTMLForms) könnte eine eigene Funktion entwickeln, welche den aktuellen Wert als String und die Auswahlliste als NodeList übergeben bekommt. Das Ergebnis der Funktion ist ein String, den der Prozessor in das Ausgabedokument einfügt. Die Verwendung der Funktion demonstriert das Stylesheet input.xsl:<xsl:value-of select="HTMLF:optionList(currOption, document('options.xml')/Options/item)" disable-output-escaping="yes"/>
public Node optionList2(String currOption, NodeList nl){Document doc= new DocumentImpl();Element option = null;Node currNode = null;Element select = doc.createElement( "select" );for (int i=0; i<nl.getLength(); i++){currNode = nl.item(i).getFirstChild();if (null != currNode){option = doc.createElement( "option" );if (currNode.getNodeValue().equals(currOption)){option.setAttribute( "selected" , "" );}option.appendChild( doc.createTextNode(currNode.getNodeValue()) );select.appendChild( option );}}return select;}
Listing 3
public String comboBox(org.apache.xalan.extensions.XSLProcessorContext context,ElemExtensionCall extElem)throws javax.xml.transform.TransformerException,org.xml.sax.SAXException{org.apache.xpath.XPathContext xctxt = context.getTransformer().getXPathContext();ResultTreeHandler rth = context.getTransformer().getResultTreeHandler();rth.processingInstruction(javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING, "");String size = extElem.getAttribute( "size" );String currOption = "";...NodeList boxItems = null;String boxItemPath = extElem.getAttribute ( "select" );if(null != boxItemPath){XPath myxpath = new XPath(boxItemPath, extElem, xctxt.getNamespaceContext(), XPath.SELECT);XObject xobj = myxpath.execute(xctxt, context.getContextNode(), extElem);boxItems = xobj.nodelist();}StringBuffer result = new StringBuffer().append("<select");if (size!=null){result.append(" size=\""+size+"\" ");}result.append(">");...result.append("</select>");return result.toString();}
rth.processingInstruction( javax.xml.transform.Result.PI_DISABLE_OUTPUT_ESCAPING , "" )









