Top > MyPage
 

Commons Validation on POJO

Commons ValidationをPOJOで使う方法のメモ書き。

まずは使い方

設定ファイル(validation.xmlとvalidator.properties)の書き方

propertiesやxmlファイルの置き場所。

現状では、設定ファイルをロードするクラス(ValidatorLoader)で

stream = ValidatorLoader.class.getResourceAsStream(property);
          

        input = ValidatorLoader.class.getResourceAsStream(validatorRules);
          

というように、ValidatorLoaderのパッケージ場所からロードしているので、実際にはcom.chikkun.common.validatorだ。

propertiesやxmlファイルの名前

優先順位がある。以下にその優先順位を挙げる。

  1. defaultでは、xmlがvalidator.xmlとvalidatorRules.xml、propertiesがvalidator.xml
  2. propertiesにvalidator-pathnames=validator-rules.xml,validator-spring.xmlというように書き込んでおくと、 これがxmlファイルのロードファイルは一番優先される(以下のValidatorUtilsクラス 生成する際の引数よりもさらに優先されることに注意!)
  3. ValidatorUtilss util = new Validatortils(String propertiesName)のように、propertiesの名前を指定した場合は それが使われる。
  4. ValidatorUtilss util = new Validatortils(String propertiesName, String xmlName)のように、propertiesの名前と xmlファイルの名前を指定した場合は、それらが使われる。ただし、propertiesNameに先に述べた validator-pathnames=validator-rules.xml,validator-spring.xmlが書かれていると、xmlファイルに関しては無視されてしまうので、 注意が必要。

validation.xml

基本的な書き方は、strutsにおける使い方と同じ(書き方を参考)。一部、日本語に関しても使えるようにしたので、例を挙げる。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
          "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">
<form-validation>
    <formset>
        <form name="commons">
            <field property="loginName" depends="required">
                <arg0 key="form.loginName.displayname"/>
            </field>
            <field property="nameRead" depends="maxlength">
                <arg0 key="form.nameRead.displayname"/>
                <arg1 name="maxlength" key="${var:maxlength}"/>
                <var>
                    <var-name>maxlength</var-name>
                    <var-value>10</var-value>
                </var>
            </field>
            <field property="quarter" depends="intRange">
                <arg0 key="form.quarter.displayname"/>
                <arg1 key="${var:min}"/>
                <arg2 key="${var:max}"/>
                <var>
                    <var-name>min</var-name>
                    <var-value>4</var-value>
                </var>
                <var>
                    <var-name>max</var-name>
                    <var-value>16</var-value>
                </var>
            </field>
            <field property="email" depends="email">
                <arg0 key="form.email.displayname"/>
            </field>
            <field property="note" depends="date">
                <arg0 key="form.note.displayname"/>
                <arg1 key="${var:datePattern}"/>
                <var>
                    <var-name>datePattern</var-name>
                    <var-value>yyyy-mm-dd</var-value>
                </var>
            </field>
            <field property="name" depends="url">
                <arg0 key="form.name.displayname"/>
                <var>
                    <var-name>allowallschems</var-name>
                    <var-value>true</var-value>
                </var>
            </field>
            <field property="pass" depends="mask">
                <arg0 key="form.pass.displayname"/>
                <var>
                    <var-name>mask</var-name>
                    <var-value>^[a-zA-Z]+$</var-value>
                </var>
            </field>
        </form>
        <form name="common">
            <field property="loginName" depends="byte">
                <arg0 key="form.loginName.displayname"/>
            </field>
            <field property="nameRead" depends="minlength">
                <arg0 key="form.nameRead.displayname"/>
                <arg1 key="${var:minlength}"/>
                <var>
                    <var-name>minlength</var-name>
                    <var-value>5</var-value>
                </var>
            </field>
            <field property="quarter" depends="doubleRange">
                <arg0 key="form.quarter.displayname"/>
                <arg1 key="${var:min}"/>
                <arg2 key="${var:max}"/>
                <var>
                    <var-name>min</var-name>
                    <var-value>-10000</var-value>
                </var>
                <var>
                    <var-name>max</var-name>
                    <var-value>10000</var-value>
                </var>
            </field>
            <field property="email" depends="creditCard">
                <arg0 key="form.email.displayname"/>
                <var>
                    <var-name>type</var-name>
                    <var-value>master</var-value>
                </var>
            </field>
            <field property="note" depends="validwhen">
                <arg0 key="form.note.displayname"/>
                <arg1 key="Password"/>
                <msg name="validwhen" key="error.password.mismatch"/>
                <var>
                    <var-name>test</var-name>
                    <var-value>(*this* == pass)</var-value>
                </var>
            </field>
            <field property="name" depends="requiredif">
                <arg0 key="form.name.displayname"/>
                <arg1 key="Password"/>
                <msg name="requiredif" key="errors.requiredif"/>
                <var>
                    <var-name>field[0]</var-name>
                    <var-value>pass</var-value>
                </var>
                <var>
                    <var-name>fieldTest[0]</var-name>
                    <var-value>NOTNULL</var-value>
                </var>
            </field>
        </form>
        <form name="staff">
            <field property="name" depends="required,kanji">
                <arg0 key="form.name.displayname"/>
            </field>
            <field property="quarter" depends="required,hiragana">
                <arg0 key="form.quarter.displayname"/>
            </field>
            <field property="nameRead" depends="required,katakana">
                <arg0 key="form.nameRead.displayname"/>
            </field>
            <field property="email" depends="required,zenkaku">
                <arg0 key="form.email.displayname"/>
            </field>
            <field property="pass" depends="required,zenkakuAll">
                <arg0 key="form.pass.displayname"/>
            </field>
        </form>
    </formset>
</form-validation>
          

propertiesの書き方

これは通常のstrutsの場合と同じ。ただ、propertiesの中にエラーメッセージがない場合のデフォルトのメッセージなどはないので、しっかり書き込まないと メッセージを取得する際などにエラーが起こるので、注意が必要(いずれ考えなきゃ)。

サンプルを記す。

#xml変更したいとき、下記を書き換える。
validator-pathnames=validator-rules.xml,validator.xml

#デフォルトのエラーメッセージ
#デフォルトを嫌う場合は<msg name="validwhen" key="errors.hoge.required"/>
#のようにxmlの中で指定し、下記に書き加える。
errors.required={0}は必須です。
errors.integer={0}は整数でなければなりません。
errors.katakana={0}はカタカナでなければなりません。
errors.hiragana={0}はひらがなでなければなりません。
errors.kanji={0}は漢字でなければなりません。
errors.zenkaku={0}は全角文字でなければなりません。
errors.mask={0}は有効な値ではありません。
errors.email={0}はメールアドレスとして有効でありません。
errors.date={0}は日付の形式になっていません。
errors.range={0}は{1}から{2}の間にしてください。
errors.minlength={0}は{1}文字以上にしてください。
errors.maxlength={0}は{1}文字以下にしてください。
errors.url={0}はURLとして有効でありません。
errors.byte={0}は-128~127までの整数です。
errors.short={0}は-32768~32767までの整数です。
errors.float={0}は1.4*10E-45~3.4*10E38まで(正負可)の数です。
errors.double={0}は4.9*10E-324~1.8*10E308(正負可)までの数です。
errors.long={0}は-2E63~2E63-1までの整数です。
errors.creditcard={0}はクレジットの番号として有効でありません。
errors.requiredif={1}を入力した場合、{0}は必須になります。
error.password.mismatch={0}と{1}が一致しません。
#....
          

ただし、もちろんnative2asciiでの変換が必要で、次のように僕の環境ではsrcディレクトリーに置いてあると、ant propertyで native2aschiiを行って、target/classes/com/chikkun/common/validatorにコピーされるようにしてある(実際には変数だらけで、わけわからいが)。

    <native2ascii encoding="${local.encoding}" src="src" dest="${classes.dir}/${validator.dir}"
      includes="${validator.name}"/>
          

実際のコード

sakai

ValidatorUtil util = new ValidateUtils("staff");
ValidatorUtil util = new ValidateUtils("staff", "validator-test.properties");
ValidatorUtil util = new ValidateUtils("staff", "validator-test.properties","validator-rules.xml,validator-test.xml");
    

のように、

validateField("Bean内・xml内で定義したfield名",Bean名)
          

validateを行い、OKな場合はtrueを、駄目な場合はfalseが返ってくる。

また、エラーメッセージの取得は

String message = util.getErrorMessage("name")

で、

            
getErrorMessage("Bean内・xml内で定義したfield名")
          

のように、欲しいエラーメッセージを取得する。

Junit

