5 Das transformer-Objekt

Das Transformer-Objekt ermöglicht den Durchlauf eines XML-Dokuments oder einer IDM-Objekthierarchie, wobei während dieses Durchlaufs an einzelnen Knoten semantische Aktionen ausgeführt werden können. Auf diese Weise kann leicht eine Transformation von Daten implementiert werden, bei der z.B. entweder XML-Daten in IDM-Objekte übertragen werden oder umgekehrt mit Daten, die in IDM-Objekten stehen, ein XML-Dokument generiert wird. Da die semantischen Aktionen durch benutzerdefinierte Codes beschrieben werden, ist die Art der Transformation prinzipiell beliebig.

Definition

{ export | reexport } { model } transformer { <Bezeichner> }
{
  [ <Attributdefinition> ]
  [ <Methodendefinition> ]
}

Zur Definition einer Transformation stehen dem IDM-Programmierer folgende Mittel zur Verfügung.

Ereignisse

keine

Kinder

document

mapping

record

transformer

Vater

application

canvas

checkbox

dialog

doccursor

document

edittext

groupbox

image

import

layoutbox

listbox

mapping

menubox

menuitem

menusep

messagebox

module

notebook

notepage

poptext

pushbutton

radiobutton

record

rectangle

scrollbar

spinbox

splitbox

statictext

statusbar

tablefield

timer

toolbar

transformer

treeview

window

Menü

keins

Methoden

:action()

:apply()

:select_next()

5.1 Attribute

.document[integer]

.external

.external[integer]

.firstrecord

.label

.lastrecord

.mapping[integer]

.model

.module

.parent

.record[integer]

.recordcount

.root

.scope

.userdata

5.2 Objektspezifische Attribute

.mapping[integer]

Über das .mapping Attribut kann auf die Mapping-Kinder zugegriffen werden. Das Attribut wird mit dem Objektindex indiziert (ähnlich zu child). Die Reihenfolge der Mappings in diesem Vektor bestimmt die Reihenfolge, in der während einer Transformation ein Knoten mit einzelnen Mappings verglichen wird. Die vererbten Mappings werden in diesen Vektor nicht übernommen. Während einer Transformation wird mit solchen Mappings erst zum Schluss verglichen, die direkten Instanzen haben also Vorrang. Das ist anders, als bei anderen vererbten Kindobjekten im IDM, die im Kindvektor der Vaterinstanz vorne eingefügt werden.

.root

Nach dem Aufruf der apply Methode wird in diesem Attribut der Ausgangspunkt der Transformation gespeichert. Damit kann während einer Transformation entschieden werden, ob der Ausgangspunkt wieder erreicht worden ist und die Transformation beendet werden kann.

Dabei sind folgende Fallunterscheidungen zu beachten.

  • Ist der Src-Parameter der apply Methode ein Document- oder Doccursor-Objekt so wird in .root ein String abgespeichert, der die entsprechende Position im XML-Baum beschreibt. Die Syntax dieses Strings entspricht der Konvention, die im .path Attribut des Doccursor-Objekts verwendet wird (siehe Objektbeschreibung zu doccursor). Vergleiche mit dem .path Attribut von Doccursor sind deswegen besonders einfach.
  • Ist der Src-Parameter der apply Methode ein IDM-Objekt, so wird im .root dieses Objekt gespeichert. Folglich ist in diesem Fall der Typ dieses Attributs object.

Anhand des Wertes im .root Attribut entscheiden die action und :select_next() Methoden des Transformers, was diese tun müssen (siehe hierzu die Beschreibung dieser Methoden).

Am Ende der apply Methode wird .root wieder auf void gesetzt.

Default-Wert ist void.

5.3 Objektspezifische Methoden

:apply()

Mit dieser Methode wird die Transformation angestoßen. Die Default-Implementierung des Algorithmus sieht folgendermaßen aus:

  • Ist der Src-Parameter ein Document- oder Doccursor-Objekt, so wird angenommen, dass Daten aus einem XML-Baum zum IDM übertragen werden sollen. Im Falle eines Document-Objekts wird ein temporäres Doccursor-Objekt erzeugt, das zur Navigation im XML-Baum benutzt wird. Im folgenden Pseudocode wurde der Einfachheit halber angenommen, dass in Src immer ein Doccursor übergeben wird.

    :apply(anyvalue Src, anyvalue Dest)
    {
      variable object NextObj;
      
      this.root ::= Src.path;
      NextObj := Src;
      while NextObj <> null do
        this:action(Src, Dest);
        NextObj := this:select_next(NextObj);
      endwhile
      this.root ::= null;
      return true;
    }
  • Ist der Src-Parameter ein IDM-Objekt (außer Document- oder Doccursor-Objekt), so wird angenommen, dass Daten vom IDM nach XML bzw. sonst wohin übertragen werden sollen. Der zugrundeliegende Code ist mit dem oben aufgeführten Codefragment identisch, außer das hier in .root nicht Src.path sondern Src selbst gespeichert wird. Also:

    this.root ::= Src;

