引き続き、集合型の検証ついて考える。

集合型を扱う場合、そこに含まれる要素数だけでなく、
各要素が取る値も検証しなければならない場合がある。

Validator 設定の <field> 要素で指定するプロパティ名は、
集合型の要素を直接指定することが可能である。

例えば、プロトコルの場合プロパティ名は rule.protocols。
このプロパティが示す値は String[] 型なので、
最初の要素は、rule.protocols[0]、
次の要素は rule.protocols[1] で表現することができる。

これら要素は、String 型の値となるので、
次のようにして、自由にルールを適用することができる。

            <field property="rule.protocols[0]"
                    depends="required,mask">

                <var>
                    <var-name>mask</var-name>
                    <var-value>^(?:tcp|udp)$</var-value>
                </var>

                <arg name="required" position="0"
                        key="property.FilterRule.protocols" />
                <arg name="mask" position="0"
                        key="property.FilterRule.protocols" />

            </field>

ただ、この方法で制約を記述するのには大きな問題がある。
それは、配列の要素番号が条件に含まれるため、
事前に要素数が分かっていなければならないということだ。

例えば、プロトコルに対して上記の方法で制約を書く場合、
プロトコルは最大 2 つの要素を含むことができるので、
rule.protocols[0]、rule.protocols[1] ……と、
中身が同じ 2 つの制約条件を書かねばならなくなる。

これは、配列要素数が多くなると、
制約条件が冗長になってしまうということだ。
もし、rule.protocols が無限の要素を受け入れる場合、
制約条件を書くことすらできなくなるだろう。

さて、Validator 設定はこのようなケースを想定しており、
<field> 要素の indexedListProperty 属性を使うことで、
可変長の配列やコレクションの要素に対して、
一括して同じルールを適用することができる。

では、書いてみよう。

========== /WEB-INF/validation.xml ==========
(…省略…)
            <!-- 各プロトコルは必須で tcp か udp -->
            <field indexedListProperty="rule.protocols"
                    property="" depends="required,mask">

                <var>
                    <var-name>mask</var-name>
                    <var-value>^(?:tcp|udp)$</var-value>
                </var>

                <arg name="required" position="0"
                        key="property.FilterRule.protocols" />
                <arg name="mask" position="0"
                        key="property.FilterRule.protocols" />

            </field>
(…省略…)
========== end of /WEB-INF/validation.xml ==========

indexedListProperty 属性で集合型のプロパティを参照する。
property 属性は、集合型プロパティの各要素に対して、
さらにプロパティを指定するという意味となる。

もし、indexedListProperty が示す集合が Object[] なら、
その各要素の型は Object である。
property 属性に "class.name" と指定したとすると、
調べられる値は、配列要素である各 Object の、
.getClass().getName() の値ということになる。

プロトコルの場合は、集合型が String[] なので、
その各要素の型は String だ。
この場合、String の持つプロパティではなく、
String 自身の値を調べるので、property 属性は空となる。

後は、いつもと同じように制約条件を定義するだけだ。
プロトコルの場合、値は「tcp」か「udp」の文字列なので、
これは、「^(?:tcp|udp)$」という正規表現で示される。

こうすることで、配列の要素が何個あっても、
それら全体に制約を課することができるようになる。

indexedListProperty を使って制約をかけた場合、
集合型に対して繰り返し検証処理が行われるのだが、
もし途中でエラーを検出すると、その場で検査は停止し、
それ以降の要素は検査されないという特徴がある。