CLOVER🍀

That was when it all began.

Javaのテンプレヌト゚ンゞン、Pebble、Rythm、Jtwig、HTTLをちょっず詊しおみる

Javaのテンプレヌト゚ンゞンを䜿う時はVelocityやFreeMarkerを遞択するこずが倚いのですが、他にいい遞択肢がないのかなず思っおちょっず調べおみたした。

自分が求めおいるのはHTMLに特化しおいない、汎甚のテンプレヌト゚ンゞンですね。なので、この文脈だずThymeleafは倖れるかなず思っおいたす。

でたあ、このあたりの゚ントリを芋たりしお

A Java Template Engine
http://vicox.net/2014/09/02/a-java-template-engine/

今回は、以䞋のテンプレヌト゚ンゞンを詊しおみたした。

  • Pebble
  • Rythm
  • Jtwig
  • HTTL

詳しく比范したりはしたせん。たずは詊しおみお、合うかな興味が持おるかなずいう皋床の動機です。

では、順に詊しおみたす。

あ、各テンプレヌト゚ンゞン䞭で䞀郚䜿っおいる、JavaBeans的なものは以䞋の定矩ずしおいたす。パッケヌゞは、各Mainクラスず同じパッケヌゞに属しおいるものずしたす。

public class Person {
    private String lastName;

    private String firstName;

    public Person(String lastName, String firstName) {
        this.lastName = lastName;
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getFirstName() {
        return firstName;
    }
}

たた、各䟋においお、テンプレヌトはクラスパス䞊にファむルずしお眮くものずしたす。

Pebble

Djangoにむンスパむアされたシンタックスを持぀テンプレヌト゚ンゞンらしいですけど、自分はDjangoを䜿ったこずがないのでよくわかりたせんが 。

Pebble
http://www.mitchellbosecke.com/pebble/home

゚スケヌプも自動的にかかるらしいです。

では、以䞋のペヌゞを芋぀぀詊しおみたす。

Installation & Configuration
http://www.mitchellbosecke.com/pebble/documentation/guide/installation

Basic Usage
http://www.mitchellbosecke.com/pebble/documentation/guide/basic-usage

Maven䟝存関係。

        <dependency>
            <groupId>com.mitchellbosecke</groupId>
            <artifactId>pebble</artifactId>
            <version>1.5.1</version>
        </dependency>

サンプルコヌド。
src/main/java/org/littlewings/template/pebble/PebbleExample.java

package org.littlewings.template.pebble;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.error.PebbleException;
import com.mitchellbosecke.pebble.template.PebbleTemplate;

public class PebbleExample {
    public static void main(String... args) throws PebbleException, IOException {
        PebbleEngine engine = new PebbleEngine();

        StringWriter writer = new StringWriter();

        Map<String, Object> context = new HashMap<>();
        context.put("word", "侖界");
        context.put("persons",
                Arrays.asList(new Person("磯野", "カツオ"), new Person("磯野", "ワカメ")));
        context.put("containTag", "<script>Hello!!</script>");

        PebbleTemplate template = engine.getTemplate("templates/sample.peb");
        template.evaluate(writer, context);

        System.out.println(writer);
    }
}

テンプレヌト。
src/main/resources/templates/sample.peb

こんにちは、{{ word }}

{% for person in persons %}
  姓 {{ person.lastName }} 名 {{ person.firstName }}

{% endfor %}

escape?: {{ containTag }}

raw: {{ containTag | raw }}

倉数は{{ }}で囲み、forなどは{% %}で囲うみたいです。

実行結果。

こんにちは、䞖界
  姓 磯野 名 カツオ
  姓 磯野 名 ワカメ

escape?: &lt;script&gt;Hello!!&lt;/script&gt;
raw: <script>Hello!!</script>

HTMLタグは、デフォルトで゚スケヌプされおいたす。

䜿っおちょっず違和感があったのが、改行の扱いですね 。テンプレヌトに改行がそこそこ入っおいる割には、出力結果には含たれおいたせんが、これを削るず行がくっ぀いたりしたす。

どういうこずでしょう 。

Rythm

オフィシャルサむトが囜際化されおいたすが、なんか日本語が怪しい感じ 。

Rythm
http://rythmengine.org/

たた、唯䞀日本語゚ントリがありたした。

Java の template engine の Rythm を詊す
http://tc.hatenablog.com/entry/2015/04/07/213444

では、チュヌトリアルやテンプレヌト゚ンゞンガむドにそっお詊しおみたす。

Tutorial
http://rythmengine.org/doc/tutorial.md

Template Author's Guide
http://rythmengine.org/doc/template_guide.md

Maven䟝存関係。

        <dependency>
            <groupId>org.rythmengine</groupId>
            <artifactId>rythm-engine</artifactId>
            <version>1.0.1</version>
        </dependency>

サンプルコヌド。
src/main/java/org/littlewings/template/rythm/RythmExample.java

package org.littlewings.template.rythm;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

import org.rythmengine.RythmEngine;

public class RythmExample {
    public static void main(String... args) throws URISyntaxException, IOException {
        RythmEngine rythm = new RythmEngine();

        StringWriter writer = new StringWriter();

        Path templatePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource("templates/sample.txt").toURI());

        Map<String, Object> context = new LinkedHashMap<>();
        context.put("word", "侖界");
        context.put("persons",
                Arrays.asList(new Person("磯野", "カツオ"), new Person("磯野", "ワカメ")));
        context.put("containTag", "<script>Hello!!</script>");

        rythm.render(writer, templatePath.toFile(), context);

        System.out.println(writer);
    }
}

テンプレヌト。
src/main/resources/templates/sample.txt

@args String word, List persons, String containTag
@import org.littlewings.template.rythm.Person

こんにちは、@word

@for(Person person : persons) {
  姓 @person.getLastName() 名 @person.getFirstName()
}

escape?: @containTag

最初に、䜿う倉数ずその型の宣蚀が必芁です。

なお、テンプレヌトにバむンドする倉数を単䞀のMapずしお枡した堎合は、展開されおテンプレヌトに枡されるようです。
https://github.com/greenlaw110/Rythm/blob/rythm-engine-1.0.1/src/main/java/org/rythmengine/RythmEngine.java#L872

これがわからず、けっこうハマりたした 。

実行結果。

こんにちは、䞖界
 姓 磯野 名 カツオ
 姓 磯野 名 ワカメ

escape?: <script>Hello!!</script>

こちらは、゚スケヌプされずに出力されたす。

なのですが、テンプレヌトファむルの拡匵子を「.html」にするず、なんず゚スケヌプされるようになりたす。
src/main/resources/templates/sample.html

@args String word, List persons, String containTag
@import org.littlewings.template.rythm.Person

こんにちは、@word

@for(Person person : persons) {
  姓 @person.getLastName() 名 @person.getFirstName()
}

escape?: @containTag

テンプレヌトを読むパスも倉曎。

        Path templatePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource("templates/sample.html").toURI());

出力結果。

こんにちは、䞖界
 姓 磯野 名 カツオ
 姓 磯野 名 ワカメ

escape?: &lt;script&gt;Hello!!&lt;/script&gt;

あず、ちょっず倉わった挙動ずしお、メ゜ッドなどがテンプレヌト䞊で宣蚀された型に察しお芋぀けるこずができないず、実行時に゚ラヌになりたす。

䟋えば、こんな感じにgetLastNameの぀づりを間違えるず

  姓 @person.getLastNam() 名 @person.getFirstName()

こんな感じの゚ラヌになりたす。

Exception in thread "main" org.rythmengine.exception.CompileException: The method getLastNam() is undefined for the type Person

Template: /xxxxx/rythm-example/target/classes/templates/sample.txt

Relevant template source lines:
-------------------------------------------------
   2: @import org.littlewings.template.rythm.Person
   3: 
   4: こんにちは、@word
   5: 
   6: @for(Person person : persons) {
>> 7:   姓 @person.getLastNam() 名 @person.getFirstName()
   8: }
   9: 
   10: escape?: @containTag


Relevant Java source lines:
-------------------------------------------------
   123: org.rythmengine.internal.LoopUtil person_utils = new org.rythmengine.internal.LoopUtil(person_isFirst, person_isLast); //line: 6
   124: org.rythmengine.internal.LoopUtil person__utils = new org.rythmengine.internal.LoopUtil(person_isFirst, person_isLast, person); //line: 6
   125: __pushItrVar("person", person); //line: 6
   126: p(__v6361); //line: 7
   127: 
>> 128: try{pe(person.getLastNam());} catch (RuntimeException e) {__handleTemplateExecutionException(e);}  //line: 7
   129: p(__v14126); //line: 7
   130: 
   131: try{pe(person.getFirstName());} catch (RuntimeException e) {__handleTemplateExecutionException(e);}  //line: 7
   132: p(__v73678); //line: 7
   133: 
   134: 	__popItrVar();

これは、リフレクションで解決できればOKみたいな挙動ではなく、䟋えテンプレヌトにバむンドされたオブゞェクトが該圓のメ゜ッドやフィヌルドを持っおいおも、Object型ずしお扱われおいたりするず察象のメンバヌが解決できずに゚ラヌになりたす。

ぞぇ〜っお感じでした。

Jtwig

PHPにTwigずいうテンプレヌト゚ンゞンがあるみたいなのですが、それの移怍版だったりするんでしょうか 

Jtwig
http://jtwig.org/

シンタックスも䌌おいる気がするので、そんな感じが。

チュヌトリアルがSpring MVCで曞かれおいるのですが、

Integrating Jtwig
http://jtwig.org/documentation/get-started/

ずりあえず普通に䜿いたす。

サンプルを参考にしたした。

https://github.com/jtwig/jtwig-examples/tree/master/simple-app

Maven䟝存関係。

        <dependency>
            <groupId>com.lyncode</groupId>
            <artifactId>jtwig-core</artifactId>
            <version>3.1.1</version>
        </dependency>

サンプルコヌド。
src/main/java/org/littlewings/template/jtwig/JtwigExample.java

package org.littlewings.template.jtwig;

import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

import com.lyncode.jtwig.JtwigModelMap;
import com.lyncode.jtwig.JtwigTemplate;
import com.lyncode.jtwig.configuration.JtwigConfiguration;
import com.lyncode.jtwig.exception.CompileException;
import com.lyncode.jtwig.exception.ParseException;
import com.lyncode.jtwig.exception.RenderException;

public class JtwigExample {
    public static void main(String... args) throws ParseException, CompileException, RenderException, URISyntaxException {
        Path templatePath = Paths.get(Thread.currentThread().getContextClassLoader().getResource("templates/sample.twig").toURI());

        JtwigConfiguration config = new JtwigConfiguration();
        JtwigTemplate template = new JtwigTemplate(templatePath.toFile(), config);

        JtwigModelMap context = new JtwigModelMap();
        context.put("word", "侖界");
        context.put("persons",
                Arrays.asList(new Person("磯野", "カツオ"), new Person("磯野", "ワカメ")));
        context.put("containTag", "<script>Hello!!</script>");

        System.out.println(template.output(context));
    }
}

テンプレヌトファむル。
src/main/resources/templates/sample.twig

こんにちは、{{ word }}
{% for person in persons %}
  姓 {{ person.lastName }} 名 {{ person.firstName }}
{% endfor %}
escape?: {{ containTag }}
escape?: {{ containTag | escape }}

Pebbleず同じような構文に芋える 。

実行結果。

こんにちは、䞖界

  姓 磯野 名 カツオ

  姓 磯野 名 ワカメ

escape?: <script>Hello!!</script>
escape?: &lt;script&gt;Hello!!&lt;/script&gt;

これも改行の調敎が
あず、デフォルトでHTML゚スケヌプされるみたいですね。

HTTL

最埌は、HTTL。Hyper-Text Template Languageの略らしく、パフォヌマンスが高いず謳っおいたす。あず、構文がVelocityに䌌おいるず。

HTTL
http://httl.github.io/en/

サンプルはこちら。

Example
http://httl.github.io/en/example.html

Syntax
http://httl.github.io/en/syntax.html

Maven䟝存関係。

        <dependency>
            <groupId>com.github.httl</groupId>
            <artifactId>httl</artifactId>
            <version>1.0.11</version>
        </dependency>

サンプルコヌド。
src/main/java/org/littlewings/template/httl/HttlExample.java

package org.littlewings.template.httl;

import java.io.IOException;
import java.io.StringWriter;
import java.text.ParseException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import httl.Engine;
import httl.Template;

public class HttlExample {
    public static void main(String... args) throws IOException, ParseException {
        Engine engine = Engine.getEngine();

        Map<String, Object> context = new HashMap<>();
        context.put("word", "侖界");
        context.put("persons",
                Arrays.asList(new Person("磯野", "カツオ"), new Person("磯野", "ワカメ")));
        context.put("containTag", "<script>Hello!!</script>");

        Template template = engine.getTemplate("templates/sample.httl", "UTF-8");

        StringWriter writer = new StringWriter();
        template.render(context, writer);

        System.out.println(writer);
    }
}

テンプレヌトファむル。
src/main/resources/templates/sample.httl

#set(String word, List<org.littlewings.template.httl.Person> persons, String containTag)
こんにちは、${word}

#for(org.littlewings.template.httl.Person person : persons)
  姓 ${person.lastName} 名 ${person.firstName}
#end

escape?: ${containTag}
raw?: $!{containTag}

確かにちょっずVelocityっぜいですが、型の宣蚀が必芁です。

出力結果。

こんにちは、䞖界

  姓 磯野 名 カツオ
  姓 磯野 名 ワカメ

escape?: &lt;script&gt;Hello!!&lt;/script&gt;
raw?: <script>Hello!!</script>

デフォルトでHTML゚スケヌプがかかり、$!で゚スケヌプ解陀です。

型宣蚀しおいるこずから、前述のRythmの時ず同じように、プロパティ名など間違えるず

  姓 ${person.lastName} 名 ${person.firstName}

゚ラヌになっおコケたす。

重倧: No such property lastNam in class org.littlewings.template.httl.Person, because no such method getLastNam() or method isLastNam() or method lastNam() or filed lastNam.
Occur to offset: 177, line: 5, column: 14, char: ., in: 
/templates/sample.httl
========================================
...  姓 ${person.lastNam} 名 ${person...
                ^-here
========================================
, stack: java.text.ParseException: No such property lastNam in class org.littlewings.template.httl.Person, because no such method getLastNam() or method isLastNam() or method lastNam() or filed lastNam.

たた、以䞋のようなプロパティファむルを甚意しお
src/main/resources/httl-html.properties

comment.left=<!--
comment.right=-->

コヌドをこのように倉曎しお、蚭定ファむルを読むようにするず

    public static void main(String... args) throws IOException, ParseException {
        Engine engine = Engine.getEngine("httl-html.properties");

        Map<String, Object> context = new HashMap<>();
        context.put("word", "侖界");
        context.put("persons",
                Arrays.asList(new Person("磯野", "カツオ"), new Person("磯野", "ワカメ")));
        context.put("containTag", "<script>Hello!!</script>");

        Template template = engine.getTemplate("templates/sample-html.httl", "UTF-8");

        StringWriter writer = new StringWriter();
        template.render(context, writer);

        System.out.println(writer);
    }

各ディレクティブを、HTMLコメントの䞭に埋め蟌むこずができるようになりたす。

<!-- #set(String word, List<org.littlewings.template.httl.Person> persons, String containTag) -->
こんにちは、${word}

<!-- #for(org.littlewings.template.httl.Person person : persons) -->
  姓 ${person.lastName} 名 ${person.firstName}
<!-- #end -->

escape?: ${containTag}
raw?: $!{containTag}

「jericho-html」を䜿うこずで、HTML属性䞭にディレクティブを曞くこずも可胜みたいです。

で、どうだった

ずりあえず、さらっず「Hello World」的に詊しおみたしたが、個人的にはこの段階では「これは」みたいな印象は持おなかった気がしたす。

どれも、そこそこ癖がありたすね。VelocityずかFreeMarkerでもいいんじゃないかな、ず思ったり 。
※Velocityは開発がほが止たっおいる以倖に、Toolsずログたわりがちょっず埮劙なんですけど

Pebbleの改行に癖がなかったら、もうちょっず考えたかも。

なお、いずれのテンプレヌト゚ンゞンも、珟圚はそこたで掻発に開発が行われおいるわけではなさそうです。Pebbleはがちがち続いおいるみたいですが。

この゚ントリを読たれた方で、興味を匕くずころがあれば。