Die apply Methode kann überdefiniert werden (ähnlich zu :init()).

:action()

Diese Methode wird von der apply Methode des Transformers benutzt (siehe oben). Damit wird festgestellt, ob der aktuelle Knoten auf eines der Mapping-Kinder passt und deswegen die action Methode dieses Mapping-Objekts aufgerufen werden muss.

Die action Methode kann überdefiniert werden (ähnlich zu :init()).

:select_next()

Diese Methode wird von der apply Methode des Transformers zum Durchlaufen aller Knoten eines XML-Baums oder der IDM-Objekthierarchie benutzt. (siehe oben). Die Default-Implementierung dieser Methode sieht so aus, dass das wiederholte Aufrufen der Methode einen Pre-Order-Durchlauf realisiert.

Die :select_next() Methode kann überdefiniert werden (ähnlich zu :init()).

5.4 Beispiel

Dieses Beispiel ist im Verzeichnis examples/xml zu finden.

Als XML-Dokument dient folgende Datei mit dem Namen CD-Katalog.xml“:

<?xml version="1.0" ?>
<CATALOG>
  <CD>
    <TITLE>Empire Burlesque</TITLE>
    <ARTIST>Bob Dylan</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>Columbia</COMPANY>
    <PRICE>10.90</PRICE>
    <YEAR>1985</YEAR>
  </CD>
  <CD>
    <TITLE>Hide your heart</TITLE>
    <ARTIST>Bonnie Tyler</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>CBS Records</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1988</YEAR>
  </CD>
  <CD>
    <TITLE>Maggie May</TITLE>
    <ARTIST>Rod Stewart</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>Pickwick</COMPANY>
    <PRICE>8.50</PRICE>
    <YEAR>1990</YEAR>
  </CD>
</CATALOG>

Dann können die Daten aus dieser Datei beispielsweise wie folgt ausgelesen werden.

dialog D {}

window Wi
{
  .title "XML-CD-CATALOG";

  on close { exit(); }

  child treeview Tv
  {
    .xauto 0;
    .yauto 0;
    .style[style_lines]   true;
    .style[style_buttons] true;
    .style[style_root]    true;
    integer CdIdx := 0;

    rule NewCatalog() {
      if this.itemcount = 0 then
        this.itemcount ::= this.itemcount + 1;
      endif
      this.content[this.itemcount] := "CD-Catalog";
      this.open[this.itemcount] := true;
    }

    rule AddCD() {
      this:insert(this.itemcount+1, 4);
      this.CdIdx := this.CdIdx + 1;
      this.content[this.itemcount-3] := ""+this.CdIdx+". CD";
      this.level[this.itemcount-3] := 2;
    }

    rule AddTitle(string S input) {
      this.content[this.itemcount-2] := "Title: " + S;
      this.level[this.itemcount-2] := 3;
    }

    rule AddArtist(string S input) {
      this.content[this.itemcount-1] := "Artist: " + S;
      this.level[this.itemcount-1] := 3;
    }

    rule AddPrice(string S input) {
      this.content[this.itemcount] := "Price: " + S;
      this.level[this.itemcount] := 3;
    }
  }
}

transformer Tr
{
  !! transformer ist zur Übernahme der Daten aus einem XML-Dokument gedacht
  !! in Src ist deswegen immer ein doccursor zu erwarten

  child mapping MCatalog {
    .name "..CATALOG";

    :action() {
      Dest:NewCatalog();
      return true;
    }
  }

  child mapping MCD {
    .name "..CD";

    :action() {
      Dest:AddCD();
      return true;
    }
  }

  child mapping MTitle {
    .name "..CD.TITLE";

    :action() {
      Dest:AddTitle(Src.text);
      return true;
    }
  }

  child mapping MArtist {
    .name "..CD.ARTIST";

    :action() {
      Dest:AddArtist(Src.text);
      return true;
    }
  }

  child mapping MPrice {
    .name "..CD.PRICE";

    :action() {
      Dest:AddPrice(Src.text);
      return true;
    }
  }
}

document Doc {}

on dialog start
{
  Doc:load("CD-Katalog.xml");

  Tv.visible := false;
  Tr:apply(Doc, Tv);
  Tv.visible := true;
}