Xoetrope
View

Introduction tutorial - Validating the application data - Declare the validations
SUMMARY  Validations in XUI make it very easy to check that the data being entered is correct. They are declared in XML making it easy to localize them and to change rules without changing and recompiling code.

FURTHER READING  You may want to create your own XValidationFactory in order to introduce your own custom validations and this is outlined in the 'Advanced Validations' step of the Advanced Tutorial.

We've created the controls, bound the data and output the results so now we want to make sure that the data being input is valid. We can achieve this by using the validation mechanism built into XUI. The validation file needs to be created in the resource directory and for now it contains five validations as follows...

Listing 1 - validations.xml
<Validations>
  <validation name="firstname" type="mandatory" msg="[Firstname] cannot be blank" 
  first="true"/>
  <validation name="surname" type="mandatory" msg="[Surname] cannot be blank"/>
  <validation name="dob" type="mandatory" min="sixtyYearsAgo()" 
	max="eighteenYearsAgo()" 
	msg="[Date of birth] must be a valid date, and the applicant must be 
		between the ages of 18 and 60"/>
  <validation name="propvalue" type="minmax" min="2000" max="2000000" 
	msg="Property value must be between {min} and {max}" mandatory="true"/>
  <validation name="mortamt" type="minmax" min="2000" max="1000000" 
	msg="Mortgage amount must be between {min} and {max}" mandatory="true"/>
</Validations>

There are two type of validations being used here, a simple mandatory validation which checks that data has been input and a minmax validation which sets limits for a numeric input. The three mandatory validations will be used on the personal page in order to validate the customer information.

The min-max validation has two extra attributes, min and max which define the upper and lower limits for the value entered. Obviously the value being entered must be numeric. The mandatory attribute indicates whether the value is necessary. If not then the validation only comes into play when data has been entered. There are two of these validations for the finance screen.

There are two types of validation which can be carried out by XUI, inline and page level. Inline validations are carried out when focusLost event is triggered for the component being validated. The data is automatically checked and an error message appears if the validation fails. The page level validations are carried out on the entire page and all validations for the page are checked at once.

So, make the necessary changes to the personal and finance pages by binding the validations to the relevant components as in listings 2 & 3.

Listing 2 - personal.xml
    ...
  <Validations>
    <Validation rule="firstname" target="firstnameText"/>
    <Validation rule="surname" target="surnameText"/>
    <Validation rule="dob" target="dobText"/>
  </Validations>
    ...

Listing 3 - finance.xml
    ...
  <Validations>
    <Validation rule="propvalue" target="propValueText"/>
    <Validation rule="mortamt" target="mortAmtText"/>
  </Validations>
    ...

The application needs to load the validations.xml file so an entry for the Validations property needs to be inserted into the startup properies file as in listing 4 file

Listing 4 - startup.properties
    ...
Validations=validations.xml
    ...

In the NavPanel class we now need to check the validations of the current page and proceed only if the validations have passed.

Listing 5 - NavPanel.java
    ...
  public void nextPage()
  {
    if ( wasMouseClicked() ) {
      XPage currentPage = ( XPage ) pageMgr.getCurrentPage( "content" );
      if ( currentPage.checkValidations() == XValidator.LEVEL_IGNORE )
        navigateToPage( "next" );
    }
  }
    ...

The new code gets a reference to the XPage within the content frame of the frameset. The checkValidations function in the XPage iterates all of the validations which have been added to the page and checks for valid data. If the value returned from the checkValidations() function is equal to LEVEL_IGNORE then proceed to the next page.

An exception handler now needs to be added to each of the pages. This is done within the navigateToPage() function of the NavPanel class

Listing 6 - NavPanel.java
    ...
    if ( dest != null ) {
      XPage newPage = ( XPage )pageMgr.showPage( dest, "content" );
      newPage.setExceptionHandler( new ExceptionHandler( newPage ) );
    }
    ...

This code adds a new ExceptionHandler to each of the pages loaded. This is a custom exception handler and is shown next.

Listing 7 - ExceptionHandler.java
package net.xoetrope.mortgage;

import java.awt.Component;
import net.xoetrope.xui.validation.XValidator;
import net.xoetrope.xui.XPage;
import net.xoetrope.xui.exception.XExceptionHandler;

public class ExceptionHandler implements XExceptionHandler
{
  boolean pageValidation = false;
  String validationText = "";
  private XPage currentPage;

  public ExceptionHandler( XPage page )
  {
    currentPage = page;
  }

  /**
   * Handles a validation error by displaying a short message in a popup dialog.
   * @param comp Component being validated
   * @param ex The exception caused
   * @param xvalidator The validator being used to validate.
   * @return true to continue with error validation or false to suppress further
   * validation.
   */
  public boolean handleException( Object comp, Exception ex, Object xvalidator )
  {
    XValidator validator = ( XValidator ) xvalidator;
    if ( (validator.getLevel() == validator.LEVEL_ERROR) && (! pageValidation) ){
      currentPage.showMessage( "Input error", ex.getMessage() );
      return true;
    }
    String msg = validator.getMessage();
    if ( validationText.length()>0 )
      validationText += "\n";
    validationText += msg;
    return true;
  }

  /**
   * informs the handler when a page validation is starting or stopping. Typically
   * when it starts the page will begin to accumulate message which are to be 
   * displayed.
   * When the parameter is false the page will usually display the accumulated
   * messages
   * @param start boolean to indicate whether the accumulation is started or stopped.
   */
  public int accumulateMessages( boolean start, int level )
  {
    pageValidation = start;
    if ( pageValidation ){
      validationText = "";
    } else {
      if ( validationText.length()>0 ) {
        currentPage.showMessage( "Input error", validationText );
      }
    }
    return level;
  }

  public boolean handleEventHandlerException( XProject project, Object container, Throwable error )
  {
	return true;
  }

}

This class implements the XExceptionHandler interface and it's constructor takes an XPage as an argument which it stores in a local variable. The first function to look at here is accumulateMessages. This function is called when the page level validations start and finish (the start parameter is true if the validations are starting) and a class variable is set to record the start parameter. When the start parameter is false the validationText is checked and if it contains validations an error message is displayed.

In this example the level parameter is returned automatically. In some cases it might be more appropriate to return an explicit LEVEL_IGNORE where the user has been informed of warnings and wishes to proceed.

The handleException function is called whenever a validation fails. The first condition being checked is for inline validation. If there is a error and the pageValidation flag is not true, simply display the error message. If this call is part of a page level validation, the the error message is appended to the validationText class variable. This variable will be displayed in the accumulateMessages function when the start parameter is false.

After compiling all edited java code, execute the run.bat file and the application will appear as in the screenshot.

Log in or register to download the source code for this step.