How to validate user input

One of the most cumbrous development tasks creating GUIs is the validation of user input. This section describes how to use the W4 Toolkit Validation API and W4T Eclipse to ease this stuff.

A Simple Example

Suppose we would like to have a text input field placed at a WebForm and a button to submit the user input to the server. The simplest imaginable scenario would be that the input field must not be empty. Of course we could solve this with the following code in the buttons action handler:

  if( wtxInput.getValue().equals( "" ) ) {
    wlbValidationMessage.setValue( "Input must not be empty!" );
  } else {
    wlbValidationMessage.setValue( "" );  
    // do important business things ...
  }
But as things are getting bigger you now have three input fields which must not be empty. Using the above approach you will end up with:
  if(    wtxInput1.getValue().equals( "" ) 
      && wtxInput2.getValue().equals( "" ) 
      && wtxInput3.getValue().equals( "" ) ) {    
    wlbValidationMessage.setValue( "Input fields must not be empty!" );
  } else {
    wlbValidationMessage.setValue( "" );  
    // do important business things ...
  }
If you have to do more sophisticated checks this will soon get awkward.

With W4 Toolkit you have the possibility to set a Validator at user input components like WebText. To validate the content of all input components placed at a WebContainer you just have to call the validate method of the container.

First let's write a simple Validator which fits our needs in the example above (do not worry about the info attribute, we will cover this later):

  package example.validation;

  import com.w4t.Validator;

  public class IsNotEmpty implements Validator {
    private String info = "";
    public boolean validate( String value ) throws Exception {
      return !value.equals( "" );
    }
    public void setInfo( final String info) {
      this.info = info;
    }
    public String getInfo() {
      return info;
    }
  }
After that use the property view to set the Validator to the input fields. Pushing the validator property button will open a dialog which shows all implementations of the Validator interface available on the projects classpath.

Note: Components added with the property view are supposed to implement the JavaBeans standard, in particular they must provide a public and parameterless constructor to work with W4T Eclipse!

Finally re-write our action handler as follows:

  if( !this.validate() ) {
    wlbValidationMessage.setValue( "Input must not be empty!" );
  } else {
    wlbValidationMessage.setValue( "" );
    // do important business things ...
  }
Launching the example WebForm and leaving one input field empty will result in the following after pushing the button:




Creating a Customizable Validator

Real world scenarios require much more sophisticated checks than our 'IsNotEmpty' example. Normally it is possible to divide these validation tasks in different categories. This chapter shows how to implement a customizable validator which handles such a category.

Date Validation

Suppose the second input field of our example form is meant to accept date inputs only. One of the problems with date input is, that it occurs in various format. For example the users input could be 1.1.2003, 01.1.2003 or 01.01.03 all specifying the same date.

The following example code shows how to write a simple date validator which can be customized in W4T Eclipse. Customizable fields are the pattern-list used for input validation and the mandatory flag (allowEmptyDate).

  package example.validation;

  import java.text.SimpleDateFormat;
  import java.util.Date;
  import com.w4t.Validator;

  public class SimpleDateValidator implements Validator {

    private String[] pattern = new String[ 0 ];
    private String info = "";
    private boolean allowEmptyDate = true;
	
    public boolean validate( final String input ) throws Exception {
      boolean result = false;
      for( int i = 0; !result && i < pattern.length; i++ ) {
        result = validateInternal( input, pattern[ i ] );
      }
      return result;
    }
    private boolean validateInternal( final String input, final String pattern ) {
      SimpleDateFormat formatter = new SimpleDateFormat( pattern );
      boolean result = false;
      try {
        Date date = formatter.parse( input );
        String parsedDate = formatter.format( date );
        // check whether re-formatted date and input date are equal
        // this fails if the input is well formatted but does not exist (e.g. 31.02.2003)
        if( input.equals( parsedDate ) ) {
          result = true;
        }
      } catch( Exception ignore ) {
        // the String is not a valid date format, result remains false
      }
      if( !result ) {
        result = checkIfEmptyStringAllowed( input );
      }
      return result;
    }
    private boolean checkIfEmptyStringAllowed( final String input ) {
      return input.equals( "" ) && allowEmptyDate;
    }
    public String[] getPattern() {
      return pattern;
    }
    public void setPattern( final String[] pattern ) {
      this.pattern = pattern;
    }
    public String getPattern( int i ) {
      return pattern[ i ];
    }
    public void setPattern( int i, final String pattern ) {
      this.pattern[ i ] = pattern;
    }
    public void addPattern( final String pattern ) {
      String[] newPattern = new String[ this.pattern.length + 1 ];
      System.arraycopy( this.pattern, 0, newPattern, 0, this.pattern.length );
      newPattern[ this.pattern.length ] = pattern;
      this.pattern = newPattern;
    }
    public boolean isAllowEmptyDate() {
      return allowEmptyDate;
    }
    public void setAllowEmptyDate( final boolean allowEmptyDate ) {
      this.allowEmptyDate = allowEmptyDate;
    }
    public void setInfo( final String info ) {
      this.info = info;
    }
    public String getInfo() {
      return info;
    }
  }
As you see our SimpleDateValidator is based on the SimpleDateFormat. This is just intended as example solution, there may be other ways to solve this problem. The important point here is that you can customize the SimpleDateValidator with W4T Eclipse for your needs.

