/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.binding.validation.support;
import com.google.common.collect.Sets;
import org.springframework.util.Assert;
import org.valkyriercp.binding.form.ValidatingFormModel;
import org.valkyriercp.binding.validation.RichValidator;
import org.valkyriercp.binding.validation.ValidationMessage;
import org.valkyriercp.binding.validation.ValidationResults;
import org.valkyriercp.core.Severity;
import org.valkyriercp.rules.reporting.ObjectNameResolver;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.util.HashSet;
import java.util.Set;
public class JSR303Validator<T> implements RichValidator<T>, ObjectNameResolver {
private ValidatingFormModel formModel;
private Class<T> beanClass;
private final Validator validator;
private Set<String> ignoredProperties;
private DefaultValidationResults results = new DefaultValidationResults();
/**
* Creates a new JSR303Validator without ignoring any properties.
*
* @param formModel The {@link ValidatingFormModel} on which validation
* needs to occur
* @param beanClass The class of the object this validator needs to check
*/
public JSR303Validator(ValidatingFormModel formModel, Class<T> beanClass) {
this(formModel, beanClass, new HashSet<String>());
}
/**
* Creates a new JSR303Validator with additionally a set of
* properties that should not be validated.
*
* @param formModel The {@link ValidatingFormModel} on which validation
* needs to occur
* @param beanClass The class of the object this validator needs to check
* @param ignoredProperties properties that should not be checked
* though are
*/
public JSR303Validator(ValidatingFormModel formModel, Class<T> beanClass,
Set<String> ignoredProperties) {
this.formModel = formModel;
this.beanClass = beanClass;
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
this.ignoredProperties = ignoredProperties;
}
/**
* {@inheritDoc}
*/
public ValidationResults validate(T object) {
return validate(object, null);
}
/**
* {@inheritDoc}
*/
public ValidationResults validate(T object, String propertyName) {
if (propertyName == null) {
results.clearMessages();
}
else {
results.clearMessages(propertyName);
}
addInvalidValues(doValidate(object, propertyName));
return results;
}
/**
* Add all {@link ConstraintViolation}s to the {@link ValidationResults}.
*/
protected void addInvalidValues(Set<ConstraintViolation<T>> invalidValues) {
if (invalidValues != null) {
for (ConstraintViolation invalidValue : invalidValues) {
results.addMessage(translateMessage(invalidValue));
}
}
}
/**
* Translate a single {@link ConstraintViolation} to a {@link ValidationMessage}.
*/
protected ValidationMessage translateMessage(ConstraintViolation invalidValue) {
return new DefaultValidationMessage(invalidValue.getPropertyPath().toString(), Severity.ERROR,
resolveObjectName(invalidValue.getPropertyPath().toString()) + " " + invalidValue.getMessage());
}
/**
* Validates the object through Hibernate Validator
*
* @param object The object that needs to be validated
* @param property The properties that needs to be validated
* @return An array of {@link ConstraintViolation}, containing all validation
* errors
*/
protected Set<ConstraintViolation<T>> doValidate(final T object, final String property) {
if (property == null) {
final Set<ConstraintViolation<T>> ret = Sets.newHashSet();
PropertyDescriptor[] propertyDescriptors;
try {
propertyDescriptors = Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors();
}
catch (IntrospectionException e) {
throw new IllegalStateException("Could not retrieve property information");
}
for (final PropertyDescriptor prop : propertyDescriptors) {
String propertyName = prop.getName();
if (formModel.hasValueModel(propertyName) && !ignoredProperties.contains(propertyName)) {
final Set<ConstraintViolation<T>> result = validator.validateValue(beanClass, propertyName, formModel
.getValueModel(propertyName).getValue()); //validator.validateProperty(object, propertyName);
if (result != null) {
ret.addAll(result);
}
}
}
return ret;
}
else if (!ignoredProperties.contains(property) && formModel.hasValueModel(property)) {
return validator.validateValue(beanClass, property, formModel
.getValueModel(property).getValue());
}
else {
return null;
}
}
/**
* Clear the current validationMessages and the errors.
*
* @see #validate(Object, String)
*/
public void clearMessages() {
this.results.clearMessages();
}
/**
* Add a property for the validator to ignore.
*
* @param propertyName Name of the property to ignore. Cannot be null.
*/
public void addIgnoredProperty(String propertyName) {
Assert.notNull(propertyName);
ignoredProperties.add(propertyName);
}
/**
* Remove a property for the Hibernate validator to ignore.
*
* @param propertyName Name of the property to be removed. Cannot be null.
*/
public void removeIgnoredProperty(String propertyName) {
Assert.notNull(propertyName);
ignoredProperties.remove(propertyName);
}
/**
* {@inheritDoc}
*/
public String resolveObjectName(String objectName) {
return formModel.getFieldFace(objectName).getDisplayName();
}
}