Friday, November 4, 2011

Validation with pure Java - 5

Finally, you complete the Validator so it can retrieve and analyze the information. It will use a handy method for retrieving a PropertyDescriptor from the BeanInfo by the property's programmatic name. In case the property was not found for any reason, the method will notify the caller with a self-explanatory exception.
import java.beans.*;
/**
 * This class implements generic data-validation mechanism that is based on the external
 * rules defined in the BeanInfo class.
 */
public class Validator implements VetoableChangeListener {
  public static final String MAX_VALUE = "maxValue";
  public static final String MIN_VALUE = "minValue";
  /**
   * The only method required by {@link VetoableChangeListener} interface.
   */
  public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
    try {
      // here we hope to retrieve explicitly defined additional information
      // about the object-source of the constrained property
      BeanInfo info = Introspector.getBeanInfo(evt.getSource().getClass());
      // find a property descriptor by given property name
      PropertyDescriptor descriptor = getDescriptor(evt.getPropertyName(), info);
      Integer max = (Integer) descriptor.getValue(MAX_VALUE);
      // check if new value is greater than allowed
      if( max != null && max.compareTo(evt.getNewValue()) < 0 ) {
        // complain!
        throw new PropertyVetoException("Value " + evt.getNewValue() + " is greater than maximum allowed " + max, evt);
      }
      Integer min = (Integer) descriptor.getValue(MIN_VALUE);
      // check if new value is less than allowed
      if( min != null && min.compareTo(evt.getNewValue()) > 0 ) {
        // complain!
        throw new PropertyVetoException("Value " + evt.getNewValue() + " is less than minimum allowed " + min, evt);
      }
    } catch (IntrospectionException ex) {
      ex.printStackTrace();
    }
  }
  /**
   * This utility method tries to fetch a PropertyDescriptor from BeanInfo object by given property
   * name.
   * @param name the programmatic name of the property
   * @param info the bean info object that will be searched for the property descriptor.
   * @throws IllegalArgumentException if a property with given name does not exist in the given
   * BeanInfo object.
   */
  private PropertyDescriptor getDescriptor(String name, BeanInfo info) throws IllegalArgumentException {
    PropertyDescriptor[] pds = info.getPropertyDescriptors();
    for( int i=0; i<pds.length; i++) {
      if( pds[i].getName().equals(name) ) {
        return pds[i];
      }
    }
    throw new IllegalArgumentException("Property " + name + " not found.");
  }
}

Well, believe it or not, that's it! You have all the pieces in place now and are ready to test your data validation. This simple driver does just that:
/**
 * A simple application that tries to cause data-validation exception
 * condition.
 */
public class Driver {
  public static void main(String[] args) {
    try {
      BusinessObject source = new BusinessObject();
      source.setNumericValue(10);
      System.out.println("Value accepted: " + source.getNumericValue());
      source.setNumericValue(-10);
      System.out.println("Value accepted: " + source.getNumericValue());
      source.setNumericValue(101);
      // we should never reach this line
      System.out.println("Value accepted: " + source.getNumericValue());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

When you run the application, you will see that the line source.setNumericValue(101) causes a PropertyVetoException with the following message: "Value 101 is greater than maximum allowed 100." Frankly, it is not often that exceptions make me happy. I guess this case is an exception.

No comments: