/*
* Copyright (c) 2016 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.core.validation.interceptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.AbstractCollection;
import java.util.AbstractList;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;
import javax.annotation.Nullable;
import org.apache.commons.beanutils.PropertyUtils;
import org.obiba.core.service.EntityQueryService;
import org.obiba.core.validation.validator.AbstractPersistenceAwareClassValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@SuppressWarnings("UnusedDeclaration")
public class ObjectValidationInspector {
private final static Logger log = LoggerFactory.getLogger(ObjectValidationInspector.class);
private List<Validator> validators = new ArrayList<Validator>();
protected EntityQueryService entityQueryService;
/**
* @return Returns the validators.
*/
public List<Validator> getValidators() {
return validators;
}
/**
* @param validators The validators to set.
*/
public void setValidators(List<Validator> validators) {
this.validators = validators;
}
public void setEntityQueryService(EntityQueryService entityQueryService) {
this.entityQueryService = entityQueryService;
}
public EntityQueryService getEntityQueryService() {
return entityQueryService;
}
private void inspectObjectProperties(Object arg, List<Errors> errors) {
// Inspect supported properties.
PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(arg.getClass());
for(PropertyDescriptor propertyDescriptor : propertyDescriptors) {
try {
inspectPropertyValue(errors, propertyDescriptor.getReadMethod().invoke(arg));
} catch(IllegalArgumentException e) {
log.error("IllegalArgumentException", e);
} catch(IllegalAccessException e) {
log.error("IllegalAccessException", e);
} catch(InvocationTargetException e) {
log.error("InvocationTargetException", e);
}
}
}
private void inspectPropertyValue(List<Errors> errors, Object propertyValue) {
if(propertyValue != null && isArrayOrCollection(propertyValue.getClass())) {
if(propertyValue.getClass().isArray()) {
for(Object propertyElementValue : (Object[]) propertyValue) {
validatePropertyValue(errors, propertyElementValue);
}
} else {
for(Object propertyElementValue : (Collection<?>) propertyValue) {
validatePropertyValue(errors, propertyElementValue);
}
}
} else {
// Non-Scalar property
validatePropertyValue(errors, propertyValue);
}
}
private void validatePropertyValue(List<Errors> errors, @Nullable Object propertyValue) {
if(propertyValue == null) return;
for(Validator validator : getValidators()) {
if(validator.supports(propertyValue.getClass())) {
log.debug("Validator supported: {}", propertyValue.getClass());
validateAndAddErrors(propertyValue, validator, errors);
inspectObjectProperties(propertyValue, errors);
}
}
}
private Errors validateAndAddErrors(Object arg, Validator validator, Collection<Errors> errors) {
Errors objErrors = new BindException(arg, arg.getClass().getName());
validator.validate(arg, objErrors);
if(objErrors.hasErrors()) {
errors.add(objErrors);
}
return objErrors;
}
public void inspectObject(List<Errors> errors, Object arg) {
for(Validator validator : getValidators()) {
if(validator.supports(arg.getClass())) {
if(entityQueryService != null && validator instanceof AbstractPersistenceAwareClassValidator) {
((AbstractPersistenceAwareClassValidator) validator).setEntityQueryService(entityQueryService);
}
log.debug("Validator supported: {}", arg.getClass());
validateAndAddErrors(arg, validator, errors);
inspectObjectProperties(arg, errors);
}
}
}
private boolean isArrayOrCollection(Class<?> clazz) {
return clazz.isArray() || clazz.isAssignableFrom(List.class) || clazz.isAssignableFrom(ArrayList.class) ||
clazz.isAssignableFrom(Set.class) || clazz.isAssignableFrom(SortedSet.class) ||
clazz.isAssignableFrom(AbstractCollection.class) || clazz.isAssignableFrom(AbstractList.class) ||
clazz.isAssignableFrom(AbstractSet.class) || clazz.isAssignableFrom(HashSet.class) ||
clazz.isAssignableFrom(LinkedHashSet.class) || clazz.isAssignableFrom(LinkedList.class) ||
clazz.isAssignableFrom(TreeSet.class) || clazz.isAssignableFrom(Vector.class);
}
}