RIFE logo
This page last changed on Apr 23, 2007 by gbevin.


Incremental validation

Validation is bound to subjects that have distinct names. Each subject corresponds to a different variable, for example a property of a bean. When a subject is found to be invalid, a corresponding instance of ValidationError has to be registered.

ValidationErrors indicate in detail why a Validated object doesn't contain valid data. They should be stored internally and can be manipulated by other classes that are able to work with Validated objects. This makes it possible to collect errors incrementally in one central place and to allow each component in a system to perform its own part of the validation.

A Validated object has a validate() method which should be used to perform mandatory validation on subjects and data that the object itself knows about. This validation has to perform all checks that guarantee a coherent, consistent internal state for the data. Note that this method should not reset the validation, but rather add new validation errors to an already existing collection

Since it is possible that subjects generate multiple ValidationErrors, it's possible to limit their number and only store the first error that occurs for a particular subject.

[top]

Automatic rule creation from constraints

Validation is able to automatically create and add validation rules according to constraints. The example Credentials class below thus already contains all information to be able to validate the data that is stored in its properties. If the constraints system is not sufficient for your need, you're of course still able to create ValidationRule objects yourself and add them to the bean explicitly.

Read more about constraints here.

[top]

Validation groups

Since it's quite common to validate different collections of rules one after the other (in a wizard or a multi-paged form for instance) you can now define validation groups. They are identified by a unique name and it is possible to validate only the rules that are included in a particular group. To define a validation group you simply use the addGroup(String name) method which create a new ValidationGroup instance and registers it in the Validation object.

Meta data

Note that constraints are not only used for validation. That's why there's a MetaData class that extends the Validation class. It's almost always recommended to use that one instead. To know more, you can read about meta data merging.

A validation group contains the same methods as the Validated interface to add rules and constraints: addRule(ValidationRule rule) and addConstraint(ConstrainedProperty constrainedProperty). These however return the ValidationGroup instance they are called upon, which makes them chainable. For example:

public class Credentials extends Validation
{
    public Credentials()
    {
        addGroup("step1")
            .addConstraint(new ConstrainedProperty("login").maxLength(6).notNull(true))
            .addConstraint(new ConstrainedProperty("password").maxLength(8).notNull(true));

        addGroup("step2")
            .addConstraint(new ConstrainedProperty("language").notNull(true));
   }

   // the properties code is trivial
}

To actually validate against the groups you have two options.

You start from scratch and validate only the rules of the group like this:

Credentials credentials = new Credentials();
credentials.validateGroup("step1");
// do something if errors occurred
credentials.resetValidation();
credentials.validateGroup("step2");
// do something if errors occurred

You can also validate normally and focus down to only the subjects that are validated by the groups, like this:

Credentials credentials = new Credentials();
credentials.validate();
credentials.focusGroup("step1");
// do something if errors occurred
credentials.resetValidation();
credentials.validate();
credentials.focusGroup("step3");
// do something if errors occurred

[top]

Example: Validating Bean Info from Multiple Forms

Occasionally it is useful to be able to work with a bean whose data will be filled in by several different forms. For example an "Account" bean may hold both the minimal/basic information needed to allow a user to register, but also extended profile information that while not needed to activate the account, may be filled in later on a "user profile" page. This kind of disjoint population of an instance bean can be done using constraint groups and telling RIFE that you want to only partially process a bean on a given submission. This can be accomplished using the following template:

  • Define different constraint groups (either in your model bean or in a separate MetaData class) for each collection of member
    variables that will be processed by the different forms. For this example, let's call this Constraint Group "ContactInfo."
  • Define the constraint group in the submission portion of your form's element as part of the submission's bean declaration, e.g.:
    <element id="ContactInfo" implementation="path.to.FormHandlerClass" >
      <submission name="update" >
        <bean classname="path.to.your.model.Account"  group="ContactInfo"/>
      </submission>
    </element>

    which narrows the submission's scope down to fields validated by this one group of constraints

  • In the class that processes the form, set the focus of the validation onto the constraint group you declared in
    the element declaration, and use the version of fillSubmissionBean that takes a submission name param which will tell
    RIFE which parameters from the form are of interest and to be filled in to the bean instance while leaving any others unchanged.
    E.g.:
    public void doUpdate() {
      account.focusGroup("ContactInfo");
      fillSubmissionBean("update", account);
      if (account.validateGroup("ContactInfo")) { // good!
        account.setModification_dt(new java.util.Date());
        int result = accountQM.update(account);
        template.clear();
        account.resetValidation();
        print(template);
      } else { // bad.  if at first you don't succeeed...
        processElement();
      }
    }

    [top]

Error message generation, decoration, specification, positioning

The previous ValidationFormatter class is now deprecated and replaced by ValidationBuilder implementations. Currently the only concrete implementation is ValidationBuilderXhtml, but there are plans to support other template types.

[top]

Fallback message positioning and decoration

The template value in which validation error messages are collected by default is now the following:

<!--V 'ERRORS:*'--><!--/V-->

