Antdoc – Ein Dokumentations-Stylesheet für Ant

(Auszug aus "Java und XSLT" von Eric M. Burke)

Apaches Ant hat die Java-Entwicklergemeinde im Sturm erobert, denn es unterstützt die traditionellen Java-Entwicklungsumgebungen und ersetzt die Makefiles in den meisten Java-Entwicklungsprojekten. Ant ist ein Konstruktionstool, ähnlich dem make-Programm, aber es nutzt XML-Dateien anstelle von Makefiles. Zusätzlich zu den portierbaren, XML-basierten build-Dateien ist Ant selbst in Java geschrieben und hat daher wenige plattformspezifische Abhängigkeiten. Last but not least: Ant kann für nahezu jeden Schritt im Konstruktionsprozeß dieselbe Laufzeitinstanz der Java Virtual Machine verwenden und ist so unglaublich schnell. Ant kann bei Apache Ant heruntergeladen werden und ist Open Source Software.

Ant-Grundlagen

Ant wird von einer XML-Konstruktionsdatei, auch Build-Datei genannt, gesteuert, die aus einem Projekt besteht. Das Projekt enthält ein oder mehrere Targets, die voneinander abhängig sein können. Das Projekt und die Targets werden in der XML-Kontruktionsdatei als <project> und <target> repräsentiert; <project> muß das Wurzelelement des Dokuments sein. Für gewöhnlich hat man ein prepare-Target, das die Ausgabeverzeichnisse erstellt, und ein compile-Target, das vom prepare-Target abhängt. Wenn Sie Ant anweisen, das compile-Target auszuführen, prüft es zunächst, ob das prepare-Target die nötigen Verzeichnisse erstellt hat. Die Struktur einer Ant-Konstruktionsdatei sieht etwa so aus:

<?xml version="1.0"?>
<project name="BeispielProjekt" default="compile" basedir=".">
   <!-- globale Eigenschaften -->
   <property name="srcdir" value="src"/>
   <property name="builddir" value="build"/>
   <target name="prepare" description="Erzeugt die Ausgabeverzeichnisse">
      ...einzelne Arbeitsschritte
   </target>
   <target name="compile" depends="prepare">
      ...einzelne Arbeitsschritte
   </target>
   <target name="distribute" depends="compile">
      ...einzelne Arbeitsschritte
   </target>
</project>

Ant ist so clever, daß es für jedes Target weiß, ob die Dateien modifiziert wurden und es folglich etwas tun muß. Bei der Kompilierung werden die Timestamps der .class-Dateien mit denen der .java-Dateien verglichen. Durch diese Abhängigkeiten kann Ant unnötige Kompilierung vermeiden und erreicht eine sehr gute Performance. Obwohl die hier gezeigten Targets nur einfache Abhängigkeiten haben, kann ein Target von verschiedenen anderen Targets abhängen:

<target name="X" depends="A,B,C">

Obwohl Ant-Konstruktionsdateien wesentlich simpler ausfallen als die korrespondierenden Makefiles, können komplexe Projekte viele Abhängigkeiten mit sich bringen, die nur schwer darstellbar sind. Es kann daher hilfreich sein, eine Liste aller Targets mit Abhängigkeiten, dargestellt in einer hierarchischen Baumstruktur, zu betrachten, wobei XSLT eingesetzt werden kann, um einen solchen Bericht zu erstellen.

Stylesheet-Funktionalität

Da die Konstruktionsdatei aus XML besteht, macht XSLT es sehr leicht, HTML-Webseiten zu erstellen, die die Targets und Abhängigkeiten zusammenfassen. Unser Stylesheet zeigt eine Liste von globalen Eigenschaften und kann leicht erweitert werden, um alle anderen Inhalte der Konstruktionsdatei anzuzeigen.

Obwohl dieses Stylesheet verschiedene nützliche HTML-Tabellen in seinen Bericht einbaut, liegen seine interessantesten Fähigkeiten darin, ein komplettes Abhängigkeitsdiagramm aller Ant-Targets auszugeben. Die Ausgabe für dieses Diagramm sehen Sie im folgenden Beispiel.

Beispiel: Target-Abhängigkeiten

clean
  all (abhängig von clean, dist)
