Skip to content

Commit bb2973c

Browse files
committed
HV-2127 Use nodes instead of paths to track processed beans
Signed-off-by: marko-bekhta <[email protected]>
1 parent 4541353 commit bb2973c

File tree

3 files changed

+51
-42
lines changed

3 files changed

+51
-42
lines changed

engine/src/main/java/org/hibernate/validator/internal/engine/path/NodeImpl.java

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,16 @@ public boolean equals(Object obj) {
502502
return false;
503503
}
504504
NodeImpl other = (NodeImpl) obj;
505+
return samePath( other );
506+
}
507+
508+
boolean sameNode(NodeImpl other) {
509+
if ( this == other ) {
510+
return true;
511+
}
512+
if ( other == null ) {
513+
return false;
514+
}
505515
if ( hashCode != -1 && other.hashCode != -1 && hashCode != other.hashCode ) {
506516
return false;
507517
}
@@ -570,7 +580,24 @@ else if ( !Arrays.equals( parameterTypes, other.parameterTypes ) ) {
570580
return true;
571581
}
572582

573-
boolean isRootPath() {
583+
boolean samePath(NodeImpl other) {
584+
if ( this.size != other.size ) {
585+
return false;
586+
}
587+
NodeImpl curr = this;
588+
NodeImpl otherCurr = other;
589+
while ( curr != null && otherCurr != null ) {
590+
if ( !curr.sameNode( otherCurr ) ) {
591+
return false;
592+
}
593+
otherCurr = otherCurr.parent;
594+
curr = curr.parent;
595+
}
596+
597+
return curr == null && otherCurr == null;
598+
}
599+
600+
public boolean isRootPath() {
574601
return parent == null && name == null;
575602
}
576603

@@ -615,7 +642,7 @@ boolean isSubPathOf(NodeImpl other) {
615642
return curr.isRootPath();
616643
}
617644

618-
boolean isSubPathOrContains(NodeImpl other) {
645+
public boolean isSubPathOrContains(NodeImpl other) {
619646
NodeImpl curr;
620647
NodeImpl otherCurr;
621648
if ( this.size > other.size ) {
@@ -644,23 +671,6 @@ boolean isSubPathOrContains(NodeImpl other) {
644671
return curr.isRootPath() && otherCurr.isRootPath();
645672
}
646673

647-
boolean samePath(NodeImpl other) {
648-
if ( this.size != other.size ) {
649-
return false;
650-
}
651-
NodeImpl curr = this;
652-
NodeImpl otherCurr = other;
653-
while ( curr != null && otherCurr != null ) {
654-
if ( !curr.equals( otherCurr ) ) {
655-
return false;
656-
}
657-
otherCurr = otherCurr.parent;
658-
curr = curr.parent;
659-
}
660-
661-
return curr == null && otherCurr == null;
662-
}
663-
664674
protected static class NodeIterator implements Iterator<Path.Node> {
665675
private final NodeImpl[] array;
666676
private int index;

engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/AbstractValidationContext.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
2626
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintViolationCreationContext;
2727
import org.hibernate.validator.internal.engine.path.ModifiablePath;
28+
import org.hibernate.validator.internal.engine.path.NodeImpl;
2829
import org.hibernate.validator.internal.engine.valuecontext.ValueContext;
2930
import org.hibernate.validator.internal.metadata.aggregated.BeanMetaData;
3031
import 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
}

engine/src/main/java/org/hibernate/validator/internal/engine/validationcontext/BaseBeanValidationContext.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
*/
55
package org.hibernate.validator.internal.engine.validationcontext;
66

7-
import jakarta.validation.Path;
87
import jakarta.validation.TraversableResolver;
98
import jakarta.validation.Validator;
109

@@ -39,9 +38,9 @@ public interface BaseBeanValidationContext<T> extends ValidationContext<T> {
3938

4039
void markCurrentBeanAsProcessed(ValueContext<?, ?> valueContext);
4140

42-
boolean hasMetaConstraintBeenProcessed(Object bean, Path path, MetaConstraint<?> metaConstraint);
41+
boolean hasMetaConstraintBeenProcessed(Object bean, ModifiablePath path, MetaConstraint<?> metaConstraint);
4342

44-
void markConstraintProcessed(Object bean, Path path, MetaConstraint<?> metaConstraint);
43+
void markConstraintProcessed(Object bean, ModifiablePath path, MetaConstraint<?> metaConstraint);
4544

4645
/**
4746
* @return {@code true} if current validation context can and should process passed meta constraint. Is used in

0 commit comments

Comments
 (0)