Model-to-Code Teil 2

Tutorial "Model-to-Code"

Mit diesem Tutorial erhalten Sie einen schnellen Einstieg in Model-to-Code mit Eclipse Epsilon. Sie installieren Eclipse Epsilon und lernen an einem einfachen Beispiel die Techniken „Model-to-Code“, „Model Validation“ und „Model-to-Model-Transformation“.

Als Programmiersprache für den erzeugten Code wird Java verwendet.

Dieses Tutorial soll „in kleinen Happen“ absolviert werden können. Jeden Abschnitt sollten Sie in weniger als zwanzig Minuten abschließen können. Um dieses Ziel zu erreichen, ist das Tutorial knapp gehalten.
Ich setze voraus, dass Sie Model-to-Code mit Eclipse Epsilon an einem praktischen Beispiel ausprobieren wollen. Eine kurze Motivation von Model-to-Code finden Sie im ersten Teil der Serie "Entwickeln Sie schneller durch Model-to-Code".

Voraussetzungen

Um dem Tutorial folgen zu können, sollten Sie bereits Java-Anwendungen mit der Eclipse Java IDE entwickelt haben. Es ist hilfreich, wenn Sie bereits Erfahrung mit Template-Engines (Apache Velocity, PHP, …) oder Code-Generierung gesammelt haben.
Vertiefende Informationen finden Sie in der englischen Dokumentation zu Eclipse Epsilon. Sie sollten über entsprechende Kenntnisse in Englisch verfügen.
Das Tutorial basiert auf Eclipse Epsilon. Epsilon ist bietet eine Vielzahl von Skriptsprachen, u.a. für Model-to-Model, Model Verification und Model-to-Code die auf der gemeinsamen Sprache EOL aufbauen. Eclipse Epsilon verbindet die Eclipse Java IDE mit einer vorkonfigurierten Installation von Epsilon.

Für die Installation benötigt ihr Computer einen Internet-Zugang und ungefähr 20 MByte freien Speicherplatz für Eclipse Epsilon. Sie müssen über die Rechte verfügen, um Software auf ihrem Computer zu installieren. Eclipse Epsilon setzt ein JDK in einer Version > 8 voraus. Falls Sie noch kein entsprechendes JDK installiert haben, benötigen sie ungefähr 500 MByte weiteren freien Speicherplatz.
Sie sollten ein Buildfile für Apache Ant verstehen können.
Für den Abschnitt „Arbeiten mit einem Excel-Modell“ benötigen Sie ein Programm um Excel-Dateien zu erstellen und zu bearbeiten.

Begriffserläuterung

Wir werden in diesem Tutorial eigene, maschinenlesbare Modelle erstellen. Die Beschreibung der möglichen Modelle geschieht mit einem Metamodell.
Für dieses, praxisorientierte Tutorial gebe ich eine knappe Beschreibung des Begriffs Metamodell.
Das Metamodell beantwortet die Fragen: Was ist für die Beschreibung unserer Lösung wichtig? Welche Elemente und welche Beziehungen zwischen den Elementen sind für uns von Bedeutung?

Verwendete Version

Im Tutorial wird Eclipse Epsilon 2.3 auf Basis von Eclipse 2021-03 verwendet.

Notation

Die folgende Tabelle erläutert die verwendete Notation

Notation Bedeutung
File

Klicken Sie den Menüeintrag oder Button "File" an

Führen Sie die einzelnen Aktionen nacheinander aus

Vorschau

1. Installation von Eclipse Epsilon

  • Sie installieren Eclipse Epsilon auf ihrem Computer.

2. Anlegen des Projekts und des Modells

  • Sie legen ein neues Projekt an, definieren das Meta-Modell und darauf basierend ein Modell.

3. Erzeugen von Code

  • Sie schreiben die Templates aus denen der Code erzeugt wird, und ein Ant-Buildfile, die die Erzeugung automatisiert.

4. Model Validation

  • Sie schreiben ein EOL-Skript und binden es in die Model Validation ein.
  • Sie binden die Model Validation in das Ant-Buildfile ein.

5. Model-to-Model-Transformation

  • Sie erweitern das Meta-Modell und das Modell
  • Sie erstellen eine Model-to-Model-Transformation
  • Sie binden die Model-to-Model-Transformation in das Ant-Buildfile ein