prepare
  tomcat (abhängig von prepare)
    j2ee (abhängig von tomcat)
      j2ee-dist (abhängig von j2ee)
    main (abhängig von tomcat, webapps)
      dist (abhängig von , webapps)
       dist-zip (abhängig von dist)
       all (abhängig von clean, dist)
    webapps (abhängig von prepare)
      dist (abhängig von main, webapps)
       dist-zip (abhängig von dist)
       all (abhängig von clean, dist)
    main (abhängig von tomcat, webapps)
      dist (abhängig von main, webapps)
       dist-zip (abhängig von dist)
       all (abhängig von clean, dist)
targets

Das hier ist die Ausgabe für die Ant-Konstruktionsdatei von Apaches Tomcat. Die Liste der Top-Level-Targets wird auf Wurzelebene ausgegeben, abhängige Targets werden eingerückt und danach ausgegeben. Die Targets in Klammern zeigen an, wovon jedes Target abhängt. Diese Baumstruktur ist unter rekursiver Analyse der Abhängigkeiten erstellt worden. Die Abhängigkeiten tauchen in der Konstruktionsdatei wie folgt auf:

<target name="all" depends="clean,dist">

Die folgende Abbildung zeigt einen Teil der Ausgabe im Webbrowser. Dem Abhängigkeitsdiagramm folgt eine Tabelle, die alle Targets listet. Die Ausgabe schließt mit einer Tabelle aller globalen Eigenschaften, die in der Konstruktionsdatei definiert sind.

Abbildung: Antdoc-Beispielausgabe

Abbildung: Antdoc-Beispielausgabe

Die kommaseparierte Liste der Abhängigkeiten stellt eine Herausforderung dar, die am besten durch Rekursion bewältigt werden kann. Für jedes Target in der Konstruktionsdatei muß eine Liste der von ihm abhängenden Targets ausgegeben werden. Da es möglich ist, mehrere Abhängigkeiten zu haben, kann die Ant-Konstruktionsdatei ein <target> wie das folgende enthalten:

<target name="dokumentation" depends="clean, prepare.dokumentation, compile">

Im ersten Prototyp des Antdoc-Stylesheets verwendet der Algorithmus zur Ausgabe des Abhängigkeitsdiagramms einfache Substring-Operationen, um zu ermitteln, ob ein anderes Target vom aktuellen Target abhängt. Das stellt ein Problem dar, weil zwei nicht zueinander in Beziehung stehende Targets ähnliche Namen haben können und einige Ant-Konstruktionsdateien daher unendliche Rekursionen hervorrufen könnten. Im vorangegangenen Beispiel sagt der originale Prototyp von Antdoc, daß "dokumentationen" von sich selbst abhängt, denn seine Abhängigkeitsliste enthält den Text prepare.dokumentation.

In der fertigen Antdoc-Version wird die Liste der Target-Abhängigkeiten gesäubert, um Kommas und Leerzeichen zu entfernen. So wird beispielsweise "clean, prepare.dokumentation, compile" umgewandelt in "|clean|prepare.dokumentation|compile|". Durch Plazierung des Pipe-Symbols (|) vor und nach jeder Abhängigkeit wird es wesentlich einfacher, Abhängigkeiten durch Suche von Strings zu finden.

Das komplette Beispiel

Das komplette XSLT-Stylesheet ist in antdoc.xslt zu sehen. Kommentare innerhalb des Quelltexts erläutern, was bei jedem Schritt geschieht. Um dieses Stylesheet zu verwenden, rufen Sie Ihren bevorzugten XSLT-Prozessor von der Kommandozeile auf, und geben Sie antdoc.xslt und Ihre Ant-Konstruktionsdateien als Parameter an.

