Thymeleafで改行コードをhtmlの改行タグに変換して出力する

Thymeleafで改行を含んだ文字列を __th:text__ 等で出力しても、改行は画面に反映されません。
改行コードを\<br/>タグへ変換してあげる必要があります。

# 環境
* Java 1.8
* spring-boot 1.5.6.RELEASE
* Thymeleaf 2.1.5

# ポイント

* 改行コード（\r, \n, \r\n）を\<br/>に変換する。
* 変換した\<br/>タグ以外のhtmlタグはエスケープする。


# 実装

2通りの方法があるので紹介します。

## ① View上で実装

既存のThymeleaf構文のみを使用。
事前の設定等はいらないので気軽に使用可能。

```html
<th:block th:if="${sentence}">
    <th:block th:each="str, stat : ${sentence.split('\r\n|\r|\n', -1)}">
        <th:block th:text="${str}"/>
        <br th:if="${!stat.last}"/>
    </th:block>
</th:block>
```

出力したい文字を改行コードで分割して __th:each__ でループ。
中身を __th:text__ で出力しつつ、間に\<br/>を挿入しているだけである。

## ② 独自ダイアレクトを定義

上記の実装は手軽だが、複数個所で使用するとなると煩雑になってしまいます。
そこで独自ダイアレクトを作成してViewから参照できるようにします。

__変換処理とテキスト出力するプロセッサ__

```java:TextLineProcessor.java
import org.thymeleaf.Arguments;
import org.thymeleaf.Configuration;
import org.thymeleaf.dom.Element;
import org.thymeleaf.processor.attr.AbstractUnescapedTextChildModifierAttrProcessor;
import org.thymeleaf.standard.expression.IStandardExpression;
import org.thymeleaf.standard.expression.IStandardExpressionParser;
import org.thymeleaf.standard.expression.StandardExpressionExecutionContext;
import org.thymeleaf.standard.expression.StandardExpressions;
import org.unbescape.html.HtmlEscape;


/**
 * 改行コードをbrタグに変換するプロセッサ
 */
public class TextLineProcessor extends AbstractUnescapedTextChildModifierAttrProcessor {

    public static final int ATTR_PRECEDENCE = 1450;
    public static final String ATTR_NAME = "textbr";


    protected TextLineProcessor() {
        super(ATTR_NAME);
    }


    @Override
    public int getPrecedence() {
        return ATTR_PRECEDENCE;
    }


    /**
     * 出力する文字列を返す
     *
     * @param arguments
     * @param element
     * @param attributeName
     * @return
     */
    @Override
    protected final String getText(
            final Arguments arguments, final Element element, final String attributeName) {

        String text = getAttributeObjectString(arguments, element, attributeName);

        // htmlエスケープ処理
        text = HtmlEscape.escapeHtml4Xml(text);

        // 改行コードをbrタグへ置換
        return text.replaceAll("\r\n|\r|\n", "<br/>");
    }


    /**
     * 属性に指定された変数を文字列として取得する
     *
     * @param arguments
     * @param element
     * @param attributeName
     * @return
     */
    protected String getAttributeObjectString(
            final Arguments arguments, final Element element, final String attributeName) {

        final String attributeValue = element.getAttributeValue(attributeName);

        final Configuration configuration = arguments.getConfiguration();
        final IStandardExpressionParser expressionParser = StandardExpressions.getExpressionParser(configuration);

        final IStandardExpression expression = expressionParser.parseExpression(configuration, arguments, attributeValue);

        final Object result =
                expression.execute(configuration, arguments, StandardExpressionExecutionContext.UNESCAPED_EXPRESSION);

        return (result == null ? "" : result.toString());

    }
}
```

__th:text__ と __th:utext__ の処理を参考に実装しました。
簡単に処理を説明すると・・・
　1. htmlタグのエスケープ処理
　2. 改行コードを\<br/>タグへ置換
　3. それをエスケープせずに画面出力

__ダイアレクトの登録__
作成したプロセッサをダイアレクトに登録する。

```java:CustomDialect.java
import org.thymeleaf.dialect.AbstractDialect;
import org.thymeleaf.processor.IProcessor;

import java.util.HashSet;
import java.util.Set;


/**
 * 独自定義したダイアレクトを登録する
 */
public class CustomDialect extends AbstractDialect{

    static final String DIALECT_PREFIX = "ex";

    @Override
    public String getPrefix() {
        return DIALECT_PREFIX;
    }


    @Override
    public Set<IProcessor> getProcessors() {
        final Set<IProcessor> processors = new HashSet<IProcessor>();
        processors.add(new TextLineProcessor());
        return processors;
    }
}

```

__Thymeleaf設定__

```java:ThymeleafConfiguration.java
import com.ushidatmhr.thymeleaf.CustomDialect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ThymeleafConfiguration {

    @Bean
    CustomDialect myDialect() {
        return new CustomDialect();
    }

}
```

__使い方__

```html
<p ex:textbr="${sentence}" />
```