2626import java .math .BigDecimal ;
2727import java .util .Arrays ;
2828import java .util .Collections ;
29- import java .util .List ;
29+ import java .util .HashSet ;
30+ import java .util .Set ;
31+ import java .util .function .Function ;
3032import javax .validation .constraints .DecimalMax ;
3133import javax .validation .constraints .DecimalMin ;
3234import javax .validation .constraints .Email ;
5658 */
5759public class JavaxValidationModule implements Module {
5860
59- private final List <JavaxValidationOption > options ;
61+ private final Set <JavaxValidationOption > options ;
62+ private Set <Class <?>> validationGroups ;
6063
6164 /**
6265 * Constructor.
6366 *
6467 * @param options features to enable
6568 */
6669 public JavaxValidationModule (JavaxValidationOption ... options ) {
67- this .options = options == null ? Collections .emptyList () : Arrays .asList (options );
70+ this .options = options == null ? Collections .emptySet () : new HashSet <>(Arrays .asList (options ));
71+ // by default: ignore validation groups
72+ this .validationGroups = null ;
73+ }
74+
75+ /**
76+ * Add validation groups to be considered.
77+ * <ul>
78+ * <li>Never calling this method will result in all annotations to be picked-up.</li>
79+ * <li>Calling this without parameters will only consider those annotations where no groups are defined.</li>
80+ * <li>Calling this with not-null parameters will only consider those annotations without defined groups or where at least one matches.</li>
81+ * </ul>
82+ *
83+ * @param validationGroups validation groups to consider
84+ * @return this module instance (for chaining)
85+ */
86+ public JavaxValidationModule forValidationGroups (Class <?>... validationGroups ) {
87+ if (validationGroups == null ) {
88+ this .validationGroups = null ;
89+ } else {
90+ this .validationGroups = new HashSet <>(Arrays .asList (validationGroups ));
91+ }
92+ return this ;
6893 }
6994
7095 @ Override
@@ -110,12 +135,14 @@ private void applyToConfigPart(SchemaGeneratorConfigPart<?> configPart) {
110135 * @param <A> type of annotation
111136 * @param member field or method to retrieve annotation instance from (or from a field's getter or getter method's field)
112137 * @param annotationClass type of annotation
138+ * @param validationGroupsLookup how to look-up the associated validation groups of an annotation instance
113139 * @return annotation instance (or {@code null})
114140 * @see MemberScope#getAnnotation(Class)
115141 * @see FieldScope#findGetter()
116142 * @see MethodScope#findGetterField()
117143 */
118- protected <A extends Annotation > A getAnnotationFromFieldOrGetter (MemberScope <?, ?> member , Class <A > annotationClass ) {
144+ protected <A extends Annotation > A getAnnotationFromFieldOrGetter (MemberScope <?, ?> member , Class <A > annotationClass ,
145+ Function <A , Class <?>[]> validationGroupsLookup ) {
119146 A annotation = member .getAnnotation (annotationClass );
120147 if (annotation == null ) {
121148 MemberScope <?, ?> associatedGetterOrField ;
@@ -128,6 +155,20 @@ protected <A extends Annotation> A getAnnotationFromFieldOrGetter(MemberScope<?,
128155 }
129156 annotation = associatedGetterOrField == null ? null : associatedGetterOrField .getAnnotation (annotationClass );
130157 }
158+ if (annotation != null ) {
159+ Class <?>[] associatedGroups = validationGroupsLookup .apply (annotation );
160+ /*
161+ * the annotation is deemed applicable in one of the following three cases:
162+ * 1. Validation groups are specifically ignored (i.e. forValidationGroups() was never called or with null as only parameter)
163+ * 2. No validation groups are specified on the annotation.
164+ * 3. Some validation group(s) are specified on the annotation and at least one of them was provided via forValidationGroups().
165+ */
166+ if (this .validationGroups != null && associatedGroups .length > 0
167+ && Collections .disjoint (this .validationGroups , Arrays .asList (associatedGroups ))) {
168+ // ignore the looked-up annotation as it is not associated with one of the desired validation groups
169+ annotation = null ;
170+ }
171+ }
131172 return annotation ;
132173 }
133174
@@ -139,12 +180,12 @@ protected <A extends Annotation> A getAnnotationFromFieldOrGetter(MemberScope<?,
139180 */
140181 protected Boolean isNullable (MemberScope <?, ?> member ) {
141182 Boolean result ;
142- if (this .getAnnotationFromFieldOrGetter (member , NotNull .class ) != null
143- || this .getAnnotationFromFieldOrGetter (member , NotBlank .class ) != null
144- || this .getAnnotationFromFieldOrGetter (member , NotEmpty .class ) != null ) {
183+ if (this .getAnnotationFromFieldOrGetter (member , NotNull .class , NotNull :: groups ) != null
184+ || this .getAnnotationFromFieldOrGetter (member , NotBlank .class , NotBlank :: groups ) != null
185+ || this .getAnnotationFromFieldOrGetter (member , NotEmpty .class , NotEmpty :: groups ) != null ) {
145186 // field is specifically NOT nullable
146187 result = Boolean .FALSE ;
147- } else if (this .getAnnotationFromFieldOrGetter (member , Null .class ) != null ) {
188+ } else if (this .getAnnotationFromFieldOrGetter (member , Null .class , Null :: groups ) != null ) {
148189 // field is specifically null (and thereby nullable)
149190 result = Boolean .TRUE ;
150191 } else {
@@ -173,12 +214,12 @@ protected boolean isRequired(MemberScope<?, ?> member) {
173214 */
174215 protected Integer resolveArrayMinItems (MemberScope <?, ?> member ) {
175216 if (member .isContainerType ()) {
176- Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class );
217+ Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class , Size :: groups );
177218 if (sizeAnnotation != null && sizeAnnotation .min () > 0 ) {
178219 // minimum length greater than the default 0 was specified
179220 return sizeAnnotation .min ();
180221 }
181- if (this .getAnnotationFromFieldOrGetter (member , NotEmpty .class ) != null ) {
222+ if (this .getAnnotationFromFieldOrGetter (member , NotEmpty .class , NotEmpty :: groups ) != null ) {
182223 return 1 ;
183224 }
184225 }
@@ -194,7 +235,7 @@ protected Integer resolveArrayMinItems(MemberScope<?, ?> member) {
194235 */
195236 protected Integer resolveArrayMaxItems (MemberScope <?, ?> member ) {
196237 if (member .isContainerType ()) {
197- Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class );
238+ Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class , Size :: groups );
198239 if (sizeAnnotation != null && sizeAnnotation .max () < 2147483647 ) {
199240 // maximum length below the default 2147483647 was specified
200241 return sizeAnnotation .max ();
@@ -214,13 +255,13 @@ protected Integer resolveArrayMaxItems(MemberScope<?, ?> member) {
214255 */
215256 protected Integer resolveStringMinLength (MemberScope <?, ?> member ) {
216257 if (member .getType ().isInstanceOf (CharSequence .class )) {
217- Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class );
258+ Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class , Size :: groups );
218259 if (sizeAnnotation != null && sizeAnnotation .min () > 0 ) {
219260 // minimum length greater than the default 0 was specified
220261 return sizeAnnotation .min ();
221262 }
222- if (this .getAnnotationFromFieldOrGetter (member , NotEmpty .class ) != null
223- || this .getAnnotationFromFieldOrGetter (member , NotBlank .class ) != null ) {
263+ if (this .getAnnotationFromFieldOrGetter (member , NotEmpty .class , NotEmpty :: groups ) != null
264+ || this .getAnnotationFromFieldOrGetter (member , NotBlank .class , NotBlank :: groups ) != null ) {
224265 return 1 ;
225266 }
226267 }
@@ -236,7 +277,7 @@ protected Integer resolveStringMinLength(MemberScope<?, ?> member) {
236277 */
237278 protected Integer resolveStringMaxLength (MemberScope <?, ?> member ) {
238279 if (member .getType ().isInstanceOf (CharSequence .class )) {
239- Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class );
280+ Size sizeAnnotation = this .getAnnotationFromFieldOrGetter (member , Size .class , Size :: groups );
240281 if (sizeAnnotation != null && sizeAnnotation .max () < 2147483647 ) {
241282 // maximum length below the default 2147483647 was specified
242283 return sizeAnnotation .max ();
@@ -254,7 +295,7 @@ protected Integer resolveStringMaxLength(MemberScope<?, ?> member) {
254295 */
255296 protected String resolveStringFormat (MemberScope <?, ?> member ) {
256297 if (member .getType ().isInstanceOf (CharSequence .class )) {
257- Email emailAnnotation = this .getAnnotationFromFieldOrGetter (member , Email .class );
298+ Email emailAnnotation = this .getAnnotationFromFieldOrGetter (member , Email .class , Email :: groups );
258299 if (emailAnnotation != null ) {
259300 // @Email annotation was found, indicate the respective format
260301 if (this .options .contains (JavaxValidationOption .PREFER_IDN_EMAIL_FORMAT )) {
@@ -277,12 +318,12 @@ protected String resolveStringFormat(MemberScope<?, ?> member) {
277318 */
278319 protected String resolveStringPattern (MemberScope <?, ?> member ) {
279320 if (member .getType ().isInstanceOf (CharSequence .class )) {
280- Pattern patternAnnotation = this .getAnnotationFromFieldOrGetter (member , Pattern .class );
321+ Pattern patternAnnotation = this .getAnnotationFromFieldOrGetter (member , Pattern .class , Pattern :: groups );
281322 if (patternAnnotation != null ) {
282323 // @Pattern annotation was found, return its (mandatory) regular expression
283324 return patternAnnotation .regexp ();
284325 }
285- Email emailAnnotation = this .getAnnotationFromFieldOrGetter (member , Email .class );
326+ Email emailAnnotation = this .getAnnotationFromFieldOrGetter (member , Email .class , Email :: groups );
286327 if (emailAnnotation != null && !".*" .equals (emailAnnotation .regexp ())) {
287328 // non-default regular expression on @Email annotation should also be considered
288329 return emailAnnotation .regexp ();
@@ -301,15 +342,15 @@ protected String resolveStringPattern(MemberScope<?, ?> member) {
301342 * @see PositiveOrZero
302343 */
303344 protected BigDecimal resolveNumberInclusiveMinimum (MemberScope <?, ?> member ) {
304- Min minAnnotation = this .getAnnotationFromFieldOrGetter (member , Min .class );
345+ Min minAnnotation = this .getAnnotationFromFieldOrGetter (member , Min .class , Min :: groups );
305346 if (minAnnotation != null ) {
306347 return new BigDecimal (minAnnotation .value ());
307348 }
308- DecimalMin decimalMinAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMin .class );
349+ DecimalMin decimalMinAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMin .class , DecimalMin :: groups );
309350 if (decimalMinAnnotation != null && decimalMinAnnotation .inclusive ()) {
310351 return new BigDecimal (decimalMinAnnotation .value ());
311352 }
312- PositiveOrZero positiveAnnotation = this .getAnnotationFromFieldOrGetter (member , PositiveOrZero .class );
353+ PositiveOrZero positiveAnnotation = this .getAnnotationFromFieldOrGetter (member , PositiveOrZero .class , PositiveOrZero :: groups );
313354 if (positiveAnnotation != null ) {
314355 return BigDecimal .ZERO ;
315356 }
@@ -325,11 +366,11 @@ protected BigDecimal resolveNumberInclusiveMinimum(MemberScope<?, ?> member) {
325366 * @see Positive
326367 */
327368 protected BigDecimal resolveNumberExclusiveMinimum (MemberScope <?, ?> member ) {
328- DecimalMin decimalMinAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMin .class );
369+ DecimalMin decimalMinAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMin .class , DecimalMin :: groups );
329370 if (decimalMinAnnotation != null && !decimalMinAnnotation .inclusive ()) {
330371 return new BigDecimal (decimalMinAnnotation .value ());
331372 }
332- Positive positiveAnnotation = this .getAnnotationFromFieldOrGetter (member , Positive .class );
373+ Positive positiveAnnotation = this .getAnnotationFromFieldOrGetter (member , Positive .class , Positive :: groups );
333374 if (positiveAnnotation != null ) {
334375 return BigDecimal .ZERO ;
335376 }
@@ -346,15 +387,15 @@ protected BigDecimal resolveNumberExclusiveMinimum(MemberScope<?, ?> member) {
346387 * @see NegativeOrZero
347388 */
348389 protected BigDecimal resolveNumberInclusiveMaximum (MemberScope <?, ?> member ) {
349- Max maxAnnotation = this .getAnnotationFromFieldOrGetter (member , Max .class );
390+ Max maxAnnotation = this .getAnnotationFromFieldOrGetter (member , Max .class , Max :: groups );
350391 if (maxAnnotation != null ) {
351392 return new BigDecimal (maxAnnotation .value ());
352393 }
353- DecimalMax decimalMaxAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMax .class );
394+ DecimalMax decimalMaxAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMax .class , DecimalMax :: groups );
354395 if (decimalMaxAnnotation != null && decimalMaxAnnotation .inclusive ()) {
355396 return new BigDecimal (decimalMaxAnnotation .value ());
356397 }
357- NegativeOrZero negativeAnnotation = this .getAnnotationFromFieldOrGetter (member , NegativeOrZero .class );
398+ NegativeOrZero negativeAnnotation = this .getAnnotationFromFieldOrGetter (member , NegativeOrZero .class , NegativeOrZero :: groups );
358399 if (negativeAnnotation != null ) {
359400 return BigDecimal .ZERO ;
360401 }
@@ -370,11 +411,11 @@ protected BigDecimal resolveNumberInclusiveMaximum(MemberScope<?, ?> member) {
370411 * @see Negative
371412 */
372413 protected BigDecimal resolveNumberExclusiveMaximum (MemberScope <?, ?> member ) {
373- DecimalMax decimalMaxAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMax .class );
414+ DecimalMax decimalMaxAnnotation = this .getAnnotationFromFieldOrGetter (member , DecimalMax .class , DecimalMax :: groups );
374415 if (decimalMaxAnnotation != null && !decimalMaxAnnotation .inclusive ()) {
375416 return new BigDecimal (decimalMaxAnnotation .value ());
376417 }
377- Negative negativeAnnotation = this .getAnnotationFromFieldOrGetter (member , Negative .class );
418+ Negative negativeAnnotation = this .getAnnotationFromFieldOrGetter (member , Negative .class , Negative :: groups );
378419 if (negativeAnnotation != null ) {
379420 return BigDecimal .ZERO ;
380421 }
0 commit comments