You can set a custom message in the area with the setFallbackErrorArea(Template template, String message) and thus use it also for general error reporting. To fill it with error messages, you have to use the generateValidationErrors(Template template, Collection validationErrors, Collection<String> onlySubjectsToClear, String prefix) method.
If you need decoration for this error area that you only want to appear when content is assigned to it, you can specify it like this:

<!--B 'ERRORS:*'--><p><!--V 'ERRORS'/--></p><!--/B-->

The ERRORS value will then contain the content.

If you want to decorate each error message individually, you should specify it like this:

<!--B 'ERRORMESSAGE:*'--><b><!--V 'ERRORMESSAGE'/--></b><br /><!--/B-->

The ERRORMESSAGE value will then contain the error message.

Considering all the above template directives, the following code:

ValidationBuilderXhtml builder = new ValidationBuilderXhtml();
builder.setFallbackErrorArea(template, "my message");

will generate the following result:

<p><b>my message</b><br /></p>

If you generate a collection of validation errors:

ValidationBuilderXhtml builder = new ValidationBuilderXhtml();
builder.generateValidationErrors(template, errors, null, null);

the following result could be generated with the same template directives:

<p><b>Invalid login.</b><br /><b>MANDATORY:password</b><br /><b>WRONGLENGTH:language</b><br /></p>

[top]

Selective error area positioning and decoration

Often you want to position error messages near the specific form fields they describe, and a global error area is not appropriate. You can do this by adding the subject name to the ERRORS value ID, like this:

<!--V 'ERRORS:language'--><!--/V-->

Dependent on your design, you might even want to display the error messages of several subjects in a certain location. This is also possible, like this:

<!--V 'ERRORS:login,password'--><!--/V-->

Note that it's possible to use the same subject name in several value IDs. To select in which value a message will appear, RIFE uses a fallback mechanism that selects values according to the number of matching erroneous subjects. For each subject it goes over all the value IDs that contain it, the order of traversal is from the value with the most subject names to the one with the least. The first value ID in which all specified subject names are invalid will be used.

For example, consider the following error area values:

<!--V 'ERRORS:login,password,language'--><!--/V-->

<!--V 'ERRORS:login,password'--><!--/V-->
<!--V 'ERRORS:login'--><!--/V-->
<!--V 'ERRORS:password'--><!--/V-->
<!--V 'ERRORS:*'--><!--/V-->

Now, examine the following situations where each time different subjects are invalid:

  • login, password, language
    all error messages will appear in 'ERRORS:login,password,language',
  • login, password
    all error messages will appear in 'ERRORS:login,password',
  • password, language
    all error messages will appear in 'ERRORS:*',
  • login, language
    login error messages will appear in 'ERRORS:login' and
    language error messages will appear in 'ERRORS:*',
  • login
    all error messages will appear in 'ERRORS:login',
  • password
    all error messages will appear in 'ERRORS:password',
  • language
    all error messages will appear in 'ERRORS:*'.

On its own this mechanism isn't very handy, but it becomes interesting when you combine it with the selective decoration for these error areas. It is based on the same principle, but uses an inverted mechanism to determine which block to use. This means that it will first start with the blocks that specify the least amount of subject names and end with those that specify the most. As soon as all subject names of a used error area are present in the decoration block, the block will be used.

For example, consider the following decoration blocks together with the error areas from above:

<!--B 'ERRORS:login,password'--><!--V 'ERRORS'/--><!--/B-->
<!--B 'ERRORS:login'--><!--V 'ERRORS'/--><!--/B-->
<!--B 'ERRORS:password'--><!--V 'ERRORS'/--><!--/B-->
<!--B 'ERRORS:*'--><!--V 'ERRORS'/--><!--/B-->

Now, examine the following situations where each time another error area value is used:

  • 'ERRORS:login,password,language'
    the 'ERRORS:*' decoration block will be used,
  • 'ERRORS:login,password'
    the 'ERRORS:login,password' decoration block will be used,
  • 'ERRORS:login'
    the 'ERRORS:login' decoration block will be used,
  • 'ERRORS:password'
    the 'ERRORS:password' decoration block will be used,
  • 'ERRORS:*'
    the 'ERRORS:*' decoration block will be used.

You now probably ask yourself why you would ever use something like this. The typical usage is when you have a design that changes according to the error messages that are displayed. For example, you have a firstname and a lastname field in different columns on the same table row. You want the messages for each field to be displayed above it and when they both are invalid, you just the whole row above. Below is an example template excerpt that demonstrates this:

<!--B 'ERRORS:firstname,lastname'-->
<tr class="error_messages">
    <td colspan="2"><!--V 'ERRORS'/--></td>

</tr>
<!--/B-->
<!--B 'ERRORS:firstname'-->
<tr>
    <td class="error_messages"><!--V 'ERRORS'/--></td>
    <td>&nbsp;</td>

</tr>
<!--/B-->
<!--B 'ERRORS:lastname'-->
<tr>
    <td>&nbsp;</td>
    <td class="error_messages"><!--V 'ERRORS'/--></td>

</tr>
<!--/B-->

<table cellspacing="0" cellpadding="5" border="0">
<tr>
    <td><em>Firstname</em></td>
    <td><em>Lastname</em></td>

</tr>
<!--V 'ERRORS:firstname,lastname'/-->
<!--V 'ERRORS:firstname'/-->
<!--V 'ERRORS:lastname'/-->
<tr>
    <td><input type="text" name="firstname" size="30" /></td>
    <td><input type="text" name="lastname" size="30" /></td>

</tr>

[top]

Selective error message decoration

Instead of using the same decoration for all error messages, you can specify different ones according to the erroneous subjects. The syntax is similar to what is used for the selective error area decoration, but you use the ERRORMESSAGE block ID as prefix instead. You can again specify several subject names for each block, but the selection mechanism is much simpler. For each subject of an error message, RIFE will just take the block that specifies it first.

For example, consider the following error message decorations:

<!--B 'ERRORMESSAGE:*'--><!--V 'ERRORMESSAGE'/--><br /><!--/B-->
<!--B 'ERRORMESSAGE:login,password'--><p><!--V 'ERRORMESSAGE'/--></p><!--/B-->

<!--B 'ERRORMESSAGE:login,language'--><div><!--V 'ERRORMESSAGE'/--></div><!--/B-->

Now, examine the following situations where each time another error message subject is displayed:

  • login
    the 'ERRORMESSAGE:login,password' message decoration block will be used,
  • password
    the 'ERRORMESSAGE:login,password' message decoration block will be used;
  • language
    the 'ERRORMESSAGE:login,language' message decoration block will be used,
  • firstname
    the 'ERRORMESSAGE:*' message decoration block will be used.

[top]

Error message content specification

As before, the content of the displayed error messages is determined according to the identifier and subject name of the error messages. For example, the validation error with the identifier 'MANDATORY' and the subject 'login' will look for a block with the ID 'MANDATORY:login'. When this block is available, its content will be used for the display of the error message. However it is now also possible to define more general blocks that will be used as fallbacks. Following is a list of the order of evaluation of the block IDs.

  • IDENTIFIER:subject (as explained before),
  • ERROR:subject (this is the ERROR literal, will be used when no block with a specific identifier is available),
  • IDENTIFIER:* (will be used for an identifier when no block for the specific subject is available),
  • ERROR:* (will be used as last resort when no other block matches).

If no block could be found to obtain the error message content from, 'IDENTIFIER:subject' will be displayed instead.

RIFE provides the following IDENTIFIER values:

  • INCOMPLETE
  • INVALID
  • MANDATORY
  • NOTNUMERIC
  • NOTSAMEAS
  • UNEXPECTED
  • UNICITY
  • WRONGFORMAT
  • WRONGLENGTH

These are described in the Javadoc for validation errors.

[top]

Custom error identifiers

It is also possible to create new identifiers very easily in RIFE. The class of interest for this purpose is the abstract ValidationError class. Simply instantiate a subclass, passing the identifier literal and the subject in the constructor.

For example:

public class DuplicateValidationError extends ValidationError {
   
}

...

bean.addValidationError(new DuplicateValidationError("DUPLICATE", "mysubject"));

...

[top]

Error marking

Whenever validation errors occur, it's nice to be able to clearly mark the parts of the page where the errors have to be corrected. RIFE supports this by looking for 'MARK:subject' values, and these will be replaced by the content of the 'MARK:ERROR' block. Typically, this block will contain the HTML attribute to use a different CSS style.

For example:

<!--B 'MARK:ERROR'--> class="error_mark"<!--/B-->

<label[!V 'MARK:login'][!/V] for="login">login</label>
<div[!V 'MARK:login'/]><input type="text" name="login" id="login" size="18" /></div>

When the markings are generated:

ValidationBuilderXhtml builder = new ValidationBuilderXhtml();
builder.generateErrorMarkings(template, errors, null, null);

the result is the following when an error occurs for the login subject:

<label class="error_mark" for="login">login</label>
<div class="error_mark"><input type="text" name="login" size="18" /></div>

and if the subject is valid, this will be generated:

<label for="login">login</label>

<div><input type="text" name="login" size="18" /></div>

[top]

Selective mark positioning

Like for the error area and error message values, you can declare several subjects after the 'MARK' prefix. The selection of which marking to fill in is done in the same way as the selection of the error area values. For each subject it goes over all the value IDs that contain it, the order of traversal is from the value with the most subject names to the one with the least. The first value ID where all specified subject names are erroneous, will be used. So the 'MARK:login,password' value will be used before the 'MARK:login' value.

[top]

Alternative markings

Instead of using the content of the 'MARK:ERROR' block for all the error markings, you can choose to differentiate them by adding a specific suffix, like this:

<!--B 'MARK:ERROR:MYALT'--> class="error_alternative"<!--/B-->

<label[!V 'MARK:MYALT:password'][!/V] for="password">password</label>
<div[!V 'MARK:MYALT:password'/]><input type="password" name="password" id="password" size="18" /></div>

This allows you to use, for example, different marking layouts for different forms on a same page or even for different sections of the same form.

[top]

Document generated by Confluence on Oct 19, 2010 14:57