Beispiel: antdoc.xslt

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
   **************************************************************
   ** Antdoc v1.0
   **
   ** Erstellt von Eric Burke (burke_e@ociweb.com)
   **
   ** Verwendet XSLT, um HTML-Zusammenfassungen von Ant-Konstruktionsdateien
   ** zu erzeugen.
   *********************************************************** -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="ISO-8859-1"/>
   <!-- globale Variable: der Projektname -->
   <xsl:variable name="projektName" select="/project/@name"/>
   <xsl:template match="/">
      <html xmlns="http://www.w3.org/1999/xhtml">
         <head>
            <title>Ant-Projektzusammenfassung -
               <xsl:value-of select="$projektName"/></title>
         </head>
         <body>
            <h1>Ant-Projektzusammenfassung</h1>
            <xsl:apply-templates select="projekt"/>
         </body>
      </html>
   </xsl:template>
   <!--
      ***************************************************************
      ** "Projekt"-Template
      ************************************************************ -->
   <xsl:template match="project">
      <!-- Die Projektzusammenfassung wird mit Basisinformationen wie Name, Default-Target und Basisverzeichnis als Tabelle ausgegeben -->
      <table border="1" cellpadding="4" cellspacing="0">
         <tr><th colspan="2">Projektzusammenfassung</th></tr>
         <tr>
            <td>Projektname:</td>
            <td><xsl:value-of select="$projektName"/></td>
         </tr>
         <tr>
            <td>Default-Target:</td>
            <td><xsl:value-of select="@default"/></td>
         </tr>
         <tr>
            <td>Basisverzeichnis:</td>
            <td><xsl:value-of select="@basedir"/></td>
         </tr>
      </table>
      <!-- alle Target-Abhängigkeiten werden als Baum ausgegeben -->
      <h3>Target-Abhängigkeiten als Baum</h3>
      <xsl:apply-templates select="target[not(@depends)]" mode="baum">
         <xsl:sort select="@name"/>
      </xsl:apply-templates>
      <p/>
      <!-- Eine Tabelle mit allen Targets wird ausgegeben -->
      <table border="1" cellpadding="4" cellspacing="0">
         <tr><th colspan="3">Liste der Targets</th></tr>
         <tr>
            <th>Name</th>
            <th>Abhängigkeiten</th>
            <th>Beschreibung</th>
         </tr>
         <xsl:apply-templates select="target" mode="tabellenZeile">
            <xsl:sort select="count(@description)" order="descending"/>
            <xsl:sort select="@name"/>
         </xsl:apply-templates>
      </table>
      <p/>
      <xsl:call-template name="globaleEigenschaften"/>
   </xsl:template>
   <!--
      ***************************************************************
      ** Eine Tabelle mit allen globalen Eigenschaften wird erzeugt.
      ************************************************************ -->
   <xsl:template name="globaleEigenschaften">
      <xsl:if test="property">
         <table border="1" cellpadding="4" cellspacing="0">
            <tr><th colspan="2">Globale Eigenschaften</th></tr>
            <tr>
               <th>Name</th>
               <th>Wert</th>
            </tr>
            <xsl:apply-templates select="property" mode="tabellenZeile">
               <xsl:sort select="@name"/>
            </xsl:apply-templates>
         </table>
      </xsl:if>
   </xsl:template>
   <!--
      ***************************************************************
      ** Eine individuelle Eigenschaft wird in einer Tabellenzeile ausgegeben.
      ************************************************************ -->
   <xsl:template match="property[@name]" mode="tabellenZeile">
      <tr>
         <td><xsl:value-of select="@name"/></td>
         <td>
            <xsl:choose>
               <xsl:when test="not(@value)">
                  <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
               </xsl:when>
               <xsl:otherwise>
                  <xsl:value-of select="@value"/>
               </xsl:otherwise>
            </xsl:choose>
         </td>
      </tr>
   </xsl:template>
   <!--
      ***************************************************************
      ** "target"-Template, mode=tabellenZeile
      ** Ein Target-Name und seine Abhängigkeiten werden in
      ** einer Tabellenzeile ausgegeben.
      ************************************************************ -->
   <xsl:template match="target" mode="tabellenZeile">
      <tr valign="top">
         <td><xsl:value-of select="@name"/></td>
         <td>
            <xsl:choose>
               <xsl:when test="@depends">
                  <xsl:call-template name="abhaengigkeitenParsen">
                     <xsl:with-param name="depends" select="@depends"/>
                  </xsl:call-template>
               </xsl:when>
               <xsl:otherwise>-</xsl:otherwise>
            </xsl:choose>
         </td>
         <td>
            <xsl:if test="@description">
               <xsl:value-of select="@description"/>
            </xsl:if>
            <xsl:if test="not(@description)">
               <xsl:text>-</xsl:text>
            </xsl:if>
         </td>
      </tr>
   </xsl:template>
   <!--
      ***************************************************************
      ** "abhaengigkeitenParsen"-Template
      ** zerlegt den übergeben Parameter und gibt eine kommaseparierte
      ** Liste von Abhängigkeiten aus. Das erste Token wird ausgegeben,
      ** und die übrigen werden diesem Template rekursiv übergeben.
      ************************************************************ -->
   <xsl:template name="abhaengigkeitenParsen">
      <!-- Dieser Parameter enthält die Liste der Abhängigkeiten -->
      <xsl:param name="depends"/>
      <!-- Alles vor dem ersten Komma wird ausgewählt bzw. der ganze String, wenn es keine Kommas gibt. -->
      <xsl:variable name="erstesToken">
         <xsl:choose>
            <xsl:when test="contains($depends, ',')">
               <xsl:value-of select="normalize-space(substring-before($depends, ','))"/>
            </xsl:when>
            <xsl:otherwise>
               <xsl:value-of select="normalize-space($depends)"/>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:variable>
      <xsl:variable name="restlicheToken" select="normalize-space(substring-after($depends, ','))"/>
      <!-- die erste Abhängigkeit wird ausgegeben -->
      <xsl:value-of select="$erstesToken"/>
      <!-- Das Template wird rekursiv mit dem Rest der kommaseparierten Liste aufgerufen -->
      <xsl:if test="$restlicheTokens">
         <xsl:text>, </xsl:text>
         <xsl:call-template name="abhaengigkeitenParsen">
            <xsl:with-param name="depends" select="$restlicheTokens"/>
         </xsl:call-template>
      </xsl:if>
   </xsl:template>
   <!--
      ***************************************************************
      ** Dieses Template startet einen rekursiven Prozeß, der das
      ** Abhängigkeitsdiagramm für alle Targets erstellt.
      ************************************************************ -->
   <xsl:template match="target" mode="baum">
      <xsl:param name="einrueckungsTiefe" select="'0'"/>
      <xsl:variable name="aktName" select="@name"/>
      <div style="text-indent: {$einrueckungsTiefe}em;">
         <xsl:value-of select="$aktName"/>
         <!-- wenn das 'depends'-Attribut vorhanden ist, wird die Liste der Abhängigkeiten angezeigt -->
         <xsl:if test="@depends">
            <xsl:text> (abhängig von </xsl:text>
            <xsl:call-template name="abhaengigkeitenParsen">
               <xsl:with-param name="depends" select="@depends"/>
            </xsl:call-template>
            <xsl:text>)</xsl:text>
         </xsl:if>
      </div>
      <!-- die Einrückung vornehmen -->
      <xsl:variable name="naechsteEbene" select="$einrueckungsTiefe+1"/>
      <!-- Alle anderen <target>-Elemente, die "depends"-Attribute haben, werden durchsucht -->
      <xsl:for-each select="../target[@depends]">
         <!-- Die kommaseparierte Liste der Abhängigkeiten wird "bereinigt". Siehe auch die Kommentare des "korrigiereAbhaengigkeit"-Templates -->
         <xsl:variable name="korrekteAbhaengigkeit">
            <xsl:call-template name="korrigiereAbhaengigkeit">
               <xsl:with-param name="depends" select="@depends"/>
            </xsl:call-template>
         </xsl:variable>
         <!-- Nun ist die Abhängigkeitsliste durch Pipes (|) begrenzt, was die Suche nach Substrings einfacher macht. Dieses Template wird für alle Targets, die vom aktuellen Target abhängen, rekursiv instantiiert. -->
         <xsl:if test="contains($korrekteAbhaengigkeit,concat('|',$aktName,'|'))">
            <xsl:apply-templates select="." mode="baum">
               <xsl:with-param name="einrueckungsTiefe" select="$naechsteEbene"/>
            </xsl:apply-templates>
         </xsl:if>
      </xsl:for-each>
   </xsl:template>
   <!--
      ***************************************************************
      ** Dieses Template nimmt eine kommaseparierte Liste von Abhängigkeiten an
      ** und wandelt alle Kommas in Pipe-Symbole (|) um. Es entfernt auch
      ** alle Leerzeichen. Zum Beispiel:
      **
      ** Eingabe: depends="a, b,c "
      ** Ausgabe: |a|b|c|
      **
      ** Der resultierende Text ist mit XSLT leichter zu parsen.
      ************************************************************ -->
   <xsl:template name="korrigiereAbhaengigkeiten">
      <xsl:param name="depends"/>
      <!-- Alles vor dem ersten Komma wird ausgewählt bzw. der ganze String, wenn es keine Kommas gibt. -->
      <xsl:variable name="erstesToken">
         <xsl:choose>
            <xsl:when test="contains($depends, ',')">
               <xsl:value-of select="normalize-space(substring-before($depends, ','))"/>
            </xsl:when>
            <xsl:otherwise>
               <xsl:value-of select="normalize-space($depends)"/>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:variable>
      <!-- Eine Variable wird definiert, die alles nach dem ersten Komma enthält -->
      <xsl:variable name="restlicheToken" select="normalize-space(substring-after($depends, ','))"/>
      <xsl:text>|</xsl:text>
      <xsl:value-of select="$erstesToken"/>
      <xsl:choose>
         <xsl:when test="$restlicheToken">
            <xsl:call-template name="korrigiereAbhaengigkeit">
               <xsl:with-param name="depends" select="$restlicheToken"/>
            </xsl:call-template>
         </xsl:when>
         <xsl:otherwise>
            <xsl:text>|</xsl:text>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

