設計

忘れないようにメモ。

一々エスケープしないで済むように、
HereDocumetn中では、特殊文字(\,",\nなど)も普通の文字として扱う。
特殊文字として扱いたい場合には、Javaコードとして埋め込む(<%= "\n" %>など)。

スクリプトレットの有効・無効モードは、以下の3種類とする。
※悩みの種。
無名インナークラスから、final宣言なしの変数を参照できれば、全有効・全無効の2モードで済むんだけど。
第3のクォーテーション記号があれば、3モードでもキレイなんだけど。
いっそ、全無効のモードを削ってしまったほうが良いだろうか?

  1. 全無効

一番単純なモード。変数も制御構造も使えない。

記法は、
シングルクォーテーションのトリプルクォートと、
シングルクォーテーションで終端文字列をくくったheredocの2種類。

// シングルクォーテーションのトリプルクォート
String tripleQuotes = '''AAA
BBB''';

// シングルクォーテーションで終端文字列をくくったheredoc
String heredoc = <<<'EOS'
AAA
BBB
EOS;
  1. 有効(制御構造は無効)

記法は、
ダブルクォーテーションのトリプルクォートと、
ダブルクォーテーションで終端文字列をくくったheredocの2種類。

// ダブルクォーテーションのトリプルクォート
String tripleQuotes = '''AAA
<%-- コメントアウト
BBB
--%>
<%= ccc %>''';

// ダブルクォーテーションで終端文字列をくくったheredoc
String heredoc = <<<"EOS"
AAA
<%-- コメントアウト
BBB
--%>
<%= ccc %>
EOS;
  1. 全有効

記法は、終端文字列がくくられていないheredocのみ。

// 終端文字列がくくられていないheredoc
String heredoc = <<<EOS
AAA
<%-- コメントアウト
BBB
--%>
<% if (null != ccc) { %><%= ccc %><% } %>
EOS;
    • 制御構造(if,forなど)を無効とする理由

制御構造を含む場合、Javaコード上は無名インナークラス(無名内部クラス:anonymous inner class)とする必要がある。
インナークラスなので、参照する変数はfinal宣言が必要。finalでないと、コンパイルエラーになる。

String sql = <<<SQL
SELECT *
    FROM aaa
    WHERE bbb = ?
<% if ((null != ccc) && (0 < ccc.length())) { %>
    AND ccc = ?
<% } %>
SQL;

↓プリコンパイル

String sql = new Object() {
    public String toString() {
        StringBuffer buf = new StringBuffer("");
        buf.append("SELECT *");
        buf.append("    FROM aaa");
        buf.append("    WHERE bbb = ?");
        
        if ((null != ccc) && (0 < ccc.length())) {
            buf.append("    AND ccc = ?");
        }
        return buf.toString();
    }
}.toString();

heredoc中の改行をJavaの文字列としてどう扱うか(実行環境の改行文字、\n、\r、改行なし、等)や、
フィルタリング機能(後述)で使うメソッド等を、設定ファイルに記述する。
終端文字列ごと設定できるので、デフォルト設定を上書きすることもできる。

この機能は、ResourceBundleの国際化機能をそのまま利用する。

hdj.properties デフォルトの設定ファイル
hdj_SQL.properties 終端文字列 SQL の設定ファイル。デフォルトの設定を継承
hdj_SQL_ORACLE.properties 終端文字列 SQL_ORACLE の設定ファイル。SQLの設定を継承
hdj_SQL_ORACLE_10G.properties 終端文字列 SQL_ORACLE_10G の設定ファイル。SQL_ORACLEの設定を継承
  • フィルタリング機能

JSPスクリプトレットに、HTMLの特殊文字エスケープして出力してくれる記法があれば、
カスタムタグなしでも、簡単なHTMLの表示には十分だと思うので、
heredocjには、出力をJavaのメソッドでフィルタリングする記法を追加。

String str = <<<"AAA"
<%--  --%>
<%af[xxx]= aaa, bbb %>
<%nf[xxx]= name=aaa, value=bbb %>
"AAA";

hdj_AAA.properties

ArrayFilter[xxx]= Aaa.xxx({0},{1})
NamedFilter[xxx]= Aaa.xxx({name},{value})
  • HereDocumentのネスト

HereDocumentのスクリプトレット内に書かれたHereDocumentについて。
トリプルクォーテーションの場合は、外側のHereDocumentのプリコンパイル設定を引き継ぐべきだと思うが、
終端文字列を指定されたらどうしよう?
どのようにすべきか悩む=使う人にとっても分かりづらい、となるはずなので、
ネストの場合、終端文字列指定のHereDocumentは許さない?