XML-Import

Das Modul tl-xio liefert einen konfigurierbaren XML-Import. Über eine XML-Deklaration lässt sich eine Übersetzung einer XML-Datei in ein TopLogic-Modell konfigurieren. Die Import-Definition ist dabei eine typisierte Konfiguration für Implementierungen des Interface com.top_logic.xio.importer.handlers.Handler. Ein solcher Handler ließt einen XML-Strom aus einem XMLStreamReader und erzeugt dabei Objekte in einem TopLogic Modell und verknüpft sie untereinander. Für verschiedene Bereiche der XML-Importdatei sind unterschiedliche Handler-Implementierungen zuständig. Ihre Interaktion wird duch die Import-Definitionsdatei gesteuert.

Dispatch

Die zentrale Steuerung übernimmt der <dispatch>-Handler. Er enthält eine Unterkonfiguration für jedes im aktuellen Kontext zu erwartende Tag. Je nachdem welches Tag er im Eingabestrom sieht, leitet er an den im Kontext dieses Tags definierten Handler weiter.

Der folgende Handler kann zwischen den Tags <foo> und <bar> unterscheiden. Der Inhalt eines <foo> Tags würde von dem Hander verarbeitet, der im Inhalt von <tag name="foo"> definiert wäre. Entsprechend würde der Inhalt eines <bar> Tags von dem Handler verarbeitet werden, der im Inhalt von <tag name="bar"> definiert wäre.

<dispatch>
  <tag name="foo">
    ...
  </tag>
  <tag name="bar">
    ...
  </tag>
</dispatch>

Ein Element, für das kein <tag>-Eintrag existiert, geht an den <default>-Handler des Dispatch, sofern einer konfiguriert ist. Andernfalls wird das Element samt seinem Inhalt übersprungen. Mit der Option unknown lässt sich steuern, ob solche unbekannten Elemente gemeldet werden: skip (Standard) überspringt sie stillschweigend, warn meldet eine Warnung mit dem Tag-Namen und der Position im Dokument, error meldet einen Fehler. Die Einstellung gilt für alle geschachtelten <dispatch>-Handler, bis einer von ihnen sie selbst wieder setzt. Eine Angabe am äußersten Handler gilt damit für die gesamte Import-Definition:

<handler unknown="warn">
  <tag name="data">
    <dispatch>
      <tag name="item">
        ...
      </tag>
    </dispatch>
  </tag>
</handler>

Objekt-Anlage

Es gibt Handler für die Anlage von Fachobjekten. Diese greifen nicht auf die XML-Eingabe zu, sondern werden im Kontext von anderen Handlern (z.B. dem Dispatch) aufgerufen.

Der <object>-Handler legt ein Fachobjekt von einem bestimmter Modell-Typ an. Das Füllen von Eigenschaften und die Verknüpfung mit anderen Fachobjekten übernehmen Handler, die im Kontext des <object>-Handlers definiert sind.

<object model-type="my.module:MyType">
   ...
</object>

Das neu angelegte Objekt wird dabei auf einen Stack gelegt und steht allen Handlern, die im Kontext des <object>-Handlers definiert sind, als Kontext-Objekt zur Verfügung.

Siehe: Konfiguration.

Zuweisen von Werten

Ein eigener Handler übernimmt die Übersetzung von XML-Inhalt in Attributwerte eines angelegeten Objekts. Mit dem <property>-Handler lässt sich ein XML-Attribut in eine Objekt-Eigneschaft des Kontext-Objektes zuweisen. Wenn die Objekt-Eigenschaft nicht string-wertig ist, kann optional ein Format angegeben werden, das die Umrechnung des XML-Attributs in den Fachobjektwert übernimmt.

<property name="myProp" xml-attribute="my-attr" />

Siehe: Konfiguration.

Ganz ähnlich funktioniert der <text-content>-Handler. Statt den Wert aus einem XML-Attribut zu nehmen, ließt dieser Handler den textuellen Inhalt des aktuellen XML-Tags und weißt diesen der Objekteigenschaft zu. Auch hier kann optional ein Format angegeben werden

<text-content name="myProp"/>

