FreshMarker User Directives (1)

Zur Vereinfachung der FreshMarker Templates sollen wiederkehrende Teile als Funktionen und Macros formulierbar sein. Statt immer wieder ähnliche Teile im Template einzufügen, sollen diese durch parametrisierbare Aufrufe ersetzt werden. Wie in FreeMarker wird dies in FreshMarker über User Directives realisiert.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<p><b>Nachname</b>: ${lastname}</p>
<p><b>Vorname</b>: ${firstname}</p>
<p><b>Nachname</b>: ${lastname}</p> <p><b>Vorname</b>: ${firstname}</p>
<p><b>Nachname</b>: ${lastname}</p>
<p><b>Vorname</b>: ${firstname}</p>

Hier wird in jedem Paragraph ein Label in Fettschrift und mit einem Doppelpunkt davon getrennt ein variabler Wert ausgegeben. Mit einem dafür geschriebenen Macro

entry
entry, ergibt sich folgendes Template.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<@entry label='Nachname' value=lastname/>
<@entry label='Vorname' value=firstname/>
<@entry label='Nachname' value=lastname/> <@entry label='Vorname' value=firstname/>
<@entry label='Nachname' value=lastname/>
<@entry label='Vorname'  value=firstname/>

Die User Directive bietet den Vorteil, dass Änderungen für alle Eintrage nur noch einmal an zentraler Stelle erfolgen müssen. Die

@
@ Notation entspricht dabei dem Aufruf einer User Directive, wie sie auch in FreeMarker verwendet wird.

Damit eine User Directive verwendet werden kann, muss sie definiert werden. Dafür bieten sich die Definition über ein Macro oder als Java Implementierung an. Gemeinsam ist beiden Ansätzen, dass ein spezielles

UserDirectiveFragment
UserDirectiveFragment im Template eine
UserDirective
UserDirective Implementierung aufruft. Die
UserDirective
UserDirective ist ein Interface das von den Java Implementierungen direkt implementiert wird und für das eine spezielle Implementierung für Macros existiert.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class UserDirectiveFragment implements Fragment {
private final String directive;
private final Map<String, TemplateObject> namedArgs;
private final BlockFragment body;
public UserDirectiveFragment(String directive, Map<String, TemplateObject> namedArgs, BlockFragment body) {
this.directive = directive;
this.namedArgs = namedArgs;
this.body = body;
}
@Override
public void process(ProcessContext context) {
context.getEnvironment().getDirective(directive).execute(context, namedArgs, body);
}
}
public class UserDirectiveFragment implements Fragment { private final String directive; private final Map<String, TemplateObject> namedArgs; private final BlockFragment body; public UserDirectiveFragment(String directive, Map<String, TemplateObject> namedArgs, BlockFragment body) { this.directive = directive; this.namedArgs = namedArgs; this.body = body; } @Override public void process(ProcessContext context) { context.getEnvironment().getDirective(directive).execute(context, namedArgs, body); } }
public class UserDirectiveFragment implements Fragment {

  private final String directive;
  private final Map<String, TemplateObject> namedArgs;
  private final BlockFragment body;

  public UserDirectiveFragment(String directive, Map<String, TemplateObject> namedArgs, BlockFragment body) {
    this.directive = directive;
    this.namedArgs = namedArgs;
    this.body = body;
  }

  @Override
  public void process(ProcessContext context) {
    context.getEnvironment().getDirective(directive).execute(context, namedArgs, body);
  }
}

Das

UserDirectiveFragment
UserDirectiveFragment kennt den Namen der User Directive und die Parameter, mit denen die User Directive aufgerufen werden soll. Zusätzlich enthält das
Fragment
Fragment noch ein
BlockFragment
BlockFragment, das den Inhalt der User Directive beschreibt. In der
process
process Methode wird die entsprechende Implementierung der User Directive aus dem Environment geholt und deren
execute
execute Methode aufgerufen.

Die weiter oben dargestellte User Directive enthält keinen Inhalt. Im Beitrag Eigene FreeMarker Direktiven in Java implementieren wurde die User Directive

oneliner
oneliner für FreeMarker vorgestellt. Diese entfernt alle Zeilenumbrüche aus dem generierten Text, um mit dem entstehenden Einzeiler beispielsweise Aufrufparameter zu befüllen.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<@oneliner>
<#list values as v>
${v?count}='${v}'
<#if v?has_next>,</#if>
</#list>
</@oneliner>
<@oneliner> <#list values as v> ${v?count}='${v}' <#if v?has_next>,</#if> </#list> </@oneliner>
<@oneliner>
  <#list values as v>
    ${v?count}='${v}'
    <#if v?has_next>,</#if>
  </#list>
</@oneliner>

Das hier dargestellte Template erzeugt für die Liste

Hund
Hund,
Katze
Katze,
Maus
Maus eine kommaseparierte Liste
1='Hund',2='Katze',3='Maus'
1='Hund',2='Katze',3='Maus'

Die Implementierung in FreshMarker sieht der in FreeMarker recht ähnlich.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class OneLinerDirective implements UserDirective {
@Override
public void execute(ProcessContext context, Map<String, TemplateObject> args, BlockFragment body) {
if (body == null) {
throw new ProcessException("one-liner body missing");
}
FlattenFilterWriter writer = new FlattenFilterWriter(context.getWriter());
body.process(new ProcessContext(new WriterEnvironment(writer, context.getEnvironment()), context));
}
}
public class OneLinerDirective implements UserDirective { @Override public void execute(ProcessContext context, Map<String, TemplateObject> args, BlockFragment body) { if (body == null) { throw new ProcessException("one-liner body missing"); } FlattenFilterWriter writer = new FlattenFilterWriter(context.getWriter()); body.process(new ProcessContext(new WriterEnvironment(writer, context.getEnvironment()), context)); } }
public class OneLinerDirective implements UserDirective {

  @Override
  public void execute(ProcessContext context, Map<String, TemplateObject> args, BlockFragment body) {
    if (body == null) {
      throw new ProcessException("one-liner body missing");
    }
    FlattenFilterWriter writer = new FlattenFilterWriter(context.getWriter());
    body.process(new ProcessContext(new WriterEnvironment(writer, context.getEnvironment()), context));
  }
}

Diese Implementierung ersetzt das aktuelle

Environment
Environment durch einen Decorator, der den aktuellen
Writer
Writer durch einen
FlattenFilterWriter
FlattenFilterWriter ersetzt. Mit diesem
Environment
Environment wird dann der Inhalt der User Directive in
body
body verarbeitet und alle Ausgaben durch den
FlattenFilterWriter
FlattenFilterWriter geleitet.

Um die

OneLinerDirective
OneLinerDirective einzusetzen, muss sie natürlich der Configuration bekannt sein. Dies kann über den dafür erweiterten
PluginProvider
PluginProvider Mechanismus erfolgen oder, der Einfachheit halber, über eine entsprechende Methode
registerUserDirective
registerUserDirective.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
templateLoader.putTemplate("test", "<@oneliner>\n<#list values as v>\n${v}\n<#if v?has_next>;</#if>\n</#list>\n</@oneliner>");
configuration.registerUserDirective( "oneliner", new OneLinerDirective());
Template template = configuration.getTemplate("test");
assertEquals("test: 1; 2; 3", template.process(Map.of("values", List.of(1,2,3))));
templateLoader.putTemplate("test", "<@oneliner>\n<#list values as v>\n${v}\n<#if v?has_next>;</#if>\n</#list>\n</@oneliner>"); configuration.registerUserDirective( "oneliner", new OneLinerDirective()); Template template = configuration.getTemplate("test"); assertEquals("test: 1; 2; 3", template.process(Map.of("values", List.of(1,2,3))));
templateLoader.putTemplate("test", "<@oneliner>\n<#list values as v>\n${v}\n<#if v?has_next>;</#if>\n</#list>\n</@oneliner>");
configuration.registerUserDirective( "oneliner", new OneLinerDirective());
Template template = configuration.getTemplate("test");
assertEquals("test: 1; 2; 3", template.process(Map.of("values", List.of(1,2,3))));

Damit ist der erste Teil der Implementierung von User Directives auch schon beendet und im nächsten Teil dreht es sich um die Bereitstellung von Macros und ihrer Schleifenvariablen

1 thought on “FreshMarker User Directives (1)”

Leave a Comment