XHTML-Ausgabe festlegen

Eins der ersten Dinge, die das Stylesheet macht, ist die Ausgabemethode auf "xml" zu setzen, da die resultierende Seite statt in HTML in XHTML vorliegen soll. Die Attribute doctype-public und doctype-system sind für gültiges XHTML erforderlich und geben an, daß die Strict-DTD verwendet wird:

<xsl:output method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="ISO-8859-1"/>

Eine weitere XHTML-Anforderung ist die Deklaration des Namensraums des <html>-Elements:

<xsl:template match="/">
   <html xmlns="http://www.w3.org/1999/xhtml">
      ...
   </html>
</xsl:template>

Durch diese XSLT-Elemente enthält der Ergebnisbaum das folgende XHTML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
   ...
</html>

Das Abhängigkeitsdiagramm erzeugen

Der interessanteste und zugleich schwierigste Aspekt dieses Stylesheets ist seine Fähigkeit, das komplette Abhängigkeitsdiagramm für alle Ant-Targets auszugeben. Der erste Schritt ist dabei die Lokalisierung aller Targets, die keine Abhängigkeiten haben. Wie im Beispiel Target-Abhängigkeiten gezeigt, heißen diese Targets in der Tomcat-Konstruktionsdatei clean, prepare und targets. Sie werden ausgewählt durch Suche nach <target>-Elementen, die kein Attribut mit Namen depends haben:

