From 4d09a41742d12628773cd29bba81bb3b6a4621f2 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Mon, 9 Sep 2024 18:26:59 +0200 Subject: [PATCH] HV-1942 Allow a single impl of a DefaultGroupSequenceProvider to be used on a wider range of types Since the existing assumption is that the provider is specific to a single type and cannot be placed on multiple types --- .../BazDefaultGroupSequenceProvider.java | 2 +- ...FooBarBazDefaultGroupSequenceProvider.java | 2 +- .../FooBarDefaultGroupSequenceProvider.java | 2 +- .../FooDefaultGroupSequenceProvider.java | 2 +- .../QuxDefaultGroupSequenceProvider.java | 2 +- .../SampleDefaultGroupSequenceProvider.java | 2 +- .../RentalCarGroupSequenceProvider.java | 2 +- .../RentalCarGroupSequenceProvider.java | 2 +- .../metadata/aggregated/BeanMetaDataImpl.java | 4 +- .../provider/AnnotationMetaDataProvider.java | 18 ++- .../validator/internal/util/logging/Log.java | 4 + .../group/DefaultGroupSequenceProvider.java | 40 ++++-- .../test/cfg/ConstraintMappingTest.java | 6 +- .../cfg/MultipleConstraintMappingsTest.java | 2 +- .../DefaultGroupSequenceProviderTest.java | 132 +++++++++++++++++- ...eprecatedDynamicGroupSequenceProvider.java | 33 +++++ .../DeprecatedUser.java | 43 ++++++ .../DynamicGroupSequenceProvider.java | 4 +- 18 files changed, 272 insertions(+), 30 deletions(-) create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedDynamicGroupSequenceProvider.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedUser.java diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/BazDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/BazDefaultGroupSequenceProvider.java index daa907adee..a6e7cdc0fc 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/BazDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/BazDefaultGroupSequenceProvider.java @@ -16,5 +16,5 @@ */ public interface BazDefaultGroupSequenceProvider extends DefaultGroupSequenceProvider { @Override - List> getValidationGroups(GroupSequenceProviderDefinition.Baz object); + List> getValidationGroups(Class klass, GroupSequenceProviderDefinition.Baz object); } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarBazDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarBazDefaultGroupSequenceProvider.java index c1b95adbe6..1ace163156 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarBazDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarBazDefaultGroupSequenceProvider.java @@ -20,7 +20,7 @@ public FooBarBazDefaultGroupSequenceProvider(FooBarBaz fooBarBaz) { } @Override - public List> getValidationGroups(FooBarBaz object) { + public List> getValidationGroups(Class klass, FooBarBaz object) { return null; } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarDefaultGroupSequenceProvider.java index 04d85a4bb8..5febc8edcf 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooBarDefaultGroupSequenceProvider.java @@ -17,5 +17,5 @@ public abstract class FooBarDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public abstract List> getValidationGroups(FooBar object); + public abstract List> getValidationGroups(Class klass, FooBar object); } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooDefaultGroupSequenceProvider.java index fe595e7976..6f32393531 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/FooDefaultGroupSequenceProvider.java @@ -17,7 +17,7 @@ public class FooDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(Foo object) { + public List> getValidationGroups(Class klass, Foo object) { return null; } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/QuxDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/QuxDefaultGroupSequenceProvider.java index d67239ee1b..d72c0396b4 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/QuxDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/QuxDefaultGroupSequenceProvider.java @@ -17,7 +17,7 @@ public class QuxDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(Qux object) { + public List> getValidationGroups(Class klass, Qux object) { return null; } } diff --git a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/SampleDefaultGroupSequenceProvider.java b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/SampleDefaultGroupSequenceProvider.java index c2bb38e17b..6c4d8a4c21 100644 --- a/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/SampleDefaultGroupSequenceProvider.java +++ b/annotation-processor/src/test/java/org/hibernate/validator/ap/testmodel/groupsequenceprovider/SampleDefaultGroupSequenceProvider.java @@ -17,7 +17,7 @@ public class SampleDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(Sample object) { + public List> getValidationGroups(Class klass, Sample object) { return null; } } diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter05/groupsequenceprovider/RentalCarGroupSequenceProvider.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter05/groupsequenceprovider/RentalCarGroupSequenceProvider.java index 3b1b192645..0696270ed3 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter05/groupsequenceprovider/RentalCarGroupSequenceProvider.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter05/groupsequenceprovider/RentalCarGroupSequenceProvider.java @@ -13,7 +13,7 @@ public class RentalCarGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(RentalCar car) { + public List> getValidationGroups(Class klass, RentalCar car) { List> defaultGroupSequence = new ArrayList>(); defaultGroupSequence.add( RentalCar.class ); diff --git a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/RentalCarGroupSequenceProvider.java b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/RentalCarGroupSequenceProvider.java index 1850980327..741c08017e 100644 --- a/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/RentalCarGroupSequenceProvider.java +++ b/documentation/src/test/java/org/hibernate/validator/referenceguide/chapter12/constraintapi/RentalCarGroupSequenceProvider.java @@ -6,7 +6,7 @@ public class RentalCarGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(RentalCar car) { + public List> getValidationGroups(Class klass, RentalCar car) { return null; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java index f678c61c57..976e633b06 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/aggregated/BeanMetaDataImpl.java @@ -378,7 +378,7 @@ public Optional getMetaDataFor(Executable executable) { @Override public List> getDefaultGroupSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { - List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); + List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanClass, beanState ); return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ); } @@ -388,7 +388,7 @@ public List> getDefaultGroupSequence(T beanState) { @Override public Iterator getDefaultValidationSequence(T beanState) { if ( hasDefaultGroupSequenceProvider() ) { - List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState ); + List> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanClass, beanState ); return validationOrderGenerator.getDefaultValidationOrder( beanClass, getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence ) diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java index 09e9e5b39f..c90dc59c05 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/provider/AnnotationMetaDataProvider.java @@ -169,14 +169,30 @@ private DefaultGroupSequenceProvider getDefaultGroupSequenceProvi private DefaultGroupSequenceProvider newGroupSequenceProviderClassInstance(Class beanClass, Class> providerClass) { Method[] providerMethods = GetMethods.action( providerClass ); + int numberOfDefaultMethods = 0; for ( Method method : providerMethods ) { if ( "getValidationGroups".equals( method.getName() ) && !method.isBridge() && method.getParameterCount() == 1 && method.getParameterTypes()[0].isAssignableFrom( beanClass ) ) { - + if ( method.isDefault() ) { + numberOfDefaultMethods++; + continue; + } + return NewInstance.action( providerClass, "the default group sequence provider" ); + } + if ( "getValidationGroups".equals( method.getName() ) + && !method.isBridge() + && method.getParameterCount() == 2 && method.getParameterTypes()[1].isAssignableFrom( beanClass ) ) { + if ( method.isDefault() ) { + numberOfDefaultMethods++; + continue; + } return NewInstance.action( providerClass, "the default group sequence provider" ); } } + if ( numberOfDefaultMethods == 2 ) { + throw LOG.getDefaultGroupSequenceProviderTypeDoesNotImplementAnyMethodsException( providerClass ); + } throw LOG.getWrongDefaultGroupSequenceProviderTypeException( beanClass ); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java index f9c362c5ac..3e2d1670be 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java +++ b/engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java @@ -940,4 +940,8 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch @Message(id = 267, value = "'%c' is neither a digit nor an upper case ASCII letter.") IllegalArgumentException getCharacterIsNotDigitOrUpperCaseLetterException(char c); + + @Message(id = 268, value = "The default group sequence provider %s does not implement neither `getValidationGroups(Class klass, T object)` nor `getValidationGroups(T object)` methods." + + " One of them has to be implemented for the default group sequence provider to be correctly defined.") + GroupDefinitionException getDefaultGroupSequenceProviderTypeDoesNotImplementAnyMethodsException(@FormatWith(ClassObjectFormatter.class) Class klass); } diff --git a/engine/src/main/java/org/hibernate/validator/spi/group/DefaultGroupSequenceProvider.java b/engine/src/main/java/org/hibernate/validator/spi/group/DefaultGroupSequenceProvider.java index 8ad97d9ca6..f8493a19ae 100644 --- a/engine/src/main/java/org/hibernate/validator/spi/group/DefaultGroupSequenceProvider.java +++ b/engine/src/main/java/org/hibernate/validator/spi/group/DefaultGroupSequenceProvider.java @@ -11,28 +11,45 @@ /** * This class defines the dynamic group sequence provider contract. *

- * In order to redefine dynamically the default group sequence for a type T, the {@link org.hibernate.validator.group.GroupSequenceProvider} annotation - * must be placed on T, specifying as its value a concrete implementation of {@code DefaultGroupSequenceProvider}, which - * must be parametrized with the type T. - *

+ * In order to dynamically redefine the default group sequence for a type {@code T}, + * the {@link org.hibernate.validator.group.GroupSequenceProvider} annotation + * must be placed on {@code T}, specifying as its value a concrete implementation of {@code DefaultGroupSequenceProvider}, which + * must be parametrized with that same type {@code T}, its subclass {@code Y} ({@code T t; t instanceof Y == true } + * or an interface {@code I} that is common to the beans on for which this sequence provider is expected to be applied to. *

- * If during the validation process the {@code Default} group is validated for T, the actual validated instance + * If during the validation process the {@code Default} group is validated for {@code T}, the actual validated instance * is passed to the {@code DefaultGroupSequenceProvider} to determine the default group sequence. - *

*

* Note: *

    *
  • Implementations must provide a public default constructor.
  • *
  • Implementations must be thread-safe.
  • + *
  • Implementations must return a valid default group sequence, + * i.e. the returned sequence must contain the bean type itself, + * which represents the {@link jakarta.validation.groups.Default default group}.
  • *
* * @param The type for which an implementation is defined. - * * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI * @author Hardy Ferentschik */ public interface DefaultGroupSequenceProvider { + /** + * This method returns the default group sequence for the given {@code klass} bean type and {@code object} instance. + *

+ * The object parameter allows to dynamically compose the default group sequence based on the state of the validated value. + * + * @param klass the type of the bean for which the group sequence is requested. + * @param object the instance under validation. This value can be {@code null}, e.g. in case this method was called as part of + * {@linkplain jakarta.validation.Validator#validateValue(Class, String, Object, Class[]) Validator#validateValue}. + * @return a list of classes specifying the default group sequence. The same constraints to the redefined group list + * apply as for lists defined via {@code GroupSequence}. In particular the list has to contain the type T. + */ + default List> getValidationGroups(Class klass, T object) { + return getValidationGroups( object ); + } + /** * This method returns the default group sequence for the given instance. *

@@ -41,9 +58,12 @@ public interface DefaultGroupSequenceProvider { * * @param object the instance being validated. This value can be {@code null} in case this method was called as part of * {@linkplain jakarta.validation.Validator#validateValue(Class, String, Object, Class[]) Validator#validateValue}. - * * @return a list of classes specifying the default group sequence. The same constraints to the redefined group list - * apply as for lists defined via {@code GroupSequence}. In particular the list has to contain the type T. + * apply as for lists defined via {@code GroupSequence}. In particular the list has to contain the type T. + * @deprecated Use the {@link #getValidationGroups(Class, Object)} instead. */ - List> getValidationGroups(T object); + @Deprecated(forRemoval = true, since = "9.0.0") + default List> getValidationGroups(T object) { + throw new AssertionError( "Unexpected call to get the validation group sequence. " + "The variant receiving the bean type must be used by Hibernate Validator" ); + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java index 7a4dd4b126..fca56cbc26 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/ConstraintMappingTest.java @@ -628,21 +628,21 @@ public String getLastName() { public static class MarathonDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(Marathon object) { + public List> getValidationGroups(Class klass, Marathon object) { return Arrays.>asList( Foo.class, Marathon.class ); } } public static class BDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(B object) { + public List> getValidationGroups(Class klass, B object) { return Arrays.>asList( Foo.class, B.class ); } } public static class ADefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(A object) { + public List> getValidationGroups(Class klass, A object) { return Arrays.>asList( Foo.class, A.class ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java b/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java index efe4b5afd2..0f1d23fc58 100644 --- a/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/cfg/MultipleConstraintMappingsTest.java @@ -178,7 +178,7 @@ private interface Foo { public static class MarathonDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(Marathon object) { + public List> getValidationGroups(Class klass, Marathon object) { return Arrays.>asList( Foo.class, Marathon.class ); } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DefaultGroupSequenceProviderTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DefaultGroupSequenceProviderTest.java index 26d33c3f4d..593661afe6 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DefaultGroupSequenceProviderTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DefaultGroupSequenceProviderTest.java @@ -6,6 +6,8 @@ */ package org.hibernate.validator.test.internal.engine.groups.defaultgroupsequenceprovider; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; import static org.hibernate.validator.testutils.ValidatorUtil.getValidator; @@ -15,12 +17,16 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import jakarta.validation.ConstraintViolation; import jakarta.validation.GroupDefinitionException; import jakarta.validation.Validator; +import jakarta.validation.constraints.Negative; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Positive; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.group.GroupSequenceProvider; @@ -82,6 +88,25 @@ public void testValidateUserProviderDefaultGroupSequence() { ); } + @SuppressWarnings("removal") + @Deprecated(forRemoval = true) + @Test + public void testValidateUserProviderDefaultGroupSequenceDeprecated() { + DeprecatedUser user = new DeprecatedUser( "$password" ); + Set> violations = validator.validate( user ); + + assertThat( violations ).containsOnlyViolations( + violationOf( Pattern.class ).withMessage( "must match \"\\w+\"" ) + ); + + DeprecatedUser admin = new DeprecatedUser( "short", true ); + violations = validator.validate( admin ); + + assertThat( violations ).containsOnlyViolations( + violationOf( Length.class ).withMessage( "length must be between 10 and 20" ) + ); + } + @Test public void testValidatePropertyUserProviderDefaultGroupSequence() { User user = new User( "$password" ); @@ -128,6 +153,44 @@ public void testValidateReturnValueProviderDefaultGroupSequence() throws NoSuchM ); } + @Test + public void testGenericGroupSequenceProvider() { + GenericBean1 bean1 = new GenericBean1(); + + assertThat( validator.validate( bean1 ) ).containsOnlyViolations( + violationOf( NotNull.class ) + ); + + GenericGroupSequenceProvider.key.set( false ); + assertNoViolations( validator.validate( bean1 ) ); + + GenericBean2 bean2 = new GenericBean2( "" ); + + assertThat( validator.validate( bean2 ) ).containsOnlyViolations( + violationOf( Positive.class ) + ); + + GenericGroupSequenceProvider.key.set( true ); + assertThat( validator.validate( bean2 ) ).containsOnlyViolations( + violationOf( Negative.class ) + ); + + // validation stopped on the first (default) group validation. + assertThat( validator.validate( new GenericBean2( null ) ) ).containsOnlyViolations( + violationOf( NotNull.class ) + ); + + assertThat( validator.validateValue( GenericBean2.class, "number", 0 ) ).containsOnlyViolations( + violationOf( Negative.class ) + ); + } + + @Test + public void testNothingImplemented() { + assertThatThrownBy( () -> validator.validate( new DummyBean() ) ) + .isInstanceOf( GroupDefinitionException.class ); + } + @GroupSequenceProvider(NullGroupSequenceProvider.class) private static class A { @NotNull @@ -167,7 +230,7 @@ private interface TestGroup { public static class MethodGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(CImpl object) { + public List> getValidationGroups(Class klass, CImpl object) { return Arrays.>asList( TestGroup.class, CImpl.class ); } } @@ -175,7 +238,7 @@ public List> getValidationGroups(CImpl object) { public static class NullGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(A object) { + public List> getValidationGroups(Class klass, A object) { return null; } } @@ -183,11 +246,74 @@ public List> getValidationGroups(A object) { public static class InvalidGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(B object) { + public List> getValidationGroups(Class klass, B object) { List> defaultGroupSequence = new ArrayList>(); defaultGroupSequence.add( TestGroup.class ); return defaultGroupSequence; } } + + /* + * The idea is that the same group sequence provider can be applied to different, possibly unrelated, beans. + * Because of that the sequence provider implementation does not know the bean type, + * and passed in object value may be {@code null}, hence it wouldn't be possible to obtain the type info from the instance. + * {@code null} instance can be passed in case of validating values, or on the startup when the defaults are initialized. + */ + public static class GenericGroupSequenceProvider implements DefaultGroupSequenceProvider { + + public static final AtomicBoolean key = new AtomicBoolean( true ); + + @Override + public List> getValidationGroups(Class clazz, Object object) { + if ( key.get() ) { + return List.of( clazz, GenericGroup1.class ); + } + else { + return List.of( clazz, GenericGroup2.class ); + } + } + } + + interface GenericGroup1 { + } + + interface GenericGroup2 { + } + + @GroupSequenceProvider(GenericGroupSequenceProvider.class) + private static class GenericBean1 { + @NotNull(groups = GenericGroup1.class) + @Null(groups = GenericGroup2.class) + String string; + } + + @GroupSequenceProvider(GenericGroupSequenceProvider.class) + private static class GenericBean2 { + + private String string; + + public GenericBean2(String string) { + this.string = string; + } + + @Negative(groups = GenericGroup1.class) + @Positive(groups = GenericGroup2.class) + public int getNumber() { + return 0; + } + + @NotNull + public String getString() { + return string; + } + } + + public static class DummyGroupSequenceProvider implements DefaultGroupSequenceProvider { + + } + + @GroupSequenceProvider(DummyGroupSequenceProvider.class) + private static class DummyBean { + } } diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedDynamicGroupSequenceProvider.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedDynamicGroupSequenceProvider.java new file mode 100644 index 0000000000..9340d601ee --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedDynamicGroupSequenceProvider.java @@ -0,0 +1,33 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupsequenceprovider; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; + +/** + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + */ +@SuppressWarnings("removal") +@Deprecated(forRemoval = true, since = "9.0.0") +public class DeprecatedDynamicGroupSequenceProvider implements DefaultGroupSequenceProvider { + + @Override + public List> getValidationGroups(DeprecatedUser user) { + List> defaultGroupSequence = new ArrayList>(); + defaultGroupSequence.add( DeprecatedUser.class ); + + if ( user != null && user.isAdmin() ) { + defaultGroupSequence.add( StrongCheck.class ); + } + + return defaultGroupSequence; + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedUser.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedUser.java new file mode 100644 index 0000000000..3593dd697d --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DeprecatedUser.java @@ -0,0 +1,43 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.engine.groups.defaultgroupsequenceprovider; + +import jakarta.validation.constraints.Pattern; + +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.group.GroupSequenceProvider; + +/** + * @author Kevin Pollet <kevin.pollet@serli.com> (C) 2011 SERLI + */ +@SuppressWarnings("removal") +@Deprecated(forRemoval = true, since = "9.0.0") +@GroupSequenceProvider(DeprecatedDynamicGroupSequenceProvider.class) +public class DeprecatedUser { + + private boolean admin; + + //Define message to avoid comparison problem with validation error message + //with a different locale than en + @Pattern(regexp = "\\w+", message = "must match \"{regexp}\"") + @Length(min = 10, max = 20, message = "length must be between {min} and {max}", groups = StrongCheck.class) + private String password; + + public DeprecatedUser(String password) { + this( password, false ); + } + + public DeprecatedUser(String password, boolean admin) { + this.password = password; + this.admin = admin; + } + + public boolean isAdmin() { + return admin; + } + +} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DynamicGroupSequenceProvider.java b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DynamicGroupSequenceProvider.java index 491eb7c7df..3a13ff0d18 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DynamicGroupSequenceProvider.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/engine/groups/defaultgroupsequenceprovider/DynamicGroupSequenceProvider.java @@ -17,8 +17,8 @@ public class DynamicGroupSequenceProvider implements DefaultGroupSequenceProvider { @Override - public List> getValidationGroups(User user) { - List> defaultGroupSequence = new ArrayList>(); + public List> getValidationGroups(Class klass, User user) { + List> defaultGroupSequence = new ArrayList<>(); defaultGroupSequence.add( User.class ); if ( user != null && user.isAdmin() ) {