(2009/08/25修正: Enumだけの問題ではなく普通のBeanでも現象が発生したため、文言を若干修正しました)
OGNLのSelecting From Collections式(以下、SFC式)は、コレクションから条件にマッチする要素だけを抽出する式です。
みんな大好き内部イテレータってヤツですね。
ところがこのSFC式、引数のプロパティをSFC式の内部で使用する場合には、奇妙な動作をする場合があります。
なお、動作環境はStruts-2.1.6アンドOGNL-2.7.3です。
次のような例な考えてみましょう。
47都道府県を表すEnumのリストから、指定したキー名を持つものだけを抽出する例です。
Enumクラスはこんな感じ。何の変哲もないEnumクラスです。
public enum Pref { HOKKAIDO, AOMORI, ..., OKINAWA; }
外部イテレータでやってみる
まずは外部イテレータでやってみます。
<%-- Pref列挙型の値の一覧。型で言うと List<Pref> 相当 --%> <s:set var="#prefs" value="..." /> <%-- 選別する都道府県のキー名 --%> <s:set var="#selectionPrefKeys" value="{ 'TOKYO', 'OSAKA', 'KYOTO' }" /> <s:set var="#selectedPrefs" value="{}" /> <s:iterator var="pref" value="#prefs"> <s:if test="#selectionPrefKeys.contains(#pref.name())"> <s:set value="#selectedPrefs.add(#pref)" /> </s:if> </s:iterator> <s:property value="#selectedPrefs" />
実行結果は次の通りです。
{ TOKYO, OSAKA, KYOTO }
内部イテレータでやってみる
つぎに、この例をSFC式を使って書き直してみましょう。
<%-- Pref列挙型の値の一覧。型で言うと List<Pref> 相当 --%> <s:set var="#prefs" value="..." /> <%-- 選別する都道府県のキー名 --%> <s:set var="#selectionPrefKeys" value="{ 'TOKYO', 'OSAKA', 'KYOTO' }" /> <%-- #selectionPrefKeysがキー名を含むEnum値のみ選択する --%> <s:set var="selectedPrefs" value="#prefs.{? #selectionPrefKeys.contains(#this.name()) }" /> <s:property value="#selectedPrefs" />
実行結果は次の通りです。
{}
んん???空のリストが表示されていますね。
というわけで、原因は調べていませんが、動きません。
それでも内部イテレータじゃなきゃヤダモン!!
これも理由は調べていませんが、#selectionPrefKeys.containsに直接Enum値を渡すのではなく、ラムダ式を経由するとSFC式でも動きます。
<%-- 都道府県を表すEnumのリスト。(HOKKAIDO, AOMORI, ..., OKINAWA) --%> <s:set var="#prefs" value="..." /> <%-- 選別する都道府県のキー名 --%> <s:set var="#selectionPrefKeys" value="{ 'TOKYO', 'OSAKA', 'KYOTO' }" /> <%-- ラムダ式を経由している --%> <s:set var="selectedPrefs" value="#prefs.{? (:[#selectionPrefKeys.contains(#this.name())])(#this) }" /> <%-- これでもOK --%> <%-- <s:set var="selectedPrefs" value="#prefs.{? (:[#selectionPrefKeys.contains(#this)])(#this.name()) }" /> --%> <s:property value="#selectedPrefs" />
実行結果は次の通りです。
{ TOKYO, OSAKA, KYOTO }
ふーむ、なんでなんだろう。まあOGNLのソースを読めばわかるのでしょうが。
ぶっちゃけOGNLのソースは少しアレなので、読むモチベーションが不足気味な今日この頃。