LoginSignup
6

More than 3 years have passed since last update.

Thymeleaf3で改行コードを改行タグに変換する独自ダイアレクトを作成

Last updated at Posted at 2018-10-13

要旨

Thymeleafで改行コードを含む文字列を出力してもHTMLソース内で改行されるだけで表示上は改行されない。
改行コードを改行タグに変換して文字列を出力する機能を持つ独自ダイアレクトを作成しth:textの代替ダイアレクトとして使用する。
なお、Thymeleaf2と3では実装が異なるが本稿ではThymeleaf3を扱う。

環境

  • Java SE 8 (jdk-8u181)
  • Maven 3.5.4
  • Spring Boot 2.0.5
  • Thymeleaf 3.0.9

概要

  • Processorクラス(操作を実際に記述するクラス)を実装する
  • Dialectクラス(Processorをまとめてダイアレクトとして定義するクラス)を実装する
  • Beanに設定する
  • テンプレートで使用する

詳細手順

Processorクラス(操作を実際に記述するクラス)を実装する

クラス定義

Thymeleafが提供しているProcessor系の抽象クラスからやりたいことに近いものを選んでそれを継承するクラスを作成する。
今回はStandard Dialectであるth:textに類似したものを作成するのでAbstractStandardExpressionAttributeTagProcessorを継承する。

public class BreakLineProcessor extends AbstractStandardExpressionAttributeTagProcessor {
  ...
}

コンストラクタ

必ずコンストラクタを作成し、当該Processorがどのように使われるかを定義する。

  private static final String ATTR_NAME = "brtext";

  public BreakLineProcessor(String dialectPrefix, int precedence) {
    super(TemplateMode.HTML, dialectPrefix, ATTR_NAME, precedence, true);
  }

なお、継承元クラスであるAbstractStandardExpressionAttributeTagProcessorのコンストラクタの各引数は以下のとおりである。

変数名 クラス 役割
1 templateMode TemplateMode(enum) テンプレートの種類(HTMLなど)
2 dialectPrefix String ダイアレクトのプレフィックス(コロンの左側)
3 attrName String ダイアレクトの属性名(コロンの右側)
4 precedence int Processor処理実行の優先順位(値が小さいほうが優先)
5 removeAttribute boolean Processor処理後に当該ダイアレクト属性を除去するか(trueなら除去する)

今回はプレフィックスと優先順位のみ外から受け取り、残りは固定値とした。

処理実行メソッド

抽象メソッドであるdoProcessをオーバーライドして処理を記述する。
AbstractStandardExpressionAttributeTagProcessordoProcessの場合はEL式を解釈した結果が引数のexpressionResultに格納されるので、これを改行コードごとに分割し、改行タグをはさみながら出力すればよい。
全体の処理の概要は以下の通り。

  1. 引数のcontextからIModelFactoryインスタンスを取得する。
  2. 取得したIModelFactoryインスタンスからIModelインスタンスを作成する。
  3. 作成したIModelインスタンスにHTMLに反映する要素(文字列やタグ)を順番に追加する。 なお、追加するインスタンスもIModelFactoryインスタンスから作成する。
  4. 引数のstructureHandlerIModelインスタンスを適用する。
  @Override
    protected void doProcess(ITemplateContext context, IProcessableElementTag tag, AttributeName attributeName,
            String attributeValue, Object expressionResult, IElementTagStructureHandler structureHandler) {
        // nullの場合は何も出力せず処理を終了
        if (expressionResult == null) return;

        IModelFactory factory = context.getModelFactory();
        IModel model = factory.createModel();

        ITemplateEvent brTag = factory.createOpenElementTag("br");

    // 改行コードで分割
        String[] lines = expressionResult.toString().split("\r\n|\r|\n", -1);
        for (String line : lines) {
      // <br>+1行分の文字列をmodelに追加
            model.add(brTag);
            model.add(factory.createText(line));
        }
        // 先頭の<br>タグを除去
        model.remove(0);

        structureHandler.setBody(model, false);

Dialectクラス(Processorをまとめてダイアレクトとして定義するクラス)を実装する

クラス定義

Thymeleafが提供しているDialect系の抽象クラスやインターフェースから作成したダイアレクト処理に合致するもの選んでそれを継承(実装)するクラスを作成する。
今回はProcessorにより本処理を実装したので抽象クラスAbstractProcessorDialectを継承する。

public class KurukuruzDialect extends AbstractProcessorDialect {
  ...
}

コンストラクタ

必ずコンストラクタを作成し、当該Dialectがどのように使われるかを定義する。

  private static final String NAME = "kurukuruz original dialect";
  private static final String PREFIX = "krz";

  public KurukuruzDialect() {
          super(NAME, PREFIX, StandardDialect.PROCESSOR_PRECEDENCE);
    }

なお、継承元クラスであるAbstractProcessorDialectのコンストラクタの各引数は以下のとおりである。

変数名 クラス 役割
1 name String ダイアレクトの名前(?), 特に使うことはない
2 prefix String ダイアレクトのプレフィックス, 後述のProcessor登録メソッドの引数になる
3 processorPrecedence int Processor処理実行の優先順位に使用する数値

Processor登録

AbstractProcessorDialectの抽象メソッドであるgetProcessors内でダイアレクトに登録する各ProcessorのインスタンスをSetとしてまとめ、返却する。

  @Override
  public Set<IProcessor> getProcessors(String dialectPrefix) {
    Set<IProcessor> processors = new HashSet<>();

    processors.add(new BreakLineProcessor(dialectPrefix, getDialectProcessorPrecedence()));

    return processors;
  }

Beanに設定する

@Beanアノテーションが有効な適当なクラスで以下のようにBean定義メソッドを作成し、Dialectインスタンスを登録する。

 @Bean
 KurukuruzDialect kurukuruzDialect() {
   return new KurukuruzDialect();
 }

実行結果

  • HTMLテンプレート
<body>
<div krz:brtext="${text1}"></div>
<div krz:brtext="${text2}"></div>
<div krz:brtext="${text3}"></div>
<div krz:brtext="${text4}"></div>
<div krz:brtext="${text5}"></div>
</body>
  • 表示対象のModel
model.addAttribute("text1", "改行無し");
model.addAttribute("text2", "CRLF改行\r\nあり\r\n\r\n↑2回連続改行");
model.addAttribute("text3", "CR改行\rあり");
model.addAttribute("text4", "LF改行\nあり");
model.addAttribute("text5", null);

↓↓↓↓↓

  • 出力結果(html)
<body>
<div>改行無し</div>
<div>CRLF改行<br>あり<br><br>↑2回連続改行</div>
<div>CR改行<br>あり</div>
<div>LF改行<br>あり</div>
<div></div>
</body>
  • 出力結果(画面表示)

画面表示.png

参考サイト

Thymeleafで改行コードをhtmlの改行タグに変換して出力する - Qiita
Thymeleaf3で独自のDialectを作る - Qiita
thymeleaf3でDialectを実装する - abcdefg.....

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6