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. Where the field is an array or collection, the multiple parameters with the same name are recognised and annotations apply to each one. However, list converters read a single parameter and split it into multiple entries, whereas list post-conversion adjusters and list post-conversion validators apply to the array or collection as a whole.
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 list converter | Attempts to convert to member field's data type. No more than one converter or list converter can be set. If none are set for a single value field, uses the default converter for the data type. For single string fields, this and later steps are skipped. | |
| 4 | Post-conversion adjustors or list post-conversion adjusters | Yes | Alters member field value, such as setting start of day for date field, list as a whole for list post-conversion adjusters. This step cannot abort processing. |
| 5 | Post-conversion validators or list post-conversion validators | Yes | Validates member field value, or list as a whole for list post-conversion validators. |
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: DEFAULT - uses ERROR for form processing Actions and LOG_WARN for viewer Actions, ERROR - Action-level error, FIELD - field specific error using field name, IGNORE - none, not recommended, LOG_TRACE to LOG_ERROR - Log4j2 logger, MESSAGE - Action-level info message, WARNING - Action-level warning message. Defaults to DEFAULT. |
| 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 List 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.
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 applied to an array or collection, multiple parameters of the same name are recognised and the converter applies to each. If parameter names are appended with an index like '[2]', the index sets the position in a list. The entries for any missing indices (between 0 and the highest index) are set to null or other default.
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 |
If a list converter is set on an array or collection, a single parameter value is expected and split into multiple entries. it can apply to collections besides list but entry order may be list.
| Annotation | Array/Collection Type | Notes |
|---|---|---|
| @IntegerCSVConversion | Integer | Splits a string by comma or other separator into integers |
| @StringCSVConversion | String | 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 List Post-conversion Validators
Post-conversion validators and list post-conversion validators check converted values. The built-in post-conversion validators and list 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 doValidate 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 doValidate(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,FieldParsedParameterList> unprocessedParameters, ValidationAware2 validationAware, TextProvider textProvider) {
ManualParameterConversionResult result;
String day, month, year;
result = new ManualParameterConversionResult();
try {
day = unprocessedParameters.get("day").getSingleValue();
month = unprocessedParameters.get("month").getSingleValue();
year = unprocessedParameters.get("year").getSingleValue();
dateOfBirth = validateDate(day, month, year);
result.getProcessedSingleParameterNames().add("day");
result.getProcessedSingleParameterNames().add("month");
result.getProcessedSingleParameterNames().add("year");
}
catch (NumberFormatException e) {
validationAware.addActionError("Day, month, or year is not a number, you should not see this message");
result.getFailedSingleParameterNames().add("day");
result.getFailedSingleParameterNames().add("month");
result.getFailedSingleParameterNames().add("year");
}
catch (IllegalArgumentException e) {
validationAware.addActionError("This day, month and year combination doesn't exist in the calendar");
result.getFailedSingleParameterNames().add("day");
result.getFailedSingleParameterNames().add("month");
result.getFailedSingleParameterNames().add("year");
}
return result;
}
Viewer Actions
Some viewer Actions can accept URL parameters but non-annotated form fields to receive them are ignored. As viewer Actions have lots of display fields, this avoids wasting time processing them as form fields. To mark fields as accepting URL parameters, use @FormField, such as below.
public class ViewPrimeMinisterAction extends AbstractViewActionSupport {
@FormField
private boolean refresh;
@FormField
private Integer recordNo;
...
public String execute() throws Exception {
...
if (refresh) {
listCache.forceReload();
}
if (recordNo != null) {
listCache.setSelectedIndex(recordNo - 1);
}
...