6. Arbeiten mit einem Excel-Modell

  • Sie erstellen eine Excel-Datei und die zugehörigen Templates
  • Sie binden die Model-to-Code Erzeugung in das Ant-Buildfile ein

7. Erzeugte Dateien mit einem Zeitstempel versehen

  • Sie versehen die erzeugten Dateien mit einem Zeitstempel der letzten Änderung
    •  
    •  
    •  

1) Installation von Eclipse Epsilon

Die Installation sollte keine zwanzig Minuten dauern. Da sie nur einmal ausgeführt werden muss, habe ich ihr einen eigenen Abschnitt gegönnt.
Gehen Sie auf https://www.eclipse.org/epsilon/#installation und folgen Sie den Anweisungen zur Installation von Eclipse Epsilon.
Die verwendete Version der Eclipse IDE benötigt eine Java JRE mit einer Version > 1.8. Falls sie noch keine entsprechende JRE installiert haben, wählen Sie den Installer mit "embedded JRE".
Starten Sie Eclipse Epsilon, legen Sie einen neuen Workspace an und schließen Sie, falls erforderlich, den Begrüßungsbildschirm.

File → New

Es sollten die folgenden Auswahlmöglichkeiten erscheinen:

Zusammenfassung

Glückwunsch: Sie haben Eclipse Epsilon erfolgreich installiert!
Im nächsten Abschnitt definieren Sie das Metamodell und das Modell.


2) Anlegen des Projekts und des Modells

Im vorangegangenen Abschnitt haben Sie Eclipse Epsilon installiert und einen neuen Workspace angelegt.

Legen Sie das Projekt an

File → New → Other → Eclipse Modeling Framework → Empty EMF Project → Next

Zur Verdeutlichung zeigt die nächste Abbildung den Auswahldialog

Eclipse zeigt den Dialog zum Benennen des EMF Projekts

Geben Sie den Namen des Projekts ein („EpsilonExample“) und schließen Sie den Dialog mit "Finish" ab.

Legen Sie das Meta-Modell an

Ich zeige Ihnen, wie sie mit Emfatic ein Meta-Modell definieren.

File → New → Other → Example EMF Model Creation Wizards → Emfatic File → Next

Zur Verdeutlichung zeigt die nächste Abbildung den Auswahldialog

Eclipse zeigt den Dialog für eine neue Emfatic Datei

Setzen Sie die folgenden Werte:

Container

/EpsilonExcample/model

File name

MyMetamodel.emf

Epsilon zeigt Ihnen die folgende leere .emf-Datei

Ersetzen Sie den Inhalt des Editors wie folgt:

@namespace(
       uri="http://my.metamodel.id/version1",
       prefix="MyMetamodelPrefix")
package top;

class NamedObject {
       attr String name;
       attr String desc;
}

class Member extends NamedObject {
}

class ValueObject extends NamedObject {
       val Member[+] member;
}

class Definition {
       val ValueObject[+] vo;
}

Damit Sie das Meta-Modell verwenden können, müssen Sie noch zwei Schritte ausführen: Das ecore-Modell erzeugen und die EPackages registrieren.

Erzeugen Sie das ecore-Modell

Gehen Sie im Fenster „Project Explorer“ mit der Maus über „MyMetamodel.emf“, rufen Sie das Kontextmenü auf (rechte Maustaste) und wählen „Generate Ecore Model“ aus.
Im Project Explorer erscheint die ecore-Datei "MyMetamodel.ecore".

Registrieren Sie die EPackages

Gehen Sie im Fenster „Project Explorer“ mit der Maus über „MyMetamodel.emf“, rufen Sie das Kontextmenü auf (rechte Maustaste) und wählen „Register EPackages“ aus.
Es gibt keine sichtbare Veränderung in Eclipse.

Legen Sie das Modell an

Nachdem Sie das Metamodell angelegt und registriert haben, legen Sie nun das Modell an.

File → New → Other → Epsilon → EMF Modell → Next

Eclipse zeigt den Dialog für ein neues EMF Modell an

Füllen Sie den Dialog mit folgenden Werten aus und schließen Sie ihn mit "Finish" ab.

Feld Inhalt Erläuterung
Container

/EpsilonExample/model

 
File name

MyModel.model

 

Metamodel URI

http://my.metamodel.id/version1

Dies ist die URI, die in der namespace-Annotation angegeben wurde.

Root instance type

Definition

Dies ist das oberste Element unseres Modells, unter dem sich alle anderen Modell-Element befinden

