Eigene FreeMarker Direktiven in Java implementieren

„Ein jeder Wunsch, wenn er erfüllt, kriegt augenblicklich Junge.“

Wilhelm Busch

Die FreeMarker Template Engine ist eine hervorragende Bibliothek um eigene Inhalte Template-basiert zu erzeugen. Es existiert eine reichhaltige Anzahl von Funktionen um auch exotische Anforderungen erfüllen zu können. Manches Mal wünscht man sich jedoch zusätzliche Funktionalitäten um die eigenen Anforderungen besser umsetzen zu können. Wie erstaunt ist man dann jedoch, wenn diese Wünsche schon längst erfüllt wurden.

Im aktuellen Fall ging es darum, den erzeugten Inhalt in einer einzelnen Zeile darzustellen. Dafür gibt es zwar eine triviale Lösung, die jedoch das Template sehr unleserlich gestaltet. Daher war die Idee, ein eigenes Makro zu schreiben, dass die Zeilenumbrüche aus dem generierten Text entfernt. Eine praktikable Lösung mit den FreeMarker FTL Makros schien nicht möglich zu sein und eine Java basierte Lösung wäre banal gewesen.

Ein Entwicklergespräch brachte die Erkenntnis. Die Freemarker Bibliothek unterstützt auch Java basierte Direktiven als Implementierungen des Interface TemplateDirectiveModel.

class OnelinerDirective implements TemplateDirectiveModel {

  @Override
  public void execute(Environment environment, Map map, TemplateModel[] templateModels,
      TemplateDirectiveBody templateDirectiveBody) throws TemplateException, IOException {
    if (templateDirectiveBody == null) {
      throw new IllegalArgumentException("missing body");
    }
    templateDirectiveBody.render(new FlattenFilterWriter(environment.getOut()));
  }
}

Die hier dargestellte Implementierung prüft zuerst, ob die Direktive einen Body hat. Fehlt dieser, dann wird ein Fehler geworfen, denn ohne Inhalt ist die Verwendung unnötig. Danach wird das Rendern des Bodies durch einen zusätzlichen Writer geleitet. Dieser FlattenFilterWriter entfernt aus dem produzierten Content die Zeilenumbrüche. Die triviale Implementierung mit einem FilterWriter wird im folgenden Beispiel gezeigt.

class FlattenFilterWriter extends FilterWriter {

  FlattenFilterWriter(Writer out) {
    super(out);
  }

  @Override
  public void write(char[] cbuf, int off, int len) throws IOException {
    char[] transformedCbuf = new char[len];
    for (int i = 0; i < len; i++) {
      char c = cbuf[i + off];
      transformedCbuf[i] = c == '\n' ? ' ' : c;
    }
    out.write(transformedCbuf);
  }
}

Damit die Direktive genutzt werden kann, muss sie bekannt gegeben werden. Dafür kann sie als shared Variable in die Konfiguration eingetragen werden. Damit steh sie in allen Templates zur Verfügung.

configuration = new Configuration(Configuration.VERSION_2_3_23);
configuration.setSharedVariable("oneliner", new OnelinerDirective());

Wenn die Direktive unter dem Namen oneliner zur Verfügung steht, dann kann sie als Makro im Template genutzt werden.

<@compress><@oneliner>
<tr>
  <td>${ancestor.firstname}</td>
  <td>${ancestor.middlename!"-"}</td>
  <td>${ancestor.lastname}</td>
  <td>${ancestor.birthDate}</td>
  <td>${ancestor.placeOfBirth}</td>
  <td>${ancestor.dateOfDeath}</td>
  <td>${ancestor.placeOfDeath}</td>
</tr>
</@oneliner></@compress>

In diesem Fall ist eine Tabellenzeile in das Marko <@oneliner></@onliner> eingebettet um die Zeilenumbrüche herauszufiltern und zusätzlich in die Standard Direktive <@compress></@compress> um danach unnötige Leerzeichen zu entfernen.

Das Ergebnis im Folgenden zeigt die entsprechende Tabellenzeile für meinen Ahnen Johann Seemann.

<tr> <td>Johann</td> <td>-</td> <td>Seemann</td> <td>6 Sep 1781</td> <td>Kirchhuchting</td> <td>1815</td> <td>Bremen</td> </tr>

Java Direktiven in FreeMarker sind eine perfekte Ergänzung für die FTL Makros um exotische Anforderungen zu implementieren oder einfach nur um die Möglichkeiten der Java APIs zu nutzen.

Schreibe einen Kommentar