JSR303のアノテーションを利用する
Androidアプリケーション上でバリデーションの仕組みを構築するために、JSR 303:Beans Validationのアノテーションを利用してみよう、と以前に書いた。
AndroidのためのValidation
Beans Validationは一般的なバリデーションの制約として幾つかのアノテーションを提供している。
例えば、"NotNull"アノテーションは対象が非Nullであることを制約とするアノテーションだ。(ソースコードはJBossが公開する、"Hibernate Validator(http://www.hibernate.org/subprojects/validator.html)"のリポジトリからダウンロードできる"validation-api-1.0.0.GA"を使用している)
- NotNull.java
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {})
public @interface NotNull {
String message() default {javax.validation.constraints.NotNull.message};
Class groups() default { };
Class payload() default {};
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
NotNull[] value();
}
}
このまま使うのが筋なのだが、当面永続化のことは考えない(Hibernateはまず使わない)のと、Android向けということで極力シンプルな作りとしたいので、以下の属性は使わないこととする。
- Class[] groups()
この制約に対するバリデーションが属するグループを指定する
- Class[] payload()
この制約のペイロード(積載? 荷重?)を指定する
- @interface List
この制約を複数指定する場合に使用する
また、@ConstraintアノテーションとそのvalidatedBy属性だが
@Documented @Target({ ANNOTATION_TYPE }) @Retention(RUNTIME) public @interface Constraint { public Class>[] validatedBy(); }
中を見ると、制約バリデーションを実装するクラスをConstraintValidatorインタフェースの実装クラスに限定しているが、これも止めて単純にバリデーションを実施する型を指定する@ValidatorClassアノテーションを新規に作り、代わりに使用する。
- ValidatorClass.java
@Target({ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ValidatorClass { Class value(); }
最後にmessage()属性だがデフォルト値は、提供されているソースコードではプロパティのキーであり、リソースの外部化を実現する。
String message() default {javax.validation.constraints.NotNull.message};
このように実装ではValidationMessages.properties等のローカライズ可能なJavaプロパティファイルとして定義しておき、実行時にリソースの文字列で置き換える。
- ValidationMessages.properties
javax.validation.constraints.AssertFalse.message=must be false javax.validation.constraints.AssertTrue.message=must be true : javax.validation.constraints.NotNull.message=may not be null javax.validation.constraints.Null.message=must be null javax.validation.constraints.Past.message=must be in the past javax.validation.constraints.Pattern.message=must match "{regexp}" javax.validation.constraints.Size.message=size must be between {min} and {max} :
Androidは標準的なリソースには.propertiesは使わず、バイナリ化されるXMLを使用するので、
int message() default R.string.NotNull;
と、int型に変更してstrings.xmlを作成、参照することで.properties同様に、外部化とローカライズに対応したい。
- strings.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <!-- Beans Validation --> <string name="AssertFalse">must be false</string> <string name="AssertTrue">must be true</string> : <string name="NotNull">may not be null</string> <string name="Null">must be null</string> <string name="Past">must be in the past</string> <string name="Pattern">must match "{regexp}"</string> <string name="Size">size must be between {min} and {max}</string> : </resources>
strings.xmlはコンパイルすることでR.javaという名前のJavaソースコードに変換されて、他のコードと共にコンパイルされるため、実行前にエラーチェックを行うことができる。
- R.java
public final class R { : public static final class string { public static final int NotNull=0x7f050001; : } }
最終的イメージ
- NotNull.java (改)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented @ValidatorClass(NotNullValidator.class) public @interface NotNull { int message() default R.string.NotNull; }
次回はこれらのアノテーションを使ったバリデータの実装を考えてみようと思う。