Oliver Boehm

Maxi - Präcompiler zur Erzeugung dynamischer HTML-Seiten

Übersicht

Motivation

Die verfügbaren Möglichkeiten zur Erzeugung dynamischer HTML-Seiten lassen sich entsprechend der Sicht, die sie auf eine dynamische HTML-Seite bieten, in zwei Kategorien unterteilen:
  1. Programmsicht

  2. klassische CGI-Programme, CGI-Scripte
    Servlets
  3. Dokumentsicht

  4. CGI-Wrapper (z.B. PHP3)
    Server Pages (z.B. Active Server Pages, Java Server Pages)
Die Möglichkeiten zur Erzeugung dynamischer HTML-Seiten in beiden Kategorien haben Vor- und Nachteile.
Ein klassisches CGI-Programm hat die Vorteile, daß es in einer beliebigen Programmiersprache implementiert werden kann und vom Web-Server unabhängig ist, da es keine speziellen Erweiterungen voraussetzt. Servlets und CGI-Programme (bei CGI-Programmen abhängig von der CGI-Implementation) bieten die besten Voraussetzungen für eine hohe Performanz. Nachteilig an CGI-Programmen und Servlets ist, daß die HTML-Deklarationen in Funktionsaufrufen im Programmtext verteilt sind. Die "dynamische HTML-Seite" also nicht mit einen HTML-Editor bearbeitet werden kann. Ein komplexes HTML-Design läßt sich so schwer realisieren und warten.

CGI-Wrapper und Server Pages bieten die Sicht eines Dokumentes auf eine dynamische HTML-Seite. Diese lassen sich daher prinzipiell mit HTML-Editoren bearbeiten. Nachteile von beiden sind die Abhängigkeit von bestimmten Web-Servern oder speziellen Erweiterungen, teilweise benutzen sie eigene Programmiersprachen und bieten je nach Anbindung eine schlechtere Performanz.
 

Ziele

Das Ziel einer neuen Methode zur Erzeugung dynamischer HTML-Seiten muß es daher sein, die Vorteile der Dokumentsicht mit den Vorteilen der Programmsicht zu verbinden:
  1. Programmiersprachenunabhängigkeit
  2. Web-Serverunabhängigkeit
  3. dynamische HTML-Seiten lassen sind mit HTML-Editoren bearbeiten
  4. einfache Anwendung
  5. leicht verständliche Konzepte
Die freie Wahl der Programmiersprache bietet aus meiner Sicht nicht zu unterschätzende Vorteile:

Zum einen muß für die Implementation der Methode keine eigenen Programmiersprache entwickelt werden. Eine eigene Programmiersprache läuft immer Gefahr, im Vergleich zu anderen existierenden Sprachen unvollständig zu sein. Sie muß gewartet und weiterentwickelt werden.

Zum anderen kann der Anwender der Methode die Programmiersprache auswählen, die seinen Anforderungen entspricht:

Die freie Wahl des Web-Servers ist z.B.bedeutend für den Entwickler von Internet-Anwendungen und letztendlich für den Betreiber dieser Anwendungen.

Eine einfache Anwendung und leicht verständlichen Konzepte sind entscheidend bei der Abschätzung des zusätzlichen Aufwandes zur Implementierung  dynamischer HTML-Seiten.

Lösung

Durch eine systematische Überführung der Dokumentsicht (HTML-Seite) in die Programmsicht (ausführbares Programm) wirken die Vorteile aus beiden Sichten.
Meine Umsetzung ist konzeptionell vergleichbar mit Präcompilern.

Aus einem Html-Dokument, in dem dynamische Teile explizit markiert sind und  das Programmfragmente einer beliebigen Sprache enthalten kann, erzeugt er Programmtext in der verwendeten Programmiersprache. Diesen kann man übersetzen und das Programm über die CGI-Schnittstelle ausführen. Die Ausgabe des Programms entspricht dem gewünschten dynamischen Html-Dokument.

Bestandteile eines dynamischen HTML-Dokuments

Die vorgestellte Methode beruht auf der Unterscheidung folgender Bestandteile in einem dynamischen Html-Dokument:
 