これから以下は、開発途中まで(後半はわやくちゃになって、できなかった(--;)のメモ書きです。個人的なメモ書きと言うことで、ご勘弁を

Commons Validatorがstrutsでは標準だが、このValidatorは以前apache projectの中でstrutsから分離独立したので、 他のフレームワークでも使える、ということを示唆している。

実際には、すでにSpringでも使えるようになっているという話しだが、テストのことなども考えて、POJOでも使えるように、できれば、したい。

そこで、ついでに日本語もある程度使えるように、Commons Validatorを拡張し見ようと思う。流れとしては

  1. もしcommonsにある場合にはそれを当然使うる。
  2. ない場合は、validateするメソッドを自作する。
  3. xmlなどでvalidateを制御するために、Commons Validatorの流儀にあった、クラスを作成。

もともとは次のようなシグネチャになっている。

    public static boolean validateRequired(Object bean,
                                           ValidatorAction va, Field field,
                                           ActionErrors errors,
                                           HttpServletRequest request)
                                          

これだと、ActionErrorsやHttpServletRequestを引数にしているので、今回のようなPOJOでも使えるようにならない。これを

    public static boolean validateRequired(Object bean,
                                           Field field)
                                          

で使えるようにしたい。

そこで、それらを作成するに当たり、Junitを使って、開発していこうと思う。

まずはテストファースト

とにかくテストを作ってみよう。EclipseのJunitを使って、クラスを作成する。とりあえずsrc/test/com/chikkun/webcms/validate/ValidateTest.java を作成する。

/*
 * 作成日: 2005/09/17
 *
 */
package com.chikkun.webcms.validate;

import junit.framework.TestCase;

public class ValidateTest extends TestCase {
  public ValidateTest(String arg0) {
    super(arg0);
  }

  protected void setUp() throws Exception {
    super.setUp();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

}
                                              

最初は上記のようなひな形がeclipseによって作成される。とりあえずテストの対象となる JapaneseValidatorのインスタンスを生成するコードをsetUp()に挿入。

    JapaneseValidator val = new JapaneseValidator();
                                              

当然、これを入れてもコンパイルさえできない。当たり前。なんせJapaneseValidatorクラスなどないから。 そこで全く空のコンストラクターしかない、クラスをcom.chikkun.common.validator.JapaneseValidator.javaを作成。

package com.chikkun.common.validator;

public class JapaneseValidator {

  public JapaneseValidator() {
  }
}
                                              

現状でValidatorTestは

                    /*
                    * 作成日: 2005/09/17
                    *
                    */
                    package com.chikkun.webcms.validate;
                    
                    import junit.framework.TestCase;
                    
                    import com.chikkun.common.validator.JapaneseValidator;
                    
                    public class ValidateTest extends TestCase {
                    
                    JapaneseValidator val;
                    public ValidateTest(String arg0) {
                    super(arg0);
                    }
                    
                    protected void setUp() throws Exception {
                    val = new JapaneseValidator();
                    super.setUp();
                    }
                    
                    protected void tearDown() throws Exception {
                    super.tearDown();
                    }
                    
                    }
                                              

これで一応Junitはコンパイルができる。さてテストメソッドの作成。最初は全角カタカナかどうかを調べるメソッドをJunitに書き込む。

package com.chikkun.webcms.validate;

import junit.framework.TestCase;

import com.chikkun.common.validator.JapaneseValidator;

public class ValidateTest extends TestCase {

  JapaneseValidator val;
  public ValidateTest(String arg0) {
    super(arg0);
  }

  public void testKatakana(){
    this.assertTrue(val.isKatakana("サカイカズロウ"));
  }

  protected void setUp() throws Exception {
    val = new JapaneseValidator();
    super.setUp();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

}
                                              

だけど、これまたコンパイルできない。そりゃそうだ。JapaneseValidat

package com.chikkun.common.validator;

public class JapaneseValidator {

  public JapaneseValidator() {
  }

  public boolean isKatakana(String katakana){
    
    return false;
  }
}
                                              

return falseにしたのは、trueだとテスト通っちゃうから。さて、一度右クリックでJUnitをrunさせると、当然通らない。そこで、少し書き換えよう。

package com.chikkun.common.validator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.validator.GenericValidator;

public class JapaneseValidator {

  public JapaneseValidator() {
  }

  public boolean isKatakana(String katakana) {
    boolean result = true;
    //blankの場合はtrue(そうしないと、入力があった場合にはカタカナということができない)
    //必須でカタカナの場合はrequireでkatakana両方指定すればよい。
    if(GenericValidator.isBlankOrNull(katakana)){
      return result;
    }
    Pattern p = Pattern.compile("[\\p{InKatakana}]*");//
    Matcher matcher = p.matcher(katakana);
    result = matcher.matches();
    return result;
  }
}
                                              

そんで、実行するとOK。ようし、ということで、調子ぶっこいて、ValidateTestを増やして、

package com.chikkun.webcms.validate;

import junit.framework.TestCase;

import com.chikkun.common.validator.JapaneseValidator;

public class ValidateTest extends TestCase {

  JapaneseValidator val;
  public ValidateTest(String arg0) {
    super(arg0);
  }

  public void testKatakana(){
    this.assertTrue(val.isKatakana("サカイカズロウ"));
    this.assertTrue(val.isKatakana("マチャミチャン"));
    this.assertTrue(val.isKatakana("マナブクン"));
    this.assertTrue(val.isKatakana(""));
    this.assertTrue(val.isKatakana(null));
    this.assertFalse(val.isKatakana("さかいかずろう"));
    this.assertFalse(val.isKatakana("坂井和郎"));
    this.assertFalse(val.isKatakana("Sakai Kazuro"));
    this.assertFalse(val.isKatakana("サカイカズロウ"));
    
  }

  protected void setUp() throws Exception {
    val = new JapaneseValidator();
    super.setUp();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

}
                                              

これで実行すると、うまくいくんだな、これが。ラッキー。

てなわけで気をよくして、次に同様の「ひらがな」チェックValidatorTestのメソッドと、JapaneseValidatorのメソッドを作成、テストを実行!

実際にはいっぺんにうまくいったわけじゃなく、ValidateTestのassertを考えながら、JapaneseValidatorのメソッドを修正して、少しずつ完成していく、という感じ。 時々本体のJapaneseValidatorの方のメソッドをガンガン書いちゃうことがあるけれど、本当は避けた方が良いと思う。

/*
 * 作成日: 2005/09/17
 *
 */
package com.chikkun.webcms.validate;

import junit.framework.TestCase;

import com.chikkun.common.validator.JapaneseValidator;

public class ValidateTest extends TestCase {

  JapaneseValidator val;
  public ValidateTest(String arg0) {
    super(arg0);
  }

  public void testKatakana(){
    this.assertTrue(val.isKatakana("サカイカズロウ"));
    this.assertTrue(val.isKatakana("マチャミチャン"));
    this.assertTrue(val.isKatakana("マナブクン"));
    this.assertTrue(val.isKatakana(""));
    this.assertTrue(val.isKatakana(null));

    this.assertFalse(val.isKatakana("さかいかずろう"));
    this.assertFalse(val.isKatakana("さかい和郎"));
    this.assertFalse(val.isKatakana("サカイ和郎"));
    this.assertFalse(val.isKatakana("サカイかずろう"));
    this.assertFalse(val.isKatakana("坂井和郎"));
    this.assertFalse(val.isKatakana("Sakai Kazuro"));
    this.assertFalse(val.isKatakana("サカイカズロウ"));
    
  }


  public void testHiragana(){
    this.assertTrue(val.isHiragana("さかいかずろう"));
    this.assertTrue(val.isHiragana("まさみちゃん"));
    this.assertTrue(val.isHiragana("まなぶくん"));
    this.assertTrue(val.isHiragana(""));
    this.assertTrue(val.isHiragana(null));

    this.assertFalse(val.isHiragana("サカイカズロウ"));
    this.assertFalse(val.isHiragana("さかい和郎"));
    this.assertFalse(val.isHiragana("サカイ和郎"));
    this.assertFalse(val.isHiragana("サカイかずろう"));
    this.assertFalse(val.isHiragana("坂井和郎"));
    this.assertFalse(val.isHiragana("Sakai Kazuro"));
    this.assertFalse(val.isHiragana("サカイカズロウ"));
  }

  public void testKanji(){
    this.assertTrue(val.isKanji("坂井和郎"));
    this.assertTrue(val.isKanji("小野雅巳"));
    this.assertTrue(val.isKanji("佐藤学"));
    this.assertTrue(val.isKanji("亜唖娃阿哀愛黑"));
    this.assertTrue(val.isKanji(""));
    this.assertTrue(val.isKanji(null));

    this.assertFalse(val.isKanji("サカイカズロウ"));
    this.assertFalse(val.isKanji("さかい和郎"));
    this.assertFalse(val.isKanji("サカイ和郎"));
    this.assertFalse(val.isKanji("サカイかずろう"));
    this.assertFalse(val.isKanji("坂井かずろう"));
    this.assertFalse(val.isKanji("Sakai Kazuro"));
    this.assertFalse(val.isKanji("サカイカズロウ"));
    this.assertFalse(val.isKanji("①②③④"));
    this.assertFalse(val.isKanji("!「」"));
    this.assertFalse(val.isKanji("仝々÷♂"));
  }


  public void testZenkaku(){
    this.assertTrue(val.isZenkaku("坂井和郎"));
    this.assertTrue(val.isZenkaku("小野雅巳"));
    this.assertTrue(val.isZenkaku("佐藤学"));
    this.assertTrue(val.isZenkaku("亜唖娃阿哀愛黑"));
    this.assertTrue(val.isZenkaku(""));
    this.assertTrue(val.isZenkaku(null));
    this.assertTrue(val.isZenkaku("坂井和郎なのだ"));
    this.assertTrue(val.isZenkaku("小野雅巳ちゃんなのだ"));
    this.assertTrue(val.isZenkaku("ABC"));
    this.assertTrue(val.isZenkaku("町田市鶴間-1013ー10 LGC608号"));
    this.assertTrue(val.isZenkaku("佐藤学ナノダ"));
    this.assertTrue(val.isZenkaku("佐原市大倉779~28"));
    this.assertTrue(val.isZenkaku("いろはにほへとチリヌルヲ"));
    
    this.assertFalse(val.isZenkaku("「坂井和郎」"));
    this.assertFalse(val.isZenkaku("サカイカズロウa"));
    this.assertFalse(val.isZenkaku("bさかい和郎"));
    this.assertFalse(val.isZenkaku("!!サカイ和郎"));
    this.assertFalse(val.isZenkaku("サカイカズロウ"));
    this.assertFalse(val.isZenkaku("サカイかずろう"));
    this.assertFalse(val.isZenkaku("Sakai Kazuro"));
    this.assertFalse(val.isZenkaku("サカイカズロウ"));
    this.assertFalse(val.isZenkaku("①②③④"));
    this.assertFalse(val.isZenkaku("仝々÷♂"));
  }

  protected void setUp() throws Exception {
    val = new JapaneseValidator();
    super.setUp();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

}
                                              

/*
 * 作成日: 2005/09/17
 *
 */
package com.chikkun.common.validator;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.validator.GenericValidator;

/**
 * @author Chiku Kazuro
 * @version 0.1
 * 日本語のValidatorがないので自作。<br>
 * ひらがな、カタカナ、漢字、全角(一部の記号しか含まない)をチェックできる。<br>
 * すべて、booleanを返すメソッドに統一。
 */
public class JapaneseValidator {

  /**
   * デフォルトコンストラクタ
   */
  public JapaneseValidator() {
  }

  /**
   * カタカナかどうかをチェックする。
   * @param katakana チェックしたい文字列
   * @return カタカナだったらtrueを返す。
   */
  public boolean isKatakana(String katakana) {
    boolean result = true;
    //blankの場合はtrue(そうしないと、入力があった場合にはカタカナということができない)
    //必須でカタカナの場合はrequireでkatakana両方指定すればよい。
    if(GenericValidator.isBlankOrNull(katakana)){
      return result;
    }
    Pattern p = Pattern.compile("[\\p{InKatakana}]*");//
    Matcher matcher = p.matcher(katakana);
    result = matcher.matches();
    return result;
  }

  /**
   * ひらがなかどうかをチェックする。
   * @param hiragana チェックしたい文字列
   * @return ひらがなの場合trueを返す。
   */
  public boolean isHiragana(String hiragana) {
    boolean result = true;
    //blankの場合はtrue(そうしないと、入力があった場合にはカタカナということができない)
    //必須でカタカナの場合はrequireでkatakana両方指定すればよい。
    if(GenericValidator.isBlankOrNull(hiragana)){
      return result;
    }
    Pattern p = Pattern.compile("[\\p{InHiragana}]*");//
    Matcher matcher = p.matcher(hiragana);
    result = matcher.matches();
    return result;
  }

  /**
   * 漢字かどうかをチェックする。
   * @param kanji チェックしたい文字列
   * @return 漢字の場合trueを返す。
   */
  public boolean isKanji(String kanji) {
    boolean result = true;
    //blankの場合はtrue(そうしないと、入力があった場合にはカタカナということができない)
    //必須でカタカナの場合はrequireでkatakana両方指定すればよい。
    if(GenericValidator.isBlankOrNull(kanji)){
      return result;
    }
    Pattern p = Pattern.compile("[\\p{InCJKUnifiedIdeographs}]*");//
    Matcher matcher = p.matcher(kanji);
    result = matcher.matches();
    return result;
  }
  /**
   * 全角文字かどうかをチェックする。<br>
   * ただしここで、全角文字は「漢字+ひらがな+カタカナ+全角アルファベット+全角数字+「ー-~3つの記号」+全角スペース」。
   * @param zenkaku チェックしたい文字列
   * @return ひらがなの場合trueを返す。
   */
  public boolean isZenkaku(String zenkaku) {
    boolean result = true;
    //blankの場合はtrue(そうしないと、入力があった場合にはカタカナということができない)
    //必須でカタカナの場合はrequireでkatakana両方指定すればよい。
    if(GenericValidator.isBlankOrNull(zenkaku)){
      return result;
    }
    Pattern p = Pattern.compile("[\\p{InKatakana}\\p{InHiragana}\\p{InCJKUnifiedIdeographs}A-Za-z0-9ー-~ ]*");//
    Matcher matcher = p.matcher(zenkaku);
    result = matcher.matches();
    return result;
  }

}
                                              

とりあえず、日本語関係は(まだ考慮の余地はあるけれど)おいておいて、次に

おう、そうそうデータを保持するBeanを作っていなかった。これも作っておこうっと(本来は先にJunitからかなあ?)作るのが面倒だから、 既存のUserあたりをリファクタリング(といっても、パッケージエクスプローラでctrl-cでコピーして、testの方でctrl-vでパッケージ名は変えてくれるだけ)。 コメントや、いらないフィールドなどを削除したものが下記。

package com.chikkun.webcms.validate;

import java.io.Serializable;
import java.util.Set;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
public class Staff implements Serializable {
    static final long serialVersionUID = -2539242534546127845L;
    private String quarter;
    private String loginName;
    private String pass;
    private String email;
    private String note;
    private String name;
    private String nameRead;
    public Staff(
        final String staffLoginName,
        final String staffPass,
        final String staffEmail,
        final String staffNote,
        final String staffFullName,
        final String staffFullNameRead,
        final Set staffRole_) {
        this.loginName = staffLoginName;
        this.pass = staffPass;
        this.email = staffEmail;
        this.note = staffNote;
        this.name = staffFullName;
        this.nameRead = staffFullNameRead;
    }
    public Staff() {
    }

    public final String getLoginName() {
        return loginName;
    }
    public final void setLoginName(final String loginName_) {
        this.loginName = loginName_;
    }
    public final String getPass() {
        return pass;
    }
    public final void setPass(final String pass_) {
        this.pass = pass_;
    }
    public final String getEmail() {
        return email;
    }
    public final void setEmail(final String email_) {
        this.email = email_;
    }
    public final String getNote() {
        return note;
    }
    public final void setNote(final String note_) {
        this.note = note_;
    }
    public final String getName() {
        return name;
    }
    public final void setName(final String name_) {
        this.name = name_;
    }
    public final String getNameRead() {
        return nameRead;
    }
    public final void setNameRead(final String nameRead_) {
        this.nameRead = nameRead_;
    }
    public final String getQuarter() {
      return this.quarter;
    }
    public final void setQuarter(final String quarter_) {
        this.quarter = quarter_;
    }
}
                                              

それで、次にvalidator-rules.xmlを用意してみよう。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE form-validation PUBLIC
     "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"
     "http://jakarta.apache.org/commons/dtds/validator_1_1.dtd">
     
<form-validation>
   <global>
      <validator name="int"
                 classname="com.chikkun.webcms.commons.MyValidator"
                 method="validateInt"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.int"/>

      <validator name="required"
                 classname="com.chikkun.webcms.commons.MyValidator"
                 method="validateRequired"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.required"/>
   </global>
   <formset>
     <form name="staff">
         <field property="loginName"  depends="required">
         	   <arg0 key="form.loginName.displayname"/>
         </field>    
         <field  property="pass" depends="required">
         	     <arg0 key="form.pass.displayname"/>
         </field>
         <field  property="note" depends="required,int">
         	     <arg0 key="form.note.displayname"/>
         </field>
      </form>
   </formset>   
</form-validation>
                                              

上記を見ればわかるように、これはstrutsにおけるvalidator-rules.xmlとvalidator.xmlの両方が書いてあるという感じ。つまり、globalで定義し、fomrsetで各 validatorしたいbeanとその方法が書いている。strutsでやった人なら少なくとも、formsetの方はおなじみ。

そこで、今回は、上記のように

  1. loginNameが必須(文字等の指定なし)
  2. passも同様
  3. noteは必須で且つ数字(実際の備考ではそんなことはないが、ここは実験ということで)

という3つにのフィールドに対してvalidateかける。

そして、上記のglobalのクラス名(com.chikkun.webcms.commons.MyValidator)にあたり、method(validateIntなど) に対するものを自作することになるわけです。その前にarg0にあたる、propertiesを作成します (java/com/chikkun/common/validation.properties)。native2asciiが面倒なので、とりあえず英語で。

# The error messages for the Validation Actions
errors.required=The {0} field is required.
errors.int=The {0} field is not an integer.

# The formatted names of the properties
form.loginName.displayname=Login Name
form.note.displayname=Note
                                              

さて、実際のクラス(MyValidator)。最初は、まずはよく使いそうな、そしてCommons Validatorに最初からある「required」と「int」。

/*
 * 作成日: 2005/09/18
 *
 */
package com.chikkun.common;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.util.ValidatorUtils;

public class MyValidator {

  public static boolean validateRequired(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return !GenericValidator.isBlankOrNull(value);
 }

  public static boolean validateInt(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return GenericValidator.isInt(value);
 }

}
                                              

そして、テストケースを作成(少々手順が反対になっちゃったけど)。

/*
 * 作成日: 2005/09/18
 *
 */
package com.chikkun.webcms.validate;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import junit.framework.TestCase;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.Form;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorException;
import org.apache.commons.validator.ValidatorResources;
import org.apache.commons.validator.ValidatorResult;
import org.apache.commons.validator.ValidatorResults;

import com.chikkun.common.ValidatorLoader;

public class MyValidatorTest extends TestCase {

  public ValidatorLoader loader;

  public Staff staff;

  public Properties apps = null;

  public MyValidatorTest(String arg0) {
    super(arg0);
    try {
      loader = new ValidatorLoader();
      apps = loader.getProps();
    } catch (IOException err) {
      // TODO 自動生成された catch ブロック
      err.printStackTrace();
    }
  }

  public void testRequire() {
    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "staff");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);

    ValidatorResults results = null;
    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);
    Form form = resources.getForm(Locale.getDefault(), "staff");

    // ここから実際のテスト。最初はすべてnull

    // loginName required
    Field field = form.getField("loginName");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Login Name");
    ValidatorResult result = results.getValidatorResult("loginName");
    ValidatorAction action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    String message = apps.getProperty(action.getMsg());
    Object[] args = {prettyFieldName};
    assertEquals("message", "The Login Name field is required.", MessageFormat
        .format(message, args));

    // pass required
    field = form.getField("pass");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Password");
    result = results.getValidatorResult("pass");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Password field is required.", MessageFormat
        .format(message, args));

    // note required
    field = form.getField("note");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Note");
    result = results.getValidatorResult("note");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Note field is required.", MessageFormat
        .format(message, args));

    // note int
    field = form.getField("note");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Note");
    result = results.getValidatorResult("note");
    action = resources.getValidatorAction("int");

    assertFalse(result.isValid("int"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Note field is not an integer.", MessageFormat
        .format(message, args));

    // 最初はloginName="sakai",pass="hogehoge",note="funifuni"
    // funifuniだけが通らないはず。
    // loginName required

    staff.setLoginName("sakai");
    staff.setPass("hogehoge");
    staff.setNote("funifuni");

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);
    form = resources.getForm(Locale.getDefault(), "staff");

    field = form.getField("loginName");
    result = results.getValidatorResult("loginName");
    assertTrue(result.isValid("required"));

    // pass required
    field = form.getField("pass");
    result = results.getValidatorResult("pass");
    assertTrue(result.isValid("required"));

    // note required
    field = form.getField("note");
    result = results.getValidatorResult("note");

    /*
     * どうやら、 <field property="note" depends="required,int">
     * のように、2つ以上validatorを指定している場合は、 result.isValid("required")
     * 前のものがpassしていると、最後のものしか判定できないようだ。
     * つまり上記の場合は(note)、noteは"funifuni"なので、requireはパスする。 したがって、
     * assertTrue(result.isValid("required")); で判定しても、うまくいかない。パスした後は判定できないようだ。
     * 
     */
    // note int
    assertFalse(result.isValid("note"));

    staff.setNote("123");

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("note");

    Map actionMap = result.getActionMap();
    Iterator keys = actionMap.keySet().iterator();
    while (keys.hasNext()) {
      String actName = (String) keys.next();
      System.out.println(actName);
    }

    printResults(staff, results, resources);
    form = resources.getForm(Locale.getDefault(), "staff");

    assertTrue(result.isValid("int"));

    // これは上記の通りうまくいかない!!
    // assertTrue(result.isValid("required"));

  }

  protected void setUp() throws Exception {
    super.setUp();

    staff = new Staff();

  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

  /**
   * テスト結果を表示するユーティリティメソッド
   * 
   * @param bean
   * @param results
   * @param resources
   */
  public void printResults(Staff bean, ValidatorResults results,
      ValidatorResources resources) {

    boolean success = true;

    // Start by getting the form for the current locale and Bean.
    Form form = resources.getForm(Locale.getDefault(), "staff");

    System.out.println("\n\nValidating:");
    System.out.println(bean);

    // Iterate over each of the properties of the Bean which had messages.
    Iterator propertyNames = results.getPropertyNames().iterator();

    while (propertyNames.hasNext()) {
      String propertyName = (String) propertyNames.next();

      // Get the Field associated with that property in the Form
      Field field = form.getField(propertyName);

      // Look up the formatted name of the field from the Field arg0
      String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

      // Get the result of validating the property.
      ValidatorResult result = results.getValidatorResult(propertyName);

      // Get all the actions run against the property, and iterate over their
      // names.
      Map actionMap = result.getActionMap();
      Iterator keys = actionMap.keySet().iterator();
      while (keys.hasNext()) {
        String actName = (String) keys.next();

        // Get the Action for that name.
        ValidatorAction action = resources.getValidatorAction(actName);

        // If the result is valid, print PASSED, otherwise print FAILED
        System.out.println(propertyName + "[" + actName + "] ("
            + (result.isValid(actName) ? "PASSED" : "FAILED") + ")");

        // If the result failed, format the Action's message against the
        // formatted field name
        if (!result.isValid(actName)) {
          success = false;
          String message = apps.getProperty(action.getMsg());
          Object[] args = {prettyFieldName};
          System.out.println("     Error message will be: "
              + MessageFormat.format(message, args));

        }
      }
    }
    if (success) {
      System.out.println("FORM VALIDATION PASSED");
    } else {
      System.out.println("FORM VALIDATION FAILED");
    }

  }

}
                                              

上記のソースのコメントにも書いてあるように、色々すったもんだがあったが、とりあえずテストは通った。

次に、自作した日本語のJapanseValidatorを追加しよう。

/*
 * 作成日: 2005/09/18
 *
 */
package com.chikkun.common;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.util.ValidatorUtils;

import com.chikkun.common.validator.JapaneseValidator;

public class MyValidator {

  public static JapaneseValidator val =new JapaneseValidator();  
  
  public static boolean validateRequired(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return !GenericValidator.isBlankOrNull(value);
 }

  public static boolean validateInt(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return GenericValidator.isInt(value);
 }

  public static boolean validateKatakana(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return val.isKatakana(value);
  }

  public static boolean validateHiragana(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return val.isHiragana(value);
  }

  public static boolean validateKanji(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return val.isKanji(value);
  }

  public static boolean validateZenkaku(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return val.isZenkaku(value);
  }

}
                                              

これを登録するためのvalidation.xmlのglobalとフィールドチェック用の定義を追加。今回は下にもあるように、 staffのnameが漢字で必須、quarter(部署)がひらがなで必須、nameRead(読み仮名)がカタカナで必須、emailが全角で必須という ことにする(実際とは違うけれど)。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE form-validation PUBLIC
     "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"
     "http://jakarta.apache.org/commons/dtds/validator_1_1.dtd">
<form-validation>
   <global>
      <validator name="int"
                 classname="com.chikkun.common.MyValidator"
                 method="validateInt"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.int"/>

      <validator name="required"
                 classname="com.chikkun.common.MyValidator"
                 method="validateRequired"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.required"/>
      <validator name="katakana"
                 classname="com.chikkun.common.MyValidator"
                 method="validateKatakana"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.katakana"/>
      <validator name="hiragana"
                 classname="com.chikkun.common.MyValidator"
                 method="validateHiragana"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.hiragana"/>
      <validator name="kanji"
                 classname="com.chikkun.common.MyValidator"
                 method="validateKanji"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.kanji"/>
      <validator name="zenkaku"
                 classname="com.chikkun.common.MyValidator"
                 method="validateZenkaku"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.zenkaku"/>
   </global>
   <formset>
     <form name="staff">
         <field property="loginName"  depends="required">
         	   <arg0 key="form.loginName.displayname"/>
         </field>    
         <field  property="pass" depends="required">
         	     <arg0 key="form.pass.displayname"/>
         </field>
         <field  property="note" depends="required,int">
         	     <arg0 key="form.note.displayname"/>
         </field>
         <field  property="name" depends="required,kanji">
         	     <arg0 key="form.name.displayname"/>
         </field>
         <field  property="quarter" depends="required,hiragana">
         	     <arg0 key="form.quarter.displayname"/>
         </field>
         <field  property="nameRead" depends="required,katakana">
         	     <arg0 key="form.nameRead.displayname"/>
         </field>
         <field  property="email" depends="required,zenkaku">
         	     <arg0 key="form.email.displayname"/>
         </field>

      </form>
   </formset>   
</form-validation>
                                              

validation.propertiesも追加。

# The error messages for the Validation Actions
errors.required=The {0} field is required.
errors.int=The {0} field is not an integer.
errors.katakana=The {0} field is not a katakana.
errors.hiragana=The {0} field is not a hiragana.
errors.kanji=The {0} field is not a kanji.
errors.zenkaku=The {0} field is not a zenkaku.

# The formatted names of the properties
form.loginName.displayname=Login Name
form.pass.displayname=Password
form.note.displayname=Note
form.name.displayname=Name
form.quarter.displayname=Quarter
form.nameRead.displayname=Yomigana
form.email.displayname=Email
validator-pathnames=validator.xml
                                              

最後に、テストケースの追加(MyValidationTest.java)。

/*
 * 作成日: 2005/09/18
 *
 */
package com.chikkun.webcms.validate;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import junit.framework.TestCase;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.Form;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorException;
import org.apache.commons.validator.ValidatorResources;
import org.apache.commons.validator.ValidatorResult;
import org.apache.commons.validator.ValidatorResults;

import com.chikkun.common.ValidatorLoader;

public class MyValidatorTest extends TestCase {

  public ValidatorLoader loader;

  public Staff staff;

  public Properties apps = null;

  public MyValidatorTest(String arg0) {
    super(arg0);
    try {
      loader = new ValidatorLoader();
      apps = loader.getProps();
    } catch (IOException err) {
      // TODO 自動生成された catch ブロック
      err.printStackTrace();
    }
  }

  public void testRequireAndInt() {
    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "staff");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);

    ValidatorResults results = null;
    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);
    Form form = resources.getForm(Locale.getDefault(), "staff");

    // ここから実際のテスト。最初はすべてnull

    // loginName required
    Field field = form.getField("loginName");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Login Name");
    ValidatorResult result = results.getValidatorResult("loginName");
    ValidatorAction action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    String message = apps.getProperty(action.getMsg());
    Object[] args = {prettyFieldName};
    assertEquals("message", "The Login Name field is required.", MessageFormat
        .format(message, args));

    // pass required
    field = form.getField("pass");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Password");
    result = results.getValidatorResult("pass");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Password field is required.", MessageFormat
        .format(message, args));

    // note required
    field = form.getField("note");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Note");
    result = results.getValidatorResult("note");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Note field is required.", MessageFormat
        .format(message, args));

    // note int
    field = form.getField("note");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Note");
    result = results.getValidatorResult("note");
    action = resources.getValidatorAction("int");

    assertFalse(result.isValid("int"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Note field is not an integer.", MessageFormat
        .format(message, args));

    // 最初はloginName="sakai",pass="hogehoge",note="funifuni"
    // funifuniだけが通らないはず。
    // loginName required

    staff.setLoginName("sakai");
    staff.setPass("hogehoge");
    staff.setNote("funifuni");

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);
    form = resources.getForm(Locale.getDefault(), "staff");

    field = form.getField("loginName");
    result = results.getValidatorResult("loginName");
    assertTrue(result.isValid("required"));

    // pass required
    field = form.getField("pass");
    result = results.getValidatorResult("pass");
    assertTrue(result.isValid("required"));

    // note required
    field = form.getField("note");
    result = results.getValidatorResult("note");

    /*
     * どうやら、 <field property="note" depends="required,int">
     * のように、2つ以上validatorを指定している場合は、 result.isValid("required")
     * 前のものがpassしていると、最後のものしか判定できないようだ。
     * つまり上記の場合は(note)、noteは"funifuni"なので、requireはパスする。 したがって、
     * assertTrue(result.isValid("required")); で判定しても、うまくいかない。パスした後は判定できないようだ。
     * 
     */
    // note int
    assertFalse(result.isValid("note"));

    staff.setNote("123");

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("note");

    Map actionMap = result.getActionMap();
    Iterator keys = actionMap.keySet().iterator();
    while (keys.hasNext()) {
      String actName = (String) keys.next();
      System.out.println(actName);
    }

    printResults(staff, results, resources);
    form = resources.getForm(Locale.getDefault(), "staff");

    assertTrue(result.isValid("int"));

    // これは上記の通りうまくいかない!!
    // assertTrue(result.isValid("required"));

  }

  public void testJapanese() {
    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "staff");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);

    ValidatorResults results = null;
    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);
    Form form = resources.getForm(Locale.getDefault(), "staff");

    // 最初はnullのままなのですべてrequiredで引っかかる
    // name required
    Field field = form.getField("name");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Name");
    ValidatorResult result = results.getValidatorResult("name");
    ValidatorAction action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    String message = apps.getProperty(action.getMsg());
    Object[] args = {prettyFieldName};
    assertEquals("message", "The Name field is required.", MessageFormat
        .format(message, args));

    // nameRead required
    field = form.getField("nameRead");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Yomigana");
    result = results.getValidatorResult("nameRead");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Yomigana field is required.", MessageFormat
        .format(message, args));

    // quarter required
    field = form.getField("quarter");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Quarter");
    result = results.getValidatorResult("quarter");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Quarter field is required.", MessageFormat
        .format(message, args));

    // nameRead required
    field = form.getField("email");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Email");
    result = results.getValidatorResult("email");
    action = resources.getValidatorAction("required");

    assertFalse(result.isValid("required"));

    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Email field is required.", MessageFormat
        .format(message, args));

    // 前のは通るように。
    staff.setLoginName("sakai");
    staff.setPass("hogehoge");
    staff.setNote("123");
    // とりあえず、staffに値を入れるがすべて、アスキー
    staff.setName("sakai");
    staff.setNameRead("sakai");
    staff.setQuarter("jinji");
    staff.setEmail("abcdefg");

    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);

    field = form.getField("name");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());
    action = resources.getValidatorAction("kanji");
    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Name field is not a kanji.", MessageFormat
        .format(message, args));
    result = results.getValidatorResult("name");
    assertFalse(result.isValid("kanji"));

    field = form.getField("nameRead");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());
    action = resources.getValidatorAction("katakana");
    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Yomigana field is not a katakana.",
        MessageFormat.format(message, args));
    result = results.getValidatorResult("nameRead");
    assertFalse(result.isValid("katakana"));

    field = form.getField("email");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());
    action = resources.getValidatorAction("zenkaku");
    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Email field is not a zenkaku.", MessageFormat
        .format(message, args));
    result = results.getValidatorResult("email");
    assertFalse(result.isValid("zenkaku"));

    field = form.getField("quarter");
    prettyFieldName = apps.getProperty(field.getArg(0).getKey());
    action = resources.getValidatorAction("hiragana");
    message = apps.getProperty(action.getMsg());
    args[0] = prettyFieldName;
    assertEquals("message", "The Quarter field is not a hiragana.",
        MessageFormat.format(message, args));
    result = results.getValidatorResult("quarter");
    assertFalse(result.isValid("hiragana"));

    // とりあえず、staffに値を入れるがすべて、アスキー
    staff.setName("坂井和郎");
    staff.setNameRead("サカイカズロウ");
    staff.setQuarter("じんじ");
    staff.setEmail("サカイ坂井さかい");

    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    printResults(staff, results, resources);

    result = results.getValidatorResult("name");
    assertTrue(result.isValid("kanji"));

    result = results.getValidatorResult("nameRead");
    assertTrue(result.isValid("katakana"));

    result = results.getValidatorResult("email");
    assertTrue(result.isValid("zenkaku"));

    result = results.getValidatorResult("quarter");
    assertTrue(result.isValid("hiragana"));
  }

  protected void setUp() throws Exception {
    super.setUp();

    staff = new Staff();

  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

  /**
   * テスト結果を表示するユーティリティメソッド
   * 
   * @param bean
   * @param results
   * @param resources
   */
  public void printResults(Staff bean, ValidatorResults results,
      ValidatorResources resources) {

    boolean success = true;

    // Start by getting the form for the current locale and Bean.
    Form form = resources.getForm(Locale.getDefault(), "staff");

    System.out.println("\n\nValidating:");
    System.out.println(bean);

    // Iterate over each of the properties of the Bean which had messages.
    Iterator propertyNames = results.getPropertyNames().iterator();

    while (propertyNames.hasNext()) {
      String propertyName = (String) propertyNames.next();

      // Get the Field associated with that property in the Form
      Field field = form.getField(propertyName);

      // Look up the formatted name of the field from the Field arg0
      String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

      // Get the result of validating the property.
      ValidatorResult result = results.getValidatorResult(propertyName);

      // Get all the actions run against the property, and iterate over their
      // names.
      Map actionMap = result.getActionMap();
      Iterator keys = actionMap.keySet().iterator();
      while (keys.hasNext()) {
        String actName = (String) keys.next();

        // Get the Action for that name.
        ValidatorAction action = resources.getValidatorAction(actName);

        // If the result is valid, print PASSED, otherwise print FAILED
        System.out.println(propertyName + "[" + actName + "] ("
            + (result.isValid(actName) ? "PASSED" : "FAILED") + ")");

        // If the result failed, format the Action's message against the
        // formatted field name
        if (!result.isValid(actName)) {
          success = false;
          String message = apps.getProperty(action.getMsg());
          Object[] args = {prettyFieldName};
          System.out.println("     Error message will be: "
              + MessageFormat.format(message, args));

        }
      }
    }
    if (success) {
      System.out.println("FORM VALIDATION PASSED");
    } else {
      System.out.println("FORM VALIDATION FAILED");
    }

  }

}
                                              

