xfy/xvcd で java ファンクションを1つ追加するための作業が面倒だ。
そこでこんな方法を思いついた。
my:eval(functionname, arg0, arg1, ...)形式で eval() という関数を1つだけ定義することにする というものだ。
第一引数を関数名とすることにして、eval 関数の call() メソッド中で分岐させるのだ。
これなら、新しい関数を追加するときは、 call() 中での分岐を増やしていくだけで済む。
その分岐も 実際には if ... elseif ... の羅列でなく、hash で管理すれば分岐数増加にも対応しやすくなる。
実際のコード例はこんな感じになる。ここでは
eval('setLocale', lang), eval('getLocale') として、Locale を set/get する関数を作ってみている。
/** example:eval(funcname, p0, p1...) */
public class EvalFunction implements Function {
private static final Map<String, Function> funcSet = init();
private static final Map<String, Function> init() {
final Map<String, Function> map = new HashMap<String, Function>();
map.put("list", new ListFunction());
map.put("setLocale", new SetLocaleLanguageFunction());
map.put("getLocale", new GetLocaleLanguageFunction());
return map;
}
private Function getInstance(final Arguments args) {
final Value v = args.getArgument(0);
final String funcName = v.getAsString();
Function func = funcSet.get(funcName);
assert func != null;
return func;
}
public Value call(final Arguments args, final Context context)
throws ValueException {
final Value val = this.getInstance(args).call(args, context);
return val;
}
/* ============================================ */
/**
* 処理できる関数一覧を返す。
*/
static class ListFunction implements Function {
public Value call(final Arguments args, final Context context)
throws ValueException {
final StringBuilder sb = new StringBuilder();
for (final Map.Entry<String, Function> e : funcSet.entrySet()) {
sb.append(e + " ");
}
return new StringValue(sb.toString());
}
}
/**
*/
static class GetLocaleLanguageFunction implements Function {
public Value call(final Arguments args, final Context context)
throws ValueException {
FunctionUtility.checkCount(context, args, 1);
final Locale locale = Locale.getDefault();
return new StringValue(locale.getLanguage());
}
}
/**
*/
static class SetLocaleLanguageFunction implements Function {
public Value call(final Arguments args, final Context context)
throws ValueException {
.. 省略 ...
}
}
}
呼び出し側の xvcd はこんな感じ。
<td>
Current Locate:
<xvcd:value-of select="xvcd:user-data('locale')" />
</td>
<td>
<ctrl:trigger>
<ctrl:label>set Locale:en</ctrl:label>
<xvcd:action event="ctrl:triggered">
<instruction:message-box>
Change locale from
<xvcd:value-of select="example:eval('setLocale', 'en')" />
</instruction:message-box>
<xvcd:set-user-data name="locale"
value="{example:eval('getLocale')}" />
</xvcd:action>
</ctrl:trigger>
</td>
... 省略 ...
Arguments のなかの最初の要素を取り除く方法があれば、既存の方法で書いたFunction の call メソッドをそのまま呼び出すようにもできるのだが。
この関数名自体を argument にしてしまう方法は、xfy/xvcd の instructionを書く場合にも流用できると思う。
最近のコメント