Dokumentation der StringParser_BBCode-Klasse

1. Einführung

1.1 Allgemeines

Die StringParser_BBCode-Klasse bietet die Möglichkeit, Zeichenketten mit BB-Codes zu parsen und daraus beispielsweise HTML-Code zu erzeugen. BBCode sind Auszeichnungen, mit denen man Text formatieren oder strukturieren kann. Sie sind ähnlich wie die Auszeichnungen bei HTML aufgebaut, nutzen jedoch eckige Klammern anstelle von spitzen Klammern. Desweitern unterscheidet sich BBCode von HTML, dass ein ungültiger Code-Abschnitt ignoriert werden sollte, während bei HTML die Gültigkeit wichtig ist.

Ein Beispiel für einen Text, der mit Hilfe von BBCode ausgezeichnet wurde:

Dies ist ein [b]fetter, [i]kursiver[/i] Text[/b]!

Dieser Text könnte nun nach HTML verwandelt werden:

Dies ist ein <b>fetter, <i>kursiver</i> Text</b>!

Dies würde dann so aussehen:

Dies ist ein fetter, kursiver Text!

Die einfachste Möglichkeit, den BBCode hier in HTML umzuwandeln, wäre, [b], [i], [/b] und [/i] durch jeweils <b>, <i>, </b> und </i> zu ersetzen. Dies würde im obigen Beispiel problemlos funktionieren. Allerdings würde es zu Problemen führen, wenn jemand sich beim BBCode vertippt. Folgendes Beispiel:

Dies ist ein [b]fetter, [i]kursiver[/io] Text[/b]!

Der Autor des Textes hat sich vertippt und ist beim Eintippen des [/i] auch auf die o-Taste gekommen, was dazu geführt hat, dass er oder sie stattdessen [/io] eingetippt hat. Wenn man dies nun stupide ersetzen würde, dann würde folgender HTML-Code herauskommen:

Dies ist ein <b>fetter, <i>kursiver[/io] Text</b>!

Dies ist ungültiger HTML-Code, da der Code nicht korrekt verschachtelt ist und das <i>-Element nie geschlossen wird. Es gibt andere Ansätze, BBCode zu ersetzen, die beispielsweise mit regulären Ausdrücken arbeiten, um sicherzustellen, dass das Element auch wirklich wieder geschlossen wird, allerdings können diese für die korrekte Verschachtelung nicht garantieren.

Aus diesem Grund verfolgt die Klasse einen anderen Ansatz. Der Text wird sozusagen Zeichen für Zeichen eingelesen und der komplette Text wird dann in eine Baumstruktur umgewandelt. Diese Baumstruktur wird dann, sobald der komplette Text umgewandelt ist, in beispielswiese HTML-Code weiterverwandelt. Der Text

Dies ist ein [b]fetter, [i]kursiver[/i] Text[/b]!

würde also in folgende Baumstruktur umgewandelt werden:

Wenn nun ein Text wie oben kommen sollte, bei dem das [/i] nicht vorhanden ist, sondern durch einen Vertipper etwas anderes dort steht, dann wird die Klasse dies bei dem [/b] erkennen. Das [/b] würde nämlich kommen, während die Klasse noch auf ein [/i] wartet, weil sie weiß, dass das [i] noch offen ist. Hier gibt es nun zwei Möglichkeiten, von denen der Programmierer, der die Klasse verwendet, entscheidet, was genau passiert: Zum einen könnte die Klasse daraufhin das [i] für ungültig erklären und dort noch einmal weitermachen, wo dieses [i] stand, oder sie könnte an der Stelle des [/b] (an der sie ja merkt, dass das [i] noch offen ist, das [i] gleich mit schließen (d.h. so tun, als ob dort [/i][/b] stünde). Die Klasse versucht jedoch nicht zu erraten, dass mit [/io] vielleicht doch [/i] gemeint sein könnte, da das dazu führen könnte, dass die Klasse an anderer Stelle Fehler macht, weil sie denkt, sie korrigiere einen Fehler, der in Wirklichkeit keiner ist.

Die Klasse selbst gibt nicht vor, nach was für Codes gesucht wird. [b] und [i] sind populäre Beispiele und deswegen werden sie hier angeführt, die Klasse selbst bietet jedoch die Möglichkeit, beliebige eigene Codes zu definieren, solange sie sich in eckigen Klammern befinden. Im nächsten Kapitel wird gezeigt, wie man mit der Klasse eigene Codes definieren kann.

Es können auch - genauso, wie in HTML - Attribute angegeben werden. Diese sähen dann beispielsweise so aus:

Dies ist ein [b staerke=richtig_fett]richtig fetter Text[/b]!

Die Klasse erkennt verschiedene Formen der Attribute:

[code attribut=wert], [code attribut="wert"], [code attribut='wert']
Dies ist die Form, die sich an HTML anlehnt. Es ist desweiteren auch die einzige Form, die es erlaubt, mehrere Attribute gleichzeitig zu setzen. Der Ausdruck vor dem Gleichheitszeichen ist der Name des Attributs, der Ausdruck dahinter ist der Wert des Attributs. Wenn der Wert in Anführungszeichen gesetzt wird, dann sind auch Leerzeichen und ] im Attributwert möglich; um zusätzlich noch zu ermöglichen, dass ein Anführungszeichen selbst im Attributwert vorhanden sein kann, muss es mit \ maskiert werden. Beispiel: [code attribut="wert ] und immer noch weiter im wert\" und immer noch weiter"].
[code=wert], [code = wert], [code="wert"], [code='wert']
Mit dieser Form kann nur ein einziges Attribut gesetzt werden. Dieses Attribut hat den Namen default. Eine Angabe [code=wert] wäre also identisch mit [code default=wert]. Diese Syntax orientiert sich an klassichem BB-Code.
[code:wert], [code: wert]
Diese Syntax ist auch möglich und das Attribut heißt hier genauso default.

