The solution

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.

You must add single-field validation logic to the setters:

setName

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:

Figure 1. Creating a message catalog with validation error messages
<?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:

Figure 2. Implementing single field validation logic
/**
   * 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));
    }
  }
Note that:
  • validation regarding whether the name has been set at all will occur during mandatory-field validation; and
  • constants for the maximum length of database text columns are automatically generated into the entity adapter. These constants should be used in preference to creating your own, as they will automatically be updated should the length of the database column be customized (by changing the domain definition in the model).

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:

Figure 3. Using ValidationHelper to create temporary 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.

setDateRange

After analyzing requirements, you determine that the date range requires the following validation logic:
  • the range is valid (i.e. that the start date is not after the end date); and
  • the start date has been specified (but the end date is optional, or, more to the point, whether the end date is required is dependent on the value of other fields).

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:

Figure 4. Using DateRange to perform standard validation
/**
   * 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.

setType

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.

setMyParentEntity

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.