package org.wicketstuff.jsr303.validator; import de.flower.common.annotation.Patched; import de.flower.common.util.Check; import org.apache.wicket.Component; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.injection.Injector; 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.validation.INullAcceptingValidator; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidationError; 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.List; import java.util.Set; /** * Overriden version with custom behavior tailored to our needs. * <p/> * - can be added to component even if component is not attached to form. * * @param <T> */ @Patched public class PropertyValidator<T> implements INullAcceptingValidator<T>, Serializable { private final static Logger log = LoggerFactory.getLogger(PropertyValidator.class); private static final long serialVersionUID = 1L; private Class<T> beanClass; private String propertyExpression; private final Component fc; @SpringBean(name = "wicketValidator") private Validator validator; public PropertyValidator(FormComponent<T> componentToApplyTo, Class<T> beanClass, String propertyExpression) { this.fc = componentToApplyTo; this.beanClass = beanClass; this.propertyExpression = propertyExpression; Injector.get().inject(this); } public PropertyValidator(FormComponent<T> componentToApplyTo) { this(componentToApplyTo, null, null); // rest will be initialized lazily. } private void init() { if (this.beanClass == null) { AbstractPropertyModel<?> apm = getPropertyModel(fc.getDefaultModel()); this.beanClass = (Class<T>) apm.getTarget().getClass(); this.propertyExpression = apm.getPropertyExpression(); } } public void validate(final IValidatable<T> validatable) { init(); // skip, if propertyExpression is empty if (propertyExpression == null || propertyExpression.trim().length() == 0) return; // skip, if marked as excluded if (hasExclusionBehaviour()) return; final T value = validatable.getValue(); final Set<ConstraintViolation<T>> violations = validator.validateValue(beanClass, propertyExpression, value); for (final ConstraintViolation v : violations) { log.debug("Constraint violation: " + v); final IValidationError ve = new PropertyValidationErrorBuilder<T>(v).createError(); validatable.error(ve); } } private static AbstractPropertyModel<?> getPropertyModel(final IModel<?> model) { Check.notNull(model); if (model instanceof AbstractPropertyModel) { return ((AbstractPropertyModel) model); } else if (model instanceof IChainingModel) { return getPropertyModel(((IChainingModel) model).getChainedModel()); } else { throw new IllegalArgumentException("Could not find property model"); } } private boolean hasExclusionBehaviour() { List<? extends Behavior> behaviors = fc.getBehaviors(); for (Behavior iBehavior : behaviors) { if (iBehavior instanceof PropertyValidator.Exclude) { return true; } } return false; } public static class Exclude extends Behavior { private static final long serialVersionUID = 1L; } }