package edu.ualberta.med.biobank.mvp.presenter.validation; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.pietschy.gwt.pectin.client.binding.Disposable; import com.pietschy.gwt.pectin.client.condition.Condition; import com.pietschy.gwt.pectin.client.form.validation.EmptyValidationResult; import com.pietschy.gwt.pectin.client.form.validation.HasValidation; import com.pietschy.gwt.pectin.client.form.validation.ValidationEvent; import com.pietschy.gwt.pectin.client.form.validation.ValidationHandler; import com.pietschy.gwt.pectin.client.form.validation.ValidationResult; import com.pietschy.gwt.pectin.client.form.validation.ValidationResultImpl; /** * A delegating validator, with a condition for the validation result. Useful * for conditional validation of a {@link HasValidation}. * <p> * If the condition is not met, will delay validation until the condition is * met. Note that if the delegate has never been * {@link HasValidation#validate()}-d, then even if the condition is true, the * {@link ValidationResult} will be empty, i.e. condtion changes themselves do * not cause validation to be done. * <p> * Listens for {@link ValidationEvent}-s from the delegated * {@link HasValidation} and for {@link ValueChangeEvent}-s from the * {@link Condition}. When either event is received, update the internal * {@link ValidationResult} (if the condition is met), which will fire another * {@link ValidationEvent}, but from this instance. * * @author jferland * */ class ConditionalValidation extends AbstractValidation implements Disposable { private final ConditionMonitor conditionMonitor = new ConditionMonitor(); private final ValidatorMonitor validatorMonitor = new ValidatorMonitor(); private final HasValidation delegate; private final Condition condition; ConditionalValidation(HasValidation delegate, Condition condition) { this.delegate = delegate; this.condition = condition; handlerRegistry.add(condition.addValueChangeHandler(conditionMonitor)); handlerRegistry.add(delegate.addValidationHandler(validatorMonitor)); } @Override public boolean validate() { boolean valid = true; if (ConditionUtil.isTrue(condition)) { // will result in updateValidationResult() being called since this // listens for events from the delegate valid = delegate.validate(); conditionMonitor.setValidateOnChange(false); } else { // make sure our ValidationResult is up-to-date setValidationResult(EmptyValidationResult.INSTANCE); // we were told to validate, but the condition was not true, so, // delay validation until the condition becomes true conditionMonitor.setValidateOnChange(true); } return valid; } @Override public void clear() { super.clear(); delegate.clear(); } private void updateValidationResult() { ValidationResultImpl result = new ValidationResultImpl(); // only need to decide whether we pay attention to the ValidationResult, // no need to re-validate() if (ConditionUtil.isTrue(condition)) { result.addAll(delegate.getValidationResult().getMessages()); } setValidationResult(result); } private class ConditionMonitor implements ValueChangeHandler<Boolean> { private boolean validateOnChange = false; @Override public void onValueChange(ValueChangeEvent<Boolean> event) { if (isValidateOnChange()) { validate(); } else { updateValidationResult(); } } public void setValidateOnChange(boolean validate) { this.validateOnChange = validate; } public boolean isValidateOnChange() { return validateOnChange; } } private class ValidatorMonitor implements ValidationHandler { @Override public void onValidate(ValidationEvent event) { updateValidationResult(); } } }