In beiden Fällen können keine Handler im Kontext der Zusweisungshandler spezifiziert werden.

Siehe: Konfiguration.

Fallunterscheidung

Der <switch>-Handler wählt abhängig vom Wert eines XML-Attributs des aktuellen Elements den auszuführenden Handler. Das Attribut wird mit xml-attribute angegeben. Jeder <case> enthält den Handler für die in seinem Attribut value angegebenen Werte. Dort können auch mehrere, durch Kommas getrennte Werte stehen, die alle denselben Fall auswählen. Ein leerer Eintrag in value (oder value="") steht für den leeren Attributwert, z.B. wählt <case value="0, false,"> den Fall auch für flag="". Ein Wert darf in höchstens einem Fall desselben <switch> vorkommen. Passt der Wert zu keinem Fall, wird der Handler im <default> ausgeführt. Fehlt das Attribut am Element, wird kein Handler ausgeführt.

<switch xml-attribute="flag">
  <case value="1, true">
    <assign property="state" value="x -> 'on'"/>
  </case>
  <case value="0, false">
    <assign property="state" value="x -> 'off'"/>
  </case>
  <default>
    <assign property="state" value="x -> 'unknown'"/>
  </default>
</switch>

Für beliebige Bedingungen gibt es den <switch-block>-Handler: Er führt die Handler des ersten <if>-Falls aus, dessen TL-Script-Prädikat über dem Kontext-Objekt zutrifft, und andernfalls die Handler in <else>.

Siehe: Konfiguration.

Verknüpfung von Objekten

Objekte, die während des Imports angelegt werden, können gesteuert durch die XML-Eingabe untereinander verknüpft werden. Hierdurch werden Referenzen, die in der XML-Eingabe durch Identifier realisiert sind, in echte Modell-Verknüpfungen übersetzt.

<linking>
  <list-linking name="my.module:MyType#myListRef"/>
</linking>

Die Objekt-Verknüpfung realisiert der <linking>-Handler. Standardmäßig verknüpft er die zwei obersten Objekte im Kontext-Stack. Wie die Verknüpfung konkret ausgeführt wird, bestimmen sog. Linkings, die in seinem Kontext definiert werden können. Sind mehrere Linkings definiert, wird das erste Linking ausgeführt, das zu den zu verknüpfenden Objekten passt. Das <list-linking> fügt das Kontext-Objekt zu der angegebenen Multi-Referenz seines Kontext-Objektes hinzu. Das Linking wird nur ausgeführt, wenn das Objekt, dessen Referenz modifiziert oder gesetzt werden soll von einem zu der spezifizierten Referenz passenden Typ ist. Eine Alternative zum <list-linking> ist das <ref-linking> für die Zuweisung von Zu-Eins-Referenzen.

Siehe: Konfiguration und Linkings.

Rekursion

Rekursive Strukturen können über den Aufruf von im Kontext definierten Handlern realisiert werden. Hierbei wird der Handler, der z.B. im Kontext eines gelesenen Tags ausgeführt werden soll nicht explizit in der Import-Definition hingeschrieben, sondern referenziert. Wenn hierbei ein Handler referenziert wird, der selbst im Kontext definiert ist, findet eine Rekursion statt und es können beliebig tiefe Strukturen verarbeitet werden.

<dispatch id="tree">
  <tag name="node">
    <object model-type="test.tree:Node">
      <property name="name" xml-attribute="name"/>
      <linking>
        <list-linking name="test.tree:Node#children"/>
      </linking>
      <ref handler-id="tree"/>
    </object>
  </tag>
</dispatch>

In obiger Definition ist dem äußersten Handler die ID tree zugewiesen. Der innerste Handler ruft diesen Handler wieder auf und kann so dieselbe Struktur im Kontext des gelesenen Objektes wieder einlesen. Auf diese Art kann der so konfigurierte Importer Strukturen wie die folgende in einen Baum von Objekten übersetzen:

<node name="A">
  <node name="A.1"/>
  <node name="A.2">
    <node name="A.2.1"/>
  </node>
</node>

Siehe: Konfiguration.

Weitere Handler

Weitere Handler und deren Konfiguration findet man in der API-Dokumentation.