package es.internna.spring.mvc.api.validation;
import es.internna.spring.annotations.Bean;
import es.internna.spring.annotations.validation.DecimalConstraint;
import es.internna.spring.annotations.validation.FieldConstraint;
import es.internna.spring.annotations.validation.IntegerConstraint;
import es.internna.spring.annotations.validation.StringConstraint;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Bean(name="genericValidator", initMethod="init")
public class GenericValidator implements Validator
{
private Log log = LogFactory.getLog(GenericValidator.class);
private Set<Class<? extends Annotation>> validAnnotations = null;
private String[] errorCodes = new String[] {"error.required", "error.regexp", "error.min", "error.max"};
public void setValidAnnotations(Set<Class<? extends Annotation>> validAnnotations)
{
this.validAnnotations = validAnnotations;
}
public void init()
{
validAnnotations = new HashSet<Class<? extends Annotation>>();
validAnnotations.add(FieldConstraint.class );
validAnnotations.add(IntegerConstraint.class);
validAnnotations.add(DecimalConstraint.class);
validAnnotations.add(StringConstraint.class);
}
protected Class<? extends Annotation> getValidationAnnotation(Field field)
{
Class<? extends Annotation> validAnnotation = null;
if (field != null)
for (Annotation annotation: field.getAnnotations())
if (validAnnotation == null)
if (validAnnotations.contains(annotation.annotationType()))
validAnnotation = annotation.annotationType();
return validAnnotation;
}
public boolean supports(Class aClass)
{
boolean supports = false;
if ((aClass != null) & (validAnnotations != null))
{
for (Field field : aClass.getDeclaredFields())
if (!supports) supports |= (getValidationAnnotation(field) != null);
}
return supports;
}
private boolean validateNull(Field field, Object object)
{
boolean result = false;
try
{
field.setAccessible(true);
result = (field.get(object) == null);
}
catch (Exception ex)
{
if (log.isDebugEnabled()) log.debug("Error validating " + field, ex);
}
return result;
}
protected void validateRequiredField(Field field, Object object, Errors errors)
{
FieldConstraint req = field.getAnnotation(FieldConstraint.class);
if (req.required() && validateNull(field, object))
errors.rejectValue(field.getName(), errorCodes[0]);
}
protected void validateFieldAsString(Field field, Object object, Errors errors)
{
StringConstraint req = field.getAnnotation(StringConstraint.class);
if (req.required() && validateNull(field, object)) errors.rejectValue(field.getName(), errorCodes[0]);
String stringField = null;
try
{
field.setAccessible(true);
stringField = field.get(object).toString();
}
catch (Exception ex)
{
if (log.isDebugEnabled()) log.debug("Error validating " + field, ex);
}
if ((stringField != null) & (req.regexp().length() > 0))
{
try
{
Pattern pattern = Pattern.compile(req.regexp());
Matcher matcher = pattern.matcher(stringField);
if (!matcher.matches())
errors.rejectValue(field.getName(), errorCodes[1], req.regexp());
}
catch (Exception ex)
{
if (log.isDebugEnabled()) log.debug("Error validating " + field, ex);
}
}
if ((stringField != null) & (req.maxLength() > 0))
{
if (stringField.length() > req.maxLength())
errors.rejectValue(field.getName(), errorCodes[3], Integer.toString(req.maxLength()));
}
}
private void validateNumber(double min, double max, Field field, Object object, Errors errors)
{
try
{
double value = 0.0;
field.setAccessible(true);
Object val = field.get(object);
if (val != null)
{
String sVal = val.toString();
value = Double.valueOf(sVal).doubleValue();
if (value < min) errors.rejectValue(field.getName(), errorCodes[2], Double.toString(min));
if (value > max) errors.rejectValue(field.getName(), errorCodes[3], Double.toString(max));
}
}
catch (Exception ex)
{
if (log.isDebugEnabled()) log.debug("Error validating " + field, ex);
}
}
protected void validateFieldAsInteger(Field field, Object object, Errors errors)
{
IntegerConstraint req = field.getAnnotation(IntegerConstraint.class);
if (req.required() && validateNull(field, object)) errors.rejectValue(field.getName(), errorCodes[0]);
int min = req.minValue();
int max = req.maxValue();
validateNumber(min, max, field, object, errors);
}
protected void validateFieldAsDouble(Field field, Object object, Errors errors)
{
DecimalConstraint req = field.getAnnotation(DecimalConstraint.class);
if (req.required() && validateNull(field, object)) errors.rejectValue(field.getName(), errorCodes[0]);
double min = req.minValue();
double max = req.maxValue();
validateNumber(min, max, field, object, errors);
}
protected void validateField(Field field, Object object, Errors errors)
{
if (field != null)
{
Class<? extends Annotation> annotation = this.getValidationAnnotation(field);
if (annotation != null)
{
if (FieldConstraint.class.equals(annotation)) validateRequiredField(field, object, errors);
if (StringConstraint.class.equals(annotation)) validateFieldAsString(field, object, errors);
if (IntegerConstraint.class.equals(annotation)) validateFieldAsInteger(field, object, errors);
if (DecimalConstraint.class.equals(annotation)) validateFieldAsDouble(field, object, errors);
}
}
}
public void validate(Object object, Errors errors)
{
if (log.isDebugEnabled()) log.debug("Validating " + object);
if ((object == null) | (errors == null)) throw new IllegalArgumentException();
if (!this.supports(object.getClass())) throw new IllegalArgumentException();
for (Field field : object.getClass().getDeclaredFields()) validateField(field, object, errors);
if (log.isDebugEnabled()) log.debug("Validation returned " + errors.getErrorCount() + " errors.");
}
}