package edu.ualberta.med.biobank.mvp.presenter.validation;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.HasValue;
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.Severity;
import com.pietschy.gwt.pectin.client.form.validation.ValidationResult;
import com.pietschy.gwt.pectin.client.form.validation.ValidationResultImpl;
import com.pietschy.gwt.pectin.client.form.validation.Validator;
import com.pietschy.gwt.pectin.client.form.validation.component.ValidationDisplay;
/**
*
* @author jferland
*
* @param <T> source value type
*/
public class ValueValidation<T> extends AbstractValidation implements
Disposable {
private final SourceMonitor sourceMonitor = new SourceMonitor();
private final List<ConditionalValidator> validators =
new ArrayList<ConditionalValidator>();
private final HasValue<T> source;
ValueValidation(HasValue<T> source) {
this.source = source;
bindValidationTo(source);
handlerRegistry.add(source.addValueChangeHandler(sourceMonitor));
}
public void add(Validator<? super T> validator, Condition condition) {
ConditionalValidator conditionalValidator =
new ConditionalValidator(validator, condition);
validators.add(conditionalValidator);
}
@Override
public void bindValidationTo(ValidationDisplay validationDisplay) {
ValidationBinding binding = new ValidationBinding(validationDisplay);
handlerRegistry.add(addValidationHandler(binding));
}
@Override
public boolean validate() {
runValidators();
updateValidationResult();
return getValidationResult().contains(Severity.ERROR);
}
private void updateValidationResult() {
ValidationResultImpl result = new ValidationResultImpl();
for (ConditionalValidator validator : validators) {
result.addAll(validator.getValidationResult().getMessages());
}
setValidationResult(result);
}
private void runValidators() {
for (ConditionalValidator validator : validators) {
validator.validate();
}
}
private class SourceMonitor implements ValueChangeHandler<T> {
@Override
public void onValueChange(ValueChangeEvent<T> event) {
validate();
}
}
private class ConditionalValidator {
private final ConditionMonitor conditionMonitor =
new ConditionMonitor();
private final CachedValidator<T> validator;
private final Condition condition;
private ValidationResult result = EmptyValidationResult.INSTANCE;
private ConditionalValidator(Validator<? super T> validator,
Condition condition) {
this.validator = new CachedValidator<T>(validator);
this.condition = condition;
}
public void validate() {
if (ConditionUtil.isTrue(condition)) {
T value = source.getValue();
ValidationResultImpl result = new ValidationResultImpl();
validator.validate(value, result);
this.result = result;
conditionMonitor.setValidateOnChange(false);
} else {
// make sure our ValidationResult is up-to-date
this.result = EmptyValidationResult.INSTANCE;
// we were told to validate, but the condition was not true, so,
// delay validation until the condition becomes true
conditionMonitor.setValidateOnChange(true);
}
}
public ValidationResult getValidationResult() {
return result;
}
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(validator.getValidationResult().getMessages());
}
setValidationResult(result);
ValueValidation.this.updateValidationResult();
}
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 void bindValidationTo(Object object) {
if (object instanceof ValidationDisplay) {
ValidationDisplay validationDisplay = (ValidationDisplay) object;
bindValidationTo(validationDisplay);
}
}
}