Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/
package org.hibernate.validator.internal.engine;

import static org.hibernate.validator.internal.util.CollectionHelper.toImmutableMap;

import java.lang.invoke.MethodHandles;
import java.util.Map;

Expand Down Expand Up @@ -47,8 +45,8 @@ public MessageInterpolatorContext(ConstraintDescriptor<?> constraintDescriptor,
this.validatedValue = validatedValue;
this.rootBeanType = rootBeanType;
this.propertyPath = propertyPath;
this.messageParameters = toImmutableMap( messageParameters );
this.expressionVariables = toImmutableMap( expressionVariables );
this.messageParameters = messageParameters;
this.expressionVariables = expressionVariables;
this.expressionLanguageFeatureLevel = expressionLanguageFeatureLevel;
this.customViolation = customViolation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,9 @@ private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanVali

valueContext.setCurrentGroup( defaultSequenceMember.getDefiningClass() );

BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
valueContext.appendEmptyNode();

for ( MetaConstraint<?> metaConstraint : metaConstraints ) {
// HV-466, an interface implemented more than one time in the hierarchy has to be validated only one
// time. An interface can define more than one constraint, we have to check the class we are validating.
Expand All @@ -547,6 +550,10 @@ private <U> boolean validateConstraintsForSingleDefaultGroupElement(BaseBeanVali

validationSuccessful = validationSuccessful && tmp;
}

// reset the value context to the state before this call
valueContext.resetValueState( originalValueState );

return validationSuccessful;
}

Expand All @@ -566,18 +573,24 @@ private void validateConstraintsForNonDefaultGroup(BaseBeanValidationContext<?>
private boolean validateMetaConstraints(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent,
Iterable<MetaConstraint<?>> constraints) {
boolean validationSuccessful = true;
BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
valueContext.appendEmptyNode();

for ( MetaConstraint<?> metaConstraint : constraints ) {
validationSuccessful = validateMetaConstraint( validationContext, valueContext, parent, metaConstraint ) && validationSuccessful;
if ( shouldFailFast( validationContext ) ) {
break;
}
}

// reset the value context to the state before this call
valueContext.resetValueState( originalValueState );

return validationSuccessful;
}

private boolean validateMetaConstraint(BaseBeanValidationContext<?> validationContext, ValueContext<?, Object> valueContext, Object parent, MetaConstraint<?> metaConstraint) {
BeanValueContext.ValueState<Object> originalValueState = valueContext.getCurrentValueState();
valueContext.appendNode( metaConstraint.getLocation() );
valueContext.updateNode( metaConstraint.getLocation() );
boolean success = true;

if ( isValidationRequired( validationContext, valueContext, metaConstraint ) ) {
Expand All @@ -591,9 +604,6 @@ private boolean validateMetaConstraint(BaseBeanValidationContext<?> validationCo
validationContext.markConstraintProcessed( valueContext, metaConstraint );
}

// reset the value context to the state before this call
valueContext.resetValueState( originalValueState );

return success;
}

Expand Down Expand Up @@ -691,11 +701,31 @@ private class CascadingValueReceiver implements ValueExtractor.ValueReceiver {
private final BaseBeanValidationContext<?> validationContext;
private final ValueContext<?, ?> valueContext;
private final ContainerCascadingMetaData cascadingMetaData;
private final BeanValueContext<?, Object> cascadedValueContext;

public CascadingValueReceiver(BaseBeanValidationContext<?> validationContext, ValueContext<?, ?> valueContext, ContainerCascadingMetaData cascadingMetaData) {
this.validationContext = validationContext;
this.valueContext = valueContext;
this.cascadingMetaData = cascadingMetaData;
this.cascadedValueContext = ValueContexts.getLocalExecutionContextForBean(
valueContext,
validatorScopedContext.getParameterNameProvider(),
null,
null,
valueContext.getPropertyPath()
);
}

private BeanValueContext<?, Object> resetCascadedValueContext(Object value) {
Contracts.assertNotNull( value, "value cannot be null" );

BeanMetaData<?> currentBeanMetaData = cascadedValueContext.getCurrentBeanMetaData();
if ( currentBeanMetaData == null || currentBeanMetaData.getBeanClass() != value.getClass() ) {
currentBeanMetaData = beanMetaDataManager.getBeanMetaData( value.getClass() );
}
cascadedValueContext.reset( value, valueContext.getPropertyPath(), currentBeanMetaData );
cascadedValueContext.setCurrentValidatedValue( value );
return cascadedValueContext;
}

@Override
Expand Down Expand Up @@ -738,7 +768,7 @@ private void doValidate(Object value, String nodeName) {
// already and need only to pass the current element
ValidationOrder validationOrder = validationOrderGenerator.getValidationOrder( currentGroup, currentGroup != originalGroup );

BeanValueContext<?, Object> cascadedValueContext = buildNewLocalExecutionContext( valueContext, value );
BeanValueContext<?, Object> cascadedValueContext = resetCascadedValueContext( value );

if ( cascadingMetaData.getDeclaredContainerClass() != null ) {
cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() );
Expand All @@ -751,14 +781,13 @@ private void doValidate(Object value, String nodeName) {

// Cascade validation to container elements if we are dealing with a container element
if ( cascadingMetaData.hasContainerElementsMarkedForCascading() ) {
ValueContext<?, Object> cascadedTypeArgumentValueContext = buildNewLocalExecutionContext( valueContext, value );
if ( cascadingMetaData.getTypeParameter() != null ) {
cascadedValueContext.setTypeParameter( cascadingMetaData.getDeclaredContainerClass(), cascadingMetaData.getDeclaredTypeParameterIndex() );
}

cascadedTypeArgumentValueContext.appendTypeParameterNode( nodeName );
cascadedValueContext.appendTypeParameterNode( nodeName );

validateCascadedContainerElementsInContext( value, validationContext, cascadedTypeArgumentValueContext, cascadingMetaData, validationOrder );
validateCascadedContainerElementsInContext( value, validationContext, cascadedValueContext, cascadingMetaData, validationOrder );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public final List<ConstraintViolationCreationContext> getConstraintViolationCrea
throw LOG.getAtLeastOneCustomMessageMustBeCreatedException();
}

return CollectionHelper.toImmutableList( constraintViolationCreationContexts );
return constraintViolationCreationContexts;
}

if ( constraintViolationCreationContexts == null || constraintViolationCreationContexts.size() == 0 ) {
Expand All @@ -165,7 +165,7 @@ public final List<ConstraintViolationCreationContext> getConstraintViolationCrea
returnedConstraintViolationCreationContexts.addAll( constraintViolationCreationContexts );
returnedConstraintViolationCreationContexts.add( getDefaultConstraintViolationCreationContext() );

return CollectionHelper.toImmutableList( returnedConstraintViolationCreationContexts );
return returnedConstraintViolationCreationContexts;
}

protected final MutablePath getCopyOfBasePath() {
Expand All @@ -178,8 +178,8 @@ private ConstraintViolationCreationContext getDefaultConstraintViolationCreation
defaultConstraintExpressionLanguageFeatureLevel,
false,
basePath,
messageParameters != null ? new HashMap<>( messageParameters ) : Collections.emptyMap(),
expressionVariables != null ? new HashMap<>( expressionVariables ) : Collections.emptyMap(),
messageParameters != null ? Map.copyOf( messageParameters ) : Collections.emptyMap(),
expressionVariables != null ? Map.copyOf( expressionVariables ) : Collections.emptyMap(),
dynamicPayload
);
}
Expand Down Expand Up @@ -214,8 +214,8 @@ public ConstraintValidatorContext addConstraintViolation() {
expressionLanguageFeatureLevel,
true,
propertyPath,
messageParameters != null ? new HashMap<>( messageParameters ) : Collections.emptyMap(),
expressionVariables != null ? new HashMap<>( expressionVariables ) : Collections.emptyMap(),
messageParameters != null ? Map.copyOf( messageParameters ) : Collections.emptyMap(),
expressionVariables != null ? Map.copyOf( expressionVariables ) : Collections.emptyMap(),
dynamicPayload
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/
package org.hibernate.validator.internal.engine.constraintvalidation;

import static org.hibernate.validator.internal.util.CollectionHelper.toImmutableMap;

import java.util.Map;

import jakarta.validation.Path;
Expand Down Expand Up @@ -44,8 +42,8 @@ public ConstraintViolationCreationContext(String message,
this.customViolation = customViolation;
// at this point we make a copy of the path to avoid side effects
this.propertyPath = property.materialize();
this.messageParameters = toImmutableMap( messageParameters );
this.expressionVariables = toImmutableMap( expressionVariables );
this.messageParameters = messageParameters;
this.expressionVariables = expressionVariables;
this.dynamicPayload = dynamicPayload;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public class MutableNode

static {
ROOT_NODE = MutableNode.createBeanNode( null );
ROOT_NODE.valueSet = true;
ROOT_NODE.nodes = new MutableNode[] { ROOT_NODE };
ROOT_NODE.hashCode();
}
Expand All @@ -66,19 +65,18 @@ public class MutableNode
public static final String MAP_KEY_NODE_NAME = "<map key>";
public static final String MAP_VALUE_NODE_NAME = "<map value>";

private final String name;
private final MutableNode parent;
private final ElementKind kind;
private final int size;
private String name;
private ElementKind kind;
private boolean isIterable;
private Integer index;
private Object key;

//type-specific attributes
private final Class<?>[] parameterTypes;
private final Integer parameterIndex;
private Integer parameterIndex;
private Object value;
private boolean valueSet;
private Class<?> containerClass;
private Integer typeArgumentIndex;

Expand All @@ -88,15 +86,14 @@ public class MutableNode

private MutableNode(
String name, MutableNode parent, boolean isIterable, Integer index, Object key, ElementKind kind, Class<?>[] parameterTypes,
Integer parameterIndex, Object value, boolean valueSet, Class<?> containerClass, Integer typeArgumentIndex
Integer parameterIndex, Object value, Class<?> containerClass, Integer typeArgumentIndex
) {
this.name = name;
this.parent = parent;
this.size = ( parent == null ? 0 : parent.size ) + 1;
this.index = index;
this.key = key;
this.value = value;
this.valueSet = valueSet;
this.isIterable = isIterable;
this.kind = kind;
this.parameterTypes = parameterTypes;
Expand All @@ -105,6 +102,22 @@ private MutableNode(
this.typeArgumentIndex = typeArgumentIndex;
}

public static MutableNode createNode(MutableNode parent) {
return new MutableNode(
null,
parent,
false,
null,
null,
null,
EMPTY_CLASS_ARRAY,
null,
null,
null,
null
);
}

//TODO It would be nicer if we could return PropertyNode
public static MutableNode createPropertyNode(String name, MutableNode parent) {
return new MutableNode(
Expand All @@ -117,7 +130,6 @@ public static MutableNode createPropertyNode(String name, MutableNode parent) {
EMPTY_CLASS_ARRAY,
null,
null,
false,
null,
null
);
Expand All @@ -134,7 +146,6 @@ public static MutableNode createContainerElementNode(String name, MutableNode pa
EMPTY_CLASS_ARRAY,
null,
null,
false,
null,
null
);
Expand All @@ -151,7 +162,6 @@ public static MutableNode createParameterNode(String name, MutableNode parent, i
EMPTY_CLASS_ARRAY,
parameterIndex,
null,
false,
null,
null
);
Expand All @@ -168,18 +178,17 @@ public static MutableNode createCrossParameterNode(MutableNode parent) {
EMPTY_CLASS_ARRAY,
null,
null,
false,
null,
null
);
}

public static MutableNode createMethodNode(String name, MutableNode parent, Class<?>[] parameterTypes) {
return new MutableNode( name, parent, false, null, null, ElementKind.METHOD, parameterTypes, null, null, false, null, null );
return new MutableNode( name, parent, false, null, null, ElementKind.METHOD, parameterTypes, null, null, null, null );
}

public static MutableNode createConstructorNode(String name, MutableNode parent, Class<?>[] parameterTypes) {
return new MutableNode( name, parent, false, null, null, ElementKind.CONSTRUCTOR, parameterTypes, null, null, false, null, null );
return new MutableNode( name, parent, false, null, null, ElementKind.CONSTRUCTOR, parameterTypes, null, null, null, null );
}

public static MutableNode createBeanNode(MutableNode parent) {
Expand All @@ -193,7 +202,6 @@ public static MutableNode createBeanNode(MutableNode parent) {
EMPTY_CLASS_ARRAY,
null,
null,
false,
null,
null
);
Expand All @@ -210,7 +218,6 @@ public static MutableNode createReturnValue(MutableNode parent) {
EMPTY_CLASS_ARRAY,
null,
null,
false,
null,
null
);
Expand Down Expand Up @@ -246,9 +253,11 @@ public void setTypeParameter(Class<?> containerClass, Integer typeArgumentIndex)
public void reset() {
isIterable = false;
index = null;
parameterIndex = null;
key = null;
typeArgumentIndex = null;
containerClass = null;
value = null;
}

@Override
Expand Down Expand Up @@ -628,6 +637,37 @@ public boolean isSubPathOrContains(MutableNode other) {
return curr.isRootPath() && otherCurr.isRootPath();
}

public void resetAsProperty(String resolvedPropertyName) {
reset();
this.name = resolvedPropertyName;
this.kind = ElementKind.PROPERTY;
}

public void resetAsBeanNode() {
reset();
this.kind = ElementKind.BEAN;
this.name = null;
}

public void resetAsParameter(String parameterName, int index) {
reset();
this.kind = ElementKind.PARAMETER;
this.name = parameterName;
this.parameterIndex = index;
}

public void resetAsReturnValue() {
reset();
this.name = RETURN_VALUE_NODE_NAME;
this.kind = ElementKind.RETURN_VALUE;
}

public void resetAsCrossParameter() {
reset();
this.name = CROSS_PARAMETER_NODE_NAME;
this.kind = ElementKind.CROSS_PARAMETER;
}

protected static class NodeIterator implements Iterator<Path.Node> {
private final MutableNode[] array;
private int index;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ private void addMethodNode(String name, Class<?>[] parameterTypes) {
currentLeafNode = MutableNode.createMethodNode( name, parent, parameterTypes );
}

public void addEmptyNode() {
MutableNode parent = currentLeafNode;
currentLeafNode = MutableNode.createNode( parent );
}

public void makeLeafNodeIterable() {
currentLeafNode.makeIterable();
}
Expand All @@ -167,8 +172,6 @@ public void setLeafNodeValueIfRequired(Object value) {
// The value is only exposed for property and container element nodes
if ( currentLeafNode.getKind() == ElementKind.PROPERTY || currentLeafNode.getKind() == ElementKind.CONTAINER_ELEMENT ) {
currentLeafNode.setPropertyValue( value );

// the property value is not part of the NodeImpl hashCode so we don't need to reset the PathImpl hashCode
}
}

Expand Down
Loading
Loading