Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.micronaut.validation.validator.ValidationPath Maven / Gradle / Ivy
/*
* Copyright 2017-2023 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.validation.validator;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.type.Argument;
import io.micronaut.core.type.ReturnType;
import io.micronaut.inject.MethodReference;
import jakarta.validation.ConstraintTarget;
import jakarta.validation.ElementKind;
import jakarta.validation.Path;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Path implementation.
*
* @author Denis Stepanov
* @since 4.0.0
*/
@Internal
final class ValidationPath implements Path {
final Deque nodes;
private ContainerContext containerContext = DefaultContainerContext.NONE;
private final ContextualPath popPath = new ContextualPath() {
@Override
public void close() {
nodes.removeLast();
}
};
/**
* Copy constructor.
*
* @param nodes The nodes
*/
ValidationPath(ValidationPath nodes) {
this.nodes = new LinkedList<>(nodes.nodes);
}
ValidationPath() {
this.nodes = new LinkedList<>();
}
@Override
public Iterator iterator() {
return nodes.iterator();
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
final Iterator i = nodes.iterator();
boolean dontAddDot = true;
while (i.hasNext()) {
final Node node = i.next();
if (node.getKind() == ElementKind.BEAN) {
continue;
}
if (node.isInIterable()) {
builder.append('[');
if (node.getIndex() != null) {
builder.append(node.getIndex());
} else if (node.getKey() != null) {
builder.append(node.getKey());
}
builder.append(']');
}
if (node.getKind() == ElementKind.CONTAINER_ELEMENT) {
if (!i.hasNext()) {
builder.append(node.getName());
}
} else {
builder.append(dontAddDot ? "" : ".");
builder.append(node.getName());
}
dontAddDot = false;
}
return builder.toString();
}
ContextualPath withContainerContext(ContainerContext containerContext) {
ContainerContext prevContainerContext = this.containerContext;
this.containerContext = containerContext;
return () -> this.containerContext = prevContainerContext;
}
ContextualPath addBeanNode() {
return addBeanNode(containerContext);
}
ContextualPath addBeanNode(ContainerContext containerContext) {
return addNode(new ValidationPath.DefaultBeanNode(containerContext));
}
ContextualPath addPropertyNode(String name) {
return addPropertyNode(name, containerContext);
}
ContextualPath addPropertyNode(String name, ContainerContext containerContext) {
return addNode(new ValidationPath.DefaultPropertyNode(name, containerContext));
}
ContextualPath addParameterNode(String name, int index) {
return addNode(new ValidationPath.DefaultParameterNode(name, index));
}
ContextualPath addCrossParameterNode() {
return addNode(new ValidationPath.DefaultCrossParameterNode());
}
ContextualPath addReturnValueNode() {
return addNode(new ValidationPath.DefaultReturnValueNode());
}
ContextualPath addContainerElementNode(String name, ContainerContext containerContext) {
return addNode(new ValidationPath.DefaultContainerElementNode(name, containerContext));
}
ContextualPath addMethodNode(MethodReference, ?> reference) {
return addNode(new ValidationPath.DefaultMethodNode(reference));
}
private ContextualPath addNode(Node node) {
nodes.add(node);
ContextualPath contextualPath = withContainerContext(DefaultContainerContext.NONE);
return () -> {
popPath.close();
contextualPath.close();
};
}
ContextualPath addConstructorNode(String simpleName, Argument>... constructorArguments) {
final ValidationPath.DefaultConstructorNode node = new ValidationPath.DefaultConstructorNode(new MethodReference<>() {
@Override
public Argument[] getArguments() {
return constructorArguments;
}
@Override
public Method getTargetMethod() {
return null;
}
@Override
public ReturnType getReturnType() {
return null;
}
@Override
public Class getDeclaringType() {
return null;
}
@Override
public String getMethodName() {
return simpleName;
}
});
nodes.add(node);
return popPath;
}
public ContextualPath cascaded() {
Node last = nodes.peekLast();
if (containerContext.containerClass() == null && last != null && last.getKind() == ElementKind.CONTAINER_ELEMENT) {
DefaultContainerElementNode removed = (DefaultContainerElementNode) nodes.removeLast();
ContainerContext prevContainerContext = containerContext;
containerContext = removed.containerContext;
return () -> {
nodes.add(removed);
containerContext = prevContainerContext;
};
}
return () -> {
};
}
public Node last() {
return nodes.peekLast();
}
public ValidationPath previousPath() {
ValidationPath path = new ValidationPath(this);
path.nodes.removeLast();
if (path.nodes.isEmpty()) {
path.nodes.add(new DefaultBeanNode(containerContext));
}
return path;
}
public ConstraintTarget getConstraintTarget() {
DefaultNode node = (DefaultNode) nodes.peekLast();
return node == null ? ConstraintTarget.IMPLICIT : node.getConstraintTarget();
}
/**
* Default Return value node implementation.
*/
static final class DefaultReturnValueNode extends DefaultNode implements ReturnValueNode {
public DefaultReturnValueNode() {
super("", DefaultContainerContext.NONE);
}
@Override
public ElementKind getKind() {
return ElementKind.RETURN_VALUE;
}
@Override
public ConstraintTarget getConstraintTarget() {
return ConstraintTarget.RETURN_VALUE;
}
}
/**
* Default bean node implementation.
*/
static final class DefaultBeanNode extends DefaultNode implements BeanNode {
public DefaultBeanNode(ContainerContext containerContext) {
super(null, containerContext);
}
@Override
public Class> getContainerClass() {
return containerContext.containerClass();
}
@Override
public Integer getTypeArgumentIndex() {
return containerContext.typeArgumentIndex();
}
@Override
public ElementKind getKind() {
return ElementKind.BEAN;
}
}
/**
* Default property node implementation.
*/
static final class DefaultPropertyNode extends DefaultNode implements PropertyNode {
public DefaultPropertyNode(String name, ContainerContext containerContext) {
super(name, containerContext);
}
@Override
public Class> getContainerClass() {
return containerContext.containerClass();
}
@Override
public Integer getTypeArgumentIndex() {
return containerContext.typeArgumentIndex();
}
@Override
public ElementKind getKind() {
return ElementKind.PROPERTY;
}
}
/**
* Default parameter node implementation.
*/
static final class DefaultParameterNode extends DefaultNode implements ParameterNode {
private final int parameterIndex;
public DefaultParameterNode(@Nullable String name, int parameterIndex) {
super(name, DefaultContainerContext.NONE);
this.parameterIndex = parameterIndex;
}
@Override
public ElementKind getKind() {
return ElementKind.PARAMETER;
}
@Override
public int getParameterIndex() {
return parameterIndex;
}
}
/**
* Default cross parameter node implementation.
*/
private static final class DefaultCrossParameterNode extends DefaultNode implements CrossParameterNode {
public DefaultCrossParameterNode() {
super("", DefaultContainerContext.NONE);
}
@Override
public ElementKind getKind() {
return ElementKind.CROSS_PARAMETER;
}
@Override
public ConstraintTarget getConstraintTarget() {
return ConstraintTarget.PARAMETERS;
}
}
/**
* Default node implementation.
*/
abstract static class DefaultNode implements Node {
protected final String name;
protected final ContainerContext containerContext;
public DefaultNode(String name, ContainerContext containerContext) {
this.name = name;
this.containerContext = containerContext;
}
@Override
public String getName() {
return name;
}
@Override
public boolean isInIterable() {
return containerContext.isInIterable();
}
@Override
public Integer getIndex() {
return containerContext.index();
}
@Override
public Object getKey() {
return containerContext.key();
}
@Override
public String toString() {
return name;
}
@Override
public T as(Class nodeType) {
if (nodeType.isInstance(this)) {
return nodeType.cast(this);
}
throw new ClassCastException("Unexpected type: " + nodeType);
}
public ConstraintTarget getConstraintTarget() {
return ConstraintTarget.IMPLICIT;
}
}
/**
* Method node implementation.
*/
static class DefaultMethodNode extends DefaultNode implements MethodNode {
private final MethodReference, ?> methodReference;
public DefaultMethodNode(MethodReference, ?> methodReference) {
super(methodReference.getMethodName(), DefaultContainerContext.NONE);
this.methodReference = methodReference;
}
public MethodReference, ?> getMethodReference() {
return methodReference;
}
@Override
public List> getParameterTypes() {
return Arrays.asList(methodReference.getArgumentTypes());
}
@Override
public ElementKind getKind() {
return ElementKind.METHOD;
}
}
/**
* Default container element node implementation.
*/
private static final class DefaultContainerElementNode extends DefaultNode implements ContainerElementNode {
public DefaultContainerElementNode(@Nullable String name, @NonNull ContainerContext containerContext) {
super(name, containerContext);
}
@Override
public Class> getContainerClass() {
return containerContext.containerClass();
}
@Override
public Integer getTypeArgumentIndex() {
return containerContext.typeArgumentIndex();
}
@Override
public boolean isInIterable() {
return containerContext.isInIterable();
}
@Override
public Integer getIndex() {
return containerContext.index();
}
@Override
public Object getKey() {
return containerContext.key();
}
@Override
public ElementKind getKind() {
return ElementKind.CONTAINER_ELEMENT;
}
}
/**
* Default constructor node.
*/
private static final class DefaultConstructorNode extends DefaultMethodNode implements ConstructorNode {
public DefaultConstructorNode(MethodReference methodReference) {
super(methodReference);
}
@Override
public ElementKind getKind() {
return ElementKind.CONSTRUCTOR;
}
}
public interface ContainerContext {
static ContainerContext indexed(Class> containerClass, int index, Integer typeArgumentIndex) {
return new ValidationPath.DefaultContainerContext(containerClass, index, null, true, typeArgumentIndex);
}
static ContainerContext keyed(Class> containerClass, Object key, Integer typeArgumentIndex) {
return new ValidationPath.DefaultContainerContext(containerClass, null, key, true, typeArgumentIndex);
}
static ContainerContext iterable(Class> containerClass, Integer typeArgumentIndex) {
return new ValidationPath.DefaultContainerContext(containerClass, null, null, true, typeArgumentIndex);
}
static ContainerContext value(Class> containerClass, Integer typeArgumentIndex) {
return new ValidationPath.DefaultContainerContext(containerClass, null, null, false, typeArgumentIndex);
}
Class> containerClass();
Integer index();
Object key();
boolean isInIterable();
Integer typeArgumentIndex();
}
/**
* The container context.
*
* @param containerClass The container class
* @param index The index
* @param key The key
* @param isInIterable Is iterable
* @param typeArgumentIndex The type argument index
*/
@Internal
public record DefaultContainerContext(@Nullable Class> containerClass,
@Nullable Integer index,
@Nullable Object key,
boolean isInIterable,
@Nullable Integer typeArgumentIndex) implements ContainerContext {
/**
* Not in a container context.
*/
@SuppressWarnings("StaticVariableName")
static DefaultContainerContext NONE = new DefaultContainerContext(null, null, null, false, null);
/**
* The iterable container context.
*
* @param containerClass The container class
* @return new context
*/
public static DefaultContainerContext ofIterableContainer(Class> containerClass) {
return new DefaultContainerContext(containerClass, null, null, true, 0);
}
public static DefaultContainerContext ofContainer(Class> containerClass) {
return new DefaultContainerContext(containerClass, null, null, false, 0);
}
}
/**
* The mutable container context.
*
*/
@Internal
public static class MutableContainerContext implements ContainerContext {
private @Nullable Class> containerClass;
private @Nullable Integer index;
private @Nullable Object key;
private boolean isInIterable;
private @Nullable Integer typeArgumentIndex;
public MutableContainerContext() {
}
public MutableContainerContext(ValidationPath.ContainerContext containerContext) {
containerClass = containerContext.containerClass();
index = containerContext.index();
key = containerContext.key();
isInIterable = containerContext.isInIterable();
typeArgumentIndex = containerContext.typeArgumentIndex();
}
@Override
public Class> containerClass() {
return containerClass;
}
@Override
public Integer index() {
return index;
}
@Override
public Object key() {
return key;
}
@Override
public boolean isInIterable() {
return isInIterable;
}
@Override
public Integer typeArgumentIndex() {
return typeArgumentIndex;
}
public void inIterable() {
isInIterable = true;
}
public void inContainer(Class> containerClass, Integer typeArgumentIndex) {
this.containerClass = containerClass;
this.typeArgumentIndex = typeArgumentIndex;
}
public void atKey(Object key) {
this.key = key;
}
public void atIndex(Integer index) {
this.index = index;
}
}
@Internal
public interface ContextualPath extends AutoCloseable {
@Override
void close();
}
}