The pattern attribute of SimpleDateValidator is implemented as a public indexed JavaBeans property to be editable within the property view. To support the W4T Eclipse code generation one additional method addPattern has to be added.

Set the SimpleDateValidator as described above to the second text input field. As you can see the validator property of the text changes to 'example.validation.SimpleDateValidator'. Now push the button of the validators pattern attribute for editing the validators pattern list. Add the date formats which should be accepted by the text field to the list (see the SimpleDateFormat documentation for information about valid patterns).

The code which is generated by W4T Eclipse will look like this:

  private void initialiseWtxInput2() throws Exception {
    this.add( wtxInput2, new Position( 3, 3 ) );
    wtxInput2.setName( "wtxInput2" );
    wtxInput2.setValidator( new SimpleDateValidator() );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "d.M.yy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "dd.M.yy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "d.MM.yy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "d.M.yyyy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "dd.MM.yy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "d.MM.yyyy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "dd.M.yyyy" );
    ( ( SimpleDateValidator )wtxInput2.getValidator() ).addPattern( "dd.MM.yyyy" );
  }
Launching the example WebForm and typing a wrong date value into the second field will result in the following after pushing the button:

Change the 'XX' into e.g. '2003' and the input will be accepted.

Validation Using Regular Expressions

Another common requirement of input validation is the pattern matching. For example: checks whether a field contains a valid e-mail address pattern or whether a password value starts with a letter and so on.

This chapter shows how to use the W4 Toolkit type RegularExpression for creating a Validator.

We use the jakarta-oro library for this example. It is shipped with W4T Eclipse, the only thing you need to do is to add the classpath variable 'ORO' to your classpath:

This is the code for a simple regular expression based validator:

package example.validation;

import org.apache.oro.text.awk.*;
import com.w4t.Validator;
import com.w4t.types.RegularExpression;

public class RegularExpressionValidator implements Validator {

  private String info = "";
  private RegularExpression regularExpression = new RegularExpression( "" );
  
  public boolean validate( final String input ) throws Exception {
    AwkCompiler compiler = new AwkCompiler();
    AwkMatcher  matcher = new AwkMatcher();
    String regex = regularExpression.toUnescapedString();
    boolean result = false;
    // Note: In a real world scenario implementation you would not compile the
    // pattern for each validation. One idea is to have a pattern repository,
    // e.g. a singleton for application wide access for compiled patterns.
    AwkPattern pattern = ( AwkPattern )compiler.compile( regex );
    // perform the actual matching
    return matcher.matches( input, pattern );  
  }
  public void setInfo( final String info ) {
    this.info = info;
  }
  public String getInfo() {
    return info;
  }
  public RegularExpression getRegularExpression() {
    return regularExpression;
  }
  public void setRegularExpression( final RegularExpression regularExpression ) {
    this.regularExpression = regularExpression;
  }
}

Set the RegularExpressionValidator as described above to the third text input field. As you can see the validator property of the text changes to 'example.validation.RegularExpressionValidator'.

As we use the AwkMatcher for the validation process you should add a valid expression as the validators regularExpression property. You can find a short Awk-Syntax description at the AwkCompiler API description.

To test your expression you can use the RegularExpressions cell editor dialog:

Launching the example WebForm and putting a wrong date value into the second field will result in the following after pushing the button:

Change the 'aX' into 'a1' and the input will be accepted.


Meaningful Validation Messages

Users won't be happy if they see messages like 'Input is not valid!' at the bottom of a WebForm without getting a hint which of the values are wrong. This chapter shows how to use the validators info attribute to create userdefined components to show validation messages:

At first let's create a userdefined component with the New WebPanel Wizard. This component will be used instead of every input field in the example and acts as an adapter to the WebText fields. It will automatically handle whether to show the validators message or not. If a validation fails the component shows an additional label with an info about the natuure of the expected values. Therefore we will refer to this component in the further steps as ValidationText.

After creation change to the panels design view, add a WebText and a WebLabel and clear their value attributes. Add a validation event handler to the WebText. Implement the validation handler like this:

  private void doWebTextValidated( ValidationEvent e ) throws Exception {
    if( e.getResult() ) {
      wlbValidationMessage.setValue( "" );
    } else {
      wlbValidationMessage.setValue( e.getValidator().getInfo() );
    }
  }
As the handlers caller is a registered listener to the WebTexts validation event, this automatically will be triggered, if the validation method of our example form is called as we do in its buttons action handler. If validation fails (e.getResult() == false), the info attribute of the validator will be used to show a appropriate message.

To enable W4T Eclipse to edit the validator settings of the wrapped text add the following userdefined code:

  //$userdefined_start
  public Validator getValidator() {
    return webText.getValidator();
  }
  public void setValidator( final Validator validator ) {
    webText.setValidator( validator );
  }
  //$userdefined_end
Finally add the ValidationText to the widget selector.

In the second step replace all text input fields in the example form with our newly created ValidationText. Set the validator implementations to the component as you did before with the text fields. Also use the same validator settings. Complete it all with meaningfull messages for the info attribute of each validator.

Launch the example WebForm and test the validation, it should look like the picture above.


Copyright (c) 2003 by Innoopract Informationssysteme GmbH. All rights reserved.