Für eine solche Anwendung ist ein Inhaltsverzeichnis nur der allererste Schritt, danach beginnt die eigentliche Arbeit. Als Beispiel für weitere Sichten auf die Daten sei hier eine herausgegriffen: die Ausgabe von Daten eines einzelnen Autors. Für diesen Schritt sprechen mehrere Gründe. Obwohl dieses Stylesheet schon umfangreicher ist als das für das Inhaltsverzeichnis, hält es sich noch in darstellbaren Grenzen. Außerdem ist es damit ansatzweise möglich zu zeigen, wie man mit Hilfe der Eigenschaften von Scheme die Ausgabe verändern kann. Um auch darauf hinzuweisen, was komplexer ist und deshalb hier nicht vorkommt, seien zwei weitere mögliche Style Sheets respektive Sichten wenigstens erwähnt. Das Element what enthält das Attribut cat, das beispielsweise den Wert nobel haben kann. Dieser Attributwert soll es erleichtern, die Nobelpreisträger aus dem Dokument zu filtern. Für den Fall, dass man sie zeitlich sortiert ausgeben will, ist eine Sortierfunktion erforderlich, die die Damen und Herren in die richtige Reihenfolge bringt. Gleiches gilt für die Ausgabe aller Werke — unabhängig vom Autor — in der historischen Abfolge und für Jahresausgaben (hinsichtlich der Monate und Tage).
Umfangreicher ist das Style Sheet für einzelne
Autoren schon deshalb, weil man für mehr Elemente
festlegen muss, ob und was zur Ausgabe kommen soll. Das gilt
nicht für lithist, weil das
außer Überschrift und Nachsatz lediglich die Angabe
enthalten muss, dass die passenden Kindelemente (author) zu verarbeiten sind. Dort und
— hierarchisch gesehen — darunter stehen die
Anweisungen, wie mit den einzelnen Elementen zu verfahren
ist. Da in diesem Style Sheet fast alles ausgegeben wird,
eignet sich die Default-Anweisung
(empty-sosofo) hier nicht. Wenn überhaupt
eine Default-Regel für Elemente, deren Verarbeitung nicht
ausdrücklich angegeben ist, dann eine, die besagt:
Verarbeite deine Kinder
. Das wäre
(default (process-children))
Im Element author
selbst wird eine Tabelle aufgebaut, die — unterbrochen
von einer horizontalen Linie — die Elemente vita, event,work und comment aufbereitet. Elemente wie
born und died eröffnen jeweils eine Zeile für
das Geburts- und Sterbedatum. Andere Elemente verfahren auf dieselbe
Weise für ihre Kinder
. Die entscheidende Funktion
innerhalb von author heißt
if, denn sie bewirkt, dass nur die Daten des
Autors ausgegeben werden, die der gestellten Bedingung
genügen:
(if (string=? thisAuthor (attribute-string "ID" (current-node))) ; ---- (Anweisung) (else-Anweisung))
Nur wenn der Inhalt der Variablen
thisAuthor dem Inhalt des Attributs id gleicht, kommt es zur Ausgabe der
Daten. Ansonsten liefert author ein
(empty-sosofo) zurück, mit dem Erfolg, dass
nichts erfolgtDen Fall, dass kein Autor
gefunden wird, dessen id mit
der Variablen übereinstimmt, kann man getrost ausschließen,
denn die Auswahl an Autoren bestimmt ja diejenigen, die im Formular
auftauchen .... Leider ist es nicht möglich,
eine solche Variable an Jade zu
übergeben. Deshalb musste Perl
herhalten. Das fertige Stylesheet und eine auf Grund der
getroffenen Auswahl generierte
define-Anweisung schreibt ein
CGI-Skript in ein weiteres (temporäres) Style
Sheet, das Jade von Perl aus beim Aufruf
übergeben wird. Die Variablendeklaration sieht so
aus:
(define thisAuthor "MANNT")
Das Perl-Listing findet sich im Anhang dieses Kapitels; eleganter wäre es, zugegebenermaßen, käme man ohne solche externen Programme aus und könnte Jade die Autorenkennung als Parameter übergeben.
Noch ein paar Worte zum Style Sheet selbst. Es enthält direkt nach der default-Anweisung ein paar Funktionen, die zunächst nichts mit konkreten Elementen zu tun haben. Zwei dieser Funktionen definieren lediglich Listen: erst die Monate in zwei Sprachen, dann die Landesbezeichnungen. Dass letztere Liste außerdem die Länderkürzel enthält, liegt daran, dass diese für den Abgleich in der Funktion getcountry erforderlich sind, während die richtigen Monate sich automatisch über die Zahlen ergeben.
Ein Blick auf eine der beiden Funktionen soll an dieser Stelle nur zeigen, dass man mit DSSSL respektive Scheme Dokumente bearbeiten kann, bevor sie zu HTML-Daten mutieren.
(define (getmonth mo li lang) (if (= mo 1) (if (equal? lang "de") (car (car li)) (car (cdr (car li)))) (getmonth (- mo 1) (cdr li) lang))) (define monate (list (list "Januar" "January") (list "Februar" "February") ; ----- weitere Monate (list "Dezember" "December"))) ; ----- Aufruf im Element month mit (literal (getmonth (string->number (data (current-node))) monate "de"))
Auffällig für diejenigen, die noch nicht mit
Scheme oder einem anderen Lisp-Dialekt
gearbeitet haben, sind die in getmonth
verwendeten Funktionen car und
cdr, die hier auch noch in Kombination
auftreten (sie waren auch schon Bestandteil von
copy-attributes im vorigen Kapitel). Hinter
diesen mnemonisch perfekten
Akronymencar hat tatsächlich
nichts mit Autos zu tun, vielmehr ist es die Abkürzung von
Contents of Address Register
. Und
cdr heißt Contents of Decrement
Register
. stecken zwei der wichtigen
Lisp-Grundfunktionen zur Bearbeitung von
Listen. car extrahiert aus einem Paar die
erste Komponente (auch dann, wenn es sich wiederum um eine
Liste handelt), cdr hat den
Rest des ursprünglichen Paares zum Ergebnis.
getmonth definiert zwei mögliche
Aktionen: Falls der Monat die Zahl 1
liefert, muss die
Funktion das erste Glied der ersten Liste (Januar
) ausgeben
— vorausgesetzt, als Sprache ist Deutsch vorgegeben
(über den Parameter de beziehungsweise
lang in der Funktionsübergabe). In allen
anderen Fällen ruft sich getmonth rekursiv
auf, und zwar mit der restlichen (um den ersten Monat
verkürzten) Liste.
Bei einem Wert von mo = 3 bedeutet das nach
dem ersten (erfolglosen) Durchlauf einen Aufruf von
getmonth mit mo = 2 und der um die
Januar
-Liste verkürzten Gesamtliste der
Monate. Auch diesmal ist mo ungleich 1
,
so dass die Prozedur, diesmal mit mo = 1, noch
einmal aufgerufen werden muss — jeweils mit einer um
die erste Monatsliste verkürzten Gesamtmonatsliste.
Rekursion kann man ein Grundprinzip von Lisp-Sprachen nennen. Programme folgen oft zwei einfachen Regeln:
Im Beispiel liest sich das so: erstes Glied der Liste heraussuchen.
(list "Januar" "January")
Bedingung erfüllt? Dann: Abbruch/Ausgabe. Wenn der
Monat als Zahl nicht erreicht ist, nimm Dir den Rest der
Liste vorDas war wahrscheinlich der kürzeste und
meistverkürzte Lisp-Kurs weit und
breit..
So weit der Einbau von Funktionen. Natürlich wäre mehr möglich. Aber zurück zu den Elementen: Bei der Ausgabe der Autorenelemente sieht einiges anders aus als beim Inhaltsverzeichnis, schließlich sollen hier ja konkrete Daten ausgegeben werden (anstatt nur der Name). Innerhalb des Elements author muss also wesentlich mehr geschehen, als im Inhaltsverzeichnis beschrieben:
(element author (if (string=? thisAuthor (attribute-string "ID" (current-node))) (make sequence (process-matching-children 'name) (make element gi: "TABLE" attributes: (cons (list "border" "0") (cons (list "width" "100%") (cons (list "cellspacing" "0") (cons (list "cellpadding" "2") '())))) (make element gi: "CAPTION" (make element gi: "H3" (literal "Leben und Werk")) (make element gi: "H3" attributes: (cons (list "class" "eng") '()) (literal "Life and Works"))) (process-matching-children 'vita) (process-matching-children 'event) (process-matching-children 'work) (process-matching-children 'comment))) (empty-sosofo)))
Falls das Attribut id
mit der Variablen thisAuthor
übereinstimmtLeider ist hier auf Großschreibung zu
achten. Jade nimmt Kleinschreibung übel,
das heißt: Auch wenn die ids
klein
daherkommen, macht Jade sie
zu Versalien., wird mit make sequence
die Ausgabe eingeleitet: eine Tabelle mit Überschriften
(caption mit zwei h3) sowie die Kindelemente
(process-matching-children). Wenn Variable und
Kennung nicht übereinstimmen, gilt das
(empty-sosofo)Das
empty-sosofo steht hier nur der Form halber, denn
da in den Optionen nur vorhandene Autor(inn)en aufgenommen
sind, kann es zu diesem Fall nicht kommen., das
heißt, es geschieht nichts.
Bei der Verarbeitung von vita beziehungsweise den in der
Hierarchie noch tiefer angesiedelten Elementen born und died kommt unter anderem die oben
vorgestellte Funktion getmonth zum Einsatz,
die aus den Monatsziffern Wörter macht. Gerade in diesem
Fall hat eine solche Funktion ihren Sinn, denn an anderer
Stelle sollen die Monate als Zahlen vergleichbar sein, damit
man einen Kalender etc. umsetzen kann.Bei den
Ländernamen (siehe das gesamte Listing am Ende des Kapitels)
ergibt sich der Sinn erst bei mehreren Sprachen, denn im
XML-Quelltext geht es, wie die Entwickler
deutlich gemacht haben, nicht um Knappheit
(
terseness
). Man könnte also mit den Langbegriffen
wuchern.
Innerhalb der einzelnen Elemente sind nicht viele Besonderheiten versteckt; die meisten geben Teile einer Tabelle aus, etwa:
; ---------------------- gestorben? (element died (make element gi: "TR" (make element gi: "TH" attributes: (cons (list "class" "when") '()) (process-matching-children 'year)) (make element gi: "TD" attributes: (cons (list "class" "life") '()) (literal "gestorben/died: ") (process-matching-children 'day) (process-matching-children 'month) (make element gi: "SPAN" attributes: (cons (list "class" "life") '()) (process-matching-children 'where))))) ; ---------------------- wann geboren/gestorben? (element month (make sequence (literal (getmonth (string->number (data (current-node))) monate "de")) (literal " "))) (element day (make sequence (process-children) (literal ". ")))
Geburts- und Sterbedatum lassen sich analog
ausgeben. Der einzige Grund dafür, beide Elemente aufzuführen, ist,
dass unterschiedliche Strings wie gestorben/died:
respektive geboren/born: zu berücksichtigen
sind. Schließlich muss bei Geburts- und Todestagen dem
eigentlichen Inhalt des Elements (der Zahl) ein .
folgen,
damit der Monat nicht direkt am Tag klebt
.
In fast allen konkreten Fällen (das oben stehende Element day ist eine der Ausnahmen, weil es sich um ein Blatt, ein Endelement, handelt) sorgt das Stylesheet dafür, dass Teile einer Tabelle aufgebaut werden. Für Geburts- oder Todestage gibt es etwa zwei Zeilen (TR), in denen zunächst das Jahr (TH) und anschließend Monat, Tag und Ort samt Staat (im zweiten TD) ausgegeben werden. Das Jahr taucht hier nicht auf, weil es nicht bearbeitet werden muss — und dafür reicht die Default-Regel process-children.
Manche Leser mögen sich fragen: Ist das alles? Jein. Ja hier, nein ansonsten. Über einzelne Autor(inn)en hinausgehende Informationen lassen sich, wie schon erwähnt, ebenfalls generieren. Allerdings erfordern sowohl Übersichten über Personen als auch über Werke, etwa aufs Jahrhundert gerechnet, weitere Scheme-Funktionen. Zum Beispiel eine Sortier-Routine, deren Darstellung den Rahmen einer (wenn auch praxisorientierten) Einführung in XML sprengte.