どうやら、一応テストに通った。

次にすでにあるCommonsも使えるように、MyValidatorとは別にFieldCheck.java作成し、validator.xmlの追加を行う。ついでに、intとrequiredも自作じゃないので、 FeieldCheck.javaに移そう。こんなときJunitを作成しておくと、簡単にチェックできるから便利だ

まずはFieldCheck.javaから。

package com.chikkun.common.validator;

import java.io.Serializable;
import java.util.Date;
import java.util.Locale;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericTypeValidator;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.util.ValidatorUtils;

public class FieldCheck implements Serializable {

  /**
   * Commons Logging instance.
   */
  private static final Log log = LogFactory.getLog(FieldCheck.class);

  public static final String FIELD_TEST_NULL = "NULL";

  public static final String FIELD_TEST_NOTNULL = "NOTNULL";

  public static final String FIELD_TEST_EQUAL = "EQUAL";

  public static boolean validateRequired(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    return !GenericValidator.isBlankOrNull(value);

  }

  public static boolean validateMask(Object bean, Field field) {

    String mask = field.getVarValue("mask");
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    try {
      if (!GenericValidator.isBlankOrNull(value)
          && !GenericValidator.matchRegexp(value, mask)) {
        return false;
      } else {
        return true;
      }
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return true;
  }

  public static boolean validateInteger(Object bean, Field field) {
    boolean result = false;
    Integer num = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      num = GenericTypeValidator.formatInt(value);
    }

    if (num != null) {
      result = true;
    }
    return result;
  }