Eclipse zeigt das folgende Modell im sogenannten Tree-Editor an. Sie werden diesen Editor verwenden um das Modell zu bearbeiten.

Legen Sie ein Value Object an

Gehen Sie mit der Maus über „Definition“ und rufen Sie das Kontextmenü auf, gefolgt von

New Child VO Value Object

Wählen Sie den Reiter „Properties“ aus, wenn er noch nicht aktiv sein sollte und tragen Sie die folgenden Werte ein.

Feld Inhalt Erläuterung
Desc

An element with name and value

Dies ist ein kurzer Kommentar zur erzeugten Java-Klasse

Name

Element

Dies wird der Name der erzeugten Java-Klasse

Versehen Sie das Value Object mit Kindern vom Typ "Member"

Gehen Sie mit der Maus über „Value Object“ und rufen Sie das Kontextmenü auf, gefolgt von

New Child Member Member

Tragen Sie die folgenden Werte ein

Feld Inhalt Erläuterung
Desc

The name of the element

Dies ist ein kurzer Kommentar zum erzeugten Member

Name name

Dies wird der Name des erzeugten Member

Legen Sie ein neues Kind vom Typ "Member" für das Value Object an und tragen Sie die folgenden Werte ein

Feld Inhalt Erläuterung
Desc

The value of the element

Dies ist ein kurzer Kommentar zum erzeugten Member

Name

value

Dies wird der Name des erzeugten Member

Eclipse sollte Ihnen folgendes Bild anzeigen

Zusammenfassung

Sie haben das Meta-Modell definiert und darauf basierend ein Modell erstellt und gefüllt.
Im nächsten Schritt zeige ich Ihnen, wie sie daraus Code erzeugen.


3) Code aus dem Modell erzeugen

In diesem Abschnitt werden Sie zwei Dateien erstellen: Eine .egl-Datei, die das eigentliche Template enthält und eine .egx-Datei die die Erzeugung steuert.

Legen Sie einen Ordner templates für diese Dateien an.

Legen Sie das .egl-Template an

Das .egl-Template steuert die Erzeugung einer einzelnen Datei.

File New Other Epsilon EGL Template Next

Wählen Sie den Ordner "template", den Sie im vorangegangenen Schritt angelegt haben, als "parent folder", geben Sie der Datei den Namen "TemplateValueObject.egl" und schließen Sie den Dialog mit "Finish" ab.
Eclipse zeigt Ihnen die angelegte Datei im Project Explorer an.

Ersetzen Sie den Inhalt von "TemplateValueObject.egl" wie folgt und speichern Sie die Datei.

package example;

/**
 * [%= vo.desc %]
 */
public class [%= vo.name.firstToUpperCase() %] {
[% for (p in vo.member) { %]
       private String [%= p.name%] = "";
[%}%]
      
       public [%= vo.name.firstToUpperCase() %]() {
            
       }

[% for (p in vo.member) { %]
       /**
        * Sets "[%= p.desc %]"
        * @param [%= p.name %] The new value
        */
       public void set[%= p.name.firstToUpperCase()%](String [%= p.name%]) {
             this.[%= p.name%] = [%= p.name%];
       }

[%}%]
[% for (p in vo.member) { %]
       /**
        * @return [%= p.desc %]
        */
       public String get[%= p.name.firstToUpperCase()%]() {
             return [%= p.name%];
       }
      
[%}%]
}

Legen Sie das EGX-Programm an

File → New → Other → Epsilon → EGX Program → Next

Wählen Sie den Ordner "templates" als "parent folder", geben Sie der Datei den Namen "VoMasterTemplate.egx" und schließen Sie den Dialog mit "Finish" ab.
Ersetzen Sie den Inhalt wie folgt und speichern Sie die Datei.

rule VO2SOURCE
       transform vo: ValueObject {
      
       template: "TemplateValueObject.egl"
      
       target: "../java/example/" + vo.name + ".java"
       }

Automatisieren Sie die Code-Erzeugung mit einem Ant-buildfile

Die Code-Erzeugung lässt sich auch über das Kontextmenü ausführen. Da in den nächsten Abschnitten immer mehr Schritte hinzukommen, lohnt es sich, die Erzeugung zu automatisieren.
Legen Sie eine neue Datei "build.xml" an, setzen Sie den Inhalt wie folgt und speichern Sie die Datei.

