Skip to content

Commit

Permalink
[#395] Docs: add section on writing a custom validation check and ann…
Browse files Browse the repository at this point in the history
…otation
  • Loading branch information
Peter Hilton (Lunatech) committed Jun 24, 2011
1 parent 63b291f commit 9f24fe4
Showing 1 changed file with 95 additions and 2 deletions.
97 changes: 95 additions & 2 deletions documentation/manual/validation.textile
Expand Up @@ -333,7 +333,7 @@ h2. <a name="builtin">Built-in validations</a>

The **play.data.validation** package contains several "built-in validations":validation-builtin that you can use on the **Validation** object or with annotations.

h2. <a name="custom">Custom validation</a>
h2. <a name="custom">Custom validation using @CheckWith</a>

Can’t find the validator you need in the **play.data.validation** package? Write your own. You can use the generic **@CheckWith** annotation to bind your own **Check** implementation.

Expand All @@ -350,10 +350,103 @@ bc. public class User {
public boolean isSatisfied(Object user, Object password) {
return notMatchPreviousPasswords(password);
}

}
}

The default validation error message key is @validation.invalid@. To use a different key, call @Check.setMessage@ with a message key and message parameters.

bc. static class MyPasswordCheck extends Check {

public boolean isSatisfied(Object user, Object password) {
final Date lastUsed = dateLastUsed(password);
setMessage("validation.used", JavaExtensions.format(lastUsed));
return lastUsed == null;
}
}

The message look up always has the field name as the first parameter, and your message parameters as subsequent parameters. So, for the example above, you could define the message like:

This comment has been minimized.

Copy link
@opensource21

opensource21 Jun 27, 2011

Contributor

I think "subsequent parameters" isn't clear enough. I would add ordered by name.


bc. validation.used = &{%1$s} already used on date %2$s

This comment has been minimized.

Copy link
@opensource21

opensource21 Jun 27, 2011

Contributor

Where is the meaning of &{} explained? I have no idea what the meaning of it could be.

user.password = Password


h2. <a name="customannotations">Custom annotations</a>

You can also write your own annotation validations, which is more complex but makes your model code cleaner and allows you to introduce validator parameters.

For example, suppose we want a less restrictive version of the "@URL":validation-builtin#url validation, so we can allow URLs with any scheme such as a @file://@ URL, and with a parameter that lets us specify exactly which schemes are allowed.

First, we write a custom validation annotation, with a parameter for overriding the default message:

bc. import net.sf.oval.configuration.annotation.Constraint;
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Constraint(checkWith = URICheck.class)
public @interface URI {
String message() default URICheck.message;
}

This annotation refers to an implementation of @net.sf.oval.configuration.annotation.AbstractAnnotationCheck@.

bc. public class URICheck extends AbstractAnnotationCheck<URI> {

/** Error message key. */
public final static String message = "validation.uri";

/** URI schemes allowed by validation. */
private List<String> schemes;

@Override
public void configure(URI uri) {
setMessage(uri.message());
this.schemes = Arrays.asList(uri.schemes());
}

/**
* Add the URI schemes to the message variables so they can be included
* in the error message.
*/
@Override
public Map<String, String> createMessageVariables() {
final Map<String, String> variables = new TreeMap<String, String>();
variables.put("2", JavaExtensions.join(schemes, ", "));
return variables;
}

@Override
public boolean isSatisfied(Object validatedObject, Object value,
OValContext context, Validator validator) throws OValException {

requireMessageVariablesRecreation();
try {
final java.net.URI uri = new java.net.URI(value.toString());
final boolean schemeValid = schemes.contains(uri.getScheme());
return schemes.size() == 0 || schemeValid;
} catch (URISyntaxException e) {
return false;
}
}
}

The @isSatisfied@ method calls @requireMessageVariablesRecreation()@ to instruct OVal to call @createMessageVariables()@ before rendering the message. This returns an ordered map of variables that are passed to the message formatter. The map keys are not used; the @"2"@ in this example indicates the message parameter index. As before, the first parameter is the field name.

To use this use the annotation on a model property.

bc. public class User {

@URI(message = "validation.uri.schemes", schemes = {"http", "https"})
public String profile;
}

We can define the messages like this:

bc. validation.uri = Not a valid URI
validation.uri.schemes = &{%1$s} is not a valid URI - allowed schemes are %2$s



p(note). **Continuing the discussion**

The last layer of a Play application: %(next)"Domain object model":model%.

1 comment on commit 9f24fe4

@opensource21
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would recommend that the expression %1$s will be explained somewhere in the documentation.

Please sign in to comment.