Relaxer ist ein von Asami Tomoharu in Java geschriebener Schema-Compiler für Schemata in RELAX Core und RELAX NG (RELAX-Schemata). Basierend auf RELAX-Schemata oder XML-Instanzen generiert Relaxer mit dem Document Object Model Java-Klassen, die zur Validierung und/oder Weiterverarbeitung in eigenen Programmen benutzt werden können.
Weiterhin kann Relaxer DTDs, RELAX Core-Schemata, RELAX NG-Schemata, XML-Schemata, XSLT-Stylesheets sowie XHTML-Formulare aufgrund von XML-Instanzen erstellen. Es können auch die beiden RELAX-Schemata (Core/NG) in DTDs, XML-Schemata oder in das jeweils andere RELAX-Schemata umgewandelt werden. Die generierten Java-Klassen können eine Vielzahl von Funktionalitäten unterstützen: SAX, JAXP (Java API for XML Processing), Java Database Connectivity (JDBC), JavaBeans, Remote Method Invocation (RMI), Java API for XML Messaging (JAXM) und Design Patterns.
Damit Relaxer benutzt werden kann, wird Java 2 und JAXP benötigt.
Die beiden folgenden Tabellen geben einen Überblick über die möglichen Eingabeformate sowie über die verschiedenen Arten der Ausgabe. Darüber hinaus gibt es noch weitere allgemeine und generator-spezifische Optionen. Auf die fett gedruckten Optionen wird im nächsten Abschnitt genauer eingegangen.
Eingabe (Importer):
Dateiendung |
Typ |
|---|---|
.rxm |
RELAX Core |
.rlx |
RELAX Core |
.rxg |
RELAX Namespace |
.rng |
RELAX NG |
.xml |
XML-Instanz |
.rjdbc |
RDBMS-Schema mittels JDBC |
Ergebnis (Generator):
Option |
Generator-Funktion |
Erklärung |
|---|---|---|
version |
Versions-Information |
|
help |
Anzeigen der Hilfe |
|
java |
Java-Klassen |
default-Option |
jdbc |
Java-Klassen für den Zugriff auf JDBC |
Resultat: Java Data Access Objects (DAO) |
cdl |
Komponenten |
JavaBeans, RMI |
rng |
RELAX NG |
|
rxm |
RELAX Core |
|
dtd |
DTD |
|
xsd |
XML-Schema |
|
xslt |
XSLT-Stylesheet |
|
html |
(X)HTML-Formular |
|
editor |
Daten für XML-Editor (XML-Vokabular) |
|
meta |
Relaxer-Modell (XML-Vokabular) |
Meta-Informationen |
setup |
kopiert Spezifikationsdateien |
relaxCore.dtd, datatypes.dtd, ... |
Die Beispiele, die die grundlegenden Eigenschaften von Relaxer illustrieren
sollen, orientieren sich an einer sehr stark vereinfachten Datenerfassung von
Musik-Alben, wie z.B. in album1.xml:
<?xml version="1.0" encoding="UTF-8"?> <album id="RHCP-2"> <band>Red Hot Chili Peppers</band> <titel>Californication</titel> <jahr>1999</jahr> </album> |
Mit der Option -dtd läßt sich aus der Struktur einer
XML-Instanz eine DTD erzeugen, wobei Relaxer das Inhaltsmodell nach strengen
Annahmen produziert. Es wird z.B. angenommen, dass alle Elemente genau einmal
vorkommen müssen und dass das id-Attribut notwendig ist. Durch
den Aufruf von
relaxer -dtd album1.xml
wird album1.dtd erzeugt:
<!-- Generated by Relaxer 1.0RC --> <!-- Wed May 07 12:11:43 GMT+01:00 2003 --> <!ELEMENT album (band, titel, jahr)> <!ATTLIST album id CDATA #REQUIRED> <!ELEMENT titel (#PCDATA)> <!ELEMENT band (#PCDATA)> <!ELEMENT jahr (#PCDATA)> |
Durch Hinzufügen weiterer XML-Instanzen können genauere Vorgaben
an das Inhaltsmodell Relaxer mitgeteilt werden. So enthält
album2.xml zwar kein id-Attribut, aber ein neues
Element cds!
<?xml version="1.0" encoding="UTF-8"?> <album> <band>The Smashing Pumpkins</band> <titel>Mellon Collie and the Infinite Sadness</titel> <cds>2</cds> <jahr>1995</jahr> </album> |
In einem Inhaltsmodell, welches für beide XML-Instanzen validierend
ist, kann das id-Attribut nicht mehr vom Typ #REQUIRED
sein; außerdem muss das optionale Element cds
berücksichtigt werden. Die zutreffende DTD album2.dtd
entsteht durch:
relaxer -dtd album2.xml album1.xml
<!-- Generated by Relaxer 1.0RC --> <!-- Wed May 07 13:34:25 GMT+01:00 2003 --> <!ELEMENT album (band, titel, cds?, jahr)> <!ATTLIST album id CDATA #IMPLIED> <!ELEMENT titel (#PCDATA)> <!ELEMENT band (#PCDATA)> <!ELEMENT cds (#PCDATA)> <!ELEMENT jahr (#PCDATA)> |
Genauso wird ein RELAX NG-Schema (album1.rng) aus diesen
Instanzen generiert. Dabei benutzt Relaxer die Datentypen von XML-Schema
(token, int), wodurch genauere Vorgaben realisiert
werden können!
relaxer -rng album1.xml album2.xml
<?xml version="1.0" encoding="UTF-8" ?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
<start>
<ref name="album"/>
</start>
<define name="album">
<element name="album">
<optional>
<attribute name="id">
<data type="token"/>
</attribute>
</optional>
<element name="band">
<data type="token"/>
</element>
<element name="titel">
<data type="token"/>
</element>
<optional>
<element name="cds">
<data type="int"/>
</element>
</optional>
<element name="jahr">
<data type="int"/>
</element>
</element>
</define>
</grammar> |
Das Erstellen von XML-Schemata kann direkt erreicht werden:
relaxer -xsd album1.xml album2.xml
Oder über Umwandlung eines bestehenden RELAX-Schemas:
relaxer -xsd album1.rng
Alle produzierten Schemata sind oft nicht perfekt, aber stellen eine Erleichterung für manuelles Nachbessern dar, da sie Schreibarbeit sparen!
Ein von Relaxer erzeugtes XSLT-Stylesheet (album1.xsl)
realisiert eine identische Transformation der Eingabe(n). Solche Vorlage zum
Überarbeiten erhält man durch:
relaxer -xslt album1.xml album2.xml
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:template match="jahr">
<jahr>
<xsl:apply-templates/>
</jahr>
</xsl:template>
<xsl:template match="band">
<band>
<xsl:apply-templates/>
</band>
</xsl:template>
<xsl:template match="titel">
<titel>
<xsl:apply-templates/>
</titel>
</xsl:template>
<xsl:template match="cds">
<cds>
<xsl:apply-templates/>
</cds>
</xsl:template>
<xsl:template match="album[band and titel and jahr]">
<album>
<xsl:attribute name="id">
<xsl:value-of select="@id"/>
</xsl:attribute>
<xsl:apply-templates/>
</album>
</xsl:template>
</xsl:stylesheet> |
Relaxer ist in der Lage ein Formular zu erzeugen, dessen Eingabefelder
den XML-Daten entsprechen; album.html ensteht durch folgende
Kommandozeile:
relaxer -html album2.xml
Um Java-Quelltext vom RELAX-Schema album1.rng zu erzeugen, wird
die Option -java verwendet:
relaxer -java album1.rng
Relaxer stellt folgende 4 Klassen (mit Javadoc-Kommentaren) her.
Album.java basiert auf dem Datenmodell von album1.rng.
Weiterhin entstehen RStack.java, UJAXP.java,
URelaxer.java, die Album.java in der Funktionsweise
unterstützen, aber nicht für den Benutzer zugänglich sind.
Deshalb genügt es, einen Auszug von Album.java zu betrachten:
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
//import ...
public class Album
{
// Default-Konstruktor
public Album() {
band_ = "";
titel_ = "";
}
// Konstruktor akzeptiert Datei
public Album(File file)
throws IOException, SAXException, ParserConfigurationException {
setup(file);
}
// Konstruktor akzeptiert eine SAX InputSource als Argument
public Album(InputSource is)
throws IOException, SAXException, ParserConfigurationException {
setup(is);
}
// weitere Konstruktoren
// pro Element und pro Attribut gibt es jeweils eine set... und get... Methode
public final String getId() {
return (id_);
}
public final void setId(String id) {
this.id_ = id;
}
// ...
// stellt DOM-Präsentation des Album-Objektes her
public Document makeDocument() throws ParserConfigurationException {
Document doc = UJAXP.makeDocument();
makeElement(doc);
return (doc);
}
// stellt Text-Präsentation (in XML) des Album-Objektes her
public String makeTextDocument() {
StringBuffer buffer = new StringBuffer();
makeTextElement(buffer);
return (new String(buffer));
}
// ...
} |
Nach der Compilation von Album.java und dem Schreiben einer
eigener Klasse, z.B. ChangeAlbum.java, kann der erzeugte Code
benutzt werden.
import java.io.File;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
public class ChangeAlbum {
public static void main(String[] args)
throws IOException, SAXException, ParserConfigurationException {
if (args.length == 0) {
System.out.println("Usage: java ChangeAlbum file.xml");
System.exit(1);
}
// instanziere ein Album-Objekt mit einer Datei
Album album = new Album(new File(args[0]));
// Ausgabe der XML-Repräsentation des Dokuments
System.out.println("\nOriginal document:\n");
System.out.println(album.makeTextDocument());
// ändere Inhalt
album.setId("DS-1");
album.setBand("Die Schnitter");
album.setTitel("Arg");
album.setJahr(1998);
// Ausgabe der XML-Repräsentation des Dokuments
System.out.println("\nUpdated document:\n");
System.out.println(album.makeTextDocument());
}
}
|
Jetzt muss ChangeAlbum.java noch compiliert und ausgeführt
werden und die Daten von album1.xml können geändert
werden.
javac ChangeAlbum.java
java ChangeAlbum album1.xml
Original document: <album id="RHCP-2"><band>Red Hot Chili Peppers</band><titel>Californication</titel> <jahr>1999</jahr></album> Updated document: <album id="DS-1"><band>Die Schnitter</band><titel>Arg</titel><jahr>1998</jahr></album> |
RelaxNGCC ist ein Parser-Generator (Compiler Compiler) zur Validierung. Das Java-Programm übersetzt eine gegebene kontextfreie Grammatik (RELAX NG-Schema) mit eingebetteten Javacode-Fragmenten in Javacode. Dabei stehen für die beiden Autoren, Daisuke Okajima und Kohsuke Kawaguchi, vor allem die Vorteile der Validierung durch Programmiersprachen im Vordergrund. Dadurch können kontextsensitive Abhängigkeiten unter anderem durch die Benutzung von Bibliotheken, Datenbanken usw. überprüft werden. Bei der Verletzung der Gültigkeit in einem Dokument ist man zudem in der Lage bessere, speziellere Fehlerausschriften auszugeben.
Auch für RelaxNGCC müssen Java 2 und JAXP vorhanden sein.
In stocks.xml werden Aktien mit dem dazugehörigen
Aktienmarkt beschrieben, wobei der Inhalt vom ticker-Element
auch vom Markt abhängt. Während beim Tokyo Stock Exchange vier
Ziffern der Identifikation dienen, sind es beim NASDAQ Großbuchstaben.
<?xml version="1.0" encoding="UTF-8"?>
<stocks>
<stock>
<market>Tokyo</market>
<ticker>6758</ticker>
<name>Sony</name>
</stock>
...
<stock>
<market>NASDAQ</market>
<ticker>MSFT</ticker>
<name>Microsoft</name>
</stock>
</stocks> |
Ein dazu passendes RELAX NG-Schema stocks.rng könnte so
aussehen:
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0">
<start>
<element name="stocks">
<oneOrMore>
<element name="stock">
<element name="market"><text/></element>
<element name="ticker"><text/></element>
<element name="name"><text/></element>
</element>
</oneOrMore>
</element>
</start>
</grammar> |
Dieses Schema beschreibt zwar die Struktur von stocks.rng, doch
die einzige Möglichkeit zum Ausdrücken der
ticker-Abhängigkeit in einem Schema wäre die Aufzählung
aller gültigen Paare von market und ticker.
Dieses Vorgehen könnte sehr aufwendig werden und deshalb geht RelaxNGCC
einen anderen Weg: Die Verifizierung dieser Paare wird z.B. über eine
Datenbank mit JDBC abgewickelt.
Aus diesem Ansatz ergibt sich das gleiche RELAX NG-Schema mit eingebettetem
Code (stockscc.rng):
<?xml version="1.0" encoding="utf-8"?>
<grammar xmlns="http://relaxng.org/ns/structure/1.0"
xmlns:cc="http://www.xml.gr.jp/xmlns/relaxngcc">
<start cc:class="TickerChecker">
<cc:java-import>
import java.sql.*;
</cc:java-import>
<cc:java-body>
Connection _connection;
</cc:java-body>
<element name="stocks">
<oneOrMore>
<element name="stock">
<element name="market"><text cc:alias="market"/></element>
<element name="ticker"><text cc:alias="ticker"/></element>
<cc:java>
Statement st = _connection.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM stocklist WHERE "+
"market='"+market+"' AND ticker='"+ticker+"';");
if(!rs.next())
throw new StockNotFoundException();
st.close();
</cc:java>
<element name="name"><text/></element>
</element>
</oneOrMore>
</element>
</start>
</grammar> |
Neben dem hier erklärten RelaxNGCC-Markup (im Namensraum:
http://www.xml.gr.jp/xmlns/relaxngcc), gibt es fünf weitere
Markup-Bestandteile.
| Markup | Erklärung |
|---|---|
cc:class | Name der erzeugten Klasse ist TickerChecker. |
cc:java-import | Inhalt (<cc:java-import>) wird vor Beginn der Klasse plaziert |
cc:java-body | Inhalt wird innerhalb der Klasse plaziert, z.B. Variablen-Deklarationen oder Methoden |
cc:alias | Wert des text- oder data-Elements wird über diesen Aliasnamen angesprochen |
cc:java | Inhalt wird während des Parsens ausgeführt |
RelaxNGCC übersetzt diese RELAX NG-Grammatik in Java-Quelltext, der
das ContentHandler-Interface von SAX implementiert:
RelaxNGCC stockscc.rng
Der erzeugte Quelltext verteilt sich auf folgende 7 Klassen:
Dabei ist TickerChecker.java von besonderem Interesse, da in
dieser Klasse der Zustandsautomat implementiert ist, der die
Grammatiküberprüfung übernimmt. Der Auszug aus
TickerChecker.java zeigt, dass der Inhalt von cc:java
beim Verlassen des ticker-Elements ausgeführt wird. Die
leaveElement-Methode wird von der endElement-Methode
des SAX-ContentHandler aufgerufen.
//import ...
import java.sql.*;
class TickerChecker extends NGCCHandler {
{
public void leaveElement(String $uri, String $local, String $qname)
throws SAXException {
...
switch($_ngcc_current_state) {
...
case 6:
{
if(($uri.equals("") && $local.equals("ticker"))) {
$runtime.onLeaveElementConsumed($uri, $local, $qname);
$_ngcc_current_state = 5;
Statement st = _connection.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM stocklist WHERE "+
"market='"+market+"' AND ticker='"+ticker+"';");
if(!rs.next())
throw new StockNotFoundException();
st.close();}
}
else {
unexpectedLeaveElement($qname);
}
}
break;
default:
{
unexpectedLeaveElement($qname);
}
break;
}
}
...
public static void main( String[] args ) throws Exception {
...
}Connection _connection;
} |
Der Algorithmus erzeugt einen SAX-basierten Parser (Automat) für XML-Dokumente.
Ein Gültigkeitsbereich ist definiert durch ein start-
oder durch ein define-Element im Schema. Eine Grammatik
enthält ein oder mehrere Gültigkeitsbereiche, für die jeweils
ein Automat und eine Java-Klasse erzeugt werden.
Das Eingabealphabet, die SAX-Events, erzeugt die Zeichen des Alphabets:
elementName]elementName]elementName]elementName] (ausgelöst durch ref-Elemente im Schema)dataType] (XML Schema-Datentypen)Der eingebettete Code wird hinzugefügt, sodass die einzelnen Code-Fragmente bei bestimmten Zustandsübergängen ausgeführt werden.
Es wird pro Gültigkeitsbereich eine FIRST- und eine FOLLOW-Menge gebildet, wobei ein Element aus FIRST den Start und ein Element aus FOLLOW das Ende eines Gültigkeitsbereiches meldet.
Diese Überprüfung erfolgt mit den FIRST- und FOLLOW-Mengen.
Beispiel für eine mehrdeutige Grammatik (nichtdeterministisch):
<choice> <element name="foo">...</element> <element name="foo">...</element> </choice> |
Jetzt kann die Validierung beginnen!
Beide Java-Programme können zur Erzeugung von Java-Quellcode aufgrund einer RELAX NG-Grammatik benutzt werden.
Merkmal |
Relaxer |
RelaxNGCC |
|---|---|---|
Einsatzgebiete |
einfacher Zugriff auf XML-Strukur, |
Validierung |
kontextsensitive Validierung |
nein |
ja |
Serialisierung von XML-Dokumenten |
ja |
nein |
Schema-Sprachen |
RELAX NG, Relax Core, Relax Namespace |
RELAX NG |
Relaxer:
RelaxNGCC: