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.
Form field policy steps

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.
Common annotation parameters

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().
Adjuster annotations

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.
Adjuster annotations

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
Converters

 

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
List Converters

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 adjuster annotations

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  
Post-conversion validator annotations

 

Annotation Notes
@RequiredIntegerEntries Checks no entry is null.
List post-conversion validator annotations

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);

        }

    ...