Each field setter is responsible for ensuring that the value being set is appropriate. In general, errors arising from single-field validation should be "accumulated" using the InformationalManager, so that callers can be notified of all the single-field validation errors found. This is particularly useful to online users who may have entered several fields in error - if single-field validation errors are reported one-by-one then it would be frustrating for the user to be presented with a series of single-error messages instead of a list of all known single-field validation errors.
One important corollary of this is that each field setter should only attempt to validate the field being set. It should make no reference to other fields.
For the purposes of single-field validation, a field corresponds to the value received by the setter. Generally, there is one setter per underlying database field; however, in cases where database fields are grouped together (notably with DateRange), it is the object received by the setter which is validated, not the individual underlying database fields. In the case of a setDateRange method, it is the date range which is validated. This single-field validation of the DateRange typically includes start/end date validation which under classic Cúram would have been considered "cross-field" validation.
One other point to note is that the validation of whether mandatory fields have been set is deferred to a special "mandatory field validation" method (see You want to implement mandatory-field validation below); this is because you cannot guarantee which (if any) setters have been called from calling code.
After analyzing requirements, you determine that the setter for the name must validate that the name length is within acceptable bounds.
First create a message catalog:
<?xml version="1.0" encoding="UTF-8"?> <messages package="curam.message"> <message name="ERR_MY_NEW_ENTITY_FV_NAME_EMPTY"> <locale language="en"> The name must be specified. </locale> </message> <message name="ERR_MY_NEW_ENTITY_FV_NAME_SHORT"> <locale language="en"> The name must be at least %1n characters. </locale> </message> <message name="ERR_MY_NEW_ENTITY_FV_NAME_LONG"> <locale language="en"> The name must be no more than %1n characters. </locale> </message> </messages>
Note that the validation messages for minimum/maximum length take as argument the minimum/maximum lengths permitted, rather than hard-coding these bounds into the messages.
Now code validation logic in the setter and raise errors using the ValidationHelper:
/** * Minimum valid name length */ private static final long kMinimumNameLength = 3; /** * {@inheritDoc} */ public void setName(final String value) { getDtls().name = StringHelper.trim(value); final long nameLength = getDtls().name.length(); if (nameLength > 0 && nameLength < kMinimumNameLength) { ValidationHelper.addValidationError( MYNEWENTITYExceptionCreator .ERR_MY_NEW_ENTITY_FV_NAME_SHORT(kMinimumNameLength)); } else if (nameLength > MyNewEntityAdapter.kMaxLength_name) { ValidationHelper.addValidationError( MYNEWENTITYExceptionCreator .ERR_MY_NEW_ENTITY_FV_NAME_LONG( MyNewEntityAdapter.kMaxLength_name)); } }
The ValidationHelper class contains the convenience method addValidationError to format an error message and add it to the informational manager. It takes an AppException or CatEntry (shown here). It also has a deprecated overload which takes a String, which can be used as a "quick and dirty" way of writing error messages:
/** **** Must be "cleaned up" prior to testing and release ** */ final long nameLength = getDtls().name.length(); if (nameLength > 0 && nameLength < kMinimumNameLength) { ValidationHelper.addValidationError("Name too short!"); } else if (nameLength > MyNewEntityAdapter.kMaxLength_name) { ValidationHelper.addValidationError("Name too long!"); } /** **** Must be "cleaned up" prior to testing and release ** */
You must convert these Strings to message catalog entries prior to testing and release. This facility exists purely to minimize the "switching" you might have to do between editing Java and editing/generating message files that you might otherwise have to do when writing validation logic.
The first of these is amenable to single-field validation; the second is more appropriate for mandatory-field validation.
Code validation logic to use the standard validation message on DateRange:
/** * Sets the start and end fields from the date range supplied. * * @param value * the date range supplied */ private void setDateRange(final DateRange value) { getDtls().startDate = value.start(); getDtls().endDate = value.end(); value.validateRange(); }
The DateRange class contains the convenience method validateRange which validates the start and end dates of the range and raises a standard error message if the start date is after the end date. If you require a specific message, then use DateRange.isValidRange instead.
After analyzing requirements, you determine that the type field has no single-field validation requirements. Mandatory field validation will be required to ensure that the type has been set.
Note that the caller of this method must supply an instance of MYNEWENTITYTYPEEntry, and will fail with a runtime error if it attempts to retrieve an entry value which from a value which is not present in the corresponding code table.
After analyzing requirements, you determine that the parent entity ID field has no single-field validation requirements. Mandatory field validation will be required to ensure that the parent entity has been set.