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

 

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
Collection 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 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  
Post-conversion validator annotations
Annotation Notes
@RequiredIntegerEntries Checks no entry is null.
Collection 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 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;

}

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;

    

    private PrimeMinisterForm form;

    

    private int listSize;

    private LocaleSelectBoxDisplay localeSelectBoxDisplay;

    private PartySelectBoxDisplay partySelectBoxDisplay;

    ...

    public String execute() throws Exception {

        ...

        if (refresh) {

            listCache.forceReload();

        }

        if (recordNo != null) {

            listCache.setSelectedIndex(recordNo - 1);

        }

    ...