2525import org .hibernate .validator .internal .engine .constraintvalidation .ConstraintValidatorManager ;
2626import org .hibernate .validator .internal .engine .constraintvalidation .ConstraintViolationCreationContext ;
2727import org .hibernate .validator .internal .engine .path .ModifiablePath ;
28+ import org .hibernate .validator .internal .engine .path .NodeImpl ;
2829import org .hibernate .validator .internal .engine .valuecontext .ValueContext ;
2930import org .hibernate .validator .internal .metadata .aggregated .BeanMetaData ;
3031import org .hibernate .validator .internal .metadata .core .MetaConstraint ;
@@ -109,10 +110,10 @@ abstract class AbstractValidationContext<T> implements BaseBeanValidationContext
109110 private Set <BeanGroupProcessedUnit > processedGroupUnits ;
110111
111112 /**
112- * Maps an object to a list of paths in which it has been validated. The objects are the bean instances.
113+ * Maps an object to a list of paths (represented by leaf nodes) in which it has been validated. The objects are the bean instances.
113114 */
114115 @ Lazy
115- private Map <Object , Set <ModifiablePath >> processedPathsPerBean ;
116+ private Map <Object , Set <NodeImpl >> processedPathsPerBean ;
116117
117118 /**
118119 * Contains all failing constraints so far.
@@ -200,11 +201,10 @@ public boolean isBeanAlreadyValidated(Object value, Class<?> group, ModifiablePa
200201 return false ;
201202 }
202203
203- boolean alreadyValidated ;
204- alreadyValidated = isAlreadyValidatedForCurrentGroup ( value , group );
204+ boolean alreadyValidated = isAlreadyValidatedForCurrentGroup ( value , group );
205205
206206 if ( alreadyValidated ) {
207- alreadyValidated = isAlreadyValidatedForPath ( value , path );
207+ alreadyValidated = isAlreadyValidatedForPath ( value , path . getLeafNode () );
208208 }
209209
210210 return alreadyValidated ;
@@ -270,7 +270,7 @@ protected abstract ConstraintViolation<T> createConstraintViolation(
270270 ConstraintViolationCreationContext constraintViolationCreationContext );
271271
272272 @ Override
273- public boolean hasMetaConstraintBeenProcessed (Object bean , Path path , MetaConstraint <?> metaConstraint ) {
273+ public boolean hasMetaConstraintBeenProcessed (Object bean , ModifiablePath path , MetaConstraint <?> metaConstraint ) {
274274 // this is only useful if the constraint is defined for more than 1 group as in the case it's only
275275 // defined for one group, there is no chance it's going to be called twice.
276276 if ( metaConstraint .isDefinedForOneGroupOnly () ) {
@@ -281,7 +281,7 @@ public boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstr
281281 }
282282
283283 @ Override
284- public void markConstraintProcessed (Object bean , Path path , MetaConstraint <?> metaConstraint ) {
284+ public void markConstraintProcessed (Object bean , ModifiablePath path , MetaConstraint <?> metaConstraint ) {
285285 // this is only useful if the constraint is defined for more than 1 group as in the case it's only
286286 // defined for one group, there is no chance it's going to be called twice.
287287 if ( metaConstraint .isDefinedForOneGroupOnly () ) {
@@ -340,8 +340,8 @@ private String interpolate(
340340 }
341341 }
342342
343- private boolean isAlreadyValidatedForPath (Object value , ModifiablePath path ) {
344- Set <ModifiablePath > pathSet = getInitializedProcessedPathsPerBean ().get ( value );
343+ private boolean isAlreadyValidatedForPath (Object value , NodeImpl path ) {
344+ Set <NodeImpl > pathSet = getInitializedProcessedPathsPerBean ().get ( value );
345345 if ( pathSet == null ) {
346346 return false ;
347347 }
@@ -354,7 +354,7 @@ private boolean isAlreadyValidatedForPath(Object value, ModifiablePath path) {
354354 // it means that the new path we are testing cannot be a root path; also since we are cascading into inner
355355 // objects, i.e. going further from the object tree root, it means that the new path cannot be shorter than
356356 // the ones we've already encountered.
357- for ( ModifiablePath p : pathSet ) {
357+ for ( NodeImpl p : pathSet ) {
358358 if ( p .isSubPathOrContains ( path ) ) {
359359 return true ;
360360 }
@@ -367,16 +367,16 @@ private boolean isAlreadyValidatedForCurrentGroup(Object value, Class<?> group)
367367 }
368368
369369 private void markCurrentBeanAsProcessedForCurrentPath (Object bean , ModifiablePath path ) {
370- // HV-1031 The path object is mutated as we traverse the object tree, hence copy it before saving it
371- Map <Object , Set <ModifiablePath >> processedPathsPerBean = getInitializedProcessedPathsPerBean ();
370+ Map <Object , Set <NodeImpl >> processedPathsPerBean = getInitializedProcessedPathsPerBean ();
372371
373- Set <ModifiablePath > processedPaths = processedPathsPerBean .get ( bean );
372+ Set <NodeImpl > processedPaths = processedPathsPerBean .get ( bean );
374373 if ( processedPaths == null ) {
375374 processedPaths = new HashSet <>();
376375 processedPathsPerBean .put ( bean , processedPaths );
377376 }
378377
379- processedPaths .add ( ModifiablePath .createCopy ( path ) );
378+ // HV-1031 The path object is mutated as we traverse the object tree, hence we use the node which is not (for the most part):
379+ processedPaths .add ( path .getLeafNode () );
380380 }
381381
382382 private void markCurrentBeanAsProcessedForCurrentGroup (Object bean , Class <?> group ) {
@@ -397,7 +397,7 @@ private Set<BeanGroupProcessedUnit> getInitializedProcessedGroupUnits() {
397397 return processedGroupUnits ;
398398 }
399399
400- private Map <Object , Set <ModifiablePath >> getInitializedProcessedPathsPerBean () {
400+ private Map <Object , Set <NodeImpl >> getInitializedProcessedPathsPerBean () {
401401 if ( processedPathsPerBean == null ) {
402402 processedPathsPerBean = new IdentityHashMap <>();
403403 }
@@ -415,13 +415,13 @@ private static final class BeanPathMetaConstraintProcessedUnit {
415415
416416 // these fields are final but we don't mark them as final as an optimization
417417 private Object bean ;
418- private Path path ;
418+ private NodeImpl pathLeaf ;
419419 private MetaConstraint <?> metaConstraint ;
420420 private int hashCode ;
421421
422- BeanPathMetaConstraintProcessedUnit (Object bean , Path path , MetaConstraint <?> metaConstraint ) {
422+ BeanPathMetaConstraintProcessedUnit (Object bean , ModifiablePath path , MetaConstraint <?> metaConstraint ) {
423423 this .bean = bean ;
424- this .path = path ;
424+ this .pathLeaf = path . getLeafNode (); // because the leaf represent the entire path.
425425 this .metaConstraint = metaConstraint ;
426426 this .hashCode = createHashCode ();
427427 }
@@ -442,7 +442,7 @@ public boolean equals(Object o) {
442442 if ( metaConstraint != that .metaConstraint ) {
443443 return false ;
444444 }
445- if ( !path .equals ( that .path ) ) {
445+ if ( !pathLeaf .equals ( that .pathLeaf ) ) {
446446 return false ;
447447 }
448448
@@ -456,7 +456,7 @@ public int hashCode() {
456456
457457 private int createHashCode () {
458458 int result = System .identityHashCode ( bean );
459- result = 31 * result + path .hashCode ();
459+ result = 31 * result + pathLeaf .hashCode ();
460460 result = 31 * result + System .identityHashCode ( metaConstraint );
461461 return result ;
462462 }
0 commit comments