Thymeleaf with SpringBootで独自のUtilityを作る

# 概要

YearMonthをサンプルに、独自のUtilityを作成する方法についてまとめる。
最終的に `${#yearmonths.format(hogeYm,'yyyy-MM')}` で使えるようにする。

## 補足（追記）

Thymeleafにはデフォルトで日時APIを操作するUtilityが用意されていないので、サンプルにはYearMonthを利用しています。
ただし公式の追加モジュール（thymeleaf-extras-java8time）はあるようで、日時APIに関してはこちらを利用するほうがよさそうです。
[ThymeleafとDate and Time API](http://9q6.net/note/thymeleaf-extras-java8time)


# 前提

- thymeleaf3
- spring-boot 1.4

# Utility本体の作成

実装はThymeleafのDatesを参考にしました。

```java
public final class YearMonths {
    public String format(final YearMonth target, final String pattern) {
        if (target == null) {
            return null;
        }
        try {
            // org.thymeleaf.util.Validateを使用
            Validate.notEmpty(pattern, "Pattern cannot be null or empty");
            return target.format(DateTimeFormatter.ofPattern(pattern));
        } catch (final Exception e) {
            throw new TemplateProcessingException(
                    "Error formatting date with format pattern \"" + pattern + "\"", e);
        }
    }
}
```

# 独自Dialectの作成

日時Utilityを管理するDialectを作成します

```java
public class YearMonthDialect implements IExpressionObjectDialect {
    // Thymeleafで使用したい名前
    private static final String YEAR_MONTH_EXPRESSION_NAME = "yearmonths";

    // 名前管理するSet
    private static final Set<String> ALL_EXPRESSION_NAMES = new HashSet<String>(){
                {add(YEAR_MONTH_EXPRESSION_NAME);}
            };

    @Override
    public IExpressionObjectFactory getExpressionObjectFactory() {
        return new IExpressionObjectFactory() {
            @Override
            public Set<String> getAllExpressionObjectNames() {
                return ALL_EXPRESSION_NAMES;
            }

            @Override
            public Object buildObject(IExpressionContext context, String expressionObjectName) {
                // 独自Utilityのインスタンスと名前を紐付け
                if(expressionObjectName.equals(YEAR_MONTH_EXPRESSION_NAME)){
                    return new YearMonths();
                }
                return null;
            }

            @Override
            public boolean isCacheable(String expressionObjectName) {
                // 必要に応じて実装
                return false;
            }
        };
    }

    @Override
    public String getName() {
        return "YearMonth";
    }
}
```

# DIコンテナへの登録

SpringBootで利用可能にするには、DialectをDIコンテナへ登録しておく必要があります。
専用のConfigurationを作って登録しておく例を書いておきます。

```java
@Configuration
public class ThymeleafConfiguration {
    @Bean
    public DateTimeDialect DateTimeDialect() {
        return new DateTimeDialect();
    }
}
```

# テンプレートでの呼び出し

下記のように呼び出せるようになります。

`${#yearmonths.format(hogeYm,'yyyy-MM')}`

# まとめ

1. Utilityを作成する
1. Utilityを管理するDialectを作成する
1. DialectをDIコンテナに登録する

# 参考

 - [Thymeleaf 3.0を試す その３ ユーティリティを作る](http://d.hatena.ne.jp/shin/20160512/p1)
 - [thymeleafで Utility Object （Railsでいうところの独自View Helper）を作成する](http://blog.beaglesoft.net/entry/%3Fp%3D796)
 - [org.thymeleaf.expression.Dates.java](http://www.thymeleaf.org/apidocs/thymeleaf/3.0.1.RELEASE/org/thymeleaf/expression/Dates.html)
 - [org.thymeleaf.expression.Dates.java(GitHub)](https://github.com/thymeleaf/thymeleaf/blob/3.0-master/src/main/java/org/thymeleaf/expression/Dates.java)
 - [Thymeleafチュートリアル](http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-b-expression-utility-objects)
 - [Java8の日時APIはとりあえずこれだけ覚えとけ](http://qiita.com/tag1216/items/91a471b33f383981bfaa)