Schauen Sie zuerst, was Emugen für folgendes Beispiel generiert.
Adressbuch::= Person*: Privat GeschaeftlichEs werden Klassen für das Datenmodell (z.B. Adressbuch.java ), für die Darstellung des Modells ( z.B. AdressbuchPanel.java ), Klassen für die Aktionen (z.B. AdressbuchOeffnenAction.java ), ReferenceManager.java und die Klassen für die Visitorschnittstelle generiert.
Geschaeftlich::= Person*
Person::= String:Nachname
String:Vorname
String:Telefon
Wir erzeugen nur Datenmodellklassen für Tupelproduktionen und das auch fragmentarisch. Etwa folgendes als Adressbuch.java :
public class Adressbuch extends emu_runtime.datamodel.TupelInteractionData{Es gibt eine Besonderheit bei dieser Codeerzeugung. Für die Komponenten auf der rechten Seite einer Tupelproduktion müssen Sie viele Codeteile erzeugen. Eine Deklaration, etwas in den Konstruktor, die get... (und set...)-Methoden usw. So haben Sie eigentlich mehrere Ausgabeströme. Wie realisiert man sowas?
private PersonList Privat;
private Geschaeftlich Geschaeftlich;
public Adressbuch () {
Privat = new PersonList();
Geschaeftlich = new Geschaeftlich();
}
public PersonList getPrivat() {
return Privat;
}
public Geschaeftlich getGeschaeftlich() {
return Geschaeftlich;
}
}
Eine Lösung ist, den Code erst in einzelne StringBuffer zu schreiben und erst, wenn man mit der rechten Seite der Tupelproduktion fertig ist, auszugeben. Dafür definiert man im EmitcodeVisitor mehrere StringBuffer: z.B. für den Member-Teil, für den Konstruktorkopf und Konstruktorrumpf und für die Methoden.
Anbei ist die Klasse OutFile.java aus den EMUGEN Sourcen, die genau das zu Verfügung stellt. In Ihrem EmitcodeVisitor könnten Sie etwa folgendes schreiben:
Im printStandard würden Sie die Buffer initialisieren (den Anfang für die Klasse, Anfang für den Konstruktor usw.), im printEnd() abschließen (z.B. schliessende Klammern hinzufügen); dazwischen werden in den Kinderknoten die Buffer weiter ausgefüllt.
package node;
import java.io.*;
public class EmitcodeVisitor extends VisitorAdaptor {
private OutFile file;
private void makeSections() {
file.addSection("decl");
file.addSection("members");
file.addSection("condecl");
file.addSection("conbody");
file.addSection("methods");
file.addSection("end");
}
...
public void visit(Prod prod) {
try {
private String className=prod.getLhs();
file = new OutFile( new File(className)+".java") );
makeSections();
String extending = "extends emu_runtime.datamodel.TupelInteractionData";
printStandard(extending);
production.childrenAccept(this);
printEnd();
System.out.println("Writing class "+className);
file.emit();
}
catch (IOException e) {
System.err.println("Error writing "+className+" :");
e.printStackTrace();
System.exit(1);
}
}
Erzänzen Sie Ihren Emugen-Compiler um den EmitcodeVisitor, in dem Sie obigen Code erzeugen.
Geben Sie als Lösung der Aufgabe
Gehen Sie von der vorgegebenen Eingabe aufg14.emu aufg14.emu aus und implementieren Sie die Aktion Generate.
Hinweis: Schauen Sie die generierten Klassen an und orientieren Sie sich daran, wie die toString()-Methoden realisiert sind. Dann definieren Sie in der Eingabe aufg14.emu für die einzelnen Knotentypen eine Methode, z.B. für EMUMetatype definiert als
EMUMetatype::= TupelProductionfolgende Methode:
| ListProduction
methods of EMUMetatype {:Geben Sie als Lösung der Aufgabe
public String getString() {
switch(acttype) {
case TUPELPRODUCTION:
return TupelProduction.getString();
case LISTPRODUCTION:
return ListProduction .getString();
}
return null;
}
:}