XSLT bietet recht wenig Möglichkeiten, mit der Außenwelt zu kommunizieren. Informationen über die Umwelt zur Laufzeit des Stylesheets lassen sich mit Bordmitteln nur in sehr geringem Umfang ermitteln, Wirkungen nur über die Dateiausgabe erzielen. Dies mag daher kommen, dass das funktionale Paradigma von XSLT Seiteneffekte verbietet, führt aber in der Praxis gelegentlich zu merkwürdigen Verrenkungen. Beispielsweise habe ich öfters das Problem, Stylesheets abhängig von der Existenz einer externen Datei aufzurufen. Bisher habe ich dazu in einer externen Anwendung ein XML mit Informationen zum Dateisystem erzeugt und dieses dann im XSLT ausgewertet. Das funktioniert ganz gut mit statischen Daten, bei häufigen Änderungen im Dateisystem ist dieses Verfahren aber zu umständlich.
XPath 2.0 bietet mit doc-available()
zwar eine dedizierte Funktion, diese liefert aber nur bei der Existenz eines wohlgeformten XML-Dokumentes ein true()
. Ebenso hängt ein logisches wahr
bei unparsed-text-available()
von der Existenz eines Textes ab. Was aber, wenn ich wissen will, ob eine Bild-Datei (nein, nicht SVG ;-)) oder eine leere Datei existiert? Die wenigen Lösungsvorschläge verweisen auf xslt-externe Erweiterungsfunktionen. Diese setzen in der Regel die Existenz von Java und manchmal auch zusätzliche Java-Klassen voraus, was die Weiterverwendung vorhandener Stylesheets auf verschiedenen Rechnern erheblich erschwert.
Da in meiner Umgebung ohnehin meist Saxon eingesetzt wird, bietet es sich an, die Saxon-eigenen Erweiterungsfunktionen zu benutzen. Zwar bietet Saxon nicht den gesuchten Test, aber mit file-last-modified() eine Funktion, die einen Leerstring zurückgibt, wenn die Datei nicht vorhanden ist. Damit war die gesuchte Funktion schnell geschrieben:
<xsl:function name="misc:file-exists" as="xs:boolean" xmlns:saxon="http://saxon.sf.net/" extension-element-prefixes="saxon"> <xsl:param name="href" as="xs:string?"/> <xsl:value-of select="boolean(normalize-space(string(saxon:file-last-modified($href))))"/> </xsl:function> |
Zu beachten ist, dass diese Funktion nur mit Saxon funktioniert und keine Fehlerbehandlung stattfindet. Außerdem muss der Parameter URI-codiert werden (z.B. Leerzeichen durch %20
ersetzen). Es gibt also noch genug Raum für Optimierungen.
Nachtrag: Diese Funktion ist nun in der Beispielsammlung abgelegt.
Nachtrag II: saxon:file-last-modified()
ist spezifisch für Saxon 9.1. In Saxon 9.2 PE/EE wurde diese Funktion in saxon:last-modified()
integriert, in Saxon HE stehen keine Erweiterungsfunktionen zur Verfügung (vgl. Post von Michael Kay auf der Saxon-Mailingliste). Ich habe das Stylesheet entsprechend aktualisiert.
Nachtrag III: Eine Lösung unter Verwendung von Java, die weniger prozessorabhängig ist, habe ich hier beschrieben.
Nachtrag IV: Ich habe die verschiedenen Lösungen in der XSLT-SB zusammengeführt. Der Code kann in der Beispielsammlung eingesehen werden.