  public static Long validateLong(Object bean, Field field) {
    Long result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatLong(value);
    }

    return result;
  }

  public static Float validateFloat(Object bean, Field field) {
    Float result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatFloat(value);
    }

    return result;
  }

  public static Double validateDouble(Object bean, Field field) {
    Double result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatDouble(value);

    }

    return result;
  }

  public static Date validateDate(Object bean, Field field) {

    Date result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }
    String datePattern = field.getVarValue("datePattern");
    String datePatternStrict = field.getVarValue("datePatternStrict");
    Locale locale = Locale.getDefault();
    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        if (datePattern != null && datePattern.length() > 0) {
          result = GenericTypeValidator.formatDate(value, datePattern, false);
        } else if (datePatternStrict != null && datePatternStrict.length() > 0) {
          result = GenericTypeValidator.formatDate(value, datePatternStrict,
              true);
        } else {
          result = GenericTypeValidator.formatDate(value, locale);
        }
      } catch (Exception e) {
        log.error(e.getMessage(), e);
      }

    }

    return result;
  }

  public static boolean validateIntRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        int intValue = Integer.parseInt(value);
        int min = Integer.parseInt(field.getVarValue("min"));
        int max = Integer.parseInt(field.getVarValue("max"));

        if (!GenericValidator.isInRange(intValue, min, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateDoubleRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        double doubleValue = Double.parseDouble(value);
        double min = Double.parseDouble(field.getVarValue("min"));
        double max = Double.parseDouble(field.getVarValue("max"));

        if (!GenericValidator.isInRange(doubleValue, min, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateFloatRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        float floatValue = Float.parseFloat(value);
        float min = Float.parseFloat(field.getVarValue("min"));
        float max = Float.parseFloat(field.getVarValue("max"));

        if (!GenericValidator.isInRange(floatValue, min, max)) {

          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static Long validateCreditCard(Object bean, Field field) {

    Long result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatCreditCard(value);

    }

    return result;
  }

  public static boolean validateEmail(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)
        && !GenericValidator.isEmail(value)) {
      return false;
    } else {
      return true;
    }
  }

  public static boolean validateMaxLength(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (value != null) {
      try {
        int max = Integer.parseInt(field.getVarValue("maxlength"));

        if (!GenericValidator.maxLength(value, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateMinLength(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        int min = Integer.parseInt(field.getVarValue("minlength"));

        if (!GenericValidator.minLength(value, min)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }


  protected static boolean isString(Object o) {
    return (o == null) ? true : String.class.isInstance(o);
  }

}
                                              

次はvalidation.xmlへの追加。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE form-validation PUBLIC
     "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"
     "http://jakarta.apache.org/commons/dtds/validator_1_1.dtd">
<form-validation>
   <global>
      <validator name="int"
                 classname="com.chikkun.common.validator.FieldCheck"
                 method="validateInt"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.int"/>

      <validator name="required"
                 classname="com.chikkun.common.validator.FieldCheck"
                 method="validateRequired"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.required"/>
      <validator name="katakana"
                 classname="com.chikkun.common.validator.MyValidator"
                 method="validateKatakana"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.katakana"/>
      <validator name="hiragana"
                 classname="com.chikkun.common.validator.MyValidator"
                 method="validateHiragana"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.hiragana"/>
      <validator name="kanji"
                 classname="com.chikkun.common.validator.MyValidator"
                 method="validateKanji"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.kanji"/>
      <validator name="zenkaku"
                 classname="com.chikkun.common.validator.MyValidator"
                 method="validateZenkaku"
                 methodParams="java.lang.Object,org.apache.commons.validator.Field"
                 msg="errors.zenkaku"/>
      <validator name="mask"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateMask"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.invalid"/>
      <validator name="integer"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateInteger"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.integer"/>
      <validator name="long"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateLong"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.long"/>
      <validator name="float"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateFloat"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.float"/>

      <validator name="double"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateDouble"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.double"/>
      <validator name="date"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateDate"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.date"/>
      <validator name="intRange"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateIntRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends="integer"
                  msg="errors.range"/>
      <validator name="floatRange"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateFloatRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionMessages"
              depends="float"
                  msg="errors.range"/>
      <validator name="doubleRange"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateDoubleRange"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends="double"
                  msg="errors.range"/>
      <validator name="creditCard"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateCreditCard"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
                  msg="errors.creditcard"/>
      <validator name="email"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateEmail"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends=""
                  msg="errors.email"/>
      <validator name="url"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateUrl"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends=""
                  msg="errors.url"/>
      <validator name="minlength"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateMinLength"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends=""
                  msg="errors.minlength"/>
      <validator name="maxlength"
            classname="com.chikkun.common.validator.FieldCheck"
               method="validateMaxLength"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.Field"
              depends=""
                  msg="errors.maxlength"/>

   </global>
   <formset>
     <form name="staff">
         <field property="loginName"  depends="required">
         	   <arg0 key="form.loginName.displayname"/>
         </field>    
         <field  property="pass" depends="required">
         	     <arg0 key="form.pass.displayname"/>
         </field>
         <field  property="note" depends="required,int">
         	     <arg0 key="form.note.displayname"/>
         </field>
         <field  property="name" depends="required,kanji">
         	     <arg0 key="form.name.displayname"/>
         </field>
         <field  property="quarter" depends="required,hiragana">
         	     <arg0 key="form.quarter.displayname"/>
         </field>
         <field  property="nameRead" depends="required,katakana">
         	     <arg0 key="form.nameRead.displayname"/>
         </field>
         <field  property="email" depends="required,zenkaku">
         	     <arg0 key="form.email.displayname"/>
         </field>
      </form>
   <formset>
     <form name="commons">
         <field property="loginName"  depends="url">
         	   <arg0 key="form.loginName.displayname"/>
         </field>    
         <field  property="pass" depends="mask">
         	     <arg0 key="form.pass.displayname"/>
         </field>
         <field  property="note" depends="date">
         	     <arg0 key="form.note.displayname"/>
         </field>
         <field  property="email" depends="email">
         	     <arg0 key="form.email.displayname"/>
         </field>
      </form>

   </formset>   
</form-validation>
                                              

次はFieldCheck.java

package com.chikkun.common.validator;

import java.io.Serializable;
import java.util.Date;
import java.util.Locale;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.GenericTypeValidator;
import org.apache.commons.validator.GenericValidator;
import org.apache.commons.validator.UrlValidator;
import org.apache.commons.validator.util.ValidatorUtils;
import org.apache.struts.validator.Resources;

public class FieldCheck implements Serializable {

  /**
   * Commons Logging instance.
   */
  private static final Log log = LogFactory.getLog(FieldCheck.class);

  public static final String FIELD_TEST_NULL = "NULL";

  public static final String FIELD_TEST_NOTNULL = "NOTNULL";

  public static final String FIELD_TEST_EQUAL = "EQUAL";

  public static boolean validateRequired(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    return !GenericValidator.isBlankOrNull(value);
  }

  public static boolean validateInt(Object bean, Field field) {
    String value = ValidatorUtils.getValueAsString(bean, field.getProperty());

    return GenericValidator.isInt(value);
  }

  public static boolean validateMask(Object bean, Field field) {

    String mask = field.getVarValue("mask");
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    try {
      if (!GenericValidator.isBlankOrNull(value)
          && !GenericValidator.matchRegexp(value, mask)) {
        return false;
      } else {
        return true;
      }
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
    return true;
  }

  public static boolean validateInteger(Object bean, Field field) {
    boolean result = false;
    Integer num = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      num = GenericTypeValidator.formatInt(value);
    }

    if (num != null) {
      result = true;
    }
    return result;
  }

  public static Long validateLong(Object bean, Field field) {
    Long result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatLong(value);
    }

    return result;
  }

  public static Float validateFloat(Object bean, Field field) {
    Float result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatFloat(value);
    }

    return result;
  }

  public static Double validateDouble(Object bean, Field field) {
    Double result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatDouble(value);

    }

    return result;
  }

  public static Date validateDate(Object bean, Field field) {

    Date result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }
    String datePattern = field.getVarValue("datePattern");
    String datePatternStrict = field.getVarValue("datePatternStrict");
    Locale locale = Locale.getDefault();
    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        if (datePattern != null && datePattern.length() > 0) {
          result = GenericTypeValidator.formatDate(value, datePattern, false);
        } else if (datePatternStrict != null && datePatternStrict.length() > 0) {
          result = GenericTypeValidator.formatDate(value, datePatternStrict,
              true);
        } else {
          result = GenericTypeValidator.formatDate(value, locale);
        }
      } catch (Exception e) {
        log.error(e.getMessage(), e);
      }

    }

    return result;
  }

  public static boolean validateIntRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        int intValue = Integer.parseInt(value);
        int min = Integer.parseInt(field.getVarValue("min"));
        int max = Integer.parseInt(field.getVarValue("max"));

        if (!GenericValidator.isInRange(intValue, min, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateDoubleRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        double doubleValue = Double.parseDouble(value);
        double min = Double.parseDouble(field.getVarValue("min"));
        double max = Double.parseDouble(field.getVarValue("max"));

        if (!GenericValidator.isInRange(doubleValue, min, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateFloatRange(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        float floatValue = Float.parseFloat(value);
        float min = Float.parseFloat(field.getVarValue("min"));
        float max = Float.parseFloat(field.getVarValue("max"));

        if (!GenericValidator.isInRange(floatValue, min, max)) {

          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static Long validateCreditCard(Object bean, Field field) {

    Long result = null;
    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      result = GenericTypeValidator.formatCreditCard(value);

    }

    return result;
  }

  public static boolean validateEmail(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)
        && !GenericValidator.isEmail(value)) {
      return false;
    } else {
      return true;
    }
  }

  public static boolean validateMaxLength(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (value != null) {
      try {
        int max = Integer.parseInt(field.getVarValue("maxlength"));

        if (!GenericValidator.maxLength(value, max)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  public static boolean validateMinLength(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (!GenericValidator.isBlankOrNull(value)) {
      try {
        int min = Integer.parseInt(field.getVarValue("minlength"));

        if (!GenericValidator.minLength(value, min)) {
          return false;
        }
      } catch (Exception e) {
        return false;
      }
    }

    return true;
  }

  protected static boolean isString(Object o) {
    return (o == null) ? true : String.class.isInstance(o);
  }

  public static boolean validateUrl(Object bean, Field field) {

    String value = null;
    if (isString(bean)) {
      value = (String) bean;
    } else {
      value = ValidatorUtils.getValueAsString(bean, field.getProperty());
    }

    if (GenericValidator.isBlankOrNull(value)) {
      return true;
    }

    // Get the options and schemes Vars
    boolean allowallschemes = "true".equalsIgnoreCase(field
        .getVarValue("allowallschemes"));
    int options = allowallschemes ? UrlValidator.ALLOW_ALL_SCHEMES : 0;

    if ("true".equalsIgnoreCase(field.getVarValue("allow2slashes"))) {
      options += UrlValidator.ALLOW_2_SLASHES;
    }

    if ("true".equalsIgnoreCase(field.getVarValue("nofragments"))) {
      options += UrlValidator.NO_FRAGMENTS;
    }

    String schemesVar = allowallschemes ? null : field.getVarValue("schemes");

    // No options or schemes - use GenericValidator as default
    if (options == 0 && schemesVar == null) {
      if (GenericValidator.isUrl(value)) {
        return true;
      } else {
        return false;
      }
    }

    // Parse comma delimited list of schemes into a String[]
    String[] schemes = null;
    if (schemesVar != null) {

      StringTokenizer st = new StringTokenizer(schemesVar, ",");
      schemes = new String[st.countTokens()];

      int i = 0;
      while (st.hasMoreTokens()) {
        schemes[i++] = st.nextToken().trim();
      }

    }

    // Create UrlValidator and validate with options/schemes
    UrlValidator urlValidator = new UrlValidator(schemes, options);
    if (urlValidator.isValid(value)) {
      return true;
    } else {
      return false;
    }
  }

}
                                              

これに対するテストケース。もともとあるものだし、面倒なのでintRange、email、mask,url,date,maxlengthの6つをテストしてみようと思う。

validation.xmlの書き方は書き方を参考に。

     <form name="commons">
         <field property="loginName"  depends="url">
         	   <arg0 key="form.loginName.displayname"/>
                   <var>
                        <var-name>allowallschems</var-name>
                        <var-value>true</var-value>
                   </var>
         </field>    
         <field  property="pass" depends="mask">
         	     <arg0 key="form.pass.displayname"/>
                     <var>
                         <var-name>mask</var-name>
                         <var-value>^[a-zA-Z]*$</var-value>
                     </var>
         </field>
         <field  property="note" depends="date">
         	     <arg0 key="form.note.displayname"/>
                     <var>
                         <var-name>datePattern</var-name>
                         <var-value>yyyy-mm-dd</var-value>
                         </var>
         </field>
         <field  property="email" depends="email">
         	     <arg0 key="form.email.displayname"/>
         </field>
         <field  property="quarter" depends="intRange">
         	     <arg0 key="form.quarter.displayname"/>
                    <var>
                       <var-name>min</var-name>
                      <var-value>4</var-value>
                    </var>
                    <var>
                      <var-name>max</var-name>
                      <var-value>16</var-value>
                   </var>
         </field>
         <field  property="nameRead" depends="maxlength">
         	     <arg0 key="form.nameRead.displayname"/>
                     <arg1 name="maxlength" key="${var:maxlength}"/>
                     <var>
                         <var-name>maxlength</var-name>
                        <var-value>10</var-value>
                    </var>
         </field>
      </form>
                                              

test/com/chikkun/webcms/validagtor/CommonsValidatorTest.javaの作成。

/*
 * 作成日: 2005/09/19
 *
 */
package com.chikkun.webcms.validate;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Properties;

import junit.framework.TestCase;

import org.apache.commons.validator.Field;
import org.apache.commons.validator.Form;
import org.apache.commons.validator.Validator;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorException;
import org.apache.commons.validator.ValidatorResources;
import org.apache.commons.validator.ValidatorResult;
import org.apache.commons.validator.ValidatorResults;

import com.chikkun.common.validator.ValidatorLoader;

public class CommonsValidatorTest extends TestCase {

  public ValidatorLoader loader;

  public Staff staff;

  public Properties apps = null;

  public CommonsValidatorTest(String arg0) {
    super(arg0);
    try {
      loader = new ValidatorLoader();
      apps = loader.getProps();
    } catch (IOException err) {
      // TODO 自動生成された catch ブロック
      err.printStackTrace();
    }
  }

  public void testEmailTest(){

    //emailじゃないもの
    staff.setEmail("aaaa");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("email");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Email");
    ValidatorResult result = results.getValidatorResult("email");
    ValidatorAction action = resources.getValidatorAction("email");

    assertFalse(result.isValid("email"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName};
    assertEquals("message", "Email is an invalid e-mail address.", MessageFormat
        .format(message, args));

    //emailとして正しい
    staff.setEmail("sakai@chikkun.com");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    action = resources.getValidatorAction("email");
    result = results.getValidatorResult("email");

    //System.out.println(action.getMethod());
    assertTrue(result.isValid("email"));
    
  }

  
  public void testMaxLengthTest(){

    //11文字は駄目
    staff.setNameRead("aaaaaaaaaaa");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("nameRead");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Yomigana");
    ValidatorResult result = results.getValidatorResult("nameRead");
    ValidatorAction action = resources.getValidatorAction("maxlength");

    assertFalse(result.isValid("maxlength"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName,field.getVarValue("maxlength")};
    
    assertEquals("message", "Yomigana cannot be greater than 10 characters.", MessageFormat
        .format(message, args));

    //10文字
    staff.setNameRead("aaaaaaaaaa");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("nameRead");

    //System.out.println(action.getMethod());
    assertTrue(result.isValid("maxlength"));
    
  }

  
  public void testIntRangeTest(){

    //4-16の間
    //2(範囲外・未満)
    staff.setQuarter("2");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("quarter");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Quarter");
    ValidatorResult result = results.getValidatorResult("quarter");
    ValidatorAction action = resources.getValidatorAction("intRange");

    assertFalse(result.isValid("intRange"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName,field.getVarValue("min"),field.getVarValue("max")};
    
    assertEquals("message", "Quarter is not in the range 4 through 16.", MessageFormat
        .format(message, args));

    //12(範囲内)
    staff.setQuarter("12");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("quarter");

    //System.out.println(action.getMethod());

    assertTrue(result.isValid("intRange"));

    
    //20(範囲外・より大きい)
    staff.setQuarter("20");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("quarter");

    //System.out.println(action.getMethod());
    assertFalse(result.isValid("intRange"));

  }
  
  public void testDateTest(){

    //yyyy-mm-ddでもyyyy-m-dでも可
    //<var-name>datePattern</var-name>を<var-name>datePatternStrict</var-name>にすれば
    //yyyy-m-dは不可

    //当然駄目
    staff.setNote("sakai");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("note");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    assertEquals("propertiesの名前", prettyFieldName, "Note");
    ValidatorResult result = results.getValidatorResult("note");
    ValidatorAction action = resources.getValidatorAction("date");

    assertFalse(result.isValid("date"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName};
    
    assertEquals("message", "Note is not a date.", MessageFormat
        .format(message, args));

    //2005-09-19
    staff.setNote("2005-09-19");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("note");

    //System.out.println(action.getMethod());

    assertTrue(result.isValid("date"));

    
    //2005-9-1
    staff.setNote("2005-9-1");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("note");

    //System.out.println(action.getMethod());
    assertTrue(result.isValid("date"));

  }

  public void testMaskTest(){

    //<var-value>^[a-zA-Z]*$</var-value>

    //当然駄目
    staff.setPass("さかい");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("pass");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    System.out.println(prettyFieldName);
    
    assertEquals("propertiesの名前", prettyFieldName, "Password");
    ValidatorResult result = results.getValidatorResult("pass");
    ValidatorAction action = resources.getValidatorAction("mask");

    assertFalse(result.isValid("mask"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName};
    
    assertEquals("message", "The Password is not valid.", MessageFormat
        .format(message, args));

    //sakai
    staff.setPass("sakai");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("pass");

    //System.out.println(action.getMethod());

    assertTrue(result.isValid("mask"));

    
    //sakai123
    staff.setPass("sakai123");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("pass");

    //System.out.println(action.getMethod());
    assertFalse(result.isValid("mask"));

  }

  public void testUrlTest(){

    //<var-value>^[a-zA-Z]*$</var-value>

    //当然駄目
    staff.setLoginName("さかい");

    ValidatorResources resources = null;
    resources = loader.getResources();
    Validator validator = new Validator(resources, "commons");
    validator.setOnlyReturnErrors(false);
    // Tell the validator which bean to validate against.
    validator.setParameter(Validator.BEAN_PARAM, staff);
    ValidatorResults results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }

    Form form = resources.getForm(Locale.getDefault(), "commons");

    Field field = form.getField("loginName");
    String prettyFieldName = apps.getProperty(field.getArg(0).getKey());

    System.out.println(prettyFieldName);
    
    assertEquals("propertiesの名前", prettyFieldName, "Login Name");
    ValidatorResult result = results.getValidatorResult("loginName");
    ValidatorAction action = resources.getValidatorAction("url");

    assertFalse(result.isValid("url"));

    String message = apps.getProperty(action.getMsg());
    //System.out.println(message);

    Object[] args = {prettyFieldName};
    
    assertEquals("message", "Login Name is not an invalid URL.", MessageFormat
        .format(message, args));

    //sakai
    staff.setLoginName("http://www.chikkun.com/ml/");
    results = null;

    try {
      results = validator.validate();
    } catch (ValidatorException err) {
      err.printStackTrace();
    }
    result = results.getValidatorResult("loginName");

    //System.out.println(action.getMethod());

    assertTrue(result.isValid("url"));
  }

  
  protected void setUp() throws Exception {
    super.setUp();
    staff = new Staff();
  }

  protected void tearDown() throws Exception {
    super.tearDown();
  }

}
                                              

ふう、なんとかテストケースを通った。

落ち穂拾い(特にvalidatorの使い方)

  1. POJOで使うためにpropertiesやvalidation.xmlのロードの仕方

    下の、ValidatorLoaderを見ればわかるように、このValidatorLoaderと同じディレクトリーにある validator.propertiesがあって、そこに

    validator-pathnames=validator.xml
                                                              

    と書かれていたならば、これを読み込む。なかったならデフォルトで String paths = "validator-rules.xml,validator.xml"を読み込む。 どちらもない場合は、たぶん、validateが行われない、と思う。

    /*
     * 作成日: 2005/09/15
     *
     */
    package com.chikkun.common.validator;
    
    import java.io.BufferedInputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    import java.util.ResourceBundle;
    import java.util.StringTokenizer;
    
    import org.apache.commons.validator.ValidatorResources;
    
    public class ValidatorLoader {
      private final static String RESOURCE_DELIM = ",";
    
      protected ValidatorResources resources = null;
    
      private String pathnames = null;
    
      private Properties props;
    
      public ValidatorLoader() throws IOException {
        loadPathnames();
        initResources();
      }
    
      protected void loadPathnames() {
        // Set a default just in case
        String paths = "validator-rules.xml,validator.xml";
        InputStream stream = null;
        try {
          // Load some properties file
          stream = ValidatorLoader.class.getResourceAsStream("validator.properties");
          if (stream != null) {
            props = new Properties();
            props.load(stream);
            // Get the pathnames string from the properties file
            paths = props.getProperty("validator-pathnames");
          }
        } catch (IOException ex) {
          ex.printStackTrace();
        }
        setPathnames(paths);
      }
    
      protected void initResources() throws IOException {
        if (getPathnames() != null && getPathnames().length() > 0) {
          StringTokenizer st = new StringTokenizer(getPathnames(), RESOURCE_DELIM);
          while (st.hasMoreTokens()) {
            String validatorRules = st.nextToken();
            validatorRules = validatorRules.trim();
            InputStream input = null;
            BufferedInputStream bis = null;
            input = ValidatorLoader.class.getResourceAsStream(validatorRules);
            
            if (input != null) {
              bis = new BufferedInputStream(input);
              try {
                // pass in false so resources aren't processed
                // until last file is loaded
                resources = new ValidatorResources(bis);
              } catch (Exception ex) {
                ex.printStackTrace();
              }
            }else{
              System.out.println("can not find rules");
            }
          }
          // process resources
          resources.process();
        }
      }
    
      public Properties getProps() {
        return this.props;
      }
    
      public void setProps(Properties props) {
        this.props = props;
      }
    
      public void setResources(ValidatorResources resources) {
        this.resources = resources;
      }
    
      public ValidatorResources getResources() {
        return resources;
      }
    
      public String getPathnames() {
        return pathnames;
      }
    
      public void setPathnames(String pathnames) {
        this.pathnames = pathnames;
      }
    
                                                              }        

    上記のクラスを使って、次のようにロードする。

        Properties apps = null;
        ValidatorLoader loader;
        try {
          loader = new ValidatorLoader();
          apps = loader.getProps();
        } catch (IOException err) {
          err.printStackTrace();
        }
        ValidatorResources resources = null;
        resources = loader.getResources();
                                                              

    これで ValidatorResources resources Properties apps を取得する。

    これらの使い方は後述。

  2. validateするクラス(今回はMyValidatorとFieldCheck)と設定ファイル(validator.xml)の作成
    • validateするクラス

      Commons Validatorのクラスを利用しても、自分で用意しても(今回はベースになるJapaneseValidatorを作成)OK。ただし、validateする際のシグネチャは以下のようにする。

        public static boolean validateKatakana(Object bean, Field field)
                                                                    

      で、引数のbeanはvalidateの対象となるbean、Fieldはorg.apache.commons.validator.Fieldです。ただこのメソッド自体は、 Commons Validatorが呼び出すので、自分で呼び出すことは、たぶん、ないと思う。

    • 設定ファイルの作成

      globalの設定をまず書く。下の、name属性は、あとで作成するformの時のやりたいvalidateの名前。class属性は上記で作ったクラス名(MyValidator)。 methodは上記の実際validateするメソッド名。methodParamsはvalidateKatakana(Object bean, Field field)(これは、もちろん変えられる)。 msgはpropertiesに書かれた、validateに失敗したときのメッセージ。

            <validator name="kanji"
                       classname="com.chikkun.common.validator.MyValidator"
                       method="validateKanji"
                       methodParams="java.lang.Object,org.apache.commons.validator.Field"
                       msg="errors.kanji"/>
                                                                    
    • 実際のform(strutsの名残の名前だが、実際はデータを保持しているbean名)ごとの設定

      formタグのname属性はbeanの名前。といっても、実際のクラス名じゃなくて良い。 Validator validator = new Validator(resources, "commons");と指定するのと一致すればよい。 fieldタグのproperty属性は実際のフィール名。depends属性は上記のglobalで設定したvalidateの名前。複数ある場合にはコンマでつなげる。 その他、変数が必要な場合は書き加える。自分で作ったもの以外は、書き方参照。

           <form name="commons">
               <field property="loginName"  depends="required,url">
               	   <arg0 key="form.loginName.displayname"/>
                         <var>
                              <var-name>allowallschems</var-name>
                              <var-value>true</var-value>
                         </var>
               </field>    
            </form>
                                                                    
  3. 実際の使い方。

    上記の設定のロードの後、validateする。

        Properties apps = null;
        ValidatorLoader loader;
        try {
          loader = new ValidatorLoader();
          apps = loader.getProps();
        } catch (IOException err) {
          err.printStackTrace();
        }
        ValidatorResources resources = null;
        resources = loader.getResources();
        
        //validatorをインスタンスに"commons"がform(bean)名。
        Validator validator = new Validator(resources, "commons");
        validator.setOnlyReturnErrors(false);
        // 対象となるbeanを引数に渡す(すでに値が入っている必要がある)
        validator.setParameter(Validator.BEAN_PARAM, staff);
        ValidatorResults results = null;
    
        //ここで実際のvalidate
        try {
          results = validator.validate();
        } catch (ValidatorException err) {
          err.printStackTrace();
        }
    
        //設定ファイルに書いてある情報を得るためにFormをインスタンス化
        Form form = resources.getForm(Locale.getDefault(), "commons");
        //formから個別のfieldを取得
        Field field = form.getField("name");
        //propertiesからフィールドの正式名を取得
        String prettyFieldName = apps.getProperty(field.getArg(0).getKey());
    
        //今回ならnameのvalidateの結果を取得
        ValidatorResult result = results.getValidatorResult("name");
    
        //validateの中でもmaxlengthの結果を取得。
        //※複数している場合は、何故か、最後のものしかtrueが返ってこない。
        //つまり、requiredは通過して(OKで)、maxlengthは駄目だとして、
        //ValidatorAction action = resources.getValidatorAction("required");
        //はfalseが返ってくるんだな、これが。
        ValidatorAction action = resources.getValidatorAction("maxlength");
        
        //propertiesからエラーのメッセージを取得
        String message = apps.getProperty(action.getMsg());
        //errors.maxlength={0} cannot be greater than {1} characters.
        //{0}や{1}に当てはまる値をObject[]配列に格納。
        Object[] args = {prettyFieldName,field.getVarValue("maxlength")};
        //これで上記の値で置き換える
        String errMessage = MessageFormat.format(message, args);
                                                              

    上記の※のところが、ちょいまだCommons Validatorの仕様がわかっていないところだけど、validator.setOnlyReturnErrors(false);trueにしておけば、「Returns true if the Validator is only returning Fields that fail validation.」ということなので、これで当面いこう。

まだ少々面倒なので、さらにユーティリティクラスを作成する必要があるか・・・・。

その後、ユーティリティークラスとともに、メッセージを取得する必要があるので、FieldCheck.classの引数なども変更し、未だ未完成。ちょい飽きたので、ここで休憩。