|
Übersicht: Erweiterung um TCL-Skripte
Grundlagen eigener TCL-Skripte
Das Definieren eigener Makros ermöglicht sehr flexibles und übersichtliches Arbeiten bei der Erstellung
umfangreicher html-Seiten.
Der Einsatz von Makros stösst aber an seine Grenzen, wenn komplexe (Programmier-)Aufgaben,
wie die Auswertung von Server Log-Dateien oder Datenbank-Zugriffe, zu bewältigen sind. Die Makros
würden für diese Aufgaben sehr gross und unhandlich werden.
xml2html stellt, um diesen Schwachpunkt zu beheben, eine Programmierschnittstelle zur Verfügung
,
mit der auch komplexe Abläufe realisiert werden können. Über die Schnittstelle ist es möglich, eigene
TCL-Routinen
in die Funktionalität von xml2html zu integrieren.
Für die folgenden Erklärungen und Beispiele wird eine gewisse Grundkenntnis der Skriptsprache
TCL vorausgesetzt. Sollten noch keine Erfahrungen mit TCL vorhanden sein, lohnt sich ein Blick
auf diese Sammlung von Manuals,
bevor die nächsten Abschnitte gelesen werden.
|
|
TCL in xml-Dateien
Der <eval-tcl>-Tag ermöglicht die Ausführung von TCL-Code innerhalb einer xml-Seite:
<eval-tcl>clock format [clock seconds] -format "%d. %m. %Y"</eval-tcl>
|
Der xml-Code liefert das heutige Datum: 21. 01. 2019
(funktioniert nur bei laufendem xml2html-Tool, nicht in der statischen Version)
Um eine klare Trennung von dynamischen und statischen Inhalten einer Seite zu erreichen, sollte der
<eval-tcl>-Tag allerdings nur zum Testen der eigenen Routinen in xml-Seiten verwendet werden.
Nach dem erfolgreichem Abscchluss der Entwicklungs- und Testphase sollte der Code in spezielle Dateien ausgelagert werden.
Die nächsten beiden Abschnitte gehen auf die dafür notwendigen Schritte näher ein und beschreiben an zwei kurzen
Beispielen, worauf insbesondere zu achten ist.
|
|
Ein simple-Tag Beispiel
Der wesentliche Teil bei der Erweiterung der Funktionalität von xml2html besteht darin, eine Funktion
zu implementieren und diese einem Tag zuzuweisen. Der Name der Funktion setzt sich dabei aus dem Präfix process_
und dem Namen des neuen Tags (TAGNAME) zusammen.
proc process_TAGNAME args { .... }
|
Durch den Aufruf von <tagname> in einer xml-Datei würde die Funktion ausgeführt und das
Ergebnis der Bearbeitung an dieser Stelle ausgegeben werden.
Besonders bei der Entwicklung komplexer Routinen erweist es sich oft als hilfreich, Debug-Output zu erzeugen.
Zu diesem Zweck stellt xml2html eine eigene tracing-Methode bereit, die sich in den selbst
geschriebenen Routinen aufrufen lässt.
Dazu wird die zu tracende Funktion bei xml2html registriert:
trc_register { process_TAGNAME }
|
Der Aufruf von:
trc process_TAGNAME "some useful debugging output"
|
schreibt dann "some useful debugging output" nach stderr. Wurde die Seite über den Apache aufgerufen,
wird die Meldung entsprechend ins Apache-Error-Logfile (/var/log/httpd/error.log) geschrieben.
Nach getaner Entwicklungs- und Debuggingarbeit lässt sich der trace-output leicht per vorangestelltem Minus in der
Registrierungsroutine abstellen. Der folgender Eintrag bringt trc also zum Schweigen:
trc_register { -process_TAGNAME }
|
Um die eben genannte Funktionalität der Programmierschnittstelle einmal zu zeigen, soll ein neuer Tag erzeugt werden,
mit dem es möglich ist, verschiedene Währungen zu konvertieren. Der neue Tag soll <currency>
heissen und über folgende Werte parametrisierbar sein:
<currency src="" trg="" value="">
|
src entspricht dabei der Ausgangswährung, trg der Zielwährung und
value dem zu konvertierenden Geldbetrag.
Der TCL-Code für den <currency>-Tag:
01: trc_register process_CURRENCY
02:
03: proc process_CURRENCY args {
04: array set params {
05: SRC "EUR"
06: TRG "USD"
07: VALUE "0"
08: }
09: array set params $args
10:
11: array set eur {
12: EUR "1"
13: USD "0.9489"
14: BPF "0.6381"
15: YEN "118.1800"
16: SFR "1.4769"
17: SKR "9.0645"
18: NKR "7.4015"
19: }
20:
21: trc process_CURRENCY "Converting from $params(SRC) to $params(TRG)"
22:
23: if {$params(SRC) == "EUR" } {
24: set res [expr $params(VALUE)*$eur($params(TRG))]
25: } else {
26: set res [expr $params(VALUE)/$eur($params(SRC))*$eur($params(TRG))]
27: }
28: return [format "%1.2f" $res]
29: }
|
Zuerst wird die tracing-Routine aktiviert (Zeile 1), nach erfolgreicher Implementierung der Funktion muss nur an dieser Stelle
das tracing durch ein vorangestelltes minus wieder ausgeschaltet werden.
In Zeile 3 steht der Funktionskopf mit dem Namen der Funktion. Dieser setzt sich zusammen aus dem
Präfix process_ und dem Tagnamen.
Die Zeilen 4 - 9 sind für die Parameter des Tags zuständig. Erst werden die Parameter mit einem default-Wert
belegt (EUR, USD, 0), Zeile 9 überschreibt diese Werte dann mit den tatsächlichen durch den Tag-Aufruf übergebenen Werten.
Soll kein default-Wert für einen Parameter angegeben werden, sind leere "" zu verwenden.
Das Array eur (Zeile 11) enthält die benötigten Umrechnungskurse.
In Zeile 21 wird von der Möglichkeit des debugging gebraucht gemacht. Bei jedem Aufruf der Funktion wird der String
ausgewertet und nach stderr bzw. ins Error-Logfile geschrieben.
Die Zeilen 23 bis 27 enthalten die eigentliche Umrechnung, bevor in Zeile 28 das Ergebnis zurückgegeben wird.
Der TCL-Code eines Tags sollte jeweils in einer Datei mit dem Namen des Tags und der Endung ".tcl" abgespeichert werden.
In unserem Beispiel also:
currency.tcl
Um den neuen Tag in einer xml-Seite nutzen zu können, muss die Funktionalität per
<tcl source="currency.tcl">
|
geladen werden.
Wo die TCL-Files liegen müssen, damit sie durch xml2html gefunden werden,
wird im Abschnitt Verzeichnisstrukturen beschrieben.
In einer kleinen Beispiel Anwendung
soll jetzt das Zusammenspiel des neuen Tags mit Get/Post-Operationen sowie CGI-Variablen demonstriert werden.
|
|
Ein compound-Tag Beispiel
Im <currency>-Beispiel haben wir die Verwendung eines simple-Tags besprochen. Jetzt soll
ein compound-Tag, also ein zusammengesetzter Tag mit Start und Ende verarbeitet werden.
Der neue Tag soll diesmal <javasource> heissen und keine Parameter besitzen.
<javasource> .... </javasource>
|
Durch <javasource> soll es möglich sein, alle Schlüsselwörter einer Programmiersprache, in diesem Fall
Java, automatisch in einer bestimmten
Farbe anzuzeigen.
Der TCL-Code ist auch in diesem Beispiel relativ einfach:
javasource.tcl
01: trc_register -process_JAVASOURCE
02:
03: define_compound_tag "<javasource>compound</javasource>"
04:
05: proc process_JAVASOURCE args {
06: array set params {
07: COMPOUND ""
08: }
09: array set params $args
10:
11: set res $params(COMPOUND)
12:
13: array set array_color {
14: green "super strictfp native synchronized finally new this catch package throws throw transient
15: const try default import volatile instanceof"
16: red "case switch break for if while else do continue return goto"
17: blue "void long byte boolean float short char double int null"
18: orange "interface class extends private abstract implements public protected static final"
19: }
20:
21: foreach color [array names array_color] {
22: foreach keyword $array_color($color) {
23: trc process_JAVASOURCE "element: $keyword"
24: regsub -all $keyword $res <span style="color:$color">$keyword</span> res
25: }
26: }
27:
28: return "<pre>[parse_xml_text $res]</pre>"
29: }
|
Der wichtigste Unterschied zwischen der Verarbeitung eines simple- und eines compound-Tags
besteht in der Anweisung:
define_compound_tag "<tagname>compound</tagname>"
|
Hierdurch wird xml2html mitgeteilt, dass zwischen dem Start- und Ende-Tag noch zu bearbeitende Element stehen.
Da compound von xml2html wie ein Parameter behandelt wird,
muss es auch im array params (Zeile 6 - 8) deklariert werden.
Die Verarbeitung von compound ist ab jetzt analog zu allen anderen Parametern.
Das folgende Java-Code-Beispiel:
<javasource>
public class LinkedList {
protected static final
LinkedList empty = new LinkedList(null, null);
protected
LinkedList(Object info, LinkedList next)
{
this.info = info;
this.next = next;
}
//--------------------
public boolean isEmpty()
{
return this == empty;
}
//--------------------
public Object at(int i)
throws IndexOutOfBoundsException
{
if ( i < 0 || isEmpty() )
throw new IndexOutOfBoundsException("illegal index");
return ( i == 0 ) ? hd() : next.at(i - 1);
}
}
</javasource>
|
würde nach der Bearbeitung durch die TCL-Funktion so aussehen:
public class LinkedList {
protected static final
LinkedList empty = new LinkedList(null, null);
protected
LinkedList(Object info, LinkedList next)
{
this.info = info;
this.next = next;
}
//--------------------
public boolean isEmpty()
{
return this == empty;
}
//--------------------
public Object at(int i)
throws IndexOutOfBoundsException
{
if ( i < 0 || isEmpty() )
throw new IndexOutOfBoundsException("illegal index");
return ( i == 0 ) ? hd() : next.at(i - 1);
}
}
|
|
Mehrere Parser auf einen Text anwenden
Im <javasource>-Beispiel wurde auf den Text zwischen Start- und Endtag
ein neu entwickelter Textparser (Farbgestaltung) angewendet. Oft ist es sinnvoll und gewünscht nicht nur
eine, sondern mehrere verschiedene Textbearbeitungsschritte durchzuführen. Dafür steht in xml2html
die block-Funktion zur Verfügung. Sie erlaubt auf einen Text mehrere Parser einfach
hintereinander anzuwenden.
define_compound_tag "<newtag>compound</newtag>"
proc process_NEWTAG args {
eval process_BLOCK $args [list PROCS {parser1 parser2 ...}]
}
|
In diesem Beispiel wird die Verarbeitung des <newtag> an die block-Funktion
übergeben. Dabei wird der block-Funktion eine Liste von Parsern (parser1 parser2 ...) mitgegeben,
die auf den zu bearbeitenden Text in der Reihenfolge von links nach rechts angewendet werden.
So ist es leicht möglich, nicht nur die Farbe bestimmter Schlüsselwörter zu verändern, sondern auch deren Schriftart oder Grösse.
Es müssen dafür nur einmal die entsprechenden Parser entwickelt werden, die dann beliebig kombiniert werden können.
xml2html stellt bereits zwei wichtige Parser zur Verfügung:
- parse_xml_text
- special2html
Mit parse_xml_text werden alle xml-Tags im Text weiterverarbeitet, special2html
ersetzt alle speziellen Zeichen, wie z.B. die spitzen Klammern (< >) in ihre entsprechenden
html-Sequencen (< >).
Der in diesem Tutorial oft verwendete <escape-xml>-Tag ist ein gutes Beispiel für die Anwendung
der block-Funktion:
define_compound_tag "<keywords>compound</keywords>"
proc process_escape-xml args {
eval process_BLOCK $args [list PROCS {special2html}]
}
|
Es nutzt den special2html-Parser und ermöglicht somit die einfache Darstellung
von Tagname inkl. der spitzen Klammern, da diese in ihre html-Sequenz umgewandelt werden.
|
|
|