Guetamale Template Framework

Version 0.5.2
2005.06.13
NISHIMOTO Keisuke

1 概要

Webアプリケーションやソースの自動生成など,テキスト生成をする機会は世の中にたくさんあります.java.io.Writerなどで直接テキストを書き出せば目的を達成できますが,この場合,プログラムソースと文書が混在してしまい,可読性やメンテナンスが低下してしまいます.

この場合は,元となるテンプレートテキストと,プログラムを分離して管理した方が何かと便利です.

それらのテキスト生成を,テンプレートとコンテキスト(文脈)によって生成する仕組みをこのフレームワークでは提供します.

2 仕様

2.1 動作環境

以下の環境を前提にしています。

表1.動作環境
SDK Java 2 SDK 1.2 以上
OS Windows, Linux など 上記 SDK が動作する OS

全環境を試していないので、動作しない環境があるかもしれません。

2.2 機能

構成と内容

テンプレートファイル
テキスト生成の元となるテンプレート(型)
TemplateDocumenParser
テンプレートを内部オブジェクト(TemplateDocument)に変換するParser
TemplateDocument
テキスト書き出しをする内部オブジェクトで,TemplateContextと組みで動作する
TemplateContext
テンプレートに挿入する文書要素(リソース)を提供する
目的のテキスト
目的とするテキストファイルまたはストリーム.TemplateContextによってこの内容は動的に変わる

基本機能

表2.主な機能
機能 備考
テンプレート(ファイル) 文書要素の挿入 コンテキストリソースの値を挿入できます。
テキスト分岐(真偽) コンテキストリソースの値が"true"または"false"の時に、文書要素を差し替えることができます。
テキスト分岐(ケース) コンテキストリソースの値を条件として,文書要素を差し替えることができます。
テキストの繰り返し コンテキストリソースの値が,"true"である間、文書要素の繰り返しができます。
コンテキスト(TemplateContext) 挿入要素のマッピング そのコンテキストによって格納されたリソースの値を、挿入要素として返します.リソースの値は,Propertiesなどの固定的なものやコンテキスト内で動的に作られた値等を返せます.

2.3 インストール

lib/guatemala.jar, lib/lex.jar を 環境変数 CLASSPATH などに通してください。

2 ダウンロード

以下のページからダウンロードできます。

http://cappuccino.jp/keisuken/java/guatemala/guatemala-0.5.2.tar.gz

3 ライセンス

ライセンス

4 連絡先

質問や要望などがありましたら keisuken atmark cappuccino.ne.jp まで内容を書いてお送りください。

Appendix

A1 APIリファレンス

APIリファレンス

A2 テンプレートファイル仕様

文法

