Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Улучшенный Spring валидатор для Oval
Постоянная ссылка 05-02-2008 anydoby java

Попробовал прикрутить к своему проекту Spring валидатор от Oval и немного в нем разочаровался. У меня есть сложная форма с несколькими вложенными объектами, и вот по дереву вложенных объектов валидатор не ходит. Предлагаю несложное решение проблемы:


package com.anydoby.oval;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * This annotation tells custom spring validator to follow the path and validate a nested property
 * object.
 * @author SergeyZ
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ValidateNestedProperty {

}

Вешаем эту аннтоацию на все вложенные объекты, которые хотим валидировать. И используем вот этот валидатор для формы:


package com.anydoby.oval;


import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import net.sf.oval.ConstraintViolation;
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;
import org.springframework.validation.Validator;

/**
 * Implements spring's validator. Uses Oval {@link net.sf.oval.Validator} for actually validating
 * the objects. Supports validating nested complex properties, which should be marked with
 * {@link ValidateNestedProperty}.
 * 
 * @author SergeyZ
 * 
 */
public class SpringOvalValidator implements Validator, InitializingBean {

  private net.sf.oval.Validator validator;


  public SpringOvalValidator(net.sf.oval.Validator validator) {
    this.validator = validator;
  }


  @SuppressWarnings("unchecked")
  public boolean supports(Class clazz) {
    return true;
  }


  public void validate(Object target, Errors errors) {
    doValidate(target, errors, "");
  }


  @SuppressWarnings("unchecked")
  private void doValidate(Object target, Errors errors, String fieldPrefix) {
    try {
      for (ConstraintViolation violation : validator.validate(target)) {
        OValContext ctx = violation.getContext();
        String errorCode = violation.getErrorCode();
        String errorMessage = violation.getMessage();

        if (ctx instanceof FieldContext) {
          String fieldName = fieldPrefix + ((FieldContext)ctx).getField().getName();
          errors.rejectValue(fieldName, errorCode, errorMessage);
        } else {
          errors.reject(errorCode, errorMessage);
        }
      }

      try {
        Field[] fields = getFields(target);
        for (Field field : fields) {
          String name = field.getName();
          ValidateNestedProperty validate = field.getAnnotation(ValidateNestedProperty.class);
          if (validate != null) {
            if (!field.isAccessible()) {
              field.setAccessible(true);
            }
            Object nestedProperty = field.get(target);
            if (nestedProperty != null) {
              doValidate(nestedProperty, errors, name + ".");
            }
          }
        }
      } catch (Exception e) {
        throw new RuntimeException(e);
      }

    } catch (final ValidationFailedException ex) {
      errors.reject(ex.getMessage());
    }
  }


  @SuppressWarnings("unchecked")
  private Field[] getFields(Object target) {
    Class clazz = target.getClass();
    List<Field> fields = doGetFields(clazz);
    return fields.toArray(new Field[fields.size()]);
  }


  @SuppressWarnings("unchecked")
  private List<Field> doGetFields(Class clazz) {
    ArrayList<Field> list = new ArrayList<Field>();
    Field[] fields = clazz.getDeclaredFields();
    list.addAll(Arrays.asList(fields));
    if (clazz.getSuperclass() != null) {
      list.addAll(doGetFields(clazz.getSuperclass()));
    }
    return list;
  }


  public void afterPropertiesSet() throws Exception {
    Assert.notNull(validator, "Property [validator] is not set");
  }


  public net.sf.oval.Validator getValidator() {
    return validator;
  }


  public void setValidator(net.sf.oval.Validator validator) {
    this.validator = validator;
  }

}

Для ленивых, использующих Maven2: можно добавить мой репозиторий к себе в билд


  <repositories>
    <repository>
      <id>anydoby.com</id>
      <url>http://anydoby.com/maven</url>
    </repository>
  </repositories>

а в зависимости


    <dependency>
      <groupId>com.anydoby</groupId>
      <artifactId>oval-extensions</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    </dependency>

и пользоваться.

Добавить комментарий

Предыдущая статья E-mail валидатор и не только для Oval Следующая статья Автоматическая генерация хешей и включение исходников артефактов в Maven 2