Commons Validation on 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ファイルの名前
優先順位がある。以下にその優先順位を挙げる。
- defaultでは、xmlがvalidator.xmlとvalidatorRules.xml、propertiesがvalidator.xml。
- propertiesにvalidator-pathnames=validator-rules.xml,validator-spring.xmlというように書き込んでおくと、 これがxmlファイルのロードファイルは一番優先される(以下のValidatorUtilsクラス 生成する際の引数よりもさらに優先されることに注意!)
- ValidatorUtilss util = new Validatortils(String propertiesName)のように、propertiesの名前を指定した場合は それが使われる。
- 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を拡張し見ようと思う。流れとしては
- もしcommonsにある場合にはそれを当然使うる。
- ない場合は、validateするメソッドを自作する。
- 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の方はおなじみ。
そこで、今回は、上記のように
- loginNameが必須(文字等の指定なし)
- passも同様
- 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の使い方)
- 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 を取得する。
これらの使い方は後述。
- 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>
- validateするクラス
- 実際の使い方。
上記の設定のロードの後、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の引数なども変更し、未だ未完成。ちょい飽きたので、ここで休憩。


