diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java index 323ee74c7a..a8f40b8944 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/CascadableConstraintMappingContextImplBase.java @@ -130,11 +130,14 @@ public ContainerElementConstraintMappingContext containerElement(ContainerElemen ); } - ContainerElementConstraintMappingContextImpl containerElementContext = new ContainerElementConstraintMappingContextImpl( - typeContext, parent, location, index - ); - - containerElementContexts.put( index, containerElementContext ); + // As we already checked that the specific path was not yet configured we should not worry about returning the same context here, + // as it means that there are some nested indexes which make a difference, And at the end a new context will be returned by call + // to containerElementContext#nestedContainerElement(). + ContainerElementConstraintMappingContextImpl containerElementContext = containerElementContexts.get( index ); + if ( containerElementContext == null ) { + containerElementContext = new ContainerElementConstraintMappingContextImpl( typeContext, parent, location, index ); + containerElementContexts.put( index, containerElementContext ); + } if ( nestedIndexes.length > 0 ) { return containerElementContext.nestedContainerElement( nestedIndexes ); diff --git a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java index ca7d0f7514..cf33e40d86 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/cfg/context/ContainerElementConstraintMappingContextImpl.java @@ -168,14 +168,16 @@ ContainerElementConstraintMappingContext nestedContainerElement(int[] nestedInde throw LOG.getTypeIsNotAParameterizedNorArrayTypeException( configuredType ); } - ContainerElementConstraintMappingContextImpl nestedContext = new ContainerElementConstraintMappingContextImpl( - typeContext, - parentContainerElementTarget, - ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), - nestedIndexes[0] - ); - - nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + ContainerElementConstraintMappingContextImpl nestedContext = nestedContainerElementContexts.get( nestedIndexes[0] ); + if ( nestedContext == null ) { + nestedContext = new ContainerElementConstraintMappingContextImpl( + typeContext, + parentContainerElementTarget, + ConstraintLocation.forTypeArgument( parentLocation, typeParameter, getContainerElementType() ), + nestedIndexes[0] + ); + nestedContainerElementContexts.put( nestedIndexes[0], nestedContext ); + } if ( nestedIndexes.length > 1 ) { return nestedContext.nestedContainerElement( Arrays.copyOfRange( nestedIndexes, 1, nestedIndexes.length ) ); diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java index cb9aa67121..60a8953a37 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ProgrammaticContainerElementConstraintsForFieldTest.java @@ -30,9 +30,11 @@ import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidatorConfiguration; import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.defs.LengthDef; import org.hibernate.validator.cfg.defs.MinDef; import org.hibernate.validator.cfg.defs.NotNullDef; import org.hibernate.validator.cfg.defs.SizeDef; +import org.hibernate.validator.constraints.Length; import org.hibernate.validator.internal.util.CollectionHelper; import org.hibernate.validator.testutil.TestForIssue; import org.testng.annotations.BeforeMethod; @@ -142,6 +144,29 @@ public void canDeclareContainerElementCascadesForFieldProgrammatically() { ); } + @Test + @TestForIssue(jiraKey = "HV-1614") + public void canDeclareDeeplyNestedContainerElementConstraintsOnMultipleDifferentTypeArgumentsForFieldProgrammatically() { + ConstraintMapping newMapping = config.createConstraintMapping(); + newMapping + .type( FishTank.class ) + .property( "tagsOfFishOfTheMonth", FIELD ) + .containerElementType( 0, 0 ) + .constraint( new LengthDef().min( 10 ).max( 20 ) ) + .containerElementType( 0, 1, 0 ) + .constraint( new NotNullDef() ); + + config.addMapping( newMapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + Set> violations = validator.validate( new FishTank() ); + + assertThat( violations ).containsOnlyViolations( + violationOf( NotNull.class ).withMessage( "must not be null" ), + violationOf( Length.class ).withMessage( "length must be between 10 and 20" ) + ); + } + // HV-1428 Container element support is disabled for arrays @Test(expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "HV000226:.*") @TestForIssue(jiraKey = "HV-1239")