Get the book!
The best Java IDE

Tip of the week: Validation

Posted by Eitan Suez Sat, 12 Aug 2006 01:27:00 GMT

Shedding Light on JMatter's Design. Part 2: Validation

In this Blog entry I will describe how validation works in JMatter. There are a number of facets to validation.

Let's begin by describing one aspect of the GUI. In the user interface, when an object is displayed, it can be edited and saved (updated). The user interface code that lays out the form conveniently inserts a number of panels called validation panels. These panels are normally empty and thus not apparent in the user interface.

For an object with three atomic fields, say a Speaker with a name (StringEO), a title (StringOE), and a biography (TextEO), four validation panels will be setup: one for each field, and one for the instance.

Each atomic type asserts its own validation requirements. Let's take a look at USZipCode for a moment:

  private static String omit = "- ";
  private static String valid = "0123456789";

  public int validate()
  {
     String value = SimpleParser.parseValue(omit, valid, _value);
     if (value == null || value.length() != 5 && value.length() != 9) return invalid();
     return 0;
  }

  private int invalid()
  {
     fireValidationException("Invalid zip code: "+_value);
     return 1;
  }

Here we see that USZipCode implements the validate() method, which the framework will invoke at the proper time. The return value, an int, specifies the number of "errors" encountered. So a return value of 0 implies that everything validated just fine.

There's a publish/subscribe pattern here, where the model object does not have a reference to its corresponding view (or editor) object. Yet, somehow the UI must display the validation error message. So the model object publishes the message Invalid zip code... when a validation error is encountered. The validation panel that corresponds to a field of this type in the user interface is a listener, and receives the messages, and properly displays the error:

Zip Code Validation

The way you should view this form is that each field has its own validation panel embedded in the form, just above it. Most of the time these panels are dormant. When a validation message is published, it will appear in the appropriate place. Each panel listens to the value of its corresponding field.

The method validate() will be called on atomic objects when a user tabs out of the editor (when the field loses focus) and again when the user attempts to save the object.

So validation on atomic types such as such as USZipCode, SSN, and USPhone is implemented in this way.

Some validation logic is really more an artifact of how the atomic type's editor chose to allow you to enter the information, and so is implemented directly on the editor. Let's take a look at PasswordEditor's bind() method, which is called by the framework to bind the value entered in the editor back to the model object:

  public int bind(AtomicEObject value)
  {
     Password eo = (Password) value;

     String pwd1 = new String(_pf1.getPassword());
     String pwd2 = new String(_pf2.getPassword());

     if (pwd1.length() < Password.MINLENGTH)
     {
        eo.fireValidationException("Password must be at least "+Password.MINLENGTH+" characters long");
        return 1;
     }

     if (! pwd1.equals(pwd2) )
     {
        eo.fireValidationException("Password and repeated password do not match.");
        return 1;
     }

     eo.parseValue(pwd1);
     return 0;
  }

This editor displays two password fields. The entered value must match each time. Also there's a check to ensure that the specified password meets certain criteria, such as a minimum length, or a exceeding a certain minimum password strength, based on a specific algorithm. Again, we see here how the editor, through its reference to the model object, can also tap into the validation panels to display the proper validation error message.

In addition, baked into the framework is a simple mechanism for marking fields as required. The framework will automatically check that required fields left empty will veto the ability to save an object, and will also display a proper error message.

The way to mark required fields in JMatter at the moment is to specify them in the properties file model-metadata.properties. Here's a sample file, taken from an application having to do with aircraft that I recently implemented:

  Airport.airportID.required=true
  Airport.name.required=true
  Airport.lat.required=true
  Airport.lon.required=true
  Airport.city.required=true
  Airport.stateCode.required=true

  Trip.aircraft.default=from Aircraft a where name='CitationJet'

  TripSegment.from.required=true
  TripSegment.to.required=true

Notice that this metadata file is also used to specify default values for fields. Note how a default value is specified for an association by actually looking it up in the database. The value specified is valid hql (hibernate query language). Let's get back to discussing validation.

Any type you define in your object model can define its own validation as well, again, simply by implementing validate(). Here's how it was implemented for the type "TripSegment" in an application I developed:

  public int validate()
  {
     if (_from == null || _to == null)
     {
        String msg = "From and To fields cannot be empty";
        fireValidationException(msg);
        return 1;
     }
     if (_from.equals(_to))
     {
        fireValidationException("From and To fields cannot be the same");
        return 1;
     }
     if (_trip != null && _trip.getAircraft() != null)
     {
        Aircraft aircraft = _trip.getAircraft();
        if (aircraft.hasRangeFor(_numPassengers.intValue()))
        {
           int range = aircraft.rangeFor(_numPassengers.intValue());
           if (range < distance())
           {
              String msg = String.format("Segment distance exceeds aircraft range (%d) " +
                    "for specified number of passengers", range);
              fireValidationException(msg);
              return 1;
           }
        }
     }
     return 0;
  }

So there's a simple business rule in this case that a trip segment's distance cannot exceed the specified aircraft's range for the specified number of passengers.

So here you have it, in a nutshell, an overview of validation in JMatter, how to implement it in your applications, and how things work under the hood.

In the next tip, I will be discussing how to write your own editors and renderers for atomic types.

no comments

Comments

Comments are disabled

Powered