Beim letzten Mal haben wir uns mit der Erstellung einer Sprache in Xtext beschäftigt, genauer gesagt mit der Verwendung der Xtext Grammatik zum Erstellen eines eigenen Modells, im Grunde genommen also mit dem Kern der DSL.
In diesem Beitrag würde ich gern den Workflow beschreiben, welchen das Xtext-Projekt durchläuft. Ich hoffe, damit erst einmal ein grundsätzliches Verständnis für meine Pläne zur Umsetzung eines Akzeptanztest-Systems, welche ich in den kommenden Posts vorstellen möchte, aufbauen zu können. In dem System werden sich einige Elemente wiederfinden, denen man auch in Xtext begegnet.
Erzeugung der Artefakte mit der Modeling Workflow Engine (MWE)
Sobald man ein Xtext-Projekt angelegt hat, so findet sich neben der Grammar-Datei mit der Dateiendung xtext eine Workflowdatei mit dem Namen Generate[Sprachname].mwe:
Im Bild sieht man den Package-Explorer mit der Ansicht des auch schon im letzten Post verwendeten Projekts. Neben der Workflow-Datei gibt es noch eine Properties-Datei mit einigen projektspezifischen Eigenschaften, welche der Workflow verwendet.
Der Inhalt solch eines Workflows soll uns an dieser Stelle nicht interessieren, dies würde den Umfang des Posts sprengen. Was man wissen muss, ist, dass ein MWE-Workflow einen sequenziellen Aufruf von verschiedenen Komponenten zur modellgetriebenen Entwicklung ermöglicht, daher "Workflow". Das Ganze geschieht in einer XML-basierten deklarativen Notation, außerdem ist es möglich, eigene Komponenten zu schreiben. Mehr dazu findet man auf der MWE-Seite im Eclipsepedia. Ich werde auf diese Technologie an einem anderen Zeitpunkt genauer eingehen.
Führt man den Workflow aus (Rechtsklick –> Run as –> MWE Workflow), so erzeugt Xtext alle benötigten Artefakte, vom Ecore-Modell über das EMF-GenModel (welches wiederum den Java-Code erzeugt) bis hin zu den UI-spezifischen Komponenten wie Editor und Contentassistance.
Diesen Vorgang muss man jedes mal wiederholen, sobald man etwas an der Grammatik geändert hat.
Generiertes UI-Plugin/Projekt
Das vom Xtext-Projekt Assistenten erzeugte Projekt mit der Endung ui wird erst nach dem ersten Durchlauf des Workflows mit Inhalten gefüllt:
Hier hat man die Möglichkeit, den Proposalprovider anzupassen, einen Labelprovider zu erstellen etc., also sein Plugin für die Sprache anzupassen. In meinem Projekt für die Erstellung eines Akzeptanztest-Systems wird hier ein Wizard hinzukommen, der die Erstellung des Projektes oder auch von Akzeptanztests unterstützt.
Über die plugin.xml-Datei kann man das Plugin starten und den automatisch erstellten Editor benutzen. Man erstellt dann lediglich ein neues, leeres Projekt und eine Datei mit der vorher festgelegten Endung:
Hier zeigt sich schon eine hervorragende Möglichkeit zur Erweiterung des Plugins: das Akzeptanztest-System könnte durch einen Assistenten die Möglichkeit zum automatischen Erstellen einer DSL-Datei mit der richtigen Endung zur Verfügung stellen.
Generiertes Generator-Projekt (optional)
Zusätzlich zu dem DSL-Projekt und dem UI-Projekt kann man sich ein Generator-Projekt (endet mit .generator) angelegen lassen. Im Xtext-Projekt-Wizard muss man dazu an der entsprechenden Stelle ein Häkchen setzen.
Das erzeugte, eigenständige Projekt dient dazu, aus dem durch Xtext erzeugten Modell Code zu generieren. Damit ist nicht nur Java-Code gemeint, sondern prinzipiell irgendein Code, dies könnten zum Beispiel ein XML-Dokument sein oder SQL-Statements, je nachdem, welchem Zweck das zugrundeliegende Modell dient.
Um dieses Ziel umzusetzen, bedient man sich hier der Codegenerierungssprache Xpand. Der Projekt-Wizard erzeugt im Generator-Projekt bereits einige Standard-Dateien, wie eine DSL-Datei der Xtext-Beispiel-DSL "Entities" und ein Xpand-Template, welches auf diesem Modell arbeitet:
Des weiteren gibt es hier ein Xtend-Datei, welche von dem Xpand-Template eingebunden wird. Mit Xtend kann man Erweiterungen zu seinem Model schreiben, ohne das Modell zu ändern, ebenso wie eigens definierte Operationen, welche das Modell benutzen. Genauer möchte ich darauf nicht eingehen, da es für meine Zwecke nicht so wichtig ist. Dennoch sollte es nicht unerwähnt bleiben.
Die Beispieldateien würde man löschen und gegen das eigene Modell ersetzen. Was man nun mit Xpand machen kann, werde ich in einem neuen Post zeigen.
Aufgerufen wird Xpand wiederum durch eine Workflow-Datei, welche sich im Projekt befindet. Insgesamt hat dies jedoch wenig Sinn, wenn es nicht aus Plugin aus aufgerufen wird. Wie würde man also dazu vorgehen?
Man startet das Plugin über das UI-Projekt und legt in der neuen Workbench ein Projekt an. Dieses Projekt muss als Dependency das Generator-Plugin besitzen, um auf den Generator und die Xpand-Templates zugreifen zu können. Der im Generator vorhandene Workflow kann nun aus einem eigens erstellten Workflow aufgerufen werden. Dieses Beispiel stammt von der Seite von Peter Friese:
<workflow>
<cartridge file="workflow/EntityGenerator.mwe" model="classpath:/model/MyModel.entity"/>
</workflow>
Der Generator wird über den Workflow aufgerufen und verwendet dann die in der aktuellen Workbench erzeugte DSL-Datei, anstatt die, welchem im Generator-Projekt erzeugt wurde.
In dem oben genannten Artikel von Peter Friese findet man noch mehr Informationen zur Code-Generierung mit Xpand.
Src gegen Src-gen
Jedes der angelegten Projekte verfügt über zwei verschiedenen Source-Ordner. Einmal src, wo sich die Dateien befinden, die der Entwickler selbst ändert. Dann gibt es den Ordner src-gen, in welchem sich fast ausschließlich von Xtext erstellter Code befindet, wie zum Beispiel die Java-Klassen zum erzeugten Modell. Diese Dateien sollte man möglichst nicht verändern, da sie vom Xtext-Generator eh wieder überschrieben werden. Ebenso sollte man eigene Klassen und sonstige Dateien wie eigene Workflows stets in den src-Ordner legen.
Validation
Auch wenn man seine Xtext-Grammatik optimal angelegt hat, so kann man nicht immer vorhindern, dass die DSL eventuell an bestimmten Stellen Inhalte akzeptiert, welche man da eigentlich nicht haben wollte. Auch durch Referenzen kann dieses Problem oft nicht gelöst werden.
Die Lösung bietet ein von Xtext angebotenes Validator-Fragment, welches dem Workflow zur Generierung der Artefakte hinzugefügt wird. Letztendlich beschreibt man in einer Java-Klasse Methoden, welche Überprüfungen von einzelnen Regeln der Grammatik anstellen. Was man genau in diesen Methoden überprüft, ist jedem selbst überlassen.
Genaueres dazu wird es in einem kommenden Post von mir geben, da ich schon mehrfach Gebrauch von dieser Funktion gemacht habe. Inzwischen finden sich Informationen darüber i, Xtext User Guide in Chapter 5 – Unterpunkt "Validation".
Fortsetzung folgt…
In meinem nächsten Post habe ich vor, bereits einmal die prinzipiellen Ablauf, welchen ich mir für das Akzeptanztest-System vorgestellt habe, zu erläutern. Weitere Posts zu Validation und anderen Details möchte ich dann bereits an diesem System demonstrieren.