package org.wicketstuff.jsr303.validator;
import com.google.common.base.Preconditions;
import org.apache.wicket.Component;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.injection.Injector;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.model.AbstractPropertyModel;
import org.apache.wicket.model.IChainingModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.lang.PropertyResolver;
import org.apache.wicket.validation.INullAcceptingValidator;
import org.apache.wicket.validation.IValidatable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wicketstuff.jsr303.PropertyValidationErrorBuilder;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.io.Serializable;
import java.util.Set;
/**
* Validator that can be bound to a input field but does a bean validation.
* Requires that the form uses a CompoundPropertyModel containing the bean
* to validate.
* Must be added after the component is added to a form.
*
* @author flowerrrr
*/
public class FormComponentBeanValidator<T> extends Behavior implements INullAcceptingValidator<String>, Serializable {
private final static Logger log = LoggerFactory.getLogger(FormComponentBeanValidator.class);
private Class<?>[] groups;
private String propertyExpression;
private Form form;
@SpringBean(name = "wicketValidator")
private Validator validator;
public FormComponentBeanValidator(Class<?>[] groups) {
this.groups = groups;
Injector.get().inject(this);
}
public FormComponentBeanValidator(Class<?> group) {
this(new Class<?>[]{group});
}
@Override
public void bind(Component component) {
form = component.findParent(Form.class);
Preconditions.checkNotNull(form);
Preconditions.checkArgument(component instanceof FormComponent);
if (propertyExpression == null) {
propertyExpression = getPropertyExpression(component.getDefaultModel());
}
}
private static String getPropertyExpression(final IModel<?> model) {
Preconditions.checkNotNull(model);
if (model instanceof AbstractPropertyModel) {
return ((AbstractPropertyModel) model).getPropertyExpression();
} else if (model instanceof IChainingModel) {
return getPropertyExpression(((IChainingModel) model).getChainedModel());
} else {
throw new IllegalArgumentException("Could not resolve property expression for model [" + model + "]");
}
}
@Override
public void validate(IValidatable<String> validatable) {
T bean = (T) form.getModelObject();
Object origValue = getProperty(bean, propertyExpression);
setProperty(bean, propertyExpression, validatable.getValue());
log.debug("Validating bean[{}]", bean);
Set<ConstraintViolation<T>> violations = validator.validate(bean, groups);
for (ConstraintViolation<T> v : violations) {
log.debug("Constraint violation: " + v);
validatable.error(new PropertyValidationErrorBuilder<T>(v).createError());
}
setProperty(bean, propertyExpression, origValue);
}
private static void setProperty(final Object bean, final String propertyExpression, final Object value) {
PropertyResolver.setValue(propertyExpression, bean, value, null);
}
private static Object getProperty(final Object bean, final String propertyExpression) {
return PropertyResolver.getValue(propertyExpression, bean);
}
}