入力フォームでユーザに文字を入力させると、前後に空白スペースなどを入れちゃったりしますよね。Validatorで前後に空白スペースがあればエラーにしても良いですが、それはちょっと不親切なので自動的に取り除いてあげるのが親切かなと思います。また、String の trim() 関数だと全角スペース1文字がエラーになりません。得てして前後にスペースを入れちゃう人は全角のスペースを入れるのでこれもチェックしないといけません。というわけで、Cubby 1.1.x 向けにValidatorを書いてみました。
取り除きたい文字は半角・全角スペースだけとも限らないので、取り除きたい正規表現を前後別々に指定できるようになっています。引数が1個の時は前後とも同じ正規表現が使用されます。
- RegexTrimRequiredValidator
import org.seasar.cubby.validator.MessageHelper;
import org.seasar.cubby.validator.ScalarFieldValidator;
import org.seasar.cubby.validator.ValidationContext;
import org.seasar.framework.util.StringUtil;
/**
* 先頭と末尾を正規表現でtrimした文字列の必須検証をします。
* <p>
* trimした文字列の長さが0の場合、検証エラーとなります。
* </p>
* <p>
* デフォルトエラーメッセージキー: valid.required
* </p>
*
* @author jfut
*/
public class RegexTrimRequiredValidator implements ScalarFieldValidator {
/** メッセージヘルパ */
private final MessageHelper messageHelper;
/** 文字の先頭から除去する正規表現 */
private String prefixRegexTrim;
/** 文字の末尾から除去する正規表現 */
private String postfixRegexTrim;
/** 正規表現における先頭識別文字 */
private static final char BEGIN_CHAR = '^';
/** 正規表現における末尾識別文字 */
private static final char END_CHAR = '$';
/**
* インスタンスを作成します。
*/
public RegexTrimRequiredValidator() {
this(null, null);
}
/**
* インスタンスを作成します。
*
* @param regexTrim
* 除去する正規表現
*/
public RegexTrimRequiredValidator(final String regexTrim) {
this(regexTrim, regexTrim);
}
/**
* インスタンスを作成します。
*
* @param prefixRegexTrim
* 文字の先頭から除去する正規表現
* @param postfixRegexTrim
* 文字の末尾から除去する正規表現
*/
public RegexTrimRequiredValidator(final String prefixRegexTrim,
final String postfixRegexTrim) {
this(prefixRegexTrim, postfixRegexTrim, "valid.required");
}
/**
* エラーメッセージキーを指定してインスタンスを作成します。
*
* @param prefixRegexTrim
* 文字の先頭から除去する正規表現
* @param postfixRegexTrim
* 文字の末尾から除去する正規表現
* @param messageKey
* エラーメッセージキー
*/
public RegexTrimRequiredValidator(final String prefixRegexTrim,
final String postfixRegexTrim, final String messageKey) {
this.prefixRegexTrim = prefixRegexTrim;
this.postfixRegexTrim = postfixRegexTrim;
this.messageHelper = new MessageHelper(messageKey);
setupRegexTrim();
}
/**
* 正規表現をセットアップします。
*/
public void setupRegexTrim() {
// prefixRegexTrim
if (prefixRegexTrim != null) {
// 文字の末尾が $ の場合、取り除きます
if (prefixRegexTrim.charAt(prefixRegexTrim.length() - 1) == END_CHAR) {
prefixRegexTrim =
prefixRegexTrim.substring(0, prefixRegexTrim.length() - 1);
}
// 文字の先頭に ^ が無い場合、追加します
if (prefixRegexTrim.charAt(0) != BEGIN_CHAR) {
prefixRegexTrim = BEGIN_CHAR + prefixRegexTrim;
}
} else {
prefixRegexTrim = "";
}
// postfixRegexTrim
if (postfixRegexTrim != null) {
// 文字の先頭が ^ の場合、取り除きます
if (postfixRegexTrim.charAt(0) == BEGIN_CHAR) {
postfixRegexTrim =
postfixRegexTrim.substring(1, postfixRegexTrim.length());
}
// 文字の末尾に $ が無い場合、追加します
if (postfixRegexTrim.charAt(postfixRegexTrim.length() - 1) != END_CHAR) {
postfixRegexTrim = postfixRegexTrim + END_CHAR;
}
} else {
postfixRegexTrim = "";
}
}
/**
* {@inheritDoc}
*/
public void validate(final ValidationContext context, final Object value) {
if (value instanceof String) {
final String str = trim((String)value);
if (!StringUtil.isEmpty(str)) {
return;
}
} else if (value != null) {
return;
}
context.addMessageInfo(this.messageHelper.createMessageInfo());
}
/**
* 指定された文字列の先頭と末尾をtrimします。
*
* @param value
* 文字列
* @return trimされた文字列
*/
public String trim(String value) {
if (value != null) {
value = value.replaceAll(prefixRegexTrim, "");
value = value.replaceAll(postfixRegexTrim, "");
}
return value;
}
}
- 適当なActionでの使用例
入力値をチェックしつつ、不要な文字を取り除いても有効な文字がある時は、同じ条件で不要な文字をtrimできるようにインスタンス化して使用します。ポイントはパラメータがバインドされたインスタンスをtrimするために、ValidationRulesでValidateしつつ、最後にインナークラスRegexTrimValidationRuleFilterでtrimを実行しておくところです。これによりアクションメソッド実行時には既にtrimされた値が入ったインスタンスを使うことができます(参考: リクエストからアクション実行までのフロー、Action#preactionなんてのがあるとそこが適切かも?Interceptorだとちょっと書きにくいし)。
この例では、入力値の前後の半角・全角スペースをすべて取り除いても有効な文字があるかどうかをチェックし、そして、RegexTrimValidationRuleFilterで実際に不要な文字を取り除きます。
@Path("register")
public class RegisterAction extends Action {
// -------------------------------------------------- [Validation]
private final RegexTrimRequiredValidator trimRequiredValidator =
new RegexTrimRequiredValidator("[\\s ]*");
@Binding(bindingType = BindingType.NONE)
public ValidationRules registerValidation = new DefaultValidationRules() {
@Override
public void initialize() {
add("token", new TokenValidator());
add("lastName", trimRequiredValidator);
add("firstName", trimRequiredValidator);
add("lastNameEnglish", trimRequiredValidator);
add("firstNameEnglish", trimRequiredValidator);
add("mailAddress1", trimRequiredValidator, new EmailValidator());
add("mailAddress2", trimRequiredValidator, new EmailValidator());
add("inside", new RequiredValidator());
// add("uid", validator);
add("laboratory", trimRequiredValidator);
add("promotion", trimRequiredValidator);
add(...他のValidationRule...);
add(new RegexTrimValidationRuleFilter());
}
};
// -------------------------------------------------- [DI Filed]
@Resource
private UserService userService;
// -------------------------------------------------- [Attribute]
protected RegisterFormDto registerFormDto;
// -------------------------------------------------- [Action Method]
public ActionResult index() {
return new Forward("/register/index.html");
}
@Accept(POST)
@Form("registerFormDto")
@Validation(rules = "registerValidation", errorPage = "/register/index.html")
public ActionResult confirm() {
return new Forward("/register/confirm.html");
}
@Path("process")
@OnSubmit("apply")
@Accept(POST)
@Form("registerFormDto")
@Validation(rules = "registerValidation", errorPage = "/register/confirm.html")
public ActionResult processApply() {
registerFormDto.mailAddress = registerFormDto.mailAddress1;
User user =
Beans.createAndCopy(User.class, registerFormDto).execute();
userService.insertAndSendMail(user);
return new Forward("/register/success.html");
}
... 他のアクションメソッド省略 ...
// -------------------------------------------------- [Helper Method]
// -------------------------------------------------- [Validation Class]
private class RegexTrimValidationRuleFilter implements ValidationRule {
public void apply(Map<String, Object[]> params, Object form,
ActionErrors errors) {
// BeanDesc と PropertyDesc を使って汎用的にしても良いですね
registerFormDto.lastName =
trimRequiredValidator.trim(registerFormDto.lastName);
registerFormDto.firstName =
trimRequiredValidator.trim(registerFormDto.firstName);
registerFormDto.lastNameEnglish =
trimRequiredValidator.trim(registerFormDto.lastNameEnglish);
registerFormDto.firstNameEnglish =
trimRequiredValidator.trim(registerFormDto.firstNameEnglish);
registerFormDto.mailAddress1 =
trimRequiredValidator.trim(registerFormDto.mailAddress1);
registerFormDto.mailAddress2 =
trimRequiredValidator.trim(registerFormDto.mailAddress2);
registerFormDto.laboratory =
trimRequiredValidator.trim(registerFormDto.laboratory);
registerFormDto.promotion =
trimRequiredValidator.trim(registerFormDto.promotion);
}
}
...
}
- RegexTrimRequiredValidatorTest
テストケースも書いておきます(半角・全角スペースの違いが判り難いかも)。
public class RegexTrimRequiredValidatorTest {
@Test
public void test1() {
RegexTrimRequiredValidator validator;
validator = new RegexTrimRequiredValidator();
assertNull(validator.trim(null));
assertEquals("", validator.trim(""));
assertEquals(" ", validator.trim(" "));
assertEquals("abc", validator.trim("abc"));
validator = new RegexTrimRequiredValidator("[\\s ]*");
assertNull(validator.trim(null));
assertEquals("", validator.trim(""));
assertEquals("", validator.trim(" "));
assertEquals("", validator.trim(" "));
assertEquals("a b", validator.trim(" a b "));
assertEquals("a b", validator.trim(" a b "));
assertEquals("abc", validator.trim("abc"));
validator = new RegexTrimRequiredValidator("^[\\s ]*$");
assertNull(validator.trim(null));
assertEquals("", validator.trim(""));
assertEquals("", validator.trim(" "));
assertEquals("", validator.trim(" "));
assertEquals("a b", validator.trim(" a b "));
assertEquals("a b", validator.trim(" a b "));
assertEquals("abc", validator.trim("abc"));
validator = new RegexTrimRequiredValidator("^[\\s ]*$", "^[\\s]*$");
assertNull(validator.trim(null));
assertEquals("", validator.trim(""));
assertEquals("", validator.trim(" "));
assertEquals("", validator.trim(" "));
assertEquals("a b", validator.trim(" a b "));
assertEquals("a b ", validator.trim(" a b "));
assertEquals("abc", validator.trim("abc"));
}
}
書いてみてなかなか便利だったので3月に書いたアプリでは大活躍でした(^^)。
[2009-04-17 14:09追記]: 日記用にActionクラスを適当に書き得てたとこがおかしかったので修正。
この記事に関連する別の記事:
- 1Action 1ActionFormを1Event 1ActionFormへ ...
- S2Struts HotDeployでActionConfigのパス修正への道 (4) ...
- HOT deploy時のサブアプリケーションのServiceクラスの==コンポーネント名==setterのメソッド名 (解決) ...
- Cubbyのアクションクラスで定数を利用してリファクタリングしやすくする Cubbyのアクションクラスにはバリデーションルールの定義時やアノテーションに文字列が登場します。文字列で指定しているのでリファクタリングする時に修正漏れなどがおきそうでちょっと怖いです。そこで、それらの文字列を定数にし [...]...
- Cubbyの良いところ 先日Cubbyで作った2個目の簡単なアプリを予定通り動かし始めました。今のとこ大きなトラブルもないようです。というわけで、Cubbyの良いところを書きたいと思います。ちなみにほとんど同じことがSAStrutsにも言えます [...]...
- Newer: Windows 7 RC (x86/x64) と Vista SP2 お試し
- Older: 3月から朝から晩までモードだった
コメント:0
トラックバック:0
- このエントリーのトラックバックURL
- http://jfut.integ.jp/2009/04/16/cubby-regex-trim-required-validator/trackback/
- Listed below are links to weblogs that reference
- 入力された文字の前後の空白スペースなどの文字列を除去しても有効な文字があるか検証するValidatorと実際に取り除く処理 from ふたつの川うるおう日記

