Declarative validation
The mustSatisfy() element allows arbitrary validation to be applied to parameters using an (implementation of a) Specification object.
|
The specification implementations can (of course) be reused between parameters and properties. |
The Specification is consulted during validation, being passed the proposed value.
If the proposed value fails, then the value returned is the used as the invalidity reason.
For example:
StartWithCapitalLetterSpecification.java
public class StartWithCapitalLetterSpecification
extends AbstractSpecification<String> { (1)
public String satisfiesSafely(String proposed) {
return "".equals(proposed)
? "Empty string"
: !Character.isUpperCase(proposed.charAt(0))
? "Does not start with a capital letter"
: null;
}
}
| 1 | the AbstractSpecification class conveniently handles type-safety and dealing with null values.
The applib also provides SpecificationAnd and SpecificationOr to allow specifications to be combined "algebraically". |
can then be used:
CustomerRepository.java
public class CustomerRepository {
public Customer newCustomer(
@Parameter(
mustSatisfy=StartWithCapitalLetterSpecification.class
)
final String firstName,
@Parameter(
mustSatisfy=StartWithCapitalLetterSpecification.class
)
final String lastName) {
// ...
}
...
}
i18n
It is also possible to provide translatable reasons.
Rather than implement Specification, instead implement Specification2.
This defines the API:
public interface Specification2 extends Specification {
public TranslatableString satisfiesTranslatable(Object obj); (1)
}
| 1 | Return null if specification satisfied, otherwise the reason as a translatable string |
With Specification2 there is no need to implement the inherited satifies(Object); that method will never be called.