@Parameter

Collects together all domain semantics of an action parameter within a single annotation.

API

Parameter.java
@interface Parameter {
  String fileAccept() default "";     (1)
  int maxLength() default -1;     (2)
  Class<? extends Specification>[] mustSatisfy() default {};     (3)
  Optionality optionality() default Optionality.NOT_SPECIFIED;     (4)
  String regexPattern() default "";     (5)
  int regexPatternFlags() default 0;     (6)
  String regexPatternReplacement() default "Doesn't match pattern";     (7)
}
1 fileAccept

For uploading Blob or Clob , optionally restrict the files accepted (eg .xslx ).

2 maxLength

The maximum entry length of a string parameter (it is ignored for other types).

3 mustSatisfy

The Specification (s) to be satisfied by this parameter.

4 optionality

Whether this parameter is optional or is mandatory (ie required).

5 regexPattern

Regular expression pattern that a value should conform to, and can be formatted as.

6 regexPatternFlags

Pattern flags, as per java.util.regex.Pattern#compile(String, int) .

7 regexPatternReplacement

Replacement text for the pattern in generated error message.

Members

fileAccept

For uploading Blob or Clob , optionally restrict the files accepted (eg .xslx ).

The value should be of the form "file_extension|audio/|video/|image/*|media_type".

Note that this does not prevent the user from uploading some other file type; rather it merely defaults the file type in the file open dialog.

maxLength

The maximum entry length of a string parameter (it is ignored for other types).

The default value ( -1 ) indicates that no maxLength has been specified.

mustSatisfy

The Specification (s) to be satisfied by this parameter.

If more than one is provided, then all must be satisfied (in effect "AND"ed together).

optionality

Whether this parameter is optional or is mandatory (ie required).

For parameters the default value, org.apache.isis.applib.annotation.Optionality#DEFAULT , is taken to mean that the parameter is required.

regexPattern

Regular expression pattern that a value should conform to, and can be formatted as.

regexPatternFlags

Pattern flags, as per java.util.regex.Pattern#compile(String, int) .

The default value, 0 , means that no flags have been specified.

regexPatternReplacement

Replacement text for the pattern in generated error message.

Examples

For example:

public class Customer {
    public static class EmailSpecification extends AbstractSpecification<String> {
        public String satisfiesSafely(String proposed) {
            return EmailUtil.ensureValidEmail(proposed);    (1)
        }
    }
    @Action(semantics=SemanticsOf.IDEMPOTENT)
    public Customer updateEmail(
        @Parameter(
            maxLength=30,
            mustSatisfy=EmailSpecification.class,
            optionality=Optionality.OPTIONAL,
            regexPattern = "(\\w+\\.)*\\w+@(\\w+\\.)+[A-Za-z]+",
            regexPatternFlags=Pattern.CASE_INSENSITIVE
        )
        @ParameterLayout(named="New Email Address")
        final String newEmailAddress
        ...
    }
}
1 the (fictitious) EmailUtil.ensureValid(…​) (omitted for brevity) returns a string explaining if an email is invalid

Usage Notes

Mandatory vs Optional

By default, the framework assumes that all parameters of an action are required (mandatory). The optionality() element allows this to be relaxed.

The attribute has no meaning for a primitive type such as int: primitives will always have a default value (e.g. zero). If optionality is required, then use the corresponding wrapper class (e.g. java.lang.Integer) and annotate with Parameter#optionality() as required.

The values for the attribute are simply OPTIONAL or MANDATORY.

For example:

public class Customer {
    public Order placeOrder(
            final Product product,
            final int quantity,
            @Parameter(optionality = Optionality.OPTIONAL)
            final String specialInstructions) {
        ...
    }
    ...
}

Alternatives

It is also possible to specify optionality using @Nullable annotation.

Maximum string length

The maxLength() element applies only to String parameters, indicating the maximum number of characters that the user may enter (for example in a text field in the UI). It is ignored if applied to parameters of any other type.

For example:

public class CustomerRepository {
    public Customer newCustomer(
        @Parameter(maxLength=30)
        final String firstName,
        @Parameter(maxLength=50)
        final String lastName) {
        ...
    }
}

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.

Regular Expressions

String parameters can be checked to ensure that they match a regular expression. There are three elements involved in this:

  • The regexPattern() element validates the contents of any string parameter with respect to a regular expression pattern. It is ignored if applied to parameters of any other type.

  • The regexPatternFlags() element specifies flags that modify the handling of the pattern. The values are those that would normally be passed to java.util.regex.Pattern#compile(String,int).

  • The regexPatternReplacement() element specifies the error message to show if the provided argument does not match the regex pattern.

For example:

public class Customer {
    public void updateEmail(
            @Parameter(
                regexPattern = "(\\w+\\.)*\\w+@(\\w+\\.)+[A-Za-z]+",
                regexPatternFlags = Pattern.CASE_INSENSITIVE,
                regexPatternReplacement =
                    "Must be valid email address " +
                    "(containing a '@') symbol"                     (1)
            )
            @ParameterLayout(named = "Email")
            final String email) {
        ...
    }
)
1 Note that there is currently no i18n support for this phrase.

Uploading Blobs and Clobs

The fileAccept() element applies only to Blob or Clob parameters, indicating the type of file to accept when uploading a new value.

For example:

public class Scanner {

    public ScannedDocument newScan(
                @Parameter(
                    fileAccept="image/*"        (1)
                )
                final Blob scannedImage) {
        // ...
    }
}
1 as per reference docs, either a media type (such as image/*) or a file type extension (such as .png).