パッケージJava製品開発担当の大です。こんにちは。毎日暑いですね。

前回に引き続き、FreeMarker Template Language(以下 FTL)について書きます。

今回は、名前空間と変数のスコープに関するお話です。

名前空間

テンプレートの数が増え、開発規模が少し大きくなってくると、再利用可能なマクロや共通で使いたい変数などをまとめておいて、各テンプレートから参照したくなります。そのような場合に利用可能なディレクティブが、includeimportです。

共通部分をまとめたファイル(common.ftl):

<#macro greet name>
こんにちは、${name}さん!
</#macro>
<#assign url="http://www.hos.co.jp/">

includeを使って参照する場合は、以下のような感じになります。

<#include "common.ftl">
${url}
<@greet name="大" />

実行結果:


http://www.hos.co.jp/

こんにちは、大さん!

しかし、includeでは困ったことが起きる場合があります。共通部分をまとめたFTLファイル中で使われている変数名やマクロの名前が、呼び出し側でも使われている場合、上書きされてしまうのです。

<#assign url="http://example.com/">
include前: ${url}
<#include "common.ftl">
include後: ${url}

実行結果:

include前: http://example.com/
include後: http://www.hos.co.jp/

importを使えば、共通部分をまとめたFTLファイルを、呼び出し側の名前空間とは別の名前空間に読み込みますので、上書きされません。

<#assign url="http://example.com/">
import前: ${url}
<#import "common.ftl" as hoge>
import後: ${url}

実行結果:

import前: http://example.com/
import後: http://example.com/

アクセスするには、import時に「as 名前」で指定した名前を使ってアクセスします。

<#import "common.ftl" as hoge>
${hoge.url}
<@hoge.greet name="大" />

実行結果:


http://www.hos.co.jp/

こんにちは、大さん!

変数のスコープ

これまでこの連載中では、変数の宣言にはassignディレクティブとlocalディレクティブを使用してきました。変数は、このほかにglobalディレクティブで宣言することもできます。また、繰り返しを行うlistディレクティブでリストの各要素を参照するループ変数というものがあります。これらの違いは以下のようになっています。

種類 宣言 スコープ
グローバル変数 globalディレクティブで宣言 すべての名前空間から参照可能(どの名前空間にも含まれません)
(プレーンな)変数 assignディレクティブで宣言 名前空間内
ローカル変数 localディレクティブで宣言 マクロ/関数内
ループ変数 listディレクティブで宣言 ループ内

以上を踏まえて、宣言した変数がどのように見えるか検証してみましょう。

main.ftl:

<#global x = "グローバル1" />
01. ${x}
<#assign x = "プレーン1" />
02. ${x}
<#import "common.ftl" as common />
<@common.test />
11. ${x}
12. ${common.x}

common.ftl:

<#macro test>
	03. ${x}
	<#global x = "グローバル2">
	04. ${x}
	<#assign x = "プレーン2">
	05. ${x}
	<#local x = "ローカル1">
	06. ${x}
	<#list ["ループ1"] as x>
		07. ${x}
		<#local x = "ローカル2">
		08. ${x}
		<#assign x = "プレーン3">
		09. ${x}
	</#list>
	10. ${x}
</#macro>

実行結果:

01. グローバル1
02. プレーン1
	03. グローバル1
	04. グローバル2
	05. プレーン2
	06. ローカル1
		07. ループ1
		08. ループ1
		09. ループ1
	10. ローカル2
11. プレーン1
12. プレーン3
  1. グローバルに宣言したxです。
  2. 同じ名前でプレーンなxを宣言すると、グローバルなxが隠れます。
  3. インポートされたcommon名前空間では、グローバルなxが見えています。
  4. グローバルなxを上書きしてみます。
  5. もちろんここでもプレーンなxを宣言すると、グローバルなxが隠れます。
  6. ローカルなxを宣言すると、今度はプレーンなxが隠れます。
  7. ループ変数xを宣言すると、ローカルなxが隠れます。
  8. ローカルなxを上書きしてみますが、隠れたままです。
  9. プレーンなxを上書きしてみますが、隠れたままです。
  10. ループを抜けたので、ローカルなxが見えるようになります。08で上書きされた値になっています。
  11. メインの名前空間では、相変わらずプレーンなxが見えます。
  12. commonxを見ると、09で上書きされた値になっています。

同名の変数の宣言により隠れてしまった変数を参照するために、さまざまな「スペシャル変数」が用意されています。たとえば、グローバルのxの値を参照したい場合は、以下のように書くことができます。

${.globals.x}