<project default="main">
       <target name="main">
             <epsilon.emf.loadModel
                    name="EmfSource"
                    modelfile="model/MyModel.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="true" store="false"/>
             <epsilon.egl src="templates/VoMasterTemplate.egx">
                    <model ref="EmfSource" />
             </epsilon.egl>
       </target>
</project>

Führen Sie das Ant buildfile aus

Führen Sie die Datei build.xml mittels „Run as“ →  „Ant build…“ aus. Wichtig: Wählen Sie im Reiter „JRE“ bei „Runtime JRE“ die Option „Run in the same JRE as the workspace“ aus.

Prüfen Sie den Inhalt der erzeugten Datei

Aktualisieren Sie den Inhalt des Fensters "Project Explorer" und rufen Sie die Datei "Element.java" im Ordner "java/example" auf:

package example;
/**
 * An element with name and value
 */
public class Element {
       private String name = "";
       private String value = "";

       public Element() {
       }
       /**
        * Sets "The name of the element"
        * @param name The new value
        */
       public void setName(String name) {
             this.name = name;
       }
       /**
        * Sets "The value of the element"
        * @param value The new value
        */
       public void setValue(String value) {
             this.value = value;
       }

       /**
        * @return The name of the element
        */
       public String getName() {
             return name;
       }  
       /**
        * @return The value of the element
        */
       public String getValue() {
             return value;
       }  
}

Zusammenfassung

Glückwunsch: Sie haben das erste Zwischenziel erreicht! Sie haben ein Modell und die zugehörigen Templates definiert und automatisiert daraus den Code erzeugt!


4) Model Validation

Legen Sie das Verzeichnis "scripts" an und legen Sie in diesem Verzeichnis eine neue Datei "MyModelScript.eol" an.
Setzen Sie den Inhalt der Datei wie folgt:

operation NamedObject getClassName() : String {
       return self.name.firstToUpperCase();
}

Ändern Sie den Inhalt von "TemplateValueObject.egl" wie folgt und speichern Sie die Datei.

In Zeile 1 wird die Skriptdatei eingebunden.
In Zeile 7 wird die in der Skriptdatei definierte Funktion getClassName() für das aktuelle ValueObject aufgerufen.

Legen Sie das Validations-Skript an

File → New → Other → Epsilon → EVL Validation → Next

Wählen Sie auch hier den Ordner "scripts" aus und geben Sie den Namen “MyModelValidation.evl“ an.
Beenden Sie den Dialog mit „Finish“ und setzen Sie den Inhalt der Datei auf den folgenden Text:

pre initVars {
        var errors = new Sequence();
}

context ValueObject {
        constraint MembersValid {
                check: self.validateValueObject()
                message: self.getErrors()
        }
}

operation ValueObject validateValueObject() : Boolean {
        errors = new Sequence();
        var msg : String = self.validateNamedObject("ValueObject", 0);
        if (msg != "") {
                errors.add(msg);
        }
       
        var memberIndex = 1;
        for (p in self.member) {
                var msg : String = p.validateOneMember(memberIndex);
                if (msg != "") {
                        errors.add(msg);
                }
                memberIndex = memberIndex + 1;
        }      
       
        return errors.isEmpty();
}

operation ValueObject getErrors() : String {
        return "Error in ValueObject " + self.name + ":\n  " + errors.concat("\n  ");
}

operation Member validateOneMember(index) : String {
        return self.validateNamedObject("Member", index);
}

operation NamedObject validateNamedObject(objectName, index) : String {
        if (not self.name.isDefined()) {
                return objectName + " " + index.asString() + " has no name";
        } else if (self.name.length() != self.name.replace(" ", "").length()) {
                return objectName + " '" + self.name + "': The name contains whitespace";
        } else if (self.name.length() != self.name.replace("[^a-zA-Z_]", "").length()) {
                return objectName + " '" + self.name + "': The Name contains illegal characters";
        }
        return "";
}

Fügen Sie die Validierung in das Ant buildfile ein

Ändern Sie den Inhalt von build.xml auf den folgenden Inhalt:

Prüfen Sie nun, ob die Validierung tatsächlich Fehler findet. Benennen Sie das Value Object in "Ele ment" um und ändern Sie den Namen des Member „value“ auf „value!“.

Buildfile: D:\temp\Workspaces\WS-Model-To-Code\EpsilonExample\build.xml
main:
[epsilon.evl - MyModelValidation.evl] Error in ValueObject Ele ment:
[epsilon.evl - MyModelValidation.evl] ValueObject 'Ele ment': The name contains whitespaces
[epsilon.evl - MyModelValidation.evl] Member 'value!': The name contains illegal characters
[epsilon.evl - MyModelValidation.evl] Errors : 1
BUILD FAILED
D:\temp\Workspaces\WS-Model-To-Code\EpsilonExample\build.xml:8: 1 error(s) and 0 warning(s) found during validation

Damit haben Sie nachgewiesen, dass die Validierung ausgeführt wird.
Korrigieren Sie das Modell, in dem Sie die Änderungen wieder rückgängig machen.

Zusammenfassung

Sie haben ein EOL-Skript geschrieben und in das .egl-Template eingebunden.
Sie haben eine Validierung geschrieben und in das buildfile eingebunden.


5) Model-to-Model Transformation

Für die Model-to-Model-Transformation müssen zunächst das Meta-Modell und das Modell erweitert werden. Danach schreiben Sie die Model-to-Model Transformation und binden die Transformation in das buildfile ein.

Erweitern Sie das Meta-Modell

Ändern Sie "MyMetamodel.emf" auf folgenden Inhalt. Die Änderungen sind gelb umrahmt.

Generieren Sie die ecore-Datei neu und registrieren Sie die EPackages.

Erweitern Sie das Modell

Schließen Sie das Modell „MyModel.model“, wenn Sie es noch geöffnet haben, damit der Editor sich an das geänderte Metamodell anpassen kann.
Öffnen Sie es wieder, und wählen Sie das ValueObject „Element“ aus.

Eclipse zeigt die neue Property „ComposedOf“ an.

Erweitern Sie das Modell um ein ValueObject „Basis“:

Fügen Sie dem ValueObject „Basis“ einen Member „colour“ hinzu:

Setzen Sie im ValueObject „Element“ das Attribut „composedOf“ auf „Basis“ und speichern Sie das Modell.

Schreiben Sie die Model-to-Model Transformation

Die Model-to-Model-Transformation erweitert ein ValueObject um alle Member der ValueObject-Instanzen, die in „composedOf“ aufgeführt sind.
Legen Sie die Transformation im Verzeichnis „scripts“ an und geben Sie ihr den Namen „MyModelTransformation.etl“.

File New Other Epsilon ETL Transformation Next

Setzen Sie den Inhalt von „MyModelTransformation.etl“ wie folgt:

rule CollectMember
       transform s : EmfSource!Definition
       to t: EmfTarget!Definition {
             // .equivalent calls Vo2Vo
             t.vo.addAll(s.vo.equivalent());
       }
      
rule Vo2Vo
       transform s: EmfSource!ValueObject
       to t: EmfTarget!ValueObject {
             // Copy attributes
             t.name = s.name;
             t.desc = s.desc;
             // Add all collected members
             for (srcMember : EmfSource!Member in s.getMember()) {
                    t.member.add(srcMember.makeCopy());
             }
       }

// Create a copy of a member
operation EmfSource!Member makeCopy() : EmfTarget!Member {
       var dstMember = new EmfTarget!Member;
       dstMember.name = self.name;
       dstMember.desc = self.desc;
       return dstMember;
}

/*
Recursively collect all members
*/
operation Any getMember() : Sequence  {
       var result = new Sequence();

       if (self.composedOf.isDefined()) {
             for (baseClass : EmfSource!ValueObject in self.composedOf) {
                    result.addAll(baseClass.getMember());
             }
       }

       for (srcMember : EmfSource!Member in self.member) {
             result.add(srcMember);
       }
            
       return result;
}

Binden Sie die Model-to-Model-Transformation in das Ant buildfile ein

Dafür wird zunächst das Modell ein zweites Mal geladen, mit dem Namen "EmfTarget".
Durch read="false" wird ein leeres Modell erstellt, und durch store="true" wird das geänderte Modell gespeichert. Das ist nicht notwendig, ist aber hilfreich zur Analyse.
Die Erzeugung der Ausgabe soll auf dem transformierten Modell geschehen, daher wird dort auch "EmfTarget" verwendet.

project default="main">
       <target name="main">
             <epsilon.emf.loadModel
                    name="EmfSource"
                    modelfile="model/MyModel.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="true" store="false"/>
             <epsilon.evl src="scripts/MyModelValidation.evl">
                    <model ref="EmfSource"/>
             </epsilon.evl>
             <epsilon.emf.loadModel
                    name="EmfTarget"
                    modelfile="model/MyModelUpdated.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="false" store="true"/>
             <epsilon.etl src="scripts/MyModelTransformation.etl">
                    <model ref="EmfSource"/>
                    <model ref="EmfTarget"/>
             </epsilon.etl>
             <epsilon.egl src="templates/VoMasterTemplate.egx">
                    <model ref="EmfTarget" />
             </epsilon.egl>
       </target>
</project>

Zur Verdeutlichung, hier nochmal was genau geändert wurde:

<epsilon.emf.loadModel
                    name="EmfTarget"
                    modelfile="model/MyModelUpdated.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="false" store="true"/>
             <epsilon.etl src="scripts/MyModelTransformation.etl">
                    <model ref="EmfSource"/>
                    <model ref="EmfTarget"/>
             </epsilon.etl>
                      <epsilon.egl src="templates/VoMasterTemplate.egx">
                    <model ref="EmfTarget" />

Führen Sie das buildfile aus

Dies geht, wie bisher, über "Run as..."

Untersuchen Sie das aktualisierte Modell und den erzeugten Code

Aktualisieren Sie den Project Explorer und öffnen Sie das Modell „model/MyModelUpdated.model“.

Das ValueObject „Element“ hat nun einen zusätzlichen Member „colour“.
Achtung: Die Model-to-Model-Transformation übernimmt nicht den Wert für „composedOf“ in das aktualisierte Modell, da diese Information in der Model-to-Code-Transformation nicht berücksichtigt wird.
Im Ordner „java/example“ ist zusätzlich die Datei „Basis.java“ enthalten.
In der Datei „Element.java“ ist für den zusätzlichen Member der Java-Code erzeugt worden.

Zusammenfassung

Sie haben das Meta-Modell und das Modell erweitert, eine Model-to-Model-Transformation erstellt und in das buildfile eingebunden.
Damit können Sie ihre ersten eigenen Schritte mit Eclipse Epsilon gehen.


6) Arbeiten mit einem Excel-Modell

Das Format Excel erfreut sich bei Anwendern einer großen Beliebtheit. So ist es nicht verwunderlich, dass Epsilon auch eine Möglichkeit bietet, eine Excel-Datei als Modell zu verwenden.
Details finden Sie in der Dokumentation zu Epsilon: Scripting Excel Spreadsheets using Epsilon

Für diesen Abschnitt benötigen Sie ein Programm um Excel-Dateien anzulegen und zu bearbeiten.
In diesem Abschnitt werden Sie eine neue Excel-Datei mit zwei Sheets anlegen.

Legen Sie die neue Excel-Datei an

Legen Sie eine neue Excel-Datei mit dem Namen "MyExcelCountryData.xlsx" im Verzeichnis "model" an.

Benennen Sie das erste Sheet in "Country" um und füllen Sie es

Benennen Sie das zweite Sheet in "Capital" um und füllen Sie es

Bitte beachten Sie: Ich habe die Reihenfolge der Einträge bewusst anders gewählt als im Sheet „Country“.

Legen Sie das Master-Template für Ihre Excel-Datei an

Im Master Template gibt es eine Besonderheit: Ich lade im pre-Abschnitt alle Einträge aus dem Sheet „Capital“ in die Variable „cap“. Damit stehen diese Einträge im untergeordneten Template „ExcelCountryDataTemplate.egl“ zur Verfügung.

pre {
    var cap : Any = Capital;
    }
   
rule COUNTRY2SOURCE
       transform cnt: Country {
             template: "ExcelCountryDataTemplate.egl"
             target: "../java/example/" + cnt.ID + ".java"
       }

Legen Sie das Template für ihre Excel-Datei an

Im Template wird über „cnt“ auf die aktuelle Ausprägung von „Country“ zugegriffen.
In Zeile 9 wird über alle Ausprägungen von Capital iteriert.
In Zeile 10 wird geprüft, ob es ein zutreffendes Land gibt.
Dann wird in Zeile 11 die Hauptstadt ausgegeben.
Im Gegensatz zum bisherigen Template wird nicht nur ein Objekt, sondern zwei Objekte, verwendet.

Erweitern Sie das Buildfile um die Generierung

<project default="main">
       <target name="main">
             <epsilon.emf.loadModel
                    name="EmfSource"
                    modelfile="model/MyModel.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="true" store="false"/>
             <epsilon.evl src="scripts/MyModelValidation.evl">
                    <model ref="EmfSource"/>
             </epsilon.evl>
             <epsilon.emf.loadModel
                    name="EmfTarget"
                    modelfile="model/MyModelUpdated.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="false" store="true"/>
             <epsilon.etl src="scripts/MyModelTransformation.etl">
                    <model ref="EmfSource"/>
                    <model ref="EmfTarget"/>
             </epsilon.etl>
             <epsilon.egl src="templates/VoMasterTemplate.egx">
                    <model ref="EmfTarget" />
             </epsilon.egl>
             <!-- Excel -->
             <epsilon.loadModel name="CountryData" type="ExcelModel">
                    <parameter name="SPREADSHEET_FILE" file="model/MyExcelCountryData.xlsx"/>
             </epsilon.loadModel>
             <epsilon.egl src="templates/ExcelMasterTemplate.egx">
                    <model ref="CountryData" />
             </epsilon.egl>            
       </target>
</project>

Zur Verdeutlichung, hier nochmal was genau geändert wurde:

          <!-- Excel -->
             <epsilon.loadModel name="CountryData" type="ExcelModel">
                    <parameter name="SPREADSHEET_FILE" file="model/MyExcelCountryData.xlsx"/>
             </epsilon.loadModel>
             <epsilon.egl src="templates/ExcelMasterTemplate.egx">
                    <model ref="CountryData" />
             </epsilon.egl>   

Führen Sie das Buildfile aus und sehen Sie sich "DE.java" an

package example;

/**
 * Country code DE
 */

public class countryDE {
       public static final String capital = "Berlin";

}

Zusammenfassung

Sie haben eine Excel-Datei angelegt und daraus Code erzeugt. Dabei haben Sie den Inhalt zweier Sheets miteinander verbunden.


7) Dateien mit Zeitstempel versehen

Bei generierten Dateien finde ich es praktisch, einen Zeitstempel mit dem Datum der letzten Änderung zu haben.
Ich behelfe mir mit folgenden zwei Schritten im Buildfile:

  1. Die Dateien werden nach „install“ kopiert, die sich von den Dateien in „lastversion“ unterscheiden. Im Kopiervorgang wird der Zeitstempel gesetzt, so dass nur die Dateien in „install“ den Zeitstempel besitzen.
  2. Alle Dateien werden nach „lastversion“ kopiert, damit beim nächsten Durchlauf in Schritt 1 nur die geänderten Dateien kopiert werden.
<project default="main">
       <target name="main">
             <!-- Initialize timestamp -->
             <tstamp>
                 <format property="generationtime" pattern="dd.MM.yyyy HH:mm z"/>
             </tstamp>                        
             <epsilon.emf.loadModel
                    name="EmfSource"
                    modelfile="model/MyModel.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="true" store="false"/>
             <epsilon.evl src="scripts/MyModelValidation.evl">
                    <model ref="EmfSource"/>
             </epsilon.evl>
             <epsilon.emf.loadModel
                    name="EmfTarget"
                    modelfile="model/MyModelUpdated.model"
                    metamodeluri="http://my.metamodel.id/version1"
                    read="false" store="true"/>
             <epsilon.etl src="scripts/MyModelTransformation.etl">
                    <model ref="EmfSource"/>
                    <model ref="EmfTarget"/>
             </epsilon.etl>
             <epsilon.egl src="templates/VoMasterTemplate.egx">
                    <model ref="EmfTarget" />
             </epsilon.egl>
            
             <!-- Excel -->
             <epsilon.loadModel name="CountryData" type="ExcelModel">
                    <parameter name="SPREADSHEET_FILE" file="model/MyExcelCountryData.xlsx"/>
             </epsilon.loadModel>
             <epsilon.egl src="templates/ExcelMasterTemplate.egx">
                    <model ref="CountryData" />
             </epsilon.egl>
             <!-- Copy last version and set timestamp -->
             <copy todir="install">           
                    <fileset dir="java" includes="**/*.java">
                        <different targetdir="lastversion" ignoreFileTimes="true"/>
                    </fileset>
                    <filterset>
                        <filter token="GENERATIONTIME" value="${generationtime}"/>
                    </filterset>
             </copy>
             <copy todir="lastversion">       
                    <fileset dir="java" includes="**/*.java"/>
             </copy>
       </target>
</project>

Jetzt teilen: