SpringOvalValidator.java
/**
* Copyright 2014 Global Crop Diversity Trust
*
* 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.genesys.spring.validation.oval.spring;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import net.sf.oval.context.FieldContext;
import net.sf.oval.context.OValContext;
import net.sf.oval.exception.ValidationFailedException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
public class SpringOvalValidator implements org.springframework.validation.Validator, InitializingBean {
private Validator validator;
public SpringOvalValidator() {
validator = new Validator();
}
public SpringOvalValidator(Validator validator) {
this.validator = validator;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
@Override
public void validate(Object target, Errors errors) {
doValidate(target, errors, "");
}
private void doValidate(Object target, Errors errors, String fieldPrefix) {
try {
for (final ConstraintViolation violation : validator.validate(target)) {
final OValContext ctx = violation.getContext();
final String errorCode = violation.getErrorCode();
final String errorMessage = violation.getMessage();
if (ctx instanceof FieldContext) {
final String fieldName = fieldPrefix + ((FieldContext) ctx).getField().getName();
errors.rejectValue(fieldName, errorCode, errorMessage);
} else {
errors.reject(errorCode, errorMessage);
}
}
try {
final Field[] fields = getFields(target);
for (final Field field : fields) {
final SpringValidateNestedProperty validate = field.getAnnotation(SpringValidateNestedProperty.class);
if (validate != null) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
final Object nestedProperty = field.get(target);
if (nestedProperty != null) {
final String name = field.getName();
if (nestedProperty instanceof Collection<?>) {
// valueToValidate is a collection
final Collection<?> col = (Collection<?>) nestedProperty;
int index = 0;
for (final Object object : col) {
doValidate(object, errors, name + "[" + index + "].");
index++;
}
} else if (nestedProperty instanceof Map<?, ?>) {
// valueToValidate is a map, but only as a
// string key
final Map<?, ?> map = (Map<?, ?>) nestedProperty;
for (final Map.Entry<?, ?> entry : map.entrySet()) {
final Object key = entry.getKey();
if (!(key instanceof String)) {
throw new IllegalArgumentException("Map as a nested property supports only String keys for validation");
}
doValidate(entry.getValue(), errors, name + "['" + key + "']");
}
} else if (nestedProperty.getClass().isArray()) {
// valueToValidate is an array
final int length = Array.getLength(nestedProperty);
for (int i = 0; i < length; i++) {
final Object o = Array.get(nestedProperty, i);
doValidate(o, errors, name + "[" + i + "].");
}
} else {
// valueToValidate is other object
doValidate(nestedProperty, errors, name + ".");
}
}
}
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
} catch (final ValidationFailedException ex) {
errors.reject(ex.getMessage());
}
}
private Field[] getFields(Object target) {
final Class<?> clazz = target.getClass();
final List<Field> fields = doGetFields(clazz);
return fields.toArray(new Field[fields.size()]);
}
private List<Field> doGetFields(Class<?> clazz) {
final ArrayList<Field> list = new ArrayList<Field>();
final Field[] fields = clazz.getDeclaredFields();
list.addAll(Arrays.asList(fields));
if (clazz.getSuperclass() != null) {
list.addAll(doGetFields(clazz.getSuperclass()));
}
return list;
}
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(validator, "Property [validator] is not set");
}
public Validator getValidator() {
return validator;
}
public void setValidator(Validator validator) {
this.validator = validator;
}
}