Skip to content

Commit

Permalink
HV-1942 Allow a single impl of a DefaultGroupSequenceProvider to be u…
Browse files Browse the repository at this point in the history
…sed 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
  • Loading branch information
marko-bekhta committed Oct 14, 2024
1 parent 70fc56a commit 4d09a41
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
*/
public interface BazDefaultGroupSequenceProvider extends DefaultGroupSequenceProvider<Baz> {
@Override
List<Class<?>> getValidationGroups(GroupSequenceProviderDefinition.Baz object);
List<Class<?>> getValidationGroups(Class<?> klass, GroupSequenceProviderDefinition.Baz object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public FooBarBazDefaultGroupSequenceProvider(FooBarBaz fooBarBaz) {
}

@Override
public List<Class<?>> getValidationGroups(FooBarBaz object) {
public List<Class<?>> getValidationGroups(Class<?> klass, FooBarBaz object) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@
public abstract class FooBarDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<FooBar> {

@Override
public abstract List<Class<?>> getValidationGroups(FooBar object);
public abstract List<Class<?>> getValidationGroups(Class<?> klass, FooBar object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class FooDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<Foo> {

@Override
public List<Class<?>> getValidationGroups(Foo object) {
public List<Class<?>> getValidationGroups(Class<?> klass, Foo object) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class QuxDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<Qux> {

@Override
public List<Class<?>> getValidationGroups(Qux object) {
public List<Class<?>> getValidationGroups(Class<?> klass, Qux object) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class SampleDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<Sample> {

@Override
public List<Class<?>> getValidationGroups(Sample object) {
public List<Class<?>> getValidationGroups(Class<?> klass, Sample object) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class RentalCarGroupSequenceProvider
implements DefaultGroupSequenceProvider<RentalCar> {

@Override
public List<Class<?>> getValidationGroups(RentalCar car) {
public List<Class<?>> getValidationGroups(Class<?> klass, RentalCar car) {
List<Class<?>> defaultGroupSequence = new ArrayList<Class<?>>();
defaultGroupSequence.add( RentalCar.class );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

public class RentalCarGroupSequenceProvider implements DefaultGroupSequenceProvider<RentalCar> {
@Override
public List<Class<?>> getValidationGroups(RentalCar car) {
public List<Class<?>> getValidationGroups(Class<?> klass, RentalCar car) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ public Optional<ExecutableMetaData> getMetaDataFor(Executable executable) {
@Override
public List<Class<?>> getDefaultGroupSequence(T beanState) {
if ( hasDefaultGroupSequenceProvider() ) {
List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );
List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanClass, beanState );
return getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence );
}

Expand All @@ -388,7 +388,7 @@ public List<Class<?>> getDefaultGroupSequence(T beanState) {
@Override
public Iterator<Sequence> getDefaultValidationSequence(T beanState) {
if ( hasDefaultGroupSequenceProvider() ) {
List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanState );
List<Class<?>> providerDefaultGroupSequence = defaultGroupSequenceProvider.getValidationGroups( beanClass, beanState );
return validationOrderGenerator.getDefaultValidationOrder(
beanClass,
getValidDefaultGroupSequence( beanClass, providerDefaultGroupSequence )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,30 @@ private <T> DefaultGroupSequenceProvider<? super T> getDefaultGroupSequenceProvi
private <T> DefaultGroupSequenceProvider<? super T> newGroupSequenceProviderClassInstance(Class<T> beanClass,
Class<? extends DefaultGroupSequenceProvider<? super T>> 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 );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,45 @@
/**
* This class defines the dynamic group sequence provider contract.
* <p>
* 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.
* </p>
* 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.
* <p>
* 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.
* </p>
* <p>
* Note:
* <ul>
* <li>Implementations must provide a public default constructor.</li>
* <li>Implementations must be thread-safe.</li>
* <li>Implementations must return a valid default group sequence,
* i.e. the returned sequence <b>must</b> contain the bean type itself,
* which represents the {@link jakarta.validation.groups.Default default group}.</li>
* </ul>
*
* @param <T> The type for which an implementation is defined.
*
* @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
* @author Hardy Ferentschik
*/
public interface DefaultGroupSequenceProvider<T> {

/**
* This method returns the default group sequence for the given {@code klass} bean type and {@code object} instance.
* <p>
* 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 <b>can</b> 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<Class<?>> getValidationGroups(Class<?> klass, T object) {
return getValidationGroups( object );
}

/**
* This method returns the default group sequence for the given instance.
* <p>
Expand All @@ -41,9 +58,12 @@ public interface DefaultGroupSequenceProvider<T> {
*
* @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<Class<?>> getValidationGroups(T object);
@Deprecated(forRemoval = true, since = "9.0.0")
default List<Class<?>> 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" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -628,21 +628,21 @@ public String getLastName() {

public static class MarathonDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<Marathon> {
@Override
public List<Class<?>> getValidationGroups(Marathon object) {
public List<Class<?>> getValidationGroups(Class<?> klass, Marathon object) {
return Arrays.<Class<?>>asList( Foo.class, Marathon.class );
}
}

public static class BDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<B> {
@Override
public List<Class<?>> getValidationGroups(B object) {
public List<Class<?>> getValidationGroups(Class<?> klass, B object) {
return Arrays.<Class<?>>asList( Foo.class, B.class );
}
}

public static class ADefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<A> {
@Override
public List<Class<?>> getValidationGroups(A object) {
public List<Class<?>> getValidationGroups(Class<?> klass, A object) {
return Arrays.<Class<?>>asList( Foo.class, A.class );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ private interface Foo {

public static class MarathonDefaultGroupSequenceProvider implements DefaultGroupSequenceProvider<Marathon> {
@Override
public List<Class<?>> getValidationGroups(Marathon object) {
public List<Class<?>> getValidationGroups(Class<?> klass, Marathon object) {
return Arrays.<Class<?>>asList( Foo.class, Marathon.class );
}
}
Expand Down
Loading

0 comments on commit 4d09a41

Please sign in to comment.