1.2 Verschachtelung

Wie oben bereits gesehen, müssen Elemente korrekt verschachtelt werden. Dies wird durch die Klasse sichergestellt. Allerdings ist die obige Prüfung nur eine rein formale Kontrolle. Bei der Verarbeitung von folgendem Konstrukt wird die Problematik deutlich:

[b]Hier kommt eine Liste:
[list]
[*] Listenpunkt
[*] Listenpunkt
[/list]

[/b]

Wenn man das nun nach HTML verwandeln würde, dann würde folgendes herauskommen:

<b>Hier kommt eine Liste:
<ul>
<li> Listenpunkt</li>
<li> Listenpunkt</li>
</ul>
</b>

Dieser HTML-Code ist zwar formal korrekt verschachtelt, allerdings darf in HTML das <b>-Element keine <ul>-Elemente enthalten. Es würde also wieder ungültiges HTML produziert werden. Aus diesem Grund kann man der Klasse sagen, welches Element welches andere Element enthalten darf. Dazu gibt es sogenannte Inhaltstypen. Jedem Element wird ein Inhaltstyp zugewiesen. Desweiteren kann man bei jedem Element angeben, innerhalb von welchen Inhaltstypen es sich befinden darf. Ein Beispiel:

[a][b][c]Text[/c][/b][/a]

Hier wäre das Element [b] innerhalb des Elements [a] und das Element [c] innerhalb des Elements [b]. Folgende Struktur würde also erzeugt werden:

Wir geben jedem Element nun einen Inhaltstyp. Das Element [a] erhält den Inhaltstyp alpha, das Element [b] den Inhaltstyp beta und das Element [c] den Inhaltstyp gamma. Damit der Parser alle Elemente hier verwertet, muss das Element [b] innerhalb des Inhaltstyps alpha erlaubt sein, weil diese der Inhaltstyp von [a] ist, innerhalb dessen sich das [b]-Element hier befindet. Genauso muss das Element [c] innerhalb des Inhaltstyps beta erlaubt sein, weil das der Inhaltstyp des Elements [b] ist, innerhalb dessen sich das [c]-Element hier befindet. Allerdings muss das [c]-Element nicht innerhalb des Inhaltstyps alpha erlaubt sein, da nur die erste Ebene betrachtet wird.

Wenn noch gar kein Element geöffnet ist, gilt der sogenannte Root-Inhaltstyp. Dieser ist per Standardeinstellung block, kann jedoch geändert werden. Siehe dazu das Kapitel Parserfunktionen, in dem die Inhaltstypen eine weitere Rolle spielen.

Man hat jedoch nicht nur eine Positivliste, sondern auch eine Negativliste. Ein Link innerhalb eines anderen Links wäre nicht sinnvoll. Allerdings könnte man ja auch ein Element dazwischenschalten und somit die Positivliste aushebeln. Beispiel:

[link][b][link]Text[/link][/b][/link]

Es gäbe keinen Grund, [b] in [link] zu verbieten und auch keinen Grund [link] in [b] zu verbieten, allerdings gäbe es durchaus einen Grund, diese Konstruktion zu verbieten. Hier greift die Negativliste. Die Negativliste wird auf alle Ebenen angewandt, nicht bloß auf die nächsthöhere Ebene. Somit kann man obige Konstrukte ausschließen.

1.3 Spezielle Codes

Manchmal kann es sinnvoll sein, das Erkennen von Codes kurzzeitig zu deaktivieren. In vielen Foren wird der Code [code] angeboten, mit dem man Ausschnitte aus Quelltexten markieren kann und dafür sorgen kann, dass [b] und ähnliches innerhalb dieses Ausschnitts nicht ersetzt wird. Der Ausschnitt kann dann nur von [/code] beendet werden. Die Klasse bietet auch eine Möglichkeit, dieses Verhalten besonders einfach zu erzeugen. Beispiel:

[code]
// so wird der [b]-Code erzeugt:
// ...
[/code]

In diesem Beispiel wäre es natürlich nicht gewollt, das [b] zu interpretieren, das gehört ja zum Code, den man anzeigen lassen will, dazu. Dazu gibt es eine sogenannte Behandlungsart namens usecontent, die dazu führt, dass sobald ein Start-Tag gefunden wurde, nur noch nach dem dafür vorgesehenen End-Tag gesucht wird.