JFace Databinding enables an easy binding between values inside of data models and SWT/JFace widgets. No more boring listeners to implement – just create observables and connect them using the data binding context. There are several brilliant articles written about it. My favorites are those from Ralf Ebert and Lars Vogel.

One of the interesting aspects of databinding is data validation. The update strategies, responsible for propagation of changes in models or in widgets can be supplied with validators, making sure that the data changes are legal. In the same time the JSR-303 Bean Validation specification focuses on a modern standardized way of data validation. In this post, I combine these subjects and use JSR-303 in JFace Databinding Validators.

One of the core insights of the JSR-303 is the idea of annotation of data validation constraints on data itself. It is indeed a good observation, that validation code strongly relies on the data structure and semantics. To follow this idea consequently, the application developer should care of validation during implementation of business logic as less as possible. A much better idea is to encapsulate the entire validation into domain-specific types. Let me demonstrate it by example, imagine the following class:

public class Customer {
  private String name;
  private String address;
  private String zip;
  private String city;
}

This is perfectly reasonable, but now consider not only the data storage/transport aspects, but also the validation aspects. A standard approach would be to use the following validator logic, in the databinding:

public class CustomerComposite {
[...]
  public void bindValues(Customer model, DataBindingContext dbc) {
    UpdateValueStrategy m2t = new UpdateValueStrategy();
    m2t.setAfterGetValidator(new IValidator() {
      @Override
      public IStatus validate(Object value) {
        String name = (String) value;
        if (name == null || Helper.isRegex(name, "[A-Za-z -]*")) {
          return ValidationStatus.error("Wrong name");
        }
          return ValidationStatus.ok();
        }
      });
    dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(namefield),
      BeanProperties.value(Customer.class, "name").observe(model),
      new UpdateValueStrategy(), m2t);

    m2t = new UpdateValueStrategy();
    m2t.setAfterGetValidator(new IValidator() {
      @Override
      public IStatus validate(Object value) {
        String zipCode = (String) value;
        if (zipCode == null || zipCode.length() > 5 || zipCode.length() < 5 || Helper.isRegex(zipCode, "[0-9]*")) {
          return ValidationStatus.error("Wrong zip code");
        }
          return ValidationStatus.ok();
        }
      });
    dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(zipfield),
      BeanProperties.value(Customer.class, "zip").observe(model),
      new UpdateValueStrategy(), m2t);
  [...]
  }

Pretty much code, and rememeber that JFace Databinding code like this can not be reused in other parts of the application. Let’s put the validation logic on the data declaration in a way how JSR-303 proposes to do this:

public class Customer {
  @NotNull
  @Pattern(regexp = "[A-Za-z -]*")
  private String name;
  private String addressLine;
  @Size(min=1, max=5)
  @Pattern(regexp = "[0-9]*")
  private String zip;
  @NotNull
  @Pattern(regexp = "[A-Za-z -]*")
  private String city;
}

As a next step, let us develop an update strategy factory which create update strategies with embedded Validator for JSR-303 Bean Validation constaints.

public class BeanValidator implements IValidator {
  private ValidatorFactory factory = Validation.buildDefaultValidatorFactory();

  @Override
  public IStatus validate(Object value) {
    Set<ConstraintViolation<Object>> violations = factory.getValidator().validate(value,
      new Class<?>[] { Default.class });
    if (violations.size() > 0) {
      List<IStatus> statusList = new ArrayList<IStatus>();
      for (ConstraintViolation<Object> cv : violations) {
        statusList.add(ValidationStatus.error(cv.getMessage()));
      }
      return new MultiStatus(Activator.PLUGIN_ID, IStatus.ERROR,
        statusList.toArray(new IStatus[statusList.size()]), "Validation errors", null);
    }
    return ValidationStatus.ok();
  }
}

public class StrategyFactory {
 public static UpdateValueStrategy getStrategy() {
   UpdateValueStrategy strategy = new UpdateValueStrategy();
   strategy.setAfterConvertValidator(new BeanValidator());
   return strategy;
 }
}

Using the StrategyFactory, the validation code inside of the composite becomes trivial:

public class CustomerComposite {
[...]
  public void bindValues(Customer model, DataBindingContext dbc) {
    dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(namefield),
      BeanProperties.value(Customer.class, "name").observe(model),
      new UpdateValueStrategy(), StrategyFactory.getStrategy());

    dbc.bindValue(WidgetProperties.text(SWT.Modify).observe(zipfield),
     BeanProperties.value(Customer.class, "zip").observe(model),
     new UpdateValueStrategy(), StrategyFactory.getStrategy());
 [...]
}

An important property of the introduced validation approach is the fact, that it can be reused in other application layers (e.G. in service layer, or in data access layer). In other words you can use the same validation logic across the entire application and just remain valid…