reiner Html-Text Reinen Html-Text muß der Präcompiler in Funktionsaufrufe der Zielsprache umgewandeln, die die Ausgabe des Textes bewirken. 
Variablen der Zielsprache Um innerhalb von Html-Deklarationen z.B. in einer Zelle einer Tabelle dynamische Werte anzuzeigen, habe ich ein Variablenersetzungs-Konzept realisiert. Die Verwendung einer Variablen hat folgende Bedeutung: Gib an der Stelle des Vorkommens den Wert der Variablen der verwendeten 
Programmiersprache aus.Variablen werden von einem Html-Editor als normaler Html-Text angesehen und können direkt bearbeitet werden. 
Der Präcompiler ersetzt eine Variable durch einen Funktionsaufrufe, der ihren Wert ausgibt.
Html-Muster (Pattern) Pattern ermöglichen den Zugriff auf dynamische Teile des Html-Dokumentes, die in bestimmten Abhängigkeiten oder wiederholt auftauchen sollen. Sie sind eine Art Textbaustein, die innerhalb des Html-Dokumentes definiert und durch einen Bezeichner identifiziert werden. Sie werden somit bei der Bearbeitung des Html-Dokumentes mit einem Html-Editor an der Stelle ihrer Deklaration angezeigt und können bearbeitet werden. Das resultierenden Programm erzeugt an dieser Stelle keine Ausgabe, sondern muß einen explizierten Zugriff auf dieses Pattern realisieren. Sie können je nach verwendeter Zielsprache in Funktionen, Methoden oder Objekten bzw. in einer Hashtable abgebildet werden. 
eingebetteter Programmtext der Zielsprache Je nach Einbettung kann der Programmtext im HTML-Editor angezeigt und bearbeitet werden. Der Präcompiler kann ausgelagerten Programmtext inkludieren. Somit kann man diesen auch mit anderen Tools bearbeiten. In das resultierende Programm übernimmt er ihn ohne weitere Bearbeitung.

Aufbau des Präcompilers

Der Präcompiler unterteilt sich in zwei Komponenten: Der Präcompiler ist in Perl implementiert, da sich Perl durch sein Pattern Matching gut zum Parsieren der Html-Dokumente eignet und sein Modul-Konzept ermöglicht, die Spracherweiterung dynamisch zur Laufzeit einzubinden. Dadurch kann man die Zielsprache per Option beim Aufruf des Präcompilers angeben. Durch die klare Trennung zwischen Parsierung und sprachspezifischer Erzeugung von Programmtext ist der Präcompiler leicht an beliebige Programmiersprachen angepaßbar.

Schnittstelle zwischen Präcompiler und Spracherweiterung

Der Präcompiler ruft im Verlauf des Parsierens eines Html-Dokumentes folgende Funktionen der Spracherweiterung auf:
 
Funktion
Parameter
Beschreibung
atBegin keine wird beim Start des Präcompilers aufgerufen
atEnd keine wird bei Beendigung des Präcompilers aufgerufen
beforeFile   wird vor dem Parsieren einer neuen Datei aufgerufen
  Dateiname Name der Datei die parsiert wird
afterFile   wird nach dem Parsieren einer neuen Datei aufgerufen
  Dateiname Name der Datei die parsiert wurde
atPattern   wird beim Auftreten eines Patterns aufgerufen
  Identifier Bezeichner des Patterns
  ParamList Liste der Parameter
  Text Html-Text des Pattern
atHTML   wird beim Auftreten von Html-Text aufgerufen
  Text Html-Text
atVariable   wird beim Auftreten einer Variablen aufgerufen
  Variable Bezeichner der Variablen
atProgramText   wird beim Auftreten von eingebettetem Programmtext aufgerufen
  Identifier Bezeichner des Programmtextes
  Text eingebetteter Programmtext

Umsetzung des Html-Dokuments mit der Spracherweiterung java.pm

Die Umsetzung des Html-Dokumentes in ein Programm der Zielsprache hängt stark von deren Konzepten ab und sollte daher ausschließlich von der Spracherweiterung realisiert werden.
Exemplarisch habe ich zunächst ein Modul 'java.pm' zur Erzeugung von Java-Code implementiert, weil dadurch vergleichbar mit Java-Applets am ehesten Plattformunabhängigkeit erreicht wird und Java in Hinblick auf die Generierung von Servlets interessant erscheint.
Die Erzeugung des Programmtextes mit java.pm geschieht nach folgendem Prinzip: Im Ergebnis entsteht eine Datei, die ein Java-Klasse mit einer Methode printPage()deklariert. Diese Datei kann man kompilieren und über einen Web-Server ausführen. Sie realisiert die Ausgabe des dynamischen Html-Dokuments und die Funktionalität der eingebetteten Programmfragmente.

Beschreibung der erweiterten HTML-Syntax

Die verwendete Syntax wird im Grundsatz vom Parser bestimmt. Spezielle Konstrukte wie zum Beispiel feste Bezeichner für bestimmte Programmfragmente (#begin, #end s.o.) werden durch die Spracherweiterung festgelegt.

Integration der Programmfragmente

Für die Kennzeichnung der Programmfragmente muß ähnlich wie in CGI-Wrappern ein zusätzliches Tag definiert werden. Hierbei wurde das Format eines üblichen Html-Kommentars verwendet, um die Bearbeitung des Html-Dokuments mit Html-Editoren zu gewährleisten.

Programmfragmente werden wie folgt gekennzeichnet:

Eine Eigenschaft des Netscape Composer und wahrscheinlich auch anderer Html-Editoren ist es, keine Html-Kommentare vor dem <body>-Tag zuzulassen. Vom Netscape Composer wurde ein solcher Kommentar jedenfalls hinter das <body>-Tag verschoben. Dadurch ist eine besondere Kennzeichung der Fragmente erforderlich, die zu Beginn und am Ende des erzeugten Programms ausgeführt werden sollen.

Variablenersetzung

 Variablen haben folgendes Format: Variablen sind syntaktisch nicht auf den Variablennamen beschränkt. Angenommen, es existiert ein Java-Objekt 's' der Klasse Stack, dann würde $s.pop()$ nach System.out.print(s.pop()) umgesetzt, wodurch das letzte Objekt des Stacks 's' ausgegeben wird. Es sind also, abhängig von der verwendeten Sprachumsetzung, komplexere Ausdrücke möglich.

HTML-Muster / Pattern

 Pattern werden wie folgt gekennzeichnet:  In der exemplarischen Java-Umsetzung wird aus dem Pattern eine separate Methode erzeugt: Diese Methode kann nun an beliebiger Stelle des Programms aufgerufen werden. Natürlich werden auch in den Pattern (wie oben beschrieben) Variablen ersetzt. Um die Ausgabe von lokalen Variablen zu realisieren, ist die Definition einer Parameterliste möglich, die unbearbeitet in die resultierende Methodendeklaration übernommen wird. Ihre Syntax ist also abhängig von der verwendeten Zielsprache.

Durch die Eigenschaft, daß Html-Editoren Kommentar-Tags in bestimmten Deklarationen verschiebt, ist es u.U. notwendig, ein Pattern größer zu definieren als eigentlich erforderlich. Dazu ein Beispiel: Sollen in einer Tabelle die aktuellen Kurswerte der DAX-Aktien angezeigt werden, reicht es eigentlich aus, die Deklaration des Tabellensatzes als Pattern zu definieren, da dies der sich wiederholende Teil der Tabelle ist:

Der Html-Editor verschiebt die <pattern>-Tags allerdings zum <table>-Tag, so daß die Definition des Pattern zerstört wird.

Zerlegung von Pattern

Die Lösung des Problems stellt eine zusätzliches <split>-Tag dar: Dieses Tag muß innerhalb eines <pattern>-Tags definiert sein und bedeutet: zerlege das Pattern in Teilpattern patternname1bis patternnamem, die durch die Tags tag1oder tag2 oder tagngekennzeichnet sind. Im obigen Beispiel sieht die Lösung wie folgt aus. In der exemplarischen Java-Implementation wird dies wie folgt umgesetzt: Es werden also Methoden für den Zugriff auf die einzelnen Teilpattern erzeugt. In einem nachfolgenden Programmfragment kann die Tabelle wie folgt gedruckt werden: Die Anzahl der Namen der Teilpattern muß mit der Anzahl der Teilpattern übereinstimmen, die letztendlich angesprochen werden sollen. Werden die Teilpattern durch verschiedene Tags gekennzeichnet, können mehrere Separatoren im split-Tag angegeben werden.
In den resultierenden Methoden der Teilpattern ist die im Pattern deklarierte Parameterliste nicht erforderlich, wenn keine Variablenvorkommen enthalten sind. Wird ein '#'-Zeichen an den Namen des Teilpattern im split-Tag angefügt, wird in der resultierenden Methodendeklaration eine leere Parameterliste erzeugt.

Includes

Zusätzlich zu den oben beschriebenen Definitionen kann auch ein <!-- #include -->- Tag verwendet werden, um Teile des Html-Dokumentes z.B.ausgelagerte Programmfragmente an der Stelle des <!-- #include -->-Vorkommens einzufügen. Die Syntax entspricht den bekannten Server-Side-Includes.

Umsetzung des Html-Dokuments mit der Spracherweiterung uni.pm

Die Arbeit mit java.pm hat gezeigt, daß der Ansatz Programmtext in Html-Dokumente einzubetten (dieser war durch Active Server Pages und Java Server Pages geprägt) und damit Html-Deklarationen und Programmtext zu mischen, den Programmierer noch nicht optimal unterstützt.
Ein Programmierer, der es z.B. gewohnt ist, in seinem Emacs mittels eines komfortablen Java-Mode Java-Programme zu erstellen, kann bei dem Mix aus HTML und Java durch den Java-Mode nicht unterstüzt werden. Dies ist auf andere Sprachen und Entwicklungsumgebungen übertragbar.
Eine Trennung von HTML und Programmtext war bei Verwendung von java.pm nur durch die Auslagerung der <!-- exec --> - Bereiche und das Zusammenführen mittels <!-- #include file=".." --> -Statement möglich. Dieses Trennung ist aber unvollständig, da im Prinzip nur die HTML-Datei zerlegt wird.
Aus diesem Grund habe ich das Ziel verfolgt, die zwei Sichten Dokumentsicht (HTML-Seite) und Programmsicht (Programmtext) bei der Bearbeitung dynamischer HTML-Dokumente zuzulassen. Die HTML-Datei sollte ausschließlich zur Beschreibung der HTML-Ausgabe und zur Kennzeichnung der Pattern dienen. Die Programmdatei enthält den Programmtext zur Implementierung der Erzeugungs-Funktionalität. Die HTML-Datei kann mit HTML-Editoren und die Programmdatei mit geeigneten  Programmierwerkzeugen bearbeitet werden.
Im Gegesatz zum Modul java.pm erzeugt das Modul uni.pm nicht ausschließlich neue Programmdateien. Man kann per Option eine bestehende Programmdatei angeben, in die das Modul den aus den HTML-Deklarationen resultierenden Programmtext hineingeneriert. Die Generierung des Programmtextes ist in der Programmdatei selbst steuerbar. Dadurch kann man z.B. eine Methode (Name, Parameter, etc.) für ein HTML-Pattern in der Programmdatei deklarieren und weist das Modul an, nur die HTML-erzeugenden Statements und nicht die gesamte Methode für dieses Pattern zu generieren. Die Schnittstelle zur HTML-erzeugenden Methode eines Pattern wird so in der Programmdatei festgelegt und nicht durch die Spracherzeugung oder in der HTML-Datei per Parameterliste.
Es ist somit möglich dynamische HTML-Dokumente ohne sprachabhängiger Programmtext zu erstellen. Das gleiche HTML-Dokument kann so zur Generierung von Programmtext einer beliebigen Programmiersprache genutzt werden. Dies kann bei einer Portierung einer Internet-Anwendung helfen.

Das Modul uni.pm ist konfigurierbar, so daß keiner in Perl einsteigen muß, um die Erzeugung der Programmtextes entsprechend der gewählten Zielsprache anzupassen.

Konfiguration der Spracherweiterung uni.pm

Zur Konfiguration von uni.pm kann man dem Präcompiler per Option (-c) eine Konfigurationsdatei angeben.
Die Konfigurationsdatei hat folgendes Format:
[Sprachbezeichner.]Bezeichner_des_Erzeugungsmuster : Erzeugungstext
Der Sprachbezeichner gibt die Zielsprache des Erzeugungsmusters an und ist frei wählbar. Beim Aufruf des Präcompilers gibt die Option -l die Zielsprache an. Der Aufruf 'maxi.pl -l java test.html' legt beispielsweise Java als Zielsprache fest. Ist die Option nicht angegeben, bestimmt der Typ der Programmdatei (Option -f) die Zielsprache. Durch den Aufruf 'maxi.pl -f test.java test.html' wird also ebenfalls Java als Zielsprache festgelegt.
Hat ein Erzeugungsmuster einen Sprachbezeichner, ist diese Erzeugungsmuster der Generierung der bezeichneten Zielsprache zugewiesen. Fehlt der Sprachbezeichner, ist das Erzeugungsmuster immer gültig. In der Beispielkonfiguration verwendet das Modul das Erzeugungsmuster 'java.text' nur bei der Generierung von Java-Code, das Erzeugungsmuster 'body' bei allen Zielsprachen.

Ein Erzeugungsmuster bestimmt, welcher Programmtext generiert werden soll. Erzeugungsmuster besitzen Bezeichner, von denen einige fest zugewiesen sind. Das Modul uni.pm benutzt die Erzeugungsmuster zur Umsetzung der oben beschriebenen Bestandteile eines dynamischen HTML-Dokuments.
Die Erzeugungsmuster haben Perl-Strings, der in seiner Sonderzeichenbehandlung vergleichbar mit C oder Java ist.

Erzeugungsmuster mit fester Zuordnung

Die nachfolgende Tabelle zeigt, wie die Erzeugungsmuster den Bestandteilen eines dynamischen HTML-Dokuments zugeordnet sind.
 
Erzeugungsmuster
Dokumentbestandteil
text reiner HTML-Text
variable Variablen 
( $variable$ )
document Umsetzung des gesamten HTML-Dokuments
pattern HTML-Muster 
( <!-- pattern ... --> )
call eingebettetes Programmfragment mit Bezeichner ohne Programmtext interpretiert uni.pm als Methoden- / Funktionsaufruf 
( <!-- exec method --> )
procedure eingebettetes Programmfragment mit Bezeichner und mit Programmtext
( <!-- exec method 
statements
--> )
comment verwendet uni.pm für Generierungsanweisungen

Beispielkonfiguration für Java und C

java.text      : \tout.print(\"#text#\");\n
java.variable  : \tout.print(#variable#);\n
java.document  : \npublic void printPage(PrintWriter out) {\n#text#\n}\n
java.pattern   : \npublic void print_#name#(PrintWriter out) {\n#text#\n}\n
java.call      : \t#name#(out);\n
java.procedure : \npublic void #name# () {\n#text#\t}\n
java.comment   : \/\/ #text# \n
body           : #text#
c.text         : \tprintf(\"#text#\");\n
c.variable     : \tprintf(#variable#);\n
c.document     : \nvoid printPage() {\n#text#\n}\n
c.pattern      : \nvoid print_#name#() {\n#text#\n}\n
c.call         : \t#name#();\n
c.procedure    : \nvoid #name# () {\n#text#\t}\n
c.comment      : \/\* #text# \*\/\n

Platzhalter in Erzeugungsmustern

Die Beispielkonfiguration zeigt die Platzhalter, die im Erzeugungstext verwendet werden können:
 
Platzhalter
Erzeugungsmuster
erzeugter Text
#text# text reiner HTML-Text
  document alle Statements zur Erzeugung des dynamischen HTML-Dokuments
  pattern alle Statements zur Erzeugung eines HTML-Musters
  procedure alle Statements eines <!-- exec name statements --> - Bereiches
  comment Kommentartext
#name# pattern Bezeichner des HTML-Musters
  call Bezeichner eines <!-- exec name --> - Bereiches 
  procedure Bezeichner eines <!-- exec name statements --> - Bereiches 
#variable# variable Bezeichner einer Variablen ($variable$) bzw. Text zwischen den $-Zeichen

In der Beispielkonfiuration wird zu den fest zugeordneten Erzeugungsmustern ein Erzeugungsmuster 'body' definiert. Dieses generiert nur den durch #text# erzeugten Text.

Generierung

Das nachfolgende Programmbeispiel zeigt, wie die Generierung des Programmtextes durch die Erzeugungsmuster gesteuert werden kann.

HTML-Seite:
 

Beispiel

Spaltenüberschrift1
Spaltenüberschrift2
$wert1$ $wert2$

HTML-Text
 
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
    <meta name="Author" content="Oliver Boehm">
    <meta name="GENERATOR" content="Mozilla/4.5 [de] (Win98; I) [Netscape]">
    <title>Beispiel einer dynamischen HTML-Seite</title>
  </head>
  <body>

    <h1>Beispiel</h1>
    <!-- pattern table -->
    <!-- split tr : begin head row end -->
      <table BORDER CELLSPACING=0 CELLPADDING=0 COLS=2 >
         <tr>
           <td> 
             <center>Spalten&uuml;berschrift1</center>
           </td>
           <td>
             <center>Spalten&uuml;berschrift2</center>
           </td>
         </tr>
         <tr>
           <td>$wert1$</td>
           <td>$wert2$</td>
         </tr>
      </table>
      <!-- /pattern -->
      <!-- exec printTable -->
  </body>
</html>

Diese HTML-Seite habe ich im Netscape Composer erstellt. Sie definiert vier HTML-Muster (table_begin, table_head, table_row, table_end) und enthält ein <!-- exec -->- Bereich, der die Semantik eines Funktionsaufrufes hat, da er zwar einen Bezeichner besitzt, aber keine eigenen Statements definiert.

Die Programmdatei sieht initial wie folgt aus:
 
import java.io.*;
import java.util.*;

public class Example extends Object {

  private Hashtable ht = null;
  private String key = null;

  // Konstruktor
  public Example () {
    ht = new Hashtable();

    // "Daten"
    ht.put("Wert der Zelle11", "Wert der Zelle12");
    ht.put("Wert der Zelle21", "Wert der Zelle22");
    ht.put("Wert der Zelle31", "Wert der Zelle32");
  }

  public static void main(String args[]) {

    // Erzeugung des Objektes
    Example e = new Example();
    PrintWriter pw = new PrintWriter(System.out);
    e.printPage(pw);
    pw.flush();
  }

  // Methode zum Drucken der Tabelle
  public void printTable (PrintWriter out) {

    print_table_begin(out);

    print_table_head(out);

    for ( Enumeration e = ht.keys(); e.hasMoreElements();) {

      key = (String) e.nextElement();
      print_table_row(out, key, (String) ht.get(key)); 
    }

    print_table_end(out);

  }

  // ### generate document

}

Diese Programmdatei enthält eine Generierungsanweisung. Diese hat folgendes Format:
 

### gen[erate] document


Die Generierungsanweisung ist ein Java-Kommentar, damit das Programm syntaktisch korrekt bleibt.
Die Anweisung '### generate document' bewirkt die Umsetzung des gesamten HTML-Dokuments unter Verwendung des Erzeugungsmusters document und aller HTML-Muster unter Verwendung des Erzeugungsmusters pattern.

Das Ergebnis des Generierungsprozesses sieht wie folgt aus:
 
import java.io.*;
import java.util.*;

public class Example extends Object {

  private Hashtable ht = null;
  private String key = null;

  // Konstruktor
  public Example () {
    ht = new Hashtable();

    // "Daten"
    ht.put("Wert der Zelle11", "Wert der Zelle12");
    ht.put("Wert der Zelle21", "Wert der Zelle22");
    ht.put("Wert der Zelle31", "Wert der Zelle32");
  }

  public static void main(String args[]) {

    // Erzeugung des Objektes
    Example e = new Example();
    PrintWriter pw = new PrintWriter(System.out);
    e.printPage(pw);
    pw.flush();
  }

  // Methode zum Drucken der Tabelle
  public void printTable (PrintWriter out) {

    print_table_begin(out);

    print_table_head(out);

    for ( Enumeration e = ht.keys(); e.hasMoreElements();) {

      key = (String) e.nextElement();
      print_table_row(out, key, (String) ht.get(key)); 
    }

    print_table_end(out);

  }

// ### begin generation of document as document 

public void printPage(PrintWriter out) {
 out.print("<!doctype html public \"-//w3c//dtd html 4.0 transitional//en\">\n");
 out.print("<html>\n");
 out.print("  <head>\n");
 out.print("    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n");
 out.print("    <meta name=\"Author\" content=\"Oliver Boehm\">\n");
 out.print("    <meta name=\"GENERATOR\" content=\"Mozilla/4.5 [de] (Win98; I) [Netscape]\">\n");
 out.print("    <title>Beispiel einer dynamischen HTML-Seite</title>\n");
 out.print("    ");
 out.print("<!-- Changed by: , 01-Aug-1999 --");
 out.print(">\n");
 out.print("  </head>\n");
 out.print("  <body>\n");
 out.print("    \n");
 out.print("    <h1>Beispiel</h1>\n");
 out.print("    ");
 out.print("\n");
 out.print("      ");
 printTable(out);
 out.print("\n");
 out.print("  </body>\n");
 out.print("</html>\n");

}
// ### end generation 
// ### begin generation of table_head as pattern 

public void print_table_head(PrintWriter out) {
 out.print("<tr>\n");
 out.print("\t  <td>\n");
 out.print("\t    <center>Spalten&uuml;berschrift1</center>\n");
 out.print("\t  </td>\n");
 out.print("\t  \n");
 out.print("\t  <td>\n");
 out.print("\t    <center>Spalten&uuml;berschrift2</center>\n");
 out.print("\t  </td>\n");
 out.print("\t</tr>");

}
// ### end generation 
// ### begin generation of table_begin as pattern

public void print_table_begin(PrintWriter out) {
 out.print("\n");
 out.print("\n");
 out.print("      <table BORDER CELLSPACING=0 CELLPADDING=0 COLS=2 >\n");

}
// ### end generation 
// ### begin generation of table_end as pattern 

public void print_table_end(PrintWriter out) {
 out.print("\n");
 out.print("      </table>\n");
 out.print("      ");

}
// ### end generation
// ### begin generation of table_row as pattern

public void print_table_row(PrintWriter out) {
 out.print("\t<tr>\n");
 out.print("\t  <td>");
 out.print(wert1);
 out.print("</td>\n");
 out.print("\t  <td>");
 out.print(wert2);
 out.print("</td>\n");
 out.print("\t</tr>");

}
// ### end generation

}
 

Die Anweisung '### generate document' wird durch den Programmtext für die Erzeugung des gesamten HTML-Dokuments (Methode printPage()) und der Pattern (table_begin, table_head, table_row, table_end) ersetzt.

Syntax der Generierungsanweisungen

Der generierte Programmtext zur Erzeugung des gesamten Dokuments und jedes Pattern schließt das Modul uni.pm mit zwei Generierungsanweisungen ein, die den Beginn, das Ende und das verwendete Erzeugungsmuster eines Generierungsschrittes kennzeichnen. Diese Generierungsanweisungen haben folgende Syntax:

Beginn eines Generierungsschrittes:
 

### begin generation of   HTML-Muster | document   [as Erzeugungsmuster]

HTML-Muster      : ist der Bezeichner des generierten HTML-Musters
document         : gibt die Generierung des reinen HTML-Textes (Methode printPage()) an.
Erzeugungsmuster : bezeichnet das verwendete Erzeugungsmuster.

Diese Angabe des Erzeugungsmusters ist optional. Standard sind die fest zugeordneten Erzeugungsmuster.


Ende eines Generierungsschrittes:
 

### end generation

Anpassung der Generierung

Das generierte Beispielprogramm kann man noch nicht kompilieren. Zur Erzeugung eines Tabellensatzes verwendet das Programm in der Methode printTable() folgenden Aufruf:
 
print_table_row(out, key, (String) ht.get(key));  (zum Programmtext)


Das Modul uni.pm hat die Methode print_table_row() entsprechend des Erzeugungsmusters pattern mit einer anderen Schnittstelle generiert:
 

public void print_table_row(PrintWriter out){ ... } (zum Programmtext)


Die Methodendeklaration kann man direkt in der Programmdatei ändern und durch die Anpassung der Generierungsanweisungen bewirken, daß diese bei zukünftigen Generierungen erhalten bleibt.
 
public void print_table_row(PrintWriter out, String wert1, String wert2) {

// ### begin generation of table_row as body

 out.print("\t<tr>\n");
 out.print("\t  <td>");
 out.print(wert1);
 out.print("</td>\n");
 out.print("\t  <td>");
 out.print(wert2);
 out.print("</td>\n");
 out.print("\t</tr>");

// ### end generation
}
 

Durch das Erzeugungsmuster 'body' generiert das Modul uni.pm wird in Zukunft nicht mehr die gesamte Methodendeklaration, sondern nur die erzeugenden Statements. So ist die Schnittstelle einer HTML-Muster erzeugenden Methode in der Programmdatei frei definierbar und bedarf keiner sprachabhängiger Deklarationen in der HTML-Datei.

Die Programmdatei kann man nun kompilieren. Die Ausführung des Programms bewirkt die Erzeugung des dynamischen HTML-Dokuments.

Ergebnis
 

Beispiel

Spaltenüberschrift1
Spaltenüberschrift2
Wert der Zelle31 Wert der Zelle32
Wert der Zelle21 Wert der Zelle22
Wert der Zelle11 Wert der Zelle12

Anpassung der Konfiguration und Evaluierung in Programmdateien

Es kann sinnvoll sein, initiale Programmdateien für die Erzeugung von dynamischen HTML-Dokumenten in Projekten vorzubereiten und als Konvention festzulegen. Aus diesem Grund habe ich in uni.pm die Möglichkeit realisiert, aus einer allgemeinen initialen Programmdatei eine spezielle zu generieren.
Hier ein Beispiel:
 
// ### config output : #html_base#\.#prog_ext#

import java.io.*;
import java.io.IOException;

import javax.servlet.http.*;

// ### begin evaluation
public class #html_base# extends HttpServlet {
// ### end evaluation

   // Bearbeitung des HTTP-Request
   void doGet(HttpServletRequest req,
       HttpServletResponse resp) throws ServletException, IOException {

       // Anfrage aufbereiten und Daten aus einer Datenbank auslesen
          ....

       // Ergebnis darstellen
       printPage(resp.getWriter);

   }

// ### generate document

}

Möchte man in einer Internet-Anwendung mittels einer Datenbank Anfragen beantworten und das Ergebnis über einen Web-Server als HTML-Seite repräsentieren, könnte man eine initiale Programmdatei, wie oben gezeigt, für die Aufbereitung der Anfrage, den Zugriff auf die Datenbank und das Bereitstellen der Ergebnismenge vorbereiten.
Aus objektorientierter Sicht würde man sicher eine Basisklasse für diese Funktionalität bereitstellen. Um dieses Beispiel sprachübergreifend zu gestalten, habe ich diesen Aspekt ignoriert.
Diese initiale Programmdatei könnte als einheitliche Grundlage für die Verarbeitung unterschiedlicher Anfragen mit verschiedenen Ergebnisaufbereitungen benutzt werden.
Dadurch kann der Prozeß zur Erstellung eines neuen dynamischen Dokuments erheblich beschleunigt und vereinheitlicht werden.

Die Anweisung '### config output : #html_base#\.#prog_ext#' bewirkt eine Veränderung der Konfiguration des Moduls uni.pm aus der Programmdatei heraus.

Konfigurationsanweisungen haben folgende Syntax:
 

### config Bezeichner : Wert


Es gibt zwei feste Bezeichner 'output' für den Namen der Ausgabedatei (entspricht der Angabe der Option -f) und 'language' für die Zielsprache (wird entsprechend der Option -l oder des Typs der Programmdatei gesetzt). Alle anderen Konfigurationseinträge ergeben sich aus der Konfigurationsdatei und können in der Programmdatei überschrieben werden. Erzeugungsmuster kann man also überschreiben oder neu erzeugen.

Im obigen Beispiel setze ich den Namen der Ausgabedatei neu, da sonst die initiale Programmdatei überschrieben würde. Das Modul uni.pm evaluiert den gesetzten Wert, wie ich es schon bei den Erzeugungsmustern beschrieben habe. Zusätzlich verwende ich vier Platzhalter:
 

  1. #html_base# - Basisname der HTML-Seite
  2. #html_ext#  - Dateityp der HTML-Seite
  3. #prog_base# - Basisname der Programmdatei
  4. #prog_ext#  - Dateityp der Programmdatei


Ein Dateiname 'example.html' setzt sich laut dieser Beschreibung aus dem Basisnamen 'example' (#html_base#) und dem Dateityp 'html' (#html_ext#) zusammen.

Mit der Anweisung '### config output : #html_base#\.#prog_ext#' wird der Name der Ausgabedatei aus dem Basisnamen der HTML-Seite und dem Dateityp der Programmdatei zusammengesetzt (z.B. example.java).

Der Klassen-Name im obigen Beispiel ist gleich dem Basisnamen der HTML-Seite. Dies erreiche ich, indem ich uni.pm durch '### begin evaluation' anweise, den nachfolgenden Programmtext auszuwerten und mögliche Platzhalter zu ersetzen. Das Ende dieses Bereiches kennzeichne ich mit '### end evaluation'.

Es ist also möglich Vorlagen oder Templates bei der Generierung von dynamischen HTML-Dokumenten zu verwenden und im Ergebnis eine spezielle Programmdatei zu erzeugen.

Aufruf des Präcompilers

Entsprechend der Ausführungsumgebung kann man den Präcompiler wie folgt aufrufen.

[perl] maxi.pl [-m Spracherweiterung]
               [-l Zielsprache]
               [-c Konfigurationsdatei]
               [-f Programmdatei]  [-v]      HTML-Datei

Der explizite Aufruf von Perl ist abhängig von der Ausführungsumgebung. Unter Windows-Systemen wird er meisten benötigt.
Die Option -m gibt das Modul der Spracherweiterung an. Standard ist uni.pm aus dem Verzeichnis des Präcompilers.
Die Optionen -l, -c und -f verarbeitet nur das Modul uni.pm.
Ist die Option -l nicht angegeben, ermittelt uni.pm die Zielsprache aus dem Dateitypen der Programmdatei.
Bei fehlender Angabe der Konfigurationsdatei (Option -c) verwendet das Modul die Konfigurationsdatei 'uni.cfg' aus dem Verzeichnis des Präcompilers.
Ist keine Programmdatei angegeben (Option -f) benutzt das Modul die Standardausgabe.
Die Option -v berwirkt die Ausgabe der Konfiguration.

Im Beispiel 'perl maxi.pl -f example.java example.html' wird die HTML-Datei 'example.html'  zur Definition der HTML-Ausgabe verwendet und der erzeugende Programmtext in die Programmdatei 'example.java' generiert. Der Präcompiler verwendet das Modul 'uni.pm' zur Generierung des Programmtextes und dies benutzt die Erzeugungsmuster der Zielsprache Java aus der Standardkonfiguration 'uni.cfg'.