下面列出了javax.validation.TraversableResolver#javax.validation.Path.Node 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private static Object getValue(final Node propertyNode) {
// bean validation not sufficient for sets
// not possible to access elements, reverting to
// Hibernate implementation
// TODO investigate other implementation next to
// hibernate, JSR 303 v1.1 not sufficient
checkNodeImpl(propertyNode);
return ExceptionUtil.wrapCatchedExceptions(new Callable<Object>() {
@Override
public Object call() throws Exception {
Method parentMethod = propertyNode.getClass().getMethod("getParent");
Method valueMethod = propertyNode.getClass().getMethod("getValue");
Object parentNode = parentMethod.invoke(propertyNode);
if (parentNode != null) {
return valueMethod.invoke(parentNode);
} else {
return valueMethod.invoke(propertyNode);
}
}
});
}
private static Object getParameterValue(final Node propertyNode) {
// bean validation not sufficient for sets
// not possible to access elements, reverting to
// Hibernate implementation
// TODO investigate other implementation next to
// hibernate, JSR 303 v1.1 not sufficient
checkNodeImpl(propertyNode);
return ExceptionUtil.wrapCatchedExceptions(new Callable<Object>() {
@Override
public Object call() throws Exception {
Method valueMethod = propertyNode.getClass().getMethod("getValue");
return valueMethod.invoke(propertyNode);
}
});
}
private String makePath ( final ConstraintViolation<Object> entry )
{
final StringBuilder sb = new StringBuilder ();
final Path p = entry.getPropertyPath ();
for ( final Node n : p )
{
if ( sb.length () > 0 )
{
sb.append ( '.' );
}
sb.append ( n.getName () );
}
return sb.toString ();
}
@Override
public ValidationErrorMessage createBody(ConstraintViolationException ex, HttpServletRequest req) {
ErrorMessage tmpl = super.createBody(ex, req);
ValidationErrorMessage msg = new ValidationErrorMessage(tmpl);
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
Node pathNode = findLastNonEmptyPathNode(violation.getPropertyPath());
// path is probably useful only for properties (fields)
if (pathNode != null && pathNode.getKind() == ElementKind.PROPERTY) {
msg.addError(pathNode.getName(), convertToString(violation.getInvalidValue()), violation.getMessage());
// type level constraints etc.
} else {
msg.addError(violation.getMessage());
}
}
return msg;
}
/**
* Take a stab at fixing validation problems ?
*
* @param object
*/
private void validate(Object object) {
Set<ConstraintViolation<Object>> viols = validator.validate(object);
for (ConstraintViolation<Object> constraintViolation : viols) {
if (Null.class.isAssignableFrom(constraintViolation.getConstraintDescriptor().getAnnotation().getClass())) {
Object o = constraintViolation.getLeafBean();
Iterator<Node> iterator = constraintViolation.getPropertyPath().iterator();
String propertyName = null;
while (iterator.hasNext()) {
propertyName = iterator.next().getName();
}
if (propertyName != null) {
try {
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(o.getClass(), propertyName);
descriptor.getWriteMethod().invoke(o, new Object[] { null });
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
List<NamedNode<?>> build(ConstraintViolation<Object> violation) {
Iterator<Node> violationNodes = violation.getPropertyPath().iterator();
return asList(
methodNode(violationNodes.next()),
requestedArgument(violationNodes.next()));
// we could try to locate all other request nodes from the violation path, too
}
private Argument requestedArgument(Node node) {
String graphQLArgumentName = dfe.getFieldDefinition().getArguments().get(parameterIndex(node.getName()))
.getName();
Field requestedField = dfe.getMergedField().getSingleField();
return requestedField.getArguments().stream()
.filter(argument -> argument.getName().equals(graphQLArgumentName))
.findFirst()
.orElseThrow(() -> new AssertionError(
"expected field " + graphQLArgumentName + " in " + requestedField.getArguments()));
}
@Override
public ConstraintType.Type getConstraintType(Object o) {
if (!(o instanceof ConstraintViolation)) {
throw new RuntimeException(Messages.MESSAGES.unknownObjectPassedAsConstraintViolation(o));
}
ConstraintViolation<?> v = ConstraintViolation.class.cast(o);
Iterator<Node> nodes = v.getPropertyPath().iterator();
Node firstNode = nodes.next();
switch (firstNode.getKind()) {
case BEAN:
return ConstraintType.Type.CLASS;
case CONSTRUCTOR:
case METHOD:
Node secondNode = nodes.next();
if (secondNode.getKind() == ElementKind.PARAMETER || secondNode.getKind() == ElementKind.CROSS_PARAMETER) {
return ConstraintType.Type.PARAMETER;
} else if (secondNode.getKind() == ElementKind.RETURN_VALUE) {
return ConstraintType.Type.RETURN_VALUE;
} else {
throw new RuntimeException(Messages.MESSAGES.unexpectedPathNodeViolation(secondNode.getKind()));
}
case PROPERTY:
return ConstraintType.Type.PROPERTY;
case CROSS_PARAMETER:
case PARAMETER:
case RETURN_VALUE:
case CONTAINER_ELEMENT: // we shouldn't encounter these element types at the root
default:
throw new RuntimeException(Messages.MESSAGES.unexpectedPathNode(firstNode.getKind()));
}
}
private static void checkNodeImpl(Node propertyNode) {
boolean hibernateNodeImpl =
propertyNode.getClass().getName().equals(HIBERNATE_PROPERTY_NODE_IMPL); // NOSONAR class / may not be available
boolean hiberanteNodeImpl2 = propertyNode.getClass().getName().equals(HIBERNATE_PROPERTY_NODE_ENGINE_IMPL); // NOSONAR;
PreconditionUtil.assertTrue("cannot convert violations for java.util.Set elements, consider using Hibernate validator",
hibernateNodeImpl || hiberanteNodeImpl2);
}
/**
* Translate validated bean and root path into validated resource and
* resource path. For example, embeddables belonging to an entity document
* are mapped back to an entity violation and a proper path to the
* embeddable attribute.
*
* @param violation to compute the reference
* @return computaed reference
*/
private ResourceRef resolvePath(ConstraintViolation<?> violation) {
Object resource = violation.getRootBean();
Object nodeObject = resource;
ResourceRef ref = new ResourceRef(resource);
Iterator<Node> iterator = violation.getPropertyPath().iterator();
while (iterator.hasNext()) {
Node node = iterator.next();
// ignore methods/parameters
if (node.getKind() == ElementKind.METHOD) {
continue;
}
if (node.getKind() == ElementKind.PARAMETER) {
resource = getParameterValue(node);
nodeObject = resource;
ref = new ResourceRef(resource);
assertResource(resource);
continue;
}
// visit list, set, map references
nodeObject = ref.getNodeReference(nodeObject, node);
ref.visitNode(nodeObject);
// visit property
nodeObject = ref.visitProperty(nodeObject, node);
}
return ref;
}
private Object getNodeReference(Object element, Node node) {
Integer index = node.getIndex();
Object key = node.getKey();
if (index != null) {
appendSeparator();
appendSourcePointer(index);
return ((List<?>) element).get(index);
} else if (key != null) {
appendSeparator();
appendSourcePointer(key);
return ((Map<?, ?>) element).get(key);
} else if (element instanceof Set && getValue(node) != null) {
Object elementEntry = getValue(node);
// since sets get translated to arrays, we do the same here
// crnk-client allocates sets that preserver the order
// of arrays
List<Object> list = new ArrayList<>();
list.addAll((Set<?>) element);
index = list.indexOf(elementEntry);
appendSeparator();
appendSourcePointer(index);
return getValue(node);
}
return element;
}
@Override
public String toString() {
Iterator<Node> iterator = iterator();
StringBuilder builder = new StringBuilder();
while (iterator.hasNext()) {
Node node = iterator.next();
String name = node.getName();
if (name != null && builder.length() > 0) {
builder.append(".");
}
builder.append(node);
}
return builder.toString();
}
public ValidationResponse(final ConstraintViolationException cause) {
super(false, new ArrayList<>());
//noinspection ThrowableResultOfMethodCallIgnored
checkNotNull(cause);
Set<ConstraintViolation<?>> violations = cause.getConstraintViolations();
if (violations != null && !violations.isEmpty()) {
for (ConstraintViolation<?> violation : violations) {
List<String> entries = new ArrayList<>();
// iterate path to get the full path
Iterator<Node> it = violation.getPropertyPath().iterator();
while (it.hasNext()) {
Node node = it.next();
if (ElementKind.PROPERTY == node.getKind() || (ElementKind.PARAMETER == node.getKind() && !it.hasNext())) {
if (node.getKey() != null) {
entries.add(node.getKey().toString());
}
entries.add(node.getName());
}
}
if (entries.isEmpty()) {
if (messages == null) {
messages = new ArrayList<>();
}
messages.add(violation.getMessage());
}
else {
if (errors == null) {
errors = new HashMap<>();
}
errors.put(Joiner.on('.').join(entries), violation.getMessage());
}
}
}
else if (cause.getMessage() != null) {
messages = new ArrayList<>();
messages.add(cause.getMessage());
}
}
/**
* return {@code true}
*/
public boolean isCascadable(final Object traversableObject,
final Node traversableProperty,
final Class<?> rootBeanType,
final Path pathToTraversableObject,
final ElementType elementType)
{
return true;
}
/**
* return {@code true}
*/
public boolean isReachable(final Object traversableObject,
final Node traversableProperty,
final Class<?> rootBeanType,
final Path pathToTraversableObject,
final ElementType elementType)
{
return true;
}
private Node findLastNonEmptyPathNode(Path path) {
List<Node> list = new ArrayList<>();
for (Iterator<Node> it = path.iterator(); it.hasNext(); ) {
list.add(it.next());
}
Collections.reverse(list);
for (Node node : list) {
if (!isEmpty(node.getName())) {
return node;
}
}
return null;
}
@Override
protected String extractCategory(ValuedParameter[] params, ConstraintViolation<Object> violation) {
Iterator<Node> node = violation.getPropertyPath().iterator();
int index = getFirstParameterIndex( node );
node = violation.getPropertyPath().iterator();// Reset the interator
StringBuilder joinedCategoryBuilder = new StringBuilder( mountCategory( node ).replace( "arg" + index , params[index].getName() ) );
return appendPropertyNameIfDefined( joinedCategoryBuilder , violation ) ;
}
@Test
public void shouldUseCustomValueExtractoe() {
Customer bean = new Customer();
bean.emailsByType.put( "work", "[email protected]" );
bean.emailsByType.put( "work", "not-an-email" );
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Customer>> violations = validator.validate (bean );
assertEquals( 1, violations.size() );
ConstraintViolation<Customer> violation = violations.iterator().next();
assertEquals( "not-an-email", violation.getInvalidValue() );
assertEquals( Email.class, violation.getConstraintDescriptor().getAnnotation().annotationType() );
Iterator<Node> pathNodes = violation.getPropertyPath().iterator();
assertTrue( pathNodes.hasNext() );
Node node = pathNodes.next();
assertEquals( "emailsByType", node.getName() );
assertEquals( ElementKind.PROPERTY, node.getKind() );
assertTrue( pathNodes.hasNext() );
node = pathNodes.next();
assertEquals( "<multimap value>", node.getName() );
assertEquals( ElementKind.CONTAINER_ELEMENT, node.getKind() );
assertEquals( "work", node.getKey() );
assertFalse( pathNodes.hasNext() );
}
/**
* Returns the category for this constraint violation. By default, the category returned
* is the full path for property. You can override this method if you prefer another strategy.
*/
protected String extractCategory(ValuedParameter[] params, ConstraintViolation<Object> violation) {
Iterator<Node> path = violation.getPropertyPath().iterator();
Node method = path.next();
logger.debug("Constraint violation on method {}: {}", method, violation);
StringBuilder cat = new StringBuilder();
cat.append(params[path.next().as(ParameterNode.class).getParameterIndex()].getName());// parameter name
while (path.hasNext()) {
cat.append(".").append(path.next());
}
return cat.toString();
}
private Stream<String> items(Node node) {
if (node.getIndex() == null)
return Stream.of(node.getName());
return Stream.of(node.getIndex().toString(), node.getName());
}
/**
* 获取错误信息源
* @author Frodez
* @date 2019-06-11
*/
private static String getErrorSource(ConstraintViolation<Object> violation) {
Stream<Node> stream = StreamSupport.stream(violation.getPropertyPath().spliterator(), false);
List<String> nodes = stream.filter(isErrorSouce).map(Path.Node::toString).collect(Collectors.toList());
return nodes.isEmpty() ? null : String.join(DefStr.POINT_SEPERATOR, nodes);
}
@Override
public boolean isReachable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType,
Path pathToTraversableObject, ElementType elementType) {
return false;
}
@Override
public boolean isCascadable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType,
Path pathToTraversableObject, ElementType elementType) {
return false;
}
@Override
public boolean isReachable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
return true;
}
@Override
public boolean isCascadable(Object traversableObject, Node traversableProperty, Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType) {
return true;
}
public Object visitProperty(Object nodeObject, Node node) {
ResourceRegistry resourceRegistry = context.getResourceRegistry();
Class nodeClass = nodeObject.getClass();
ResourceInformation resourceInformation = null;
if (resourceRegistry.hasEntry(nodeClass)) {
RegistryEntry entry = resourceRegistry.getEntry(nodeClass);
resourceInformation = entry.getResourceInformation();
}
String name = node.getName();
Object next;
if (node.getKind() == ElementKind.PROPERTY) {
if (resourceRegistry.hasEntry(nodeClass)) {
ResourceFieldAccessor accessor = resourceInformation.getAccessor(name);
if (accessor != null) {
next = accessor.getValue(nodeObject);
} else {
next = PropertyUtils.getProperty(nodeObject, name);
}
} else {
next = PropertyUtils.getProperty(nodeObject, name);
}
} else if (node.getKind() == ElementKind.BEAN) {
next = nodeObject;
} else {
throw new UnsupportedOperationException("unknown node: " + node);
}
if (name != null) {
ResourceField resourceField =
resourceInformation != null ? resourceInformation.findFieldByUnderlyingName(name) : null;
String mappedName = name;
if (resourceField != null) {
// in case of @JsonApiRelationId it will be mapped to original name
resourceField = resourceInformation.findFieldByUnderlyingName(resourceField.getUnderlyingName());
mappedName = resourceField.getJsonName();
}
appendSeparator();
if (resourceField == null || resourceField.getResourceFieldType() == ResourceFieldType.ID) {
// continue along attributes path or primary key on root
appendSourcePointer(mappedName);
} else if (resourceField != null && resourceField.getResourceFieldType() == ResourceFieldType.RELATIONSHIP) {
appendSourcePointer("/data/relationships/");
appendSourcePointer(mappedName);
} else {
appendSourcePointer("/data/attributes/");
appendSourcePointer(mappedName);
}
}
return next;
}
public PathImpl(List<? extends Node> nodes) {
this.nodes = nodes;
}
@SuppressWarnings("unchecked")
@Override
public Iterator<Node> iterator() {
return (Iterator<Node>) nodes.iterator();
}
@Override
public <T extends Node> T as(Class<T> nodeType) {
throw new UnsupportedOperationException();
}
private String mountCategory( Iterator<Node> node ){
ignoreMethodName( node );
String joined = Joiner.on(".").join( node );
return removeLastDotIndexIfAny(joined);
}