Modellgetriebene Softwareentwicklung mit EMF und Xtext
Seite 2: Codegenerierung von EMF-Fragmenten
Codegenerierung von EMF-Fragmenten
Aus einem Ecore-Modell lassen sich EMF-Codefragmente generieren. Dabei erzeugt man für jedes EClass-Element ein Java-Interface sowie eine implementierende Klasse und für jedes Attribut eine set- und eine get-Methode. Das für die Erzeugung zuständige enthält Zusatzinformationen für den Generator, die im Ecore-Modell nicht vorliegen, zum Beispiel in welches Verzeichnis der Code zu stellen ist. Der Codegenerator ist aus dem Kontextmenü des Generatormodells aufzurufen.
Der Codegenerator erzeugt zu jeder Ecore-Klasse ein Java-Interface und eine implementierende Klasse, die vom Basis-Interface EObject erbt. Letzteres ist die Mutter aller Modellobjekte und lässt sich mit java.lang.object vergleichen. Wie man sich leicht überzeugen kann, enthält der Code der Set-Methoden eine Benachrichtigung an alle Beobachter, die sich hierfür registriert haben. Zusätzlich bekommt man eine Factory mit einer create-Methode, die sich zum Erzeugen neuer Objektinstanzen verwenden lässt, sowie ein Paket, das Zugriffsmethoden für die Ecore-Metadaten mitbringt.
Da jede generierte Modellklasse EObject implementiert, kann man neben der Verwendung der generierten Methoden ĂĽber die reflektive EObject API generisch auf Objektinstanzen zugreifen.
Will man zusätzlich Modelle anzeigen und editieren, lässt sich das EMF.Edit-Framework nutzen, den Code zu erzeugen, der notwendig ist, um Instanzen des Modells zu editieren. Das geschieht, indem man oben im Kontextmenü den Eintrag "Generate Edit Code" beziehungsweise "Generate Editor Code" auswählt. Ersteres erzeugt ein Plug-in, das die UI-unabhängigen Hilfsklassen für den Editor generiert, der zweite Fall erstellt die übrigen Objekte, die von der Eclipse-Nutzerschnittstelle abhängig sind.
Der Edit-Code umfasst ein vollständiges Plug-in, das den UI-unabhängigen Teil des Modell-Editors enthält. Unter anderem findet man für jede Klasse des Modells sogenannte Item-Provider-Klassen und eine Factory, die die benötigten ItemProvider erzeugt. Letztere sind Adapterklassen, die man benötigt, um die einzelne Objekte im UI darzustellen.
Der Editor-Code ist ein Plug-in, das den UI-abhängigen Teil des Editors enthält. Dazu gehörigen eine Editor-Klasse und ein Wizard zum Erzeugen neuer Modellinstanzen.
DSLs bequem erstellen und verwenden
Domänenspezifischen Sprachen
Domänenspezifische Sprachen (DSLs) sind formale Sprachen, die für ein bestimmtes Problemfeld (Domäne) entworfen sind. Da sie bewusst einfach aufgebaut sind und die Domäne präzise beschreiben sollen, mag ihr Einsatz zu beträchtlichen Effizienzsteigerungen führen. Eine Abgrenzung zu "normalen" Programmiersprachen, UML und XML findet sich hier.
Xtext ist eine Komponente im Eclipse-Modellierungsprojekt und ein Framework zur Entwicklung von DSLs. Es ist in EMF integriert. Xtext verwendet einen sogenannten LL(*)-Parser (siehe "Xtext User Guide" ). Ohne sich zu sehr in die Theorie der formalen Sprachen zu vertiefen, sei angemerkt, dass ein LL-Parser die Eingabe von links nach rechts abarbeitet, um eine Linksableitung der Eingabe zu berechnen. Darunter versteht man eine Ableitung, die in jedem Schritt das am weitesten links stehende Nichtterminal ersetzt. Die Parser sind damit in der Lage, für eine Teilmenge der kontextfreien Grammatiken die Ableitung eines Worts der zugehörigen Sprache zu konstruieren.
Beispielhaft sei im Folgenden eine DSL für einen Überweisungsauftrag entwickelt. Zunächst ist seine Grammatik zu definieren. Von ihr ausgehend generiert Xtext unter anderem einen Parser, um Modelle als Text zu lesen und zu verarbeiten, sowie die Ecore-Modelle, einen Editor und Sprachartefakte, etwa EMF-Code für die erzeugten Modelle, Modell-zu-Text-Serialisierung, Modell-Validierung, Code Formatter, Label Provider und Content Assist Helper.
Zum Anlegen eines Xtext-Projekts ruft man den Projekt-Wizard auf (File | New | Other ... Xtext | Xtext Project) und gibt die Daten ein, wie in Abbildung 5 beschrieben.
Daraufhin sind die Ordner org.xtext.example.ptdsl, org.xtext.example.ptdsl.generator und org.xtext.example.ptdsl.ui anzulegen, die in der Navigator View erscheinen, und die Datei PtDsl.xtext ist im Editor zu öffnent. Nun muss man den Beispielcode durch die Datei, wie in Abbildung 6 gezeigt, ersetzen.
Das Beispiel beschreibt einige einfache Merkmale der Grammatik für den Überweisungsauftrag. In der ersten Zeile deklariert es den Namen der Grammatik. Der Dateiname muss die Erweiterung .xtext besitzen und mit dem Namen der Grammatik übereinstimmen. Im Beispiel bedeutet das, dass die Datei PtDsl.xtext heißt und im Ordner org.xtext.example irgendwo auf dem Projekt Klassenpfad liegt. Zusätzlich verwendet man die Grammatik org.eclipse.xtext.common.Terminals, die eine Reihe gebräuchlicher Terminalsymbole definiert.
In der zweiten Zeile ist eine Anweisung, ein EPackage ptDsl, im Namensraum http://www.xtext.org/example/PtDsl zu erzeugen. Die dritte Zeile dient dazu, ein EMF-Paket über den Namespace URI zu importieren. Das Paket benötigt man später bei den EMF-Datentypen.
Der folgende Abschnitt definiert die Regeln für die Grammatik des Überweisungsauftrags. Regeln in Xtext sind ähnlich aufgebaut wie Ausdrücke in der Extended Backus-Naur Form (EBNF). Für eine vollständige Beschreibung der Regeln sei auf den Xtext User Guide verwiesen. Man unterscheidet zwischen sogenannten Terminal- und Parser-Regeln. Das Beispiel definiert keine Terminal-Regel, sondern verwendet die in der importieren Grammatik org.eclipse.xtext.common.Terminals definierten Terminalregeln ID und INT, die wie folgt festgelegt sind:
terminal INT returns ecore::EInt : ('0'..'9')+;terminal ID : ('^')?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;
Die erste Regel besagt, dass ein INT-Token aus einer beliebigen Folge von Ziffern besteht, die mindestens eine Ziffer enthält. Die zweite Regel bedeutet, dass ein ID Token aus einem optionalen Caret-Zeichen besteht, gefolgt von einem Buchstaben ('a'..'z'|'A'..'Z'), einem Unterstrich ('_') und einer beliebigen Folge aus Buchstaben, Unterstrichen und Ziffern.