Xoetrope
View

Advanced Tutorial - Advanced Validations - Creating a custom validation class
SUMMARY  Up to this point the validations have been fairly basic, so now it's necessary to carry out some more complex checks. It would be impossible to predict every possible type of validation which might be required for an application, so when designing XUI the ablilty to add custom validations was added. This allows the developer to create their own validation classes which can form a library of validations for use in further XUI projects.

FURTHER READING  The introductory tutorial provided us with the means to carry out some basic validations on our XUI application.

In the introductory tutorial some basic validations were added to the application in order to carry out the basic mandatory and minmax validations which were required. You may need to develop your own custom XBaseValidator classes and load them using a custom XValidationFactory class. This step will show you how create your custom classes and how to load them into the XUI framework.

The first thing to do is to create a new validation. This validation will simply call a named function in the XPage which expects an Integer return object. The new FunctionValidation class will take care of carrying out some validations which are controlled from the XPage.

Listing 1 - FunctionValidation.java
package net.xoetrope.mortgage.validation;

import net.xoetrope.xui.validation.*;
import net.xoetrope.xml.*;
import net.xoetrope.xui.XProject;

public class FunctionValidation extends XBaseValidator {

  public FunctionValidation( XProject project )
  {
    super( project );
  }

  public void validate( Object c, boolean forceMandatory ) throws Exception {
    Integer ret = ( Integer ) invokeMethod();
    if ( ret.intValue() > LEVEL_IGNORE ) {
      errorLevel = ret.intValue();
      throwException();
    }
  }

  public void setup( XmlElement element )
  {
    message = element.getAttribute( "msg" );
    super.setup( element );
  }
}

The setup method is called when any custom XBaseValidator class is instantiated. It allows the derived class to inspect the XML element which defined it. In this case, the message variable of the base class is initialised from the msg attribute of the XML definition. The validate function is called whenever the XPage is carrying out it's validations.

As the default XValidationFactory class built into the XUI core does not know anything about the new FunctionValidation class, a custom one needs to be created which can take care of creating it as needed.

Listing 2 - ValidationFactory.java
package net.xoetrope.mortgage.validation;

import net.xoetrope.xui.validation.XValidationFactory;
import java.io.Reader;
import net.xoetrope.xui.validation.XValidator;
import java.awt.Container;
import java.lang.reflect.Method;
import net.xoetrope.xml.XmlElement;
import net.xoetrope.xui.build.BuildProperties;
import net.xoetrope.debug.DebugLogger;
import net.xoetrope.xui.validation.*;
import net.xoetrope.xui.XProject;

public class ValidationFactory extends XValidationFactory {

  XProject currentProject;
  
  public ValidationFactory( XProject project )
  {
    super( project );
    currentProject = project;
  }

  public XValidator getValidation( 
	String validationName, Method m, int mask, Object page )
  {
    checkRegistration();
    XmlElement ele = ( XmlElement ) validations.get( validationName );
    if ( BuildProperties.DEBUG ) {
      if ( ele == null ) {
        DebugLogger.logError( "Cannot find the validation rule: " + validationName );
        return null;
      }
    }

    String type = ele.getAttribute( "type" );

    if ( type.compareTo( "function" ) == 0 ) {
      FunctionValidation validator = new FunctionValidation();
      validator.setName( validationName );
      validator.setValidationMethod( m, page );
      validator.setup( ele );
      return validator;
    } else {
      XBaseValidator validator = ( XBaseValidator ) 
	  super.getValidation( validationName, mask, page );
      validator.setup( ele );
      return validator;
    }
  }

}

The ValidationFactory constructor calls the super constructor and creates a reference to the XProject instance. The getValidation function retrieves the XML declaration of the validation specified by the validationName parameter. The type attribute of the XML element is then retrieved and checked. If the type attribute is function then a new FunctionValidation instance is created. The setName function of the base class is called with the name of the validation. The setValidationMethod is called with the passed Method Object and the page Object which contains the Method which is to be called.

The new FunctionValidation is returned. If the type is anything other than one that the XValidationFactory is concerned with, the super.getValidation function of the base class is called in order to construct the validator.

The new ValidationFactory now needs to be specified as the validation factory for the project by setting the ValidationFactory startup property

Listing 3 - startup.properties
    ...
ValidationFactory=net.xoetrope.mortgage.validation.ValidationFactory
    ...

Now the validation needs to be defined in the validations declaration file as follows...

Listing 4 - validations.xml
<Validations>
  <validation name="firstname" type="mandatory" msg="VALID_FNAME" first="true"/>
  <validation name="surname" type="mandatory" msg="VALID_SNAME"/>
  <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="custom" 
	class="net.xoetrope.mortgage.MinMaxValidator" 
	min="2000" max="2000000" msg="VALID_PROPVALUE" mandatory="true"/>
  <validation name="mortamt" type="custom" 
	class="net.xoetrope.mortgage.MinMaxValidator" 
	min="2000" max="1000000" msg="VALID_MORT_AMT" mandatory="true"/>
  <validation name="createApp" type="function"  
	msg="Please select 'Sole' or 'Joint' in order to proceed!"/>
</Validations>

The validations declaration file now contains a new validation, createApp which will check on the welcome page that at least one of the radio buttons have been selected. So, the welcome page XML declaration needs to be amended to include the new validation.

Listing 5 - welcome.xml
    ...
<Validations>
	<Validation rule="createApp" target="jointRadio" method="checkCreateSetup"/>
</Validations>
    ...

The method attribute specifies the name of the function which will be called in the Welcome class to carry out the validation. All that is left to do at this point is to create the valiation function in the Welcome class.

Listing 6 - Welcome.java
    ...
public Integer checkCreateSetup()
{
  int level = XBaseValidator.LEVEL_IGNORE;
  if ( !jointRadio.isSelected() && !soleRadio.isSelected() )
    level = XBaseValidator.LEVEL_ERROR;

  return new Integer( level );
}
    ...

The checkCreateSetup function returns an Integer Object indicating the result of the validation. In this case the validation checks to see that at least one of the radio buttons has been selected by the user. The appropriate error levels are defined as public static ints in the XBaseValidator class.

Quite a lot of work has been put into creating the FunctionValidation class but now that it is available it can be reused in any of the pages or projects which might require it. Of course, the FunctionValidation class is only one example of the type of custom validations which can be created. Others might include mask, integer, regular expression or whatever might be required by the developer.

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