View Javadoc
1   package cn.home1.oss.lib.errorhandle.internal.resolver;
2   
3   import static com.google.common.collect.Lists.newArrayList;
4   import static com.google.common.collect.Lists.newLinkedList;
5   import static org.springframework.util.StringUtils.isEmpty;
6   
7   import cn.home1.oss.lib.errorhandle.api.AbstractConcreteExceptionResolver;
8   import cn.home1.oss.lib.errorhandle.api.ValidationError;
9   
10  import lombok.extern.slf4j.Slf4j;
11  
12  import org.springframework.core.convert.ConversionException;
13  import org.springframework.core.convert.ConversionService;
14  
15  import java.util.Collections;
16  import java.util.List;
17  import java.util.Optional;
18  
19  import javax.validation.ConstraintViolation;
20  import javax.validation.ConstraintViolationException;
21  import javax.validation.ElementKind;
22  import javax.validation.Path;
23  import javax.validation.Path.Node;
24  
25  @Slf4j
26  public class ConstraintViolationExceptionResolver
27    extends AbstractConcreteExceptionResolver<ConstraintViolationException> {
28  
29    static String convertToString( //
30      final ConversionService conversionService, //
31      final Object value //
32    ) {
33      String reuslt;
34      if (value == null) {
35        reuslt = null;
36      } else if (conversionService == null) {
37        reuslt = value.toString();
38      } else {
39        try {
40          reuslt = conversionService.convert(value, String.class);
41        } catch (final ConversionException ex) {
42          if (log.isDebugEnabled()) {
43            log.debug("failed to convert value '{}' to String.class.", value, ex);
44          }
45          reuslt = value.toString();
46        }
47      }
48      return reuslt;
49    }
50  
51    static Node findLastNonEmptyPathNode(final Path path) {
52      Node found = null;
53      final List<Node> list = newArrayList(path.iterator());
54      Collections.reverse(list);
55      for (final Node node : list) {
56        if (!isEmpty(node.getName())) {
57          found = node;
58          break;
59        }
60      }
61      return found;
62    }
63  
64    @Override
65    public Optional<List<ValidationError>> validationErrors( //
66      final ConstraintViolationException exception //
67    ) {
68      final List<ValidationError> validationErrors = newLinkedList();
69  
70      for (final ConstraintViolation<?> violation : exception.getConstraintViolations()) {
71        final Node pathNode = findLastNonEmptyPathNode(violation.getPropertyPath());
72  
73        // path is probably useful only for properties (fields)
74        if (pathNode != null && pathNode.getKind() == ElementKind.PROPERTY) {
75          validationErrors.add( //
76            ValidationError.validationErrorBuilder() //
77              .field(pathNode.getName()) //
78              .rejected(convertToString(this.conversionService, violation.getInvalidValue())) //
79              .message(violation.getMessage()) //
80              .build() //
81          );
82          // type level constraints etc.
83        } else {
84          validationErrors.add( //
85            ValidationError.validationErrorBuilder() //
86              .message(violation.getMessage()) //
87              .build() //
88          );
89        }
90      }
91  
92      return Optional.of(validationErrors);
93    }
94  }