<!-- Alle Abhängigkeiten als Baum ausgeben -->
<h3>Target Abhängigkeitsbaum</h3>
<xsl:apply-templates select="target[not(@depends)]" mode="baum">
   <xsl:sort select="@name"/>
</xsl:apply-templates>

Das Prädikat [not(@depends)] verfeinert die Liste der <target>-Elemente, so daß nur die Elemente ohne depends-Attribut darin enthalten sind. <xsl:apply-templates> instantiiert das folgende Template ohne Parameter:

<xsl:template match="target" mode="baum">
 <xsl:param name="einrueckungsTiefe" select="'0'"/>
 <xsl:variable name="aktName" select="@name"/>

Wenn Sie sich das Beispiel antdoc.xslt anschauen, sehen Sie, daß dies das zweitletzte Template im Stylesheet ist. Weil es hier in viele Stücke heruntergebrochen wurde, ist es wahrscheinlich einfacher, sich den Original-Quelltext anzusehen, als diese Beschreibung. Da der Parameter einrueckungsTiefe nicht angegeben wurde, hat er den Standardwert '0', was für Top-Level-Targets durchaus Sinn macht. Wenn dieses Template rekursiv instantiiert wird, nimmt die Einrückungstiefe zu. Die Variable aktName ist für dieses Template lokal und enthält den Namen des aktuellen Ant-Targets. Die Textzeilen werden mit einem style-Attribut eingerückt:

<div style="text-indent: {$einrueckungsTiefe}em;">

Mittels CSS wird hier alles innerhalb des <div>-Tags um den in ems (ein em entspricht in etwa der Breite eines klein geschriebenen »m« im aktuellen Font) angegebenen Wert eingerückt. Der Wert des aktuellen Target-Namens wird dann mit der korrekten Einrückung ausgegeben:

<xsl:value-of select="$aktName"/>

Wenn das aktuelle <target>-Element in der Ant-Konstruktionsdatei ein depends-Attribut hat, werden seine Abhängigkeiten neben dem Target-Namen als Teil des Berichts ausgegeben. Diese Aufgabe übernimmt das abhaengigkeitenParsen-Template. Dieses Template, ebenfalls Teil von antdoc.xslt, wird mit <xsl:call-template> instantiiert, wie hier gezeigt:

<xsl:if test="@depends">
 <xsl:text> (abhängig von </xsl:text>
 <xsl:call-template name="abhaengigkeitenParsen">
  <xsl:with-param name="depends" select="@depends"/>
 </xsl:call-template>
 <xsl:text>)</xsl:text>
</xsl:if>

Um mit dem Abhängigkeitsdiagramm fortzufahren, muß das target-Template sich selbst rekursiv instantiieren. Bevor das geschieht, muß die Einrücktiefe erhöht werden. Da in XSLT Variablen nicht modifiziert werden können, wird eine neue Variable erzeugt:

<xsl:variable name="naechsteEbene" select="$einrueckungsTiefe+1"/>

Wenn das Template rekursiv instantiiert wird, wird naechsteEbene als Parameter für den Parameter einrueckungsTiefe übergeben:

<xsl:apply-templates select="." mode="baum">
 <xsl:with-param name="einrueckungsTiefe" select="$naechsteEbene"/>
</xsl:apply-templates>

Der Rest dieses Templates wird hier nicht nochmal aufgeführt, ist aber im Beispiel antdoc.xslt dargestellt. Der grundlegende Algorithmus lautet wie folgt:

  • Mit <xsl:for-each> werden alle Targets mit Abhängigkeiten selektiert.
  • Das »korrigiereAbhaengigkeit«-Template wird instantiiert, um Kommas durch |-Symbole zu ersetzen.
  • Das »target«-Template wird für alle vom aktuellen Target abhängenden Targets rekursiv instantiiert.

Abhängigkeitslisten säubern

Das letzte Template im Antdoc-Stylesheet ist dafür zuständig, eine kommaseparierte Liste von Abhängigkeiten in einzelne Token zu zerlegen, indem es ein Pipe-Symbol (|) zwischen den Abhängigkeiten einfügt:

<xsl:template name="korrigiereAbhaengigkeit">
  <xsl:param name="depends"/>

Der depends-Parameter kann Text wie »a, b, c« enthalten. Das Template erzeugt aus diesem Text dann folgende Ausgabe:

|a|b|c|

Da XSLT kein Äquivalent zu Javas StringTokenizer-Klasse kennt, wird wieder Rekursion benötigt. Zuerst wird der Text vor dem ersten Komma verarbeitet und danach rekursiv alles hinter dem Komma. Der folgende Code weist alles vor dem ersten Komma der Variable erstesToken zu:

<xsl:variable name="erstesToken">
   <xsl:choose>
      <xsl:when test="contains($depends, ',')">
         <xsl:value-of select="normalize-space(substring-before($depends, ','))"/>
      </xsl:when>
      <xsl:otherwise>
         <xsl:value-of select="normalize-space($depends)"/>
      </xsl:otherwise>
   </xsl:choose>
</xsl:variable>

Wenn der depends-Parameter ein Komma enthält, lokalisiert die substring-before( )-Funktion den Text vor dem Komma, und normalize-space( ) entfernt Whitespace. Werden keine Kommas gefunden, kann es nur eine Abhängigkeit geben.

Danach wird jeglicher Text nach dem ersten Komma der Variable restlicheToken zugewiesen. Wenn es keine Kommas gibt, enthält restlicheToken einen leeren String:

<xsl:variable name="restlicheToken" select="normalize-space(substring-after($depends, ','))"/>

Das Template gibt dann ein Pipe-Symbol gefolgt vom ersten Token aus:

<xsl:text>|</xsl:text>
<xsl:value-of select="$erstesToken"/>

Als nächstes wird, wenn die Variable restlicheToken nicht leer ist, das Template korrigiereAbhaengigkeit rekursiv instantiiert. Ansonsten wird ein weiteres Pipe-Symbol am Ende ausgegeben:

<xsl:choose>
 <xsl:when test="$restlicheToken">
  <xsl:call-template name="korrigiereAbhaengigkeit">
   <xsl:with-param name="depends" select="$restlicheToken"/>
  </xsl:call-template>
 </xsl:when>
 <xsl:otherwise>
  <xsl:text>|</xsl:text>
 </xsl:otherwise>
</xsl:choose>

Im Idealfall helfen diese Beschreibungen, einige der komplexeren Aspekte dieses Stylesheets aufzuklären. Der einzige Weg zu lernen, wie das alles wirklich funktioniert, führt über das Experimentieren. Verändern Sie Teile des XSLT-Stylesheets, und betrachten Sie sich die Ergebnisse dann in einem Webbrowser. Sie sollten auch einen Kommandozeilen-XSLT-Prozessor ausprobieren und die Ergebnisse dann in einem Texteditor betrachten. Das ist wichtig, weil Browser Tags überspringen, die sie nicht verstehen, und Sie so eventuelle Fehler erst sehen, wenn Sie sich den Quelltext anschauen.

   

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Java & XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Java und XSLT" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de