FreshMarker Built-Ins und Plug-Ins

Im letzten Beitrag wurden die Built-Ins vorgestellt. Dies sind Funktionen, die auf den Instanzen von TemplateObject ausgeführt werden. Das TemplateObject Interface und seine Implementierungen bilden das Datenmodel der Template-Engine. Das BuiltIn Interface seinerseits definiert eine apply Methode, die auf einem TemplateObject ausgeführt wird. Die Methode besitzt als zusätzliche Parameter das aktuelle Environment und zusätzliche Built-In Parametern in Form einer Liste von TemplateObject Instanzen.

public interface BuiltIn {
  void validate(List<TemplateObject> parameters);

  TemplateObject apply(TemplateObject value, List<TemplateObject> parameter, Environment environment);
}

Bei einer Variableninterpolation ${variable?upper_case} wird im ersten Schritt die Variable ausgewertet und dann abhängig von ihrem Typ nach einem Built-In upper_case gesucht. Da dieses Built-In nur für den Typ TemplateString definiert ist, wird bei einem anderem Typen ein entsprechende Exception geworfen.

Um die Verwaltung der Built-Ins einfach zu gestalten, werden alle in einer Map<BuiltInKey, BuiltIn> gespeichert. Der BuildInKey setzt sich aus dem Namen des Built-Ins und seinem Operationstyp zusammen. Der Operationstyp ist dabei der Typ der Eingangsvariable des Built-Ins. Bei der Initialisierung der FreshMarker Konfiguration wird diese Map mit allen vorhandenen BuiltIn Implementierungen gefüllt.

Das BuiltIn Interface kann auf verschiedene Weisen implementiert werden. FreshMarker liefert die beiden konkurrierenden Implementierungen MethodBuitIn und FunctionalBuiltIn. Erstere verwendet Reflections um das Built-In auf eine Java Methode abzubilden und Letztere verwendet ein funktionales Interface.

Im folgenden Beispiel sind drei Java Methoden für die Built-Ins trim, upper_case und then dargestellt. Es handelt sich jeweils um öffentliche, statische Methoden, mit einem Rückgabewert der von TemplateObject erbt. Der erste Parameter entspricht dem Typ des Built-Ins und die folgenden Parameter sind die weiteren Built-Ins Parameter. Als zweiter Parameter kann optional das aktuelle Environment übergeben werden. Die Methoden sind zusätzlich mit der Annotation @BuiltInMethod versehen um das Auswählen der Methoden zu vereinfachen. Zusätzlich kann die Annotation genutzt werden um den BuiltIn einen alternativen Namen zu geben. Die Methode stringBuiltIn definiert das Built-In stringhen und die Methode trim das BuiltIn trim. Für die Methode upperCase erhält das BuiltIn die beiden Namen upperCase und upper_case.

@BuiltInMethod
public static TemplateString trim(TemplateString value) {
  return new TemplateString(value.getValue().trim());
}

@BuiltInMethod
public static TemplateString upperCase(TemplateString value, Environment environment) {
  return new TemplateString(value.getValue().toUpperCase(environment.getLocale()));
}

@BuiltInMethod("string")
public static  TemplateString stringBuiltIn(TemplateBoolean value, TemplateString trueValue, TemplateString falseValue) {
  return value == TemplateBoolean.TRUE ? trueValue : falseValue;
}

Statt die Built-Ins hart zu verdrahten, wurde zusätzlich das Konzept des Plug-Ins eingeführt. Jedes Plug-In implementiert das Interface PluginProvider und besitzt damit eine registerBuiltIn Methode. Diese Methode dient zum Einfügen, der vom Plug-In bereitgestellten Built-Ins in die als Parameter übergebene Map. Die Plug-Ins können noch andere Objekte in der Konfiguration registrieren, dies wird aber in einem anderen Beitrag erörtert.

Interessant werden die Plug-Ins durch den Java ServiceLoader, mit denen FreshMarker alle verfügbaren PluginProvider sucht ihre registerBuiltIn Methoden aufruft. Die von FreeMarker mitgelieferten Plug-Ins sind in der Datei META-INF/services/org.freshmarker.core.plugin.PluginProvider zu finden.

org.freshmarker.core.plugin.StringPluginProvider
org.freshmarker.core.plugin.BooleanPluginProvider
org.freshmarker.core.plugin.NumberPluginProvider
org.freshmarker.core.plugin.TemporalPluginProvider
org.freshmarker.core.plugin.DatePluginProvider
org.freshmarker.core.plugin.FileAndPathPluginProvider
org.freshmarker.core.plugin.LooperPluginProvider

Bei den Plug-Ins StringPluginProvider, BooleanPluginProvider, NumberPluginProvider handelt es sich um Plug-Ins mit den Built-Ins für die Basistypen TemplateString, TemplateBoolean und TemplateNumber. Das Plug-In LooperPlugInProvider stellt die Built-Ins für Schleifenvariablen zur Verfügung. Dazu mehr im Beitrag über FreeMarker Direktiven.

Die Plug-Ins DatePlugInProvider und TemporalPluginProvider kümmern sich um die Typen TemplateDateTime, TemplateDate, TemplateTime mit Date bzw. mit einer LocalDateTime Implementierung. Zusätzlich bietet der TemporalPluginProvider noch die Typen TemplateDuration und TemplatePeriod für Zeiträume an.

Zuletzt stellt das Plug-In FileAndPathPluginProvider die Built-Ins für die Typen TemplatePath und TemplateFile zur Verfügung. Über diese kann auf die Namen und Attribute von Dateien zugegriffen werden.

Der Einsatz des Java ServiceLoader bietet an dieser Stelle nicht nur einen einfachen Mechanismus um die Implementierungen der Built-Ins zu modularisieren, sondern eröffnet auch die Möglichkeit eigene Built-Ins einfach zu integrieren.

Leave a Comment