ConstraintViolationExceptionResolver.java

package cn.home1.oss.lib.errorhandle.internal.resolver;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.newLinkedList;
import static org.springframework.util.StringUtils.isEmpty;

import cn.home1.oss.lib.errorhandle.api.AbstractConcreteExceptionResolver;
import cn.home1.oss.lib.errorhandle.api.ValidationError;

import lombok.extern.slf4j.Slf4j;

import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ElementKind;
import javax.validation.Path;
import javax.validation.Path.Node;

@Slf4j
public class ConstraintViolationExceptionResolver
  extends AbstractConcreteExceptionResolver<ConstraintViolationException> {

  static String convertToString( //
    final ConversionService conversionService, //
    final Object value //
  ) {
    String reuslt;
    if (value == null) {
      reuslt = null;
    } else if (conversionService == null) {
      reuslt = value.toString();
    } else {
      try {
        reuslt = conversionService.convert(value, String.class);
      } catch (final ConversionException ex) {
        if (log.isDebugEnabled()) {
          log.debug("failed to convert value '{}' to String.class.", value, ex);
        }
        reuslt = value.toString();
      }
    }
    return reuslt;
  }

  static Node findLastNonEmptyPathNode(final Path path) {
    Node found = null;
    final List<Node> list = newArrayList(path.iterator());
    Collections.reverse(list);
    for (final Node node : list) {
      if (!isEmpty(node.getName())) {
        found = node;
        break;
      }
    }
    return found;
  }

  @Override
  public Optional<List<ValidationError>> validationErrors( //
    final ConstraintViolationException exception //
  ) {
    final List<ValidationError> validationErrors = newLinkedList();

    for (final ConstraintViolation<?> violation : exception.getConstraintViolations()) {
      final Node pathNode = findLastNonEmptyPathNode(violation.getPropertyPath());

      // path is probably useful only for properties (fields)
      if (pathNode != null && pathNode.getKind() == ElementKind.PROPERTY) {
        validationErrors.add( //
          ValidationError.validationErrorBuilder() //
            .field(pathNode.getName()) //
            .rejected(convertToString(this.conversionService, violation.getInvalidValue())) //
            .message(violation.getMessage()) //
            .build() //
        );
        // type level constraints etc.
      } else {
        validationErrors.add( //
          ValidationError.validationErrorBuilder() //
            .message(violation.getMessage()) //
            .build() //
        );
      }
    }

    return Optional.of(validationErrors);
  }
}