Entity-Referenzen zur Abkürzung und
Mehrfachverwendung von wiederkehrenden Phrasen haben Sie
bereits kennen gelernt. Der Einsatz ist auf die Instanz
beschränkt, in der DTD jedoch nicht
zulässig. An ihre Stelle tritt die so genannte
Parameter Entity ReferenceDer
Name rührt wohl daher, dass sie in der Elementtyp-Deklaration
als
Parameter
zulässig ist.
(PEReference). Die Schreibweise unterscheidet
sich von den Entity References dadurch, dass statt des
et-Zeichens (&) nun ein Prozentzeichen (%) als Präfix
dient. Ein häufiger Anwendungsfall besteht darin,
Inhaltsmodelle, die für mehr als einen Elementtyp verwendet
werden, unter einem Namen zusammenzufassen.
<!ENTITY % block "absatz | listing | abbildung | kasten"> <!ELEMENT buch (kapitel+)> <!ELEMENT kapitel (ueberschrift, (%block;)*, abschnitt+)> <!ELEMENT abschnitt (ueberschrift, (%block;)*, unterabschnitt*)> <!ELEMENT unterabschnitt (ueberschrift, (%block;)+)>
In obigem Beispiel gibt es mehrere Textblöcke, die
im Inhaltsmodell dreier Elementtypen auftreten. Auf den
ersten Blick ist damit nur eine kürzere Schreibweise erzielt
worden. Sollten nun aber
Änderungen anstehen, wie etwa das Hinzufügen eines
Blockelements Tabelle
, so muss lediglich die
Definition der PEReference geändert werden. Neben der
leichteren Wartbarkeit einer DTD erhöht sich
durch den sinnvollen Einsatz von PEReferences auch die
Lesbarkeit.
Innerhalb einer DTD kann man auf diese
Weise Ordnung schaffen. Ein anderes Problem ist noch offen:
Wie schafft man es, dass Elementtypen, die in
mehreren DTDs enthalten sind, nicht in
jede DTD-Datei einzeln hineingeschrieben werden
müssen? Ein konkretes Beispiel sind Tabellen. Es ist nicht
leicht, ein gutes Tabellenmodell zu entwerfenIn
der Praxis wird man ein Tabellenmodell verwenden, mit dem
man schon vertraut ist, wie zum Beispiel die
HTML-Tabellen oder das
CALS-Modell, das zum Teil als Vorlage für
HTML gedient hat.. Hat man
einmal eines, so wird man es sicherlich gern in alle eigenen
DTDs einbauen wollen. Auch dies lässt sich
bequem mit PEReferences
realisieren, und zwar als externes
Entity. Unser obiges Beispiel ändern wir dafür wie
folgt ab:
<!ENTITY % tab-modell SYSTEM "/usr/local/sgml/tabelle.dtd"> %tab-modell; <!ENTITY % block "absatz | listing | abbildung | kasten | tabelle">
Hier sind die Blöcke um eine Tabelle ergänzt
worden. Die Tabelle selbst ist aber in einer externen Datei
namens tabelle.dtd gespeichert, die sich im
Verzeichnis /usr/local/sgml/ befindet. Die erste
Zeile definiert das Entity tab-modell. In der
zweiten Zeile wird dieses Entity expandiert, d.h. der Inhalt
der Datei wird an dieser Stelle eingefügt
. Die
Wirkung ist also die gleiche, als ob die Deklaration für den
Elementtyp Tabelle
(und seine ggf. benötigten
weiteren Elementtypen) in der DTD selbst stünde.
Auf diese Weise können Sie immer wieder benutzte DTD-Fragmente in einzelne Dateien auslagern und Ihre DTDs wie mit einem Baukasten zusammensetzen. Beispiele für solche Fragmente sind neben der Tabelle auch das Glossar, die Bibliografie, aber auch solche auf den ersten Blick einfachen Elementtypen wie Abbildungen. Sehen Sie sich einmal die Beschreibung des Elementtyps object in HTML 4 an. Es ist aus dem img hervorgegangen und zeigt, wie kompliziert es sein kann, einfach ein Objekt in ein Dokument einzufügen.
In der Definition des externen Entity haben wir im
Beispiel einen (Unix-)Pfadnamen angegeben. Wie diese
Zeichenkette verarbeitet wird, liegt außerhalb des
XML-Standards. Auf einem PC würde
also ein anderer Pfadname stehen, etwa
C:\sgml\tabelle.dtd. Zusätzlich zu den
systemabhängigen Bezeichnern kann man auch noch die
Public Identifier verwenden. Sie sind
system-unabhängig und müssen vom
XML-Prozessor in geeigneter Weise in einen
Dateinamen o.Ä. umgewandelt werden. Die HTML-4-DTD lädt
zum Beispiel die Entities für
den ISO-Latin-1-Zeichensatz mit einer
Kombination aus Public und System IdentifierDas
Beispiel zeigt auch, dass ein System Identifier auch ein
URL sein kann. Es hängt von dem verarbeitenden
Programm ab, ob es etwas damit anfangen kann.:
<!ENTITY % HTMLlat1 PUBLIC "-//W3C//ENTITIES Latin1//EN//HTML" "http://www.w3.org/DTD/ISOlat1.ent"> %HTMLlat1;
Bedingte Abschnitte
Das
nächste Feature, das wir Ihnen vorstellen wollen, hat zwar
nicht direkt mit Parameter-Entities zu tun, lässt sich damit
aber besser nutzen. Es geht um Abschnitte einer
DTD, die nicht immer sichtbar
sind, sondern nur
unter gewissen Bedingungen.
Stellen Sie sich vor, Sie schreiben einen etwas umfangreicheren Text, vielleicht ein Buch. Meist werden Sie es nicht schaffen, einen so langen Text beim ersten Versuch so zu schreiben, wie Sie es gerne möchten. Dem ersten Entwurf folgt der zweite und so weiter, bis endlich nach einigen Schritten die endgültige Form vor Ihnen liegt. In der Entwurfsphase ist es üblich, Kommentare und Bemerkungen zu schreiben, die im gedruckten Buch nicht enthalten sein dürfen. Dazu können Sie natürlich XML-Kommentare einsetzen. Der Nachteil ist dabei, dass der Kommentartext in einem Probeausdruck nicht sichtbar ist. Schöner wäre es doch, einen Elementtyp kommentar zur Verfügung zu haben, dessen Inhalt auch im Probeausdruck erscheint. Nichts leichter als das:
<!ELEMENT kommentar (#PCDATA)>
So weit so gut. Nun müssen Sie nur noch aufpassen, dass Sie Ihre Kommentare löschen, sobald die Arbeit beendet ist. Auch hier liegt eine Idee zur Verbesserung nahe: Das XML-System soll prüfen, ob alle Kommentare entfernt sind. Diese Überprüfung überlässt man am besten dem Parser, indem man die Definition des Elementtyps kommentar aus der DTD entfernt. Umgangssprachlich ist also Folgendes gewünscht: Kommentare sind in der Entwurfsphase erlaubt, in der endgültigen Fassung jedoch verboten. XML bietet dafür ein Konstrukt an, genannt bedingter Abschnitt. Realisiert wird er durch einen markierten Abschnitt der externen (!) DTD, der entweder berücksichtigt (include) oder ignoriert wird (ignore). Für die Entwurfsphase sieht das so aus:
<![INCLUDE[ <!ELEMENT kommentar (#PCDATA)> ]]>
Nach getaner Arbeit weisen Sie den Parser an, die Kommentar-Deklaration zu ignorieren:
<![IGNORE[ <!ELEMENT kommentar (#PCDATA)> ]]>
Sollte Ihre Instanz nun noch ein Element vom Typ kommentar enthalten, so wird der Parser dies als nicht deklarierten Typ, also als Fehler, anzeigen.
Wenn Sie solche bedingten Abschnitte an mehr als einer Stelle einsetzen, kann es recht lästig sein, zwischen INCLUDE und IGNORE zu wechseln. Leichter geht es mit geschickt benutzten Parameter-Entities, wie das folgende Beispiel demonstriert.
<!ENTITY % entwurf 'INCLUDE' > <!ENTITY % fertig 'IGNORE' > <![%entwurf;[ <!ELEMENT kommentar (#PCDATA)> ]]> <![%fertig;[ <!ELEMENT kommentar EMPTY> ]]>
Die Funktion ist die gleiche wie
zuvorEin
Unterschied besteht darin, dass ein Kommentar in der
endgültigen Fassung zwar noch erlaubt ist, jedoch nur als
leeres Element. Der Parser zeigt also bei einem nicht leeren
Kommentar immer noch einen Fehler an.. Hier genügt
es jedoch, in der Definition der Entities die Worte
INCLUDE und IGNORE zu vertauschen. Die
Änderung wird damit automatisch für alle bedingten
Abschnitte wirksam.