Codegenerierung mit Eclipse Xtend

Whitespace Handling

Ein typisches Problem von Template-Sprachen ist die Formatierung. Einerseits soll der generierte Code verständlich und suggestiv formatiert sein. Andererseits will man das Template selbst auch gut lesen können und z. B. Blöcke in Kontrollstrukturen einrücken. In unserem Generator haben wir einerseits den Funktionsrumpf und den FOR-Block eingerückt, um das Template besser lesen zu können, andererseits wollen wir, dass die generierten Java-Felder innerhalb der generierten Klasse nur einfach eingerückt sind. Das Problem besteht darin zu unterscheiden, welcher Whitespace für das Layout des Templates und welcher für das Layout der zu generierenden Datei bestimmt ist.

Die meisten Codegenerierungstechnologien greifen auf die Nachbearbeitung des Generats durch einen so genannten Formatter oder Pretty Printer zurück. Diese gibt es allerdings nicht für alle möglichen Zielsprachen. Außerdem ist die Generierung Whitespace-sensitiver Sprachen (z. B. Python) problematisch. Xtend bietet daher eine feste Strategie, die unterscheiden kann, welche Einrückungen und Zeilenumbrüche für die Ausgabe gedacht sind und welche nicht. Obwohl der Algorithmus relativ kompliziert ist, ist das Verhalten sehr intuitiv und wird zusätzlich über Syntax-Highlighting im Xtend-Editor verdeutlicht (Abb. 2).

Abb. 2: Whitespace Highlighting im Xtend-Editor

Die Einrückungen werden übrigens auch auf eingefügte mehrzeilige Strings übertragen. Extrahieren wir den Körper unserer FOR-Schleife in eine eigene Funktion, bleibt die Einrückung aus der aufrufenden FOR-Schleife erhalten und das Generat unverändert:

   def generateJavaCode(Class> clazz) '''
       package «clazz.getPackage.name»;
       
       public class «clazz.name» {
           «FOR f : clazz.fields »
               «f.generateProperty»
           «ENDFOR»
       }
   '''
   
   def generateProperty(Field f) '''
       private «f.type.name» «f.name»;
   '''
Extension Methods

Vielleicht ist Ihnen aufgefallen, dass die Funktion generateProperty(Field) aus der ersten Funktion heraus aufgerufen wird, als ob die Funktion direkt auf dem Typ java.lang.reflect.Field deklariert wäre. Xtend hat seinen Namen der umfangreichen Unterstützung der so genannten Extension Methods [3] zu verdanken. Mit diesem Konzept ist es möglich, für bestehende Typen weitere Funktionen zu deklarieren und diese dann auf den Objekten sichtbar zu machen. Das Prinzip ist ganz einfach: Der erste Parameter einer Funktion, in unserem Fall Field f, wird einfach als Empfänger des Methodenaufrufs interpretiert. Wenn man sich den kompilierten Java-Code anschaut, sieht man, dass aus f.generateProperty das erwartete this.generateProperty(f) wird. Extension Methods gibt es z. B. auch in C#, sie sind dort aber leider nur im Zusammenhang mit statischen Funktionen möglich.

Mehr Expressions

Wir wollen nun die Felder in alphabetischer Reihenfolge generieren und erstellen zu diesem Zweck eine Funktion sortedFields(Class<? > clazz), die ein sortiertes java.lang.Iterable<Field> zurückgibt:

   def sortedFields(Class> clazz) {
       clazz.fields.sortBy(f | f.name)
   }

Wir bedienen uns hier der Hilfsfunktion sortBy aus der Xtend2-Library. Die Funktionen in dieser Library sind ebenfalls Extension Methods und erweitern Datentypen aus dem Java-SDK. Die Funktion sortBy ist in Java deklariert und erwartet ein Argument vom Typ Function1. Xtend unterstützt Closures, was es erlaubt, anonyme Funktionen in sehr leserlicher und knapper Syntax aufzuschreiben. In Java müsste man obigen Funktionsaufruf mithilfe einer anonymen Klasse formulieren:

  IterableExtensions.sortBy(clazz.getFields(), 
    new Functions.Function1() {
      @Override
      public String apply(Field f) {
        return f.getName();
      }
    });

Nun müssen wir noch den Aufruf in der FOR-Schleife ändern, damit die neu definierte Sortierfunktion auch genutzt wird:

   def generateJavaCode(Class> clazz) '''
       ...
           «FOR f : clazz.sortedFields»
       ...
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.