TAG_NORMAL           ::= '%%'
TAG_TRIM_SPACE       ::= '!!'
TAG_TRIM_LINE        ::= '##'
TAG_TRIM_WHITE_SPACE ::= '@@'
TAG_DESC ::= (TAG_NORMAL | TAG_TRIM_SPACE | TAG_TRIM_LINE | TAG_TRIM_WHITE_SPACE)
TAG_SEPARATOR ::= ':'
LABEL ::= Character.isJavaIdentifierStart Character.isJavaIdentifierPart*
VALUE ::= '"' [^"] '"' | Character.isJavaIdentifierPart*
TAG ::= TAG_DESC LABEL (':' (LABEL | VALUE))? TAG_DESC

インクルード

構文
%%include:"{パス}"%%
%%include:"foo/bar.tpl"%%

コメント

構文
%%-コメント-%%
%%- ここからユーザーリストの出力 -%%

繰り返し

構文
%%while:{リソース名}%% {文} %%endwhile%%
%%while:userList%%
ID: %%id%%, Name: %%name%%
%%endwhile%%

if 分岐

構文
%%if:{リソース名}%% {真時の文} %%else%% {偽時の文} %%endif%%
%%if:isMember%%
あなたはメンバーです。
%%else%%
あなたはメンバーではありません。
%%endif%%

switch 分岐

構文
%%switch:{リソース名}%% %%case:{ケース名}%% {文} %%endcase%% %%default%% {文} %%enddefault%% %%endswitch%%
%%switch:memberType%%
  %%case:admin%%管理者%%endcase%%
  %%case:guest%%ゲスト%%endcase%%
  %%default%%メンバー%%enddefault%%
%%endswitch%%

トリミング

構文
テンプレート要素タグは、前後のシグネチャにより前後の文字列をトリミングできます。
表1.タグシグネチャとトリミング
タグシグネチャ トリミング
%% なし
!! 改行までのスペースを取り除く
## 改行までのスペースと1行分の改行を取り除く
@@ 改行, スペースを取り除く
表2.トリミングによる実行例
右側 左側
テンプレート 出力 テンプレート 出力
abc

__%%foo%%__

def
abc

__{foo}__

def
abc

__%%foo%%__

def
abc

__{foo}__

def
abc

__!!foo%%__

def
abc

{foo}__

def
abc

__%%foo!!__

def
abc

__{foo}

def
abc

__##foo%%__

def
abc
{foo}__

def
abc

__%%foo##__

def
abc

__{foo}
def
abc

__@@foo%%__

def
abc{foo}__

def
abc

__%%foo@@__

def
abc

__{foo}def
注釈: '_': スペース, "{foo}": 挿入される文字列

A3 サンプル1 (Hello, world)

実行結果

Hello, world!

テンプレート

%%message%%

コンテキスト設定ファイル

context.class=HelloWorldContext
message=Hello, world!

コンテキストクラス

public class HelloWorldContext implements TemplateContext {

  private Map elements;

  pubilic void init(Properties config) {
    elements = config;
  }

  public Object getElement(String name) {
    return elements.get(name);
  }
} 

A4 サンプル (時刻表示)

実行結果

こんにちは
ただ今2005/05/06 15:05です。

テンプレート

%%switch:time%%
%%case:"goodmorning"%% %%goodmorning%% %%endcase%%
%%case:"goodafternoon"%% %%goodafternoon%% %%endcase%%
%%case:"goodevening"%% %%goodevening%% %%endcase%%
%%endswitch%%
ただ今%%date%%です。

コンテキスト設定ファイル

context.class=DateContext
goodmorning=おはようございます
goodafternoon=こんにちは
goodeveninig=こんばんは
message=Hello, world!

コンテキストクラス

public class HelloWorldContext implements TemplateContext {

  private Map elements;
  private Calendar calendar = Calendar.getInstance();
  private Date date = new Date();
  private DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm");

  pubilic void init(Properties config) {
    elements = config;
    Calendar calendar = Calendar
    elements.put("date", new Object() {
      public String toString() {
        return dateFormat.format(date);
      }
    });
    elements.put("time", new Object() {
      public String toString() {
        calendar.setTime(date);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        if(hour >= 4 && hour < 10) {
          return "goodmorning";
        } else if(hour >= 10 && hour < 17) {
          return "goodafternoon";
        } else {
          return "goodevening";
        }
      }
    });
  }

  public Object getElement(String name) {
    return elements.get(name);
  }
} 

A5 サンプル (シンプルループ)

実行結果

1
2
3
4
5
6
7
8
9
10

テンプレート

%%while:loop%%
%%i%%
%%endwhile%%

コンテキスト設定ファイル

context.class=SimpleLoopContext
start=1
end=10

コンテキストクラス

public class SimpleLoopContext implements TemplateContext {

  private Map elements;
  private int start;
  private int end;
  private int i;

  pubilic void init(Properties config) {
    elements = config;
    start = getIntProperty("start") - 1;
    end = getIntProperty("end");
    elements.put("i", new Object() {
      public String toString() {
        return Integer.toString(i);
      }
    });
    elements.put("loop", new Object() {
      public String toString() {
        i++;
        return new Boolean(i <= end);
      }
    });
  }

  public Object getElement(String name) {
    return elements.get(name);
  }

  private int getIntProperty(String name) {
    try {
      String value = elements.get(name).trim();
      return Integer.parseInt(value);
    } catch(Exception e) {
      return 0;
    }
  }
} 

A6 サンプル (テンプレートの生成とドキュメントの生成)

// Configファイル読み込み
Properties config = new Properties();
FileInputStream configIn = null;
try {
  configIn = new FileInputStream("hello.props");
  config.load(configIn);
} catch(IOException e) {
  ...
} finally {
  if(configIn != null) {
    configIn.close();
  }
}

// TemplateDocumentFactory生成
TemplateDocument document = null;
try {
  document = TemplateDocumentParser.parse("hello.tpl");
} catch(TemplateException e) {
  ...
} catch(IOException e) {
  ...
}

// 必要なContextを設定
Properties configInstance = (Properties)config.clone();
configInstance.put(..., ...);

// Document生成
FileWriter writer = null;
try {
  writer = new FileWriter("hello.txt");
  document.write(configInstance, writer);
} catch(IOException e) {
  ...
} finally {
  if(writer != null) {
    writer.close();
  }
}

A7.履歴

A5.ToDo