Zunächst sollten also die Daten extrahiert werden. Das erledigten ein Shell-Script und ein Datenbankreport, der logischerweise abhängig vom verwendeten DBMS ist. Im vorliegenden Fall handelte es sich um Informix, das eine Reportsprache beinhaltet. Das folgende Listing schreibt den Rahmen der künftigen XML-Quelle und ruft den Report auf.
#!/bin/sh
# CeBIT-Daten aus Datenbank in XML ausgeben
CHOME=/path/to/cebit2000
FILE=$CHOME/cebit2000
# Pfade fuer die DB, beispielsweise:
DB-SYSTEMDIR=/path/to/db-system
export CHOME FILE DB-SYSTEMDIR
cd $CHOME
mv $FILE.xml $FILE.xml.old
echo '<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>' > $FILE.xml
echo "" >> $FILE.xml
echo '<!DOCTYPE cebit SYSTEM "cebit.dtd">' >> $FILE.xml
echo "" >> $FILE.xml
echo "<cebit>" >> $FILE.xml
echo "" >> $FILE.xml
# eigentlicher DB-Report (hier: Informix)
${DB-SYSTEMDIR}/bin/sacego -q $FILE.arc >> $FILE.xml
echo "" >> $FILE.xml
echo "</cebit>" >> $FILE.xml
echo "" >> $FILE.xml
In der so entstandenen XML-Datei befinden sich jetzt circa 500 Einträge, die etwa folgendes Aussehen haben:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <!DOCTYPE cebit SYSTEM "cebit.dtd"> <cebit> <appsrv thema="appsrv" longname="Application Server"> <group which="1"> <firma url="www.firma.com"> <name>Wonder Corporation</name> <halle> 4</halle> <stand>G42 [bei Galaxy, Inc.]</stand> <produkt>WebHiker's Guide</produkt> </firma> <!-- weitere Firmen --> </group> <!-- weitere Fuenfergruppen --> </appsrv> <!-- weitere Produktgruppen --> </cebit>
Eingeschlossen vom Element cebit folgen
verschiedene Produktgruppen (von appsrv wie
Application Server bis wbt wie Windows Based
Terminals), darin befinden sich die Firmeneinträge. Um
sicherzustellen, dass tatsächlich nicht mehr als 3000 Zeichen
in einer Datei landen, hat bereits der DB-Report
jede dieser Produktgruppen in Fünferblocks aufgeteiltDas Element
group ist ein Kompromiss; es hat wirklich nur den
Sinn, die Erstellung von WML-Dateien zu
ermöglichen. Ansonsten ist es unsinnig, ein solches Element in
die Dokumentstruktur einzubinden.. Anstatt wie hier (siehe die folgende DTD)
Elementnamen wie appsrv, datam (für
Data Mining) oder linux für die Produktgruppen zu
verwenden, hätte es gereicht,
ein
Element
productgroup mit dem entsprechenden Attribut zu benutzen. Dass das nicht geschehen ist, lag
größtenteils daran, dass es sich als nicht trivial erwies,
kleine Gruppen (mit jeweils fünf Einträgen) ohne
group zu
verarbeiten.N ur durch die
Saxon-Erweiterungen war das in den Griff zu
bekommen. Eine
möglichst kurz gehaltene Dokumentstruktur sieht etwa so
aus:
<!-- DTD fuer die Ausgabe von Firmendaten aus dem
iX-Special zur CeBIT 2000,
© Copyright by iX
-->
<!ENTITY % productgroup " ( appsrv | bluet | datam | java |
intzu | linux | pki | san | sysm |
unis | viren | wap | wbt ) " >
<!ELEMENT cebit ( %productgroup;+ ) >
<!ELEMENT productgroup ( group+ ) >
<!ATTLIST productgroup
thema CDATA #REQUIRED
longname CDATA #REQUIRED >
<!ELEMENT group ( firma+ ) >
<!ATTLIST firma
which CDATA #REQUIRED >
<!ELEMENT firma ( name, halle, stand, produkt ) >
<!ELEMENT name ( #PCDATA ) >
<!ELEMENT halle ( #PCDATA ) >
<!ELEMENT stand ( #PCDATA ) >
<!ELEMENT produkt ( #PCDATA ) >
<!ATTLIST firma
url CDATA #REQUIRED >
Ein paar Worte zur Wahl des Werkzeugs müssen hier sein. Ursprünglich sollte James Clarks Implementierung der XSLT-Spezifikation XT die Transformationen leisten. Leider hat Clark seine Erweiterungen nicht gerade extensiv dokumentiert, sodass die Wahl auf Michael Kays Tool Saxon fiel. Kay hat sein in Java geschriebenes Programm nicht nur gut dokumentiert; vor allem hat er ein paar Erweiterungen integriert, die meines Erachtens unbedingt in eine spätere Version von XSLT einfließen sollten.
Ähnlich wie in Kapitel 11
müssen auch im folgenden XSLT-Stylesheet einige
Variablen gesetzt werden. Hier gilt dies umso mehr, als für
das Erzeugen der einzelnen Dateien und der in ihnen
enthaltenen Verweise Variablen zugewiesen werden müssen, die
tiefer in der Elementhierarchie nicht einfach zur Verfügung
stehen; es sei denn, man deklariert sie global. Aus diesem
Grund folgen den aus Kapitel 11
bekannten Variablen dir und filesep
einige, die es ermöglichen, tiefer innerhalb der
Elementhierarchie auf oben
deklarierte und zugewiesene
Variablen zuzugreifen.
Im Template für die Produktgruppen geht es zum ersten Mal an die Verwendung einer global deklarierten Vaiablen: currentgroup. Ihr weist das assign-Statement, eine von Kays Erweiterungen, den Wert des Attributs thema zu.
<saxon:assign name="currentgroup"> <xsl:value-of select="@thema"/> </saxon:assign>
Die eigentliche Arbeit nimmt das Template für die Fünfergruppen vor. In ihm erzeugt das Stylesheet die Ausgabe, indem es aus den unterschiedlichen Variablen einen Pfad und einen Dateinamen zusammensetzt:
<saxon:output file="{$dir}{$filesep}{$currentgroup}{$grpno}.wml" method="xml" indent="yes" doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml">
dir und filesep sind zu Anfang des Stylesheets global deklariert und zugewiesen worden; der Wert von currentgroup stammt aus dem Template für die Produktgruppen. Einzig die Gruppennummer (grpno) wird in dem Template festgestellt und an die Variable übergeben, das auch das saxon:output-Statement enthält. Das Attribut file ist übrigens die Erweiterung, die Michael Kay in seiner Implementierung hinzugefügt hat. Die XSLT-Spezifikation sieht es nicht vor.
Außerdem stellen die Variable isfirstgroup und islastgroup sowie das Attribut thispos sicher, dass das Stylesheet bei der Erzeugung der einzelnen Karten feststellen kann, ob es
<saxon:set-attribute name="thispos" select="position()"/> <saxon:assign name="islastgroup" select="position()=last()"/> <saxon:assign name="isfirstgroup" select="position()=1"/> <xsl:variable name="grpno"> <xsl:value-of select="@thispos"/> </xsl:variable>
Abhängig vom Wert dieser Variablen wird den
Prev
- und Next
-Buttons der jeweils richtige Wert
zugewiesen, wie ein Blick auf die oben zitierte Ergebnisdatei
wap4.wml zeigt: <go
href="wap3.wml#wap5"/> verweist im ersten
Datensatz
auf die vorherige Datei und innerhalb dieser
auf den letzten (fünften) Eintrag, weil not
(position()=1) und not ($isfirstgroup) jeweils den
Wert wahr ergeben.
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.wapforum.org/DTD/wml_1.1.xml" xmlns:saxon="http://icl.com/saxon" saxon:trace="no" extension-element-prefixes="saxon"> <!-- Parameter und Variable --> <xsl:variable name="dir">wml</xsl:variable> <xsl:variable name="filesep" select="system-property('file.separator')" /> <xsl:variable name="islastgroup"/> <xsl:variable name="isfirstgroup"/> <xsl:variable name="currentgroup"/> <xsl:variable name="grpno"/> <!-- ....................Root............... --> <xsl:template match="/"> <xsl:apply-templates/> </xsl:template> <!-- ............Element cebit............... --> <xsl:template match="/cebit"> <xsl:apply-templates/> </xsl:template> <!-- ......P r o d u k t g r u p p e n...... --> <!-- beim gegenwaertigen Stand koennte ein Element "prodgroup" die Vielfalt hier ersetzen --> <xsl:template match="appsrv|bluet|datam|intzu|java| linux|pki|san|sysm|unis|viren|wap|wbt"> <saxon:assign name="currentgroup"> <xsl:value-of select="@thema"/> </saxon:assign> <xsl:apply-templates select="group"/> </xsl:template> <!-- Ausgabe der kleinen Gruppen (maximal fuenf Firmen) --> <xsl:template match="group"> <saxon:set-attribute name="thispos" select="position()"/> <saxon:assign name="islastgroup" select="position()=last()"/> <saxon:assign name="isfirstgroup" select="position()=1"/> <xsl:variable name="grpno"> <xsl:value-of select="@thispos"/> </xsl:variable> <saxon:output file="{$dir}{$filesep}{$currentgroup}{$grpno}.wml" method="xml" indent="yes" doctype-public="-//WAPFORUM//DTD WML 1.1//EN" doctype-system="http://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <template> <do type="options" label="Top"> <go href="index.wml" /> </do> </template> <xsl:apply-templates select="firma"/> </wml> </saxon:output> </xsl:template> <!-- ................ Firmendaten............ --> <xsl:template match="firma"> <card> <xsl:attribute name="id"> <xsl:value-of select="$currentgroup"/> <xsl:value-of select="position()"/> </xsl:attribute> <xsl:attribute name="title"> <xsl:value-of select="../../@longname"/> </xsl:attribute> <p> <strong> <xsl:apply-templates select="name"/> </strong> <br/> <xsl:text>Halle</xsl:text> <xsl:apply-templates select="halle"/> <xsl:text>, Stand </xsl:text> <xsl:apply-templates select="stand"/> <br/> <xsl:text>Produkt: </xsl:text> <xsl:apply-templates select="produkt"/> </p> <!-- Next Button --> <xsl:choose> <xsl:when test="not (position()=last())"> <do> <xsl:attribute name="type">accept</xsl:attribute> <xsl:attribute name="label">Next</xsl:attribute> <go> <xsl:attribute name="href"> <xsl:text>#</xsl:text> <xsl:value-of select="$currentgroup"/> <xsl:number value="position() + 1"/> </xsl:attribute> </go> </do> </xsl:when> <!-- letzte Firma... --> <xsl:otherwise> <!-- aber _nicht_ die letzte der Gruppen in diesem Segment --> <xsl:if test="not($islastgroup)"> <do> <xsl:attribute name="type">accept</xsl:attribute> <xsl:attribute name="label">Next</xsl:attribute> <go> <xsl:attribute name="href"> <xsl:value-of select="../@thema"/> <xsl:value-of select="../@which + 1"/> <xsl:text>.wml</xsl:text> </xsl:attribute> </go> </do> </xsl:if> </xsl:otherwise> </xsl:choose> <!-- Prev-Button --> <xsl:choose> <xsl:when test="not (position()=1)"> <do> <xsl:attribute name="type">prev</xsl:attribute> <xsl:attribute name="label">Prev</xsl:attribute> <go> <xsl:attribute name="href"> <xsl:text>#</xsl:text> <xsl:value-of select="$currentgroup"/> <xsl:number value="position() - 1"/> </xsl:attribute> </go> </do> </xsl:when> <!-- erste Firma... --> <xsl:otherwise> <!-- aber _nicht_ die erste der Gruppen in diesem Segment --> <xsl:if test="not($isfirstgroup)"> <do> <xsl:attribute name="type">prev</xsl:attribute> <xsl:attribute name="label">Prev</xsl:attribute> <go> <xsl:attribute name="href"> <xsl:value-of select="$currentgroup"/> <xsl:value-of select="../@which - 1"/> <xsl:text>.wml#</xsl:text> <xsl:value-of select="$currentgroup"/> <xsl:text>5</xsl:text> </xsl:attribute> </go> </do> </xsl:if> </xsl:otherwise> </xsl:choose> </card> </xsl:template> <!-- Templates fuer Name, Halle, Stand, Produkt --> <xsl:template match="name"> <xsl:apply-templates/> </xsl:template> <xsl:template match="halle"> <xsl:apply-templates/> </xsl:template> <xsl:template match="stand"> <xsl:apply-templates/> </xsl:template> <xsl:template match="produkt"> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet>
Wie das Ergebnis dieser Transformation auf einer Handy-Emulation aussieht, zeigt die folgende Abbildung:
Abbildung
75:
Das Nokia-Toolkit emuliert WAP, wie
es das 7110 auch in Wirklichkeit können
soll — hier die Darstellung der beschriebenen Anwendung
Mit Hilfe dieses Stylesheets — vor allem durch die variable Festlegung des Pfades — war es möglich, alle der über 100 WML-Dateien über einen Aufruf von Saxon zu erzeugen:
saxon cebit2000.xml cebit_wml.xsl
Gleiches gilt für die HTML-Dateien; davon sollten allerdings nur ein Dutzend erzeugt werden.