Form Field Annotations - Built-in
Introduction
Form field annotations configure policies to apply to the submitted parameter value with the same name as the member field, if any, and look like the following. Unlike standard Struts, parameters with duplicate names aren't recognised. However, an annotated collection field means a single parameter value is split into a collection.
public class ContentTypeForm extends AbstractForm {
@Required(message="The id must be a number")
@IntegerConversion(message="The id must be a number")
private int id;
@Trim
@Required(message="A name is required")
private String name;
private String notes;
private boolean watermark;
...
}
For each member field, policies are grouped by type and applied in the following order of type, then in written order. If a policy fails or rejects, it sets an error message for the user and aborts processing for later types, completing any remaining policies of the same type (though see shortCircuit parameter below).
| Step | Policy type | Field set | Notes |
|---|---|---|---|
| 1 | Adjusters | Alters submitted value, such as trimming whitespace. This step cannot abort processing. | |
| 2 | Non-conversion validators | Typically validates for string fields only, with @Required as the notable exception for all data types. | |
| 3 | Converter or collection converter | Attempts to convert to member field's data type. No more than one converter or collection converter can be set. If none are set, uses the default converter or collection converter for the data type. For single string fields, this and later steps are skipped. | |
| 4 | Post-conversion adjustors or post-conversion collection adjustors | Yes | Alters member field value, such as setting start of day for date field. This step cannot abort processing. |
| 5 | Post-conversion validators or post-conversion collection validators | Yes | Validates member field value. |
Common Annotation Parameters
If a conversion fails or a validator rejects, the message for the user and behaviour is controlled by annotation parameters.
| Parameter | Notes |
|---|---|
| message | If not empty string, message to present. |
| messageKey | If not empty string, message name to extract from Action TextProvider, or uses message value if not found. |
| messageType | Sets message list for message: ERROR - Action-level error, FIELD - field specific error using field name, MESSAGE - Action-level info message, WARNING - Action-level warning message. Defaults to ERROR. |
| shortCircuit | If true and validation fails, remaining validators are skipped. Usually defaults to false. |
Adjusters
Adjusters edit submitted parameter values before any validation and conversion. The built-in adjusters are:
| Annotation | Notes |
|---|---|
| @ToLowerCase | Applies String.toLowerCase(). |
| @ToUpperCase | Applies String.toUpperCase(). |
| @Trim | Applies String.trim(). |
Non-conversion Validators
Non-conversion validators check submitted parameter values before conversion. They're rarely applied to non-string fields except for @Required. The built-in non-conversion validators are:
| Annotation | Notes |
|---|---|
| @MaxLength | |
| @Regex | Checks using regular expression. |
| @Required | Checks any value set. |
Converters and Collection Converters
Converters convert a submitted value, which is a string, to the form field's type and only one applies. They don't apply to string fields. Converters for collection fields are known as collection converters.
If no submitted value exists or is empty string, no conversion happens and the field is not set. Later steps still happen but most post-conversion policies ignore no value.
If no converter annotation is set, the default converter for the field type is used. If no applicable default converter exists, no conversion happens and later steps are skipped. The built-in converters are:
| Annotation | Field Type | Default Converter | Notes |
|---|---|---|---|
| @BigDecimalConversion | BigDecimal | Yes | |
| @BooleanConversion | Boolean or boolean | Yes | |
| @ByteConversion | Byte or byte | Yes | |
| @CharacterConversion | Character or char | Yes | |
| @DateConversion | Date | Yes | |
| @DoubleConversion | Double or double | Yes | |
| @EnumConversion | Any enumerated type | Yes | |
| @FloatConversion | Float or float | Yes | |
| @IntegerConversion | Integer or int | Yes | |
| @LongConversion | Long | Yes | |
| @ShortConversion | Short or short | Yes |
| Annotation | Collection Type | Default Converter | Notes |
|---|---|---|---|
| @IntegerCSVConversion | Integer | Yes | Splits a string by comma or other separator into integers |
| @StringCSVConversion | String | Yes | Splits a string by comma or other separator |
Post-conversion Adjusters
Post-conversion adjusters edit converted values before post-conversion validation. To date, there are no collection post-conversion adjusters. The built-in post-conversion adjusters are:
| Annotation | Notes |
|---|---|
| @ToEndOfDayAdjuster | Sets midnight of the day. |
| @ToStartOfDayAdjuster | Sets the last millisecond of the day. |
Post-conversion Validators and Collection Post-conversion Validators
Post-conversion validators and collection post-conversion validators check converted values. The built-in post-conversion validators and collection post-conversion validators are:
| Annotation | Notes |
|---|---|
| @IntegerRange | |
| @MinInteger |
| Annotation | Notes |
|---|---|
| @RequiredIntegerEntries | Checks no entry is null. |
Manual Validation
Where validation is too complex for built-in annotations, manual validation can always be used. This only applies to the form of a form processing Action (see Quick Start). Override the doValidate2 function, such as below. Note that a custom or bespoke validator might be possible. See Form Field Annotations - Custom and Bespoke.
private String name;
@Override
public void doValidate2(ValidationAware2 validationAware, TextProvider textProvider) {
if (name.equals("Other")) {
validationAware.addActionError("'Other' is a reserved filter name");
}
}
Manual Parameter Conversion
Where conversion is too complex for built-in annotations, manual parameter conversion can be used. Override the manualParameterConvert function, such as below. Note that a custom or bespoke validator might be possible. See Form Field Annotations - Custom and Bespoke.
private Date dateOfBirth;
@Override
public ManualParameterConversionResult manualParameterConvert(Map<String,String> unprocessedParameters, ValidationAware validationAware, TextProvider textProvider) {
ManualParameterConversionResult result;
String day, month, year;
result = new ManualParameterConversionResult();
try {
day = unprocessedParameters.get("day");
month = unprocessedParameters.get("month");
year = unprocessedParameters.get("year");
dateOfBirth = validateDate(day, month, year);
result.getProcessedParameterNames().add("day");
result.getProcessedParameterNames().add("month");
result.getProcessedParameterNames().add("year");
}
catch (NumberFormatException e) {
validationAware.addActionError("Day, month, or year is not a number, you should not see this message");
result.getFailedParameterNames().add("day");
result.getFailedParameterNames().add("month");
result.getFailedParameterNames().add("year");
}
catch (IllegalArgumentException e) {
validationAware.addActionError("This day, month and year combination doesn't exist in the calendar");
result.getFailedParameterNames().add("day");
result.getFailedParameterNames().add("month");
result.getFailedParameterNames().add("year");
}
return result;
}