Ein Prototyp für die XML-Daten

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

   

   

Wenn die Anforderungen und der Informationsfluss spezifiziert sind, kann man im Entwurfsprozess weiter ins Detail gehen. Auf XSLT basierende Websites sind modular, so daß sowohl Entwurf als auch Implementierung auf mehrere Mitglieder eines Entwicklungsteams aufgeteilt werden können. Jede Komponente kann unabhängig von den anderen entwickelt und getestet werden, bevor die Teile schließlich zu einer Webanwendung zusammengefügt werden.

XHTML-Prototypen

Die Prototypen der Nutzerschnittstellen zu entwerfen ist eine der ersten Aufgaben und kann von weniger erfahrenen Programmierern oder Webdesignern übernommen werden. An dieser Stelle wird noch kein komplexes Webinterface benötigt. Die letzten Feinheiten können später durch das Anpassen der XSLT-Stylesheets hinzugefügt werden. Tatsächlich können zu viele Details zu diesem Zeitpunkt vom wesentlichen Entwurf der XML- und XSLT-Daten ablenken.

Da die Nutzerschnittstelle mit XHTML Strict entworfen wird, benötigen wir ein separates Cascading Style Sheet (CSS), um die Seiten ansprechender gestalten zu können. (Weitere Informationen zu XHTML Strict finden Sie unter XHTML™ 1.0 The Extensible HyperText Markup Language.) Die strikte Variante von XHTML verbietet die meisten HTML 4.0 Format-Tags und legt somit die Verwendung von CSS nahe. Das folgende Beispiel enthält die CSS-Datei, wie sie vom Diskussionsforum verwendet wird.

Beispiel: forum.css

body {
  font-family : Verdana, Geneva, Arial, Helvetica, sans-serif;
}

.box1 {
  border: 3px solid Navy;
  text-align: center;
  padding: 4px;
  margin : 2px;
  background-color: #c0c0c0;
 }

.box2 {
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

h1 {
  font-size: 22pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h2 {
  font-size: 18pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

h3 {
  font-size: 14pt;
  font-weight: normal;
  margin: 0px 0px 0px 0px;
}

ul {
  margin-top: 0px;
}

.msgSummaryLine {
  font-size: smaller;
  font-weight: normal;
}

a:hover {
  background-color:yellow;
}

.error {
  font-weight: bold;
  color: red;
}

Jede der XHTML-Webseiten verweist auf diese CSS-Datei:

<link type="text/css" rel="stylesheet" href="/forum/forum.css" />

Durch diese Technik bleiben XSLT-Stylesheets und XHTML-Seiten klein und überschaubar. Änderungen an Schriftarten und Farben werden in einer einzigen CSS-Datei vorgenommen und wirken sich sofort auf die ganze Webanwendung aus. Das einzige Problem sind Webbrowser, die die CSS-Spezifikation nicht korrekt umsetzen. Obwohl die Unterstützung von CSS besser wird, müssen Webseiten auf einer Vielzahl von Browsern getestet werden, um Formatierungsfehler aufzuspüren.

Hinweis:
Ein roter Faden, der sich durch dieses Buch zieht, ist die von XSLT unterstützte Trennung von Daten und Präsentation. CSS erweitert diese Möglichkeiten, indem der Inhalt von XHTML von vielen Aspekten der visuellen Präsentation getrennt wird. CSS und XSLT sind sehr unterschiedliche Technologien, die sich aber perfekt ergänzen.

CSS-Dateien sind oft selbsterklärend. Der Stil h2 gilt z.B. für alle <h2>-Elemente im XHTML-Code. Ein Stilelement, mit dem viele Programmierer wahrscheinlich nicht vertraut sind, ist:

.box2 {
  border: 1px solid Navy;
  padding: 4px;
  margin: 2px;
  background-color: #FFFFCC;
}

Der Punkt in .box2 steht für die Definition einer Style-Klasse. Die Style-Klasse box2 wird in XHTML folgendermaßen benutzt:

<div class="box2">Nachrichten vom März 2001</div>

Der Vorteil einer Style-Klasse ist, daß sie auf jedes XHTML-Element angewendet werden kann. In diesem Fall wird jedes Element der Klasse box2 mit einem dünnen Rahmen und einem gelben Hintergrund dargestellt.

Die Webdesigner sollten an dieser Stelle grundlegende Repräsentationen für jede Seite der Anwendung entwerfen. Die folgende Abbildung zeigt die Homepage der Anwendung.

Prototyp der Homepage

Abbildung: Prototyp der Homepage

Die vollständigen XHTML-Quellen dieser Homepage finden Sie im folgenden Beispiel. Dieser Code enthält noch keine gültigen Hyperlinks, denn der Entwurf für das Servlet wurde noch nicht gemacht, und die endgültigen URLs sind noch nicht bekannt. Es handelt sich aber auch nur um einen Code-Entwurf, denn letztendlich werden die XHTML-Webseiten durch eine XSLT-Transformation dynamisch aus XML-Daten erzeugt.

Beispiel: XHTML-Quellcode der Homepage

<?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">
   <head>
      <title>Diskussionsforum Home</title>
      <link href="../docroot/forum.css" rel="stylesheet" type="text/css" />
   </head>
   <body>
      <div class="box1">
         <h1>Diskussionsforum Home</h1>
      </div>
      <div class="box2">
         <h2>Java-Programmierung</h2>
         <div>Allgemeine Fragen zur Java-Programmierung</div>
         <div style="margin-left: 2em;">
            <p><a href="link_to_post_message">Nachricht senden</a> an Java-Programmierung</p>
            <a href="link_to_feb_messages">Feb 2001</a> | <a href="link_to_mar_messages">Mär 2001</a> | <a href="link_to_apr_messages">Apr 2001</a>
         </div>
      </div>
      <div class="box2">
         <h2>XSLT-Grundlagen</h2>
         <div>Effektive XSLT-Stylesheets schreiben</div>
         <div style="margin-left: 2em;">
            <p><a href="link_to_post_message">Nachricht senden</a> an XSLT-Grundlagen</p>
            <a href="link_to_feb_messages">Feb 2001</a> | <a href="link_to_mar_messages">Mär 2001</a> | <a href="link_to_apr_messages">Apr 2001</a>
         </div>
      </div>
      <div class="box2">
         <h2>Beispiel-Board</h2>
         <div>Dieses Board enthält keine Nachrichten</div>
         <div style="margin-left: 2em;">
            <p><a href="link_to_post_msg">Nachricht senden</a> an Beispiel-Board</p>
         </div>
      </div>
   </body>
</html>

Die Tags <div> und <span> waren vielen HTML-Autoren unbekannt, bevor CSS so weit verbreitet war wie heute. Das Element <div> umschließt eine beliebige Anzahl von anderen Elementen und erzeugt so ein Gruppenelement. Das Element <span> wird ähnlich verwendet, ist aber ein Inline-Element, d.h. es ist Bestandteil einer bestehenden Zeile, während das <div>-Element, ähnlich den Elementen <p> und <h1>, eine neue Zeile erzeugt. Die Möglichkeit, Style-Klassen zu definieren, macht <div> und <span> besonders im Zusammenhang mit XHTML Strict attraktiv, da hier in HTML 4.0 veraltete Elemente wie <font> nicht zugelassen werden. Während <span> in unserem Beispiel keine Verwendung findet, wird <div> häufig zum Erzeugen von Zeilenumbrüchen und der Anwendung von CSS-Styles benutzt.

Der nächste Prototyp zeigt, wie ein Nachrichten-Board aussieht, siehe die folgenden Abbildung. Der XHTML-Quellcode der restlichen Seiten wird nicht abgedruckt.

Prototyp der Monatsübersicht

Abbildung: Prototyp der Monatsübersicht

Antworten auf Nachrichten werden um einige Leerzeichen eingerückt. Später können mit einfachen Änderungen an den XSLT-Stylesheets graphische Ordner oder andere Symbole vor jeder Nachricht dargestellt werden. Auf der nächsten Seite, siehe die folgende Abbildung, können Nutzer neue Nachrichten an das Diskussionsforum abschicken.

Prototyp der Seite »Nachricht senden«

Abbildung: Prototyp der Seite »Nachricht senden«

Diese Seite wird auch verwendet, um auf eine existierende Nachricht zu antworten. Der Titel wird dann zu »Antworten« und die Felder Thema und Nachricht werden mit den Einträgen der Originalnachricht vorbelegt. Wenn der Nutzer die Nachricht mit fehlenden Einträgen abschickt, wird die Webseite mit einer Fehlermeldung noch einmal dargestellt.

Die letzte Seite ist in folgender Abbildung zu sehen. Hier können Nutzer existierende Nachrichten anschauen.

Prototyp für die Seite »Nachricht anzeigen«

Abbildung: Prototyp für die Seite »Nachricht anzeigen«

XML-Beispiele

Während Graphiker das Layout der XHTML-Seiten gestalten, kann jemand anders beispielhafte XML-Daten für die Webseiten erstellen. Auch wenn diese Aufgaben auf unterschiedliche Personen übertragen werden können, ist hier ein Minimum an Abstimmung unumgänglich. Beim Layout der XHTML-Seiten muß darauf geachtet werden, daß die XML-Struktur auch alle nötigen Daten für deren Erstellung bereitstellt. Die Personen, die die XML-Daten entwerfen, müssen auch mit den Entwicklern der Datenbank kooperieren, um sicherzustellen, daß die geforderten Daten überhaupt verfügbar sind.

Beim Entwurf des XML-Codes sollte der Schwerpunkt immer auf den Daten und nicht auf der Präsentation liegen. All die Farben und Fonts, die im CSS spezifiziert werden, sollten keinerlei Einfluß auf den Entwurf des XML-Codes haben. Auf der anderen Seite wird der XML-Code auch Daten enthalten, die nicht dargestellt werden. Damit das Servlet herausfinden kann, welche Nachricht dargestellt werden soll, benötigen Hyperlinks z. B. eine Identifizierung. Die XML-Daten enthalten diese Identifizierung, aber das XHTML-Markup für den Link wird von einem XSLT-Stylesheet erzeugt.

Das folgende Beispiel zeigt den XML-Code für die Homepage. Da dieser keine Informationen für die Präsentation enthält, ist er viel kürzer als der XHTML-Code.

Beispiel: home.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="../xslt/home.xslt"?>
<home>
  <board id="0">
    <name>Java-Programmierung</name>
    <description>Allgemeine Fragen zur Java-Programmierung</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="1">
    <name>XSLT-Grundlagen</name>
    <description>Effektive XSLT-Stylesheets schreiben</description>
    <messages month="1" year="2001"/>
    <messages month="2" year="2001"/>
    <messages month="3" year="2001"/>
  </board>
  <board id="3">
    <name>Beispiel-Board</name>
    <description>Dieses Board enthält keine Nachrichten</description>
  </board>
</home>

Vergessen Sie nicht, daß dies nur ein Prototyp einer XML-Datei ist. Wenn die Anwendung fertiggestellt ist, werden die XML-Daten dynamisch von JDOM erzeugt. Der Prototyp dient dem Testen und Entwickeln.

In den XML-Daten besteht jedes Nachrichten-Board aus dem Element <board> mit dem Attribut id. Wenn der Nutzer den Link »Nachricht senden« betätigt, wird diese ID benutzt, um das Board zu ermitteln, an das die Nachricht gerichtet ist. Die Liste der <messages>-Elemente repräsentiert die Monate, für die Nachrichten in der Datenbank vorhanden sind. Hier wird keine ID benötigt, da im Hyperlink der Monat und das Jahr verwendet werden.

Die zweite Zeile der XML-Datei stellt die Verknüpfung zum XSLT-Stylesheet her:

<?xml-stylesheet type="text/xsl" href="../xslt/home.xslt"?>

Dies wird in der endgültigen Anwendung nicht so umgesetzt, ist aber während der Entwicklung der Prototypen durchaus sinnvoll. Durch die Angabe des Stylesheets kann die Transformation von einem XSLT-kompatiblen Webbrowser durchgeführt werden, wenn die XML-Seite geladen wird.

Iterativer Entwurf
Die Beispiele in diesem Kapitel sind das Ergebnis mehrerer Anläufe, einen guten Entwurf zu finden. Der Software-Entwurf ist in der Regel ein iterativer Prozeß, bei dem es mehrerer Anläufe bedarf, bevor man zu einem zufriedenstellenden Ergebnis kommt. In unserem Fall bezieht sich dieser Prozeß auf den Inhalt der XHTML-, XML- und XSLT-Dateien:

  • Erstellen von Prototypen für die Webseiten mit HTML oder XHTML
  • Erstellen von XML-Prototypen und evtl. DTDs
  • Erstellen von XSLT-Stylesheets, die XML nach XHTML transformieren
  • Erstellen von Backend-Datenquellen und Klassen, die daraus XML-Daten erzeugen
  • Erstellen von Servlets als Bindeglieder der einzelnen Komponenten

Während der Implementierung der einzelnen Teile machen sich fehlende oder redundante Merkmale in anderen Teilen bemerkbar. Hier beginnt der iterative Prozeß. Wenn etwas beim ersten Durchlauf nicht korrekt erscheint, wird es einfach überarbeitet, und verschiedene Prozesse werden wiederholt, bis alle Teile zusammenpassen.

Die nächste XML-Datei enthält die Daten für die Seite »Monatsübersicht«, siehe das folgende Beispiel.

Beispiel: viewMonth.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMonth.xslt"?>
<viewMonth month="1" year="2001">
   <board id="1">
      <name>Java-Programmierung</name>
      <description>Allgemeine Fragen zur Java-Programmierung</description>
   </board>
   <message id="1" day="1">
      <subject>erste Testnachricht</subject>
      <authorEmail>burke_e@yahoo.com</authorEmail>
      <message id="2" day="2">
         <subject>Re: erste Testnachricht</subject>
         <authorEmail>aidan@nirgends.de</authorEmail>
      </message>
   </message>
   <message id="3" day="4">
      <subject>Zweite Testnachricht</subject>
      <authorEmail>burke_e@yahoo.com</authorEmail>
   </message>
</viewMonth>

Im folgenden Beispiel werden die XML-Daten der Seite »Nachricht senden/Antworten« gezeigt.

Beispiel: postMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/postMsg.xslt"?>
<postMsg>
   <board id="1">
      <name>Java-Programmierung</name>
      <description>Beschreibung des Boards</description>
   </board>
   <inResponseTo id="4">
      <subject>Testthema</subject>
   </inResponseTo>
   <error code="ALL_FIELDS_REQUIRED"/>
   <prefill>
      <subject>Testthema</subject>
      <authorEmail></authorEmail>
      <message>Meine Nachricht</message>
   </prefill>
</postMsg>

Diese XML-Datei wird sowohl für das Versenden neuer Nachrichten als auch für das Antworten auf alte Nachrichten verwendet, denn die Webseiten sind nahezu identisch und die Daten sind in beiden Fällen dieselben. Die Elemente <error> und <prefill> waren im ursprünglichen Prototyp nicht vorhanden. Sie werden dann benötigt, wenn der Nutzer nicht alle benötigten Felder ausgefüllt hat. Beim ersten Anzeigen der Seite »Nachricht senden« sind diese XML-Elemente nicht vorhanden. Falls der Nutzer aber den Senden-Button betätigt und ein Feld nicht ausgefüllt wurde, werden diese Elemente in die XML-Daten aufgenommen, und die Seite wird noch einmal dargestellt.

Das folgende Beispiel zeigt die XML-Daten der Seite »Nachrichtenansicht«.

Beispiel: viewMsg.xml

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="../xslt/viewMsg.xslt"?>
<message id="5" month="1" day="4" year="2001">
   <board id="1">
      <name>Java-Programmierung</name>
   </board>
   <inResponseTo id="4">
      <subject>Testnachricht</subject>
   </inResponseTo>
   <subject>Re: Testthema</subject>
   <authorEmail>burke_e@yahoo.com</authorEmail>
   <text>Das ist der Text einer Testnachricht.</text>
</message>

Schon beim ersten Anschauen fällt auf, daß postMsg.xml und viewMsg.xml viele Ähnlichkeiten besitzen. Einige wenige Änderungen an einer der XML-Dateien erlaubt es uns später, denselben JDOM-Code zu verwenden, um beide Seiten zu erzeugen. Als Alternative können beide Seiten weiterhin getrennt werden, wodurch aber mindestens eine zusätzliche Java-Klasse benötigt wird. Der Vorteil einer Trennung wäre ein übersichtlicher Code zum Erzeugen der XML-Daten, der keine if/else-Anweisungen zum Unterscheiden der unterschiedlichen Arbeitsmodi benötigt.

XSLT-Stylesheets

Ein weiteres Mitglied des Entwicklungsteams kann die XSLT-Stylesheets entwerfen, allerdings muß er warten, bis Prototypen der XML- und der XHTML-Daten fertiggestellt sind. Ziemlich oft wird der Entwickler der XML-Daten auch die ersten XSLT-Stylesheets entwerfen.

An dieser Stelle ist ein Werkzeug wie XMLSpy von großem Wert. (XMLSpy ist ein kommerzieller XML-Editor, der für die XSLT-Entwicklung gut geeignet ist.) Die Möglichkeit, ein XSLT-Stylesheet zu bearbeiten und die Auswirkungen durch Betätigen des Aktualisieren-Buttons in einer IDE darzustellen, kann die Arbeit sehr erleichtern. Auch mit einem XSLT-kompatiblen Webbrowser können Änderungen an Stylesheets sofort dargestellt werden. Wie schon beschrieben, unterstützt Microsofts Internet Explorer 5.x XSLT, vorausgesetzt der aktualisierte Parser msxml wurde mit dem Werkzeug xmlinst installiert. (Als dieses Buch geschrieben wurde, befand sich der IE 6.0 im Beta-Test. Er unterstützt die neueste XSLT-Spezifikation. Auch der Mozilla-Browser wird bald XSLT unterstützen.)

Das folgende Beispiel zeigt die XSLT-Datei für die Homepage des Diskussionsforums.

Beispiel: XSLT-Datei für die Homepage

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
   ***********************************************************
   ** home.xslt
   ** Transformiert die Homepage nach XHTML
   *********************************************************** -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
   <xsl:import href="utils.xslt"/>
   <xsl:param name="rootDir" select="'../docroot/'"/>
   <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
   <!--
      **********************************************************
      ** Erzeuge die XHTML-Webseite
      ******************************************************* -->
   <xsl:template match="/">
      <html>
         <head>
            <title>Diskussionsforum Home</title>
            <link href="{$rootDir}forum.css" rel="stylesheet" type="text/css"/>
         </head>
         <body>
            <div class="box1">
               <h1>Diskussionsforum Home</h1>
            </div>
            <xsl:apply-templates select="home/board"/>
         </body>
      </html>
   </xsl:template>
   <!--
      **********************************************************
      ** Erzeuge eine Box für jedes Board im Diskussionsforum
      ********************************************************** -->
   <xsl:template match="board">
      <xsl:variable name="boardID" select="@id"/>
      <div class="box2">
         <h2><xsl:value-of select="name"/></h2>
         <div><xsl:value-of select="description"/></div>
         <div style="margin-left: 2em;">
            <!-- erzeuge einen Link, mit dem der Nutzer eine neue Nachricht an dieses Board versenden kann -->
            <p><a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Nachricht senden</a> to <xsl:value-of select="name"/></p>
            <!-- zeige für jeden Monat, der eine Nachricht enthält, den Monatsnamen und die Jahreszahl als Link an -->
            <xsl:for-each select="messages">
               <a href="forum/main/viewMonth?boardID={$boardID}&amp;month={@month}&amp;year={@year}">
                  <xsl:call-template name="utils.printShortMonthName">
                     <xsl:with-param name="monthNumber" select="@month"/>
                  </xsl:call-template>
                  <xsl:text>, </xsl:text>
                  <xsl:value-of select="@year"/>
               </a>
               <!-- schreibe ein Pipe-Zeichen nach jedem außer dem letzten Monat -->
               <xsl:if test="position() != last( )">
                  <xsl:text> | </xsl:text>
               </xsl:if>
            </xsl:for-each>
         </div>
      </div>
   </xsl:template>
</xsl:stylesheet>

Das Stylesheet beginnt mit dem üblichen Tag <xsl:stylesheet> und importiert dann utils.xslt. Dieses Stylesheet enthält Templates zur Formatierung von Datumsangaben. Da diese Hilfs-Templates für fast jede Seite benötigt werden, wurden sie in einer eigenen Datei definiert, die, wie hier gezeigt, importiert werden kann. Das Stylesheet übernimmt den Parameter rootDir, mit dem die Webanwendung das Wurzelverzeichnis des Dokuments spezifizieren kann:

<xsl:param name="rootDir" select="'../docroot/'"/>

Das Attribut select definiert einen Standardwert für diesen Parameter, falls er nicht angegeben wird. Während der Entwicklung des Stylesheets kann der XSLT-Code mit der statischen XML-Datei getestet werden. Das geschieht außerhalb der Webanwendung, so daß der Parameter nicht angegeben wird und das Wurzelverzeichnis auf ../docroot/ verweist. Dadurch kann die CSS-Datei während der Entwicklung lokalisiert werden, wenn auf einer statischen Verzeichnisstruktur auf dem lokalen Dateisystem gearbeitet wird. Wenn das XSLT-Stylesheet später als Teil einer Webanwendung eingesetzt wird, kann das Servlet einen anderen Wert für diesen Parameter festlegen, der sich auf ein Verzeichnis relativ zum Kontext der Webanwendung bezieht. Diese Technik ist nützlich, wenn ein Stylesheet externe Ressourcen, wie CSS-Dateien, JavaScript-Dateien oder Bilder, referenziert.

Als nächstes wird mit dem Element <xsl:output> die XHTML-Ausgabe konfiguriert. Es wird die DTD XHTML 1.0 Strict verwendet, die viele Features von HTML 4.0 verbietet. Weil diese strikte DTD viele Formatierungstags nicht kennt, wird eine CSS-Datei benötigt, um die Seiten zu formatieren. Das XSLT-Stylesheet muß nun nur noch HTML-Code erzeugen, der das externe Stylesheet referenziert:

<html>
   <head>
      <title>Diskussionsforum Home</title>
      <link href="{$rootDir}forum.css" rel="stylesheet" type="text/css"/>
   </head>

Dabei wird die CSS-Datei nicht etwa vom XSLT-Prozessor verarbeitet. Aus der Perspektive von XSLT ist <link> nur ein Element, das während der Transformation in den Ergebnisbaum kopiert wird. Erst wenn der Webbrowser die XHTML-Seite darstellt, wird die CSS-Datei tatsächlich geladen. Auf diese Weise können Styles von allen Webseiten verwendet werden, ohne daß die XSLT-Stylesheets an Komplexität zunehmen.

Der Rest des Stylesheets ist ziemlich einfach. Es wird nach Mustern im XML-Code gesucht und der entsprechende XHTML-Inhalt im Ergebnisbaum erzeugt. Interessant ist vielleicht noch die Art, wie Hyperlinks erzeugt werden:

<a href="/forum/main/postMsg?mode=postNewMsg&amp;boardID={@id}">Nachricht senden</a>

Da das Ampersand-Zeichen (&) im Wert eines XML-Attributes nicht zugelassen ist, muß es mit dem eingebauten Entitiy &amp; dargestellt werden. Browser können damit umgehen, und auch der Hyperlink funktioniert problemlos. (Wir kommen darauf unter Java, XSLT und WML beim Thema WML zurück.)

Was ist die URL?
Sie fragen sich vielleicht, wie die einzelnen Hyperlinks aussehen sollen. Tatsächlich können Sie das zu diesem Zeitpunkt noch nicht wissen, so daß Ihre Links ungefähr so aussehen könnten:

<a href="TODO: Link zum Versenden einer neuen Nachricht">Nachricht senden</a>

Für diesen Zeitpunkt ist das hinreichend, denn Sie kennen die Links nicht, bevor das Servlet komplett entworfen wurde. Ein Teil des Servlet-Entwurfs besteht im Ermitteln der nötigen Parameter und deren gültiger Werte. Bevor diese Arbeit fertiggestellt ist, reicht ein »TODO«-Kommentar völlig aus.

Ein anderer Teil des Stylesheets zeigt, wie ein Hilfs-Template aufgerufen wird:

<xsl:call-template name="utils.printShortMonthName">
  <xsl:with-param name="monthNumber" select="@month"/>
</xsl:call-template>

Das Template utils.printShortMonthName wird in utils.xslt definiert und wie ein normales lokales Template aufgerufen. Das aktuelle Stylesheet muß dazu utils.xslt nur importieren. Das Voranstellen des Strings »utils« hat nichts mit dem tatsächlichen Dateinamen zu tun, sondern ist nur eine Vereinbarung in dieser Anwendung, damit der Code besser lesbar ist und die Gefahr von Namenskonflikten reduziert wird.

Das wiederverwendbare XSLT-Stylesheet utils.xslt ist im folgenden Beispiel zu sehen.

Beispiel: Wiederverwendbarer XSLT-Code

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template name="utils.printShortMonthName">
      <xsl:param name="monthNumber"/>
      <xsl:choose>
         <xsl:when test="$monthNumber='0'">Jan</xsl:when>
         <xsl:when test="$monthNumber='1'">Feb</xsl:when>
         <xsl:when test="$monthNumber='2'">Mär</xsl:when>
         <xsl:when test="$monthNumber='3'">Apr</xsl:when>
         <xsl:when test="$monthNumber='4'">Mai</xsl:when>
         <xsl:when test="$monthNumber='5'">Jun</xsl:when>
         <xsl:when test="$monthNumber='6'">Jul</xsl:when>
         <xsl:when test="$monthNumber='7'">Aug</xsl:when>
         <xsl:when test="$monthNumber='8'">Sep</xsl:when>
         <xsl:when test="$monthNumber='9'">Okt</xsl:when>
         <xsl:when test="$monthNumber='10'">Nov</xsl:when>
         <xsl:when test="$monthNumber='11'">Dez</xsl:when>
      </xsl:choose>
   </xsl:template>
   <xsl:template name="utils.printLongMonthName">
      <xsl:param name="monthNumber"/>
      <xsl:choose>
         <xsl:when test="$monthNumber='0'">Januar</xsl:when>
         <xsl:when test="$monthNumber='1'">Februar</xsl:when>
         <xsl:when test="$monthNumber='2'">März</xsl:when>
         <xsl:when test="$monthNumber='3'">April</xsl:when>
         <xsl:when test="$monthNumber='4'">Mai</xsl:when>
         <xsl:when test="$monthNumber='5'">Juni</xsl:when>
         <xsl:when test="$monthNumber='6'">Juli</xsl:when>
         <xsl:when test="$monthNumber='7'">August</xsl:when>
         <xsl:when test="$monthNumber='8'">September</xsl:when>
         <xsl:when test="$monthNumber='9'">Oktober</xsl:when>
         <xsl:when test="$monthNumber='10'">November</xsl:when>
         <xsl:when test="$monthNumber='11'">Dezember</xsl:when>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Um mit der Klasse java.util.Calendar konsistent zu sein, bei der der Januar durch die 0 dargestellt wird, werden die Monatsnummern mit 0 beginnend gezählt. Die Templates liefern für eine Nummer den deutschen Monatsnamen zurück.

viewMonth.xslt wird im folgenden Beispiel gezeigt. Es generiert eine XHTML-Seite, die alle Nachrichten eines Monats und eines bestimmten Boards darstellt.

Beispiel: XSLT-Code für die Monatsübersicht

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
   ***********************************************************
   ** viewMonth.xslt
   **
   ** Zeigt eine Monatsübersicht der Nachrichten eines bestimmten Boards.
   *********************************************************** -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
   <xsl:import href="utils.xslt"/>
   <xsl:param name="rootDir" select="'../docroot/'"/>
   <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
   <!-- ================= Globale Variablen ================= -->
   <xsl:variable name="global.boardName" select="/viewMonth/board/name"/>
   <xsl:variable name="global.boardID" select="/viewMonth/board/@id"/>
   <xsl:variable name="global.monthNum" select="/viewMonth/@month"/>
   <xsl:variable name="global.yearNum" select="/viewMonth/@year"/>
   <!--
      **********************************************************
      ** Erzeuge die XHTML-Webseite
      ******************************************************* -->
   <xsl:template match="/">
      <html>
         <head>
            <title>
               <xsl:value-of select="$global.boardName"/>
            </title>
            <!-- referenziere eine externe CSS-Datei, um dieses XSLT-Stylesheet kleiner zu halten -->
            <link href="{$rootDir}forum.css" rel="stylesheet" type="text/css"/>
         </head>
         <body>
            <div class="box1">
               <h1>
                  <xsl:value-of select="$global.boardName"/>
               </h1>
            </div>
            <div class="box2">
               <xsl:text>Nachrichten des Monats </xsl:text>
               <xsl:call-template name="utils.printLongMonthName">
                  <xsl:with-param name="monthNumber" select="$global.monthNum"/>
               </xsl:call-template>
               <xsl:text>, </xsl:text>
               <xsl:value-of select="$global.yearNum"/>
            </div>
            <!-- ===== Aktionen ====== -->
            <h3>Aktionen</h3>
            <ul>
               <li><a href="postMsg?mode=postNewMsg&amp;boardID={$global.boardID}">Sende</a> eine neue Nachricht...</li>
               <li>Zurück zur <a href="home">Homepage</a>...</li>
            </ul>
            <!-- ===== Zeige den Nachrichtenbaum rekursiv ===== -->
            <xsl:apply-templates select="viewMonth/message"/>
         </body>
      </html>
   </xsl:template>
   <!--
      **********************************************************
      ** Zeige eine Zusammenfassung für jede Nachricht.
      ******************************************************* -->
   <xsl:template match="message">
      <xsl:param name="indent" select="0"/>
      <!-- Einrückung entsprechend dem Parameter 'indent' -->
      <div style="margin-left: {$indent}em;">
         <a href="viewMsg?msgID={@id}"><xsl:value-of select="subject"/></a>
         <xsl:text> gesendet von </xsl:text>
         <xsl:apply-templates select="authorEmail"/>
         <xsl:text> am </xsl:text>
         <xsl:value-of select="@day"/>
         <xsl:text>&#160;</xsl:text>
         <xsl:call-template name="utils.printShortMonthName">
            <xsl:with-param name="monthNumber" select="$global.monthNum"/>
         </xsl:call-template>
         <xsl:text>&’160;</xsl:text>
         <xsl:value-of select="$global.yearNum"/>
         <!-- Wähle rekursiv alle Nachrichten, die Antworten dieser Nachricht sind. Erhöhe die Einrückung mit jedem Aufruf -->
         <xsl:apply-templates select="message">
            <xsl:with-param name="indent" select="$indent + 1"/>
         </xsl:apply-templates>
      </div>
   </xsl:template>
   <!--
      **********************************************************
      ** Zeige die E-Mail-Adresse des Autors.
      ******************************************************* -->
   <xsl:template match="authorEmail">
      <a href="mailto:{.}"><xsl:value-of select="."/></a>
   </xsl:template>
</xsl:stylesheet>

Weil viewMonth.xslt eine Zusammenfassung vieler Nachrichten anzeigt, wird der eigentliche Textinhalt jeder Nachricht nicht mit in die Ausgabe aufgenommen. Statt dessen werden der Betreff, der Autor und das Datum angezeigt. Diese Zeilen werden eingerückt, je nachdem, ob es sich um Antworten handelt, so daß der Ablauf einer Diskussion schnell erkennbar wird.

Das Stylesheet deklariert eine Reihe globaler Variablen. Diese können im Stylesheet referenziert werden und ermöglichen eine leichte Wartung des Codes. Da jede Variable den String global. als Präfix hat, ist der Code leicht verständlich:

<xsl:value-of select="$global.boardName"/>

Hinweis:
Die Namenskonvention global. ist in XSLT kein Standard. Diese Vereinbarung wurde hier getroffen, damit der XSLT-Code selbsterklärend wird.

Der interessanteste Teil des Stylesheets erzeugt die Darstellung der Nachrichten in einer Baumstruktur. Dazu werden die hierarchischen XML-Daten rekursiv abgearbeitet und so der Diskussionsverlauf dargestellt. Wir wollen uns noch einmal einen Teil von viewMonth.xml anschauen:

<viewMonth month="1" year="2001">
   <board id="1">
      <name>Java-Programmierung</name>
      <description>Allgemeine Fragen zur Java-Programmierung</description>
   </board>
   <message id="1" day="1">
      <subject>Erste Testnachricht</subject>
      <authorEmail>burke_e@yahoo.com</authorEmail>
      <message id="2" day="2">
         <subject>Re: Erste Testnachricht</subject>
         <authorEmail>aidan@irgendwo.de</authorEmail>
      </message>
   </message>
   <message id="3" day="4">
      <subject>Zweite Testnachricht</subject>
      <authorEmail>burke_e@yahoo.com</authorEmail>
   </message>
</viewMonth>

Im XSLT-Stylesheet werden im ersten Teil der rekursiven Abarbeitung alle <message>-Elemente ausgewählt, die direkte Nachkommen des Elementes <viewMonth> sind:

<xsl:apply-templates select="viewMonth/message"/>

Dies betrifft die Nachrichten mit den IDs 1 und 3, für die das folgende Template instantiiert wird:

<xsl:template match="message">
 <xsl:param name="indent" select="0"/>

Das Template besitzt einen Parameter für die Einrückungstiefe, der die Voreinstellung 0 hat. Anschließend wird mit einfachem XSLT-Code in einer Zeile eine Zusammenfassung der aktuellen Nachricht ausgegeben. Nun instantiiert sich das Template rekursiv selbst:

<xsl:apply-templates select="message">
  <xsl:with-param name="indent" select="$indent + 1"/>
</xsl:apply-templates>

Dadurch werden alle <message>-Elemente innerhalb der aktuellen Nachricht ausgewählt und die Einrückung um 1 erhöht, so daß Antworten immer korrekt eingerückt werden. Dieser rekursive Prozeß läuft, bis keine Nachrichten mehr übrig sind.

Ein weiteres Stylesheet, viewMsg.xslt, ist dafür verantwortlich, eine einzelne Nachricht darzustellen. Im folgenden Beispiel wird das Stylesheet postMsg.xslt gezeigt, das in zwei unterschiedlichen Modi arbeiten kann und deshalb etwas komplizierter als die vorherigen Beispiele ist.

Beispiel: XSLT-Stylesheet für die Seiten »Nachricht senden« bzw. »Antworten«

<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
   ***********************************************************
   ** postMsg.xslt
   **
   ** Erzeugt die XHTML-Seiten "Nachricht senden" und "Antworten".
   *********************************************************** -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
   <xsl:import href="utils.xslt"/>
   <!-- übergebe das Wurzelverzeichnis als Parameter, so daß das Stylesheet auf die CSS-Datei zugreifen kann -->
   <xsl:param name="rootDir" select="'../docroot/'"/>
   <xsl:output method="xml" version="1.0" encoding="ISO-8859-1" indent="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>
   <!-- ===== Globale Variablen ===== -->
   <xsl:variable name="global.subject" select="/postMsg/prefill/subject"/>
   <xsl:variable name="global.email" select="/postMsg/prefill/authorEmail"/>
   <xsl:variable name="global.message" select="/postMsg/prefill/message"/>
   <xsl:variable name="global.title">
      <xsl:choose>
         <xsl:when test="/postMsg/inResponseTo">
            <xsl:text>Antworten</xsl:text>
         </xsl:when>
         <xsl:otherwise>
            <xsl:text>Nachricht senden</xsl:text>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:variable>
   <!--**********************************************************
      ** Erzeuge die XHTML-Webseite
      ******************************************************* -->
   <xsl:template match="/">
      <html>
         <head>
            <title><xsl:value-of select="$global.title"/></title>
            <link href="{$rootDir}forum.css" rel="stylesheet" type="text/css"/>
         </head>
         <body>
            <!-- zeige den Titel der Seite und den Namen des Boards -->
            <div class="box1">
               <h1><xsl:value-of select="$global.title"/></h1>
               <div>
                  <xsl:value-of select="postMsg/board/name"/>
               </div>
            </div>
            <xsl:apply-templates select="postMsg/inResponseTo"/>
            <div class="box2">
               <!-- zeige eine optionale Fehlermeldung an -->
               <xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
                  <p class="error">Es müssen alle Felder ausgefüllt werden...</p>
               </xsl:if>
               <!-- Erzeuge ein XHTML-Formular. Der Nutzer gibt den Betreff, die E-Mail-Adresse und den Nachrichtentext ein -->
               <form method="post" action="postMsg">
                  <div>
                     <input type="hidden" name="boardID"
                        value="{postMsg/board/@id}"/>
                     <!-- Lege den Arbeitsmodus fest -->
                     <xsl:choose>
                        <xsl:when test="/postMsg/inResponseTo">
                           <input type="hidden" name="origMsgID"
                              value="{postMsg/inResponseTo/@id}"/>
                           <input type="hidden" name="mode" value="replyToMsg"/>
                        </xsl:when>
                        <xsl:otherwise>
                           <input type="hidden" name="mode" value="postNewMsg"/>
                        </xsl:otherwise>
                     </xsl:choose>
                  </div>
                  <!-- Stelle die Eingabefelder in einer Tabelle dar, um sie korrekt anzuordnen -->
                  <table>
                     <tr>
                        <td>Betreff:</td>
                        <td>
                           <input type="text" name="msgSubject"
                              value="{$global.subject}" size="60" maxlength="70"/>
                        </td>
                     </tr>
                     <tr>
                        <td nowrap="nowrap">E-Mail:</td>
                        <td>
                           <input type="text" name="authorEmail"
                              value="{$global.email}" size="60" maxlength="70"/>
                        </td>
                     </tr>
                     <tr valign="top">
                        <td>Nachricht:</td>
                        <td>
                           <!-- xsl:text verhindert, daß der XSLT-Prozessor die Ausgabe auf <textarea/> reduziert, was bei vielen Browsern zu Problemen führt. -->
                           <textarea name="msgText" wrap="hard" rows="12" cols="60"><xsl:value-of select="$global.message"/><xsl:text> </xsl:text></textarea>
                        </td>
                     </tr>
                     <!-- Die letzte Spalte der Tabelle enthält die Buttons "Senden" und "Abbrechen" -->
                     <tr>
                        <td> </td>
                        <td>
                           <input type="submit" name="submitBtn" value="Senden"/>
                           <input type="submit" name="cancelBtn" value="Abbrechen"/>
                        </td>
                     </tr>
                  </table>
               </form>
            </div>
         </body>
      </html>
   </xsl:template>
   <!--
      **********************************************************
      ** Zeige den Text: 'Antwort auf: Nachricht Betreff'
      ******************************************************* -->
   <xsl:template match="inResponseTo">
      <div>Antwort auf: <span style="font-weight: bold;"><xsl:value-of select="subject"/></span></div>
   </xsl:template>
</xsl:stylesheet>

Da das Stylesheet sowohl für das Senden neuer Nachrichten als auch für das Antworten auf Nachrichten verwendet werden soll, muß es den entsprechenden Arbeitsmodus herausfinden. Dazu kann geprüft werden, ob Elemente vorhanden sind, die nur in einem bestimmten Modus vorkommen. Zum Beispiel existiert das XML-Element <inResponseTo> nur, wenn der Nutzer auf eine bestehende Nachricht antwortet. Das XSLT-Stylesheet kann also eine Variable für den Seitentitel wie folgt definieren:

<xsl:variable name="global.title">
   <xsl:choose>
      <xsl:when test="/postMsg/inResponseTo">
         <xsl:text>Antworten</xsl:text>
      </xsl:when>
      <xsl:otherwise>
         <xsl:text>Nachricht senden</xsl:text>
      </xsl:otherwise>
   </xsl:choose>
</xsl:variable>

<xsl:when test="/postMsg/inResponseTo"> gibt true zurück, wenn das Element <inResponse-To> in den XML-Daten existiert. In diesem Fall wird der Variablen global.title der Wert »Antworten« zugewiesen. Ansonsten hat diese Variable den Standardwert »Nachricht senden«.

Das Stylesheet gibt eine Fehlermeldung aus, wenn der Nutzer das XHTML-Formular nur teilweise ausfüllt und somit unvollständige Daten abschickt. Die Seite wird dann mit einer Fehlermeldung noch einmal dargestellt, so daß der Nutzer Gelegenheit bekommt, den Fehler zu beheben. Dazu wird das folgende XML-Element in die Daten aufgenommen:

<error code="ALL_FIELDS_REQUIRED"/>

Das XSLT-Stylesheet prüft das Vorhandensein dieses Elements wie folgt:

<xsl:if test="postMsg/error/@code='ALL_FIELDS_REQUIRED'">
  <p class="error">Es müssen alle Felder ausgefüllt werden...</p>
</xsl:if>

Ein weiterer Trick dieses Stylesheets besteht in seiner Interaktion mit dem Servlet. Wenn der Nutzer die Daten des XHTML-Formulars sendet, muß das Servlet den Arbeitsmodus des Nutzers ermitteln. Dazu sucht das Servlet nach dem Anfrageparameter mode. Gültige Werte dieses Parameters sind replyToMsg und postNewMsg. Da der Nutzer ein XHTML-Formular sendet, kann dieser Parameter einfach über das versteckte Formularfeld mode übertragen werden. Hier ist der entsprechende Code:

<xsl:choose>
   <xsl:when test="/postMsg/inResponseTo">
      <input type="hidden" name="origMsgID" value="{postMsg/inResponseTo/@id}"/>
      <input type="hidden" name="mode" value="replyToMsg"/>
   </xsl:when>
   <xsl:otherwise>
      <input type="hidden" name="mode" value="postNewMsg"/>
   </xsl:otherwise>
</xsl:choose>

Außerdem fügt das Stylesheet ein verstecktes Formularfeld für die ID der ursprünglichen Nachrichten ein, wenn der Modus replyToMsg ist. Im Servlet gestaltet sich der Code wie folgt:

public void doGet(HttpServletRequest request, HttpServletResponse response) ... {
  String mode = request.getParameter("mode");
  if ("replyToMsg".equals(mode)) {
     String origMsgID = request.getParameter("origMsgID");
     ....
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