9
9
import static org .hibernate .validator .constraints .CompositionType .ALL_FALSE ;
10
10
import static org .hibernate .validator .constraints .CompositionType .AND ;
11
11
import static org .hibernate .validator .constraints .CompositionType .OR ;
12
- import static org .hibernate .validator .internal .util .CollectionHelper .newHashSet ;
13
12
14
13
import java .lang .annotation .Annotation ;
15
14
import java .lang .invoke .MethodHandles ;
16
15
import java .lang .reflect .Type ;
17
- import java .util .Collections ;
16
+ import java .util .ArrayList ;
17
+ import java .util .Collection ;
18
18
import java .util .List ;
19
- import java .util .Set ;
19
+ import java .util .Optional ;
20
20
import java .util .stream .Collectors ;
21
21
22
22
import javax .validation .ConstraintValidator ;
23
- import javax .validation .ConstraintViolation ;
24
23
25
24
import org .hibernate .validator .constraints .CompositionType ;
26
25
import org .hibernate .validator .internal .engine .ValidationContext ;
@@ -67,15 +66,15 @@ private <U extends Annotation> ConstraintTree<U> createConstraintTree(Constraint
67
66
@ Override
68
67
protected <T > void validateConstraints (ValidationContext <T > validationContext ,
69
68
ValueContext <?, ?> valueContext ,
70
- Set < ConstraintViolation < T >> constraintViolations ) {
69
+ Collection < ConstraintValidatorContextImpl > violatedConstraintValidatorContexts ) {
71
70
CompositionResult compositionResult = validateComposingConstraints (
72
- validationContext , valueContext , constraintViolations
71
+ validationContext , valueContext , violatedConstraintValidatorContexts
73
72
);
74
73
75
- Set < ConstraintViolation < T >> localViolations ;
74
+ Optional < ConstraintValidatorContextImpl > violatedLocalConstraintValidatorContext ;
76
75
77
76
// After all children are validated the actual ConstraintValidator of the constraint itself is executed
78
- if ( mainConstraintNeedsEvaluation ( validationContext , constraintViolations ) ) {
77
+ if ( mainConstraintNeedsEvaluation ( validationContext , violatedConstraintValidatorContexts ) ) {
79
78
80
79
if ( LOG .isTraceEnabled () ) {
81
80
LOG .tracef (
@@ -98,41 +97,40 @@ protected <T> void validateConstraints(ValidationContext<T> validationContext,
98
97
);
99
98
100
99
// validate
101
- localViolations = validateSingleConstraint (
102
- validationContext ,
100
+ violatedLocalConstraintValidatorContext = validateSingleConstraint (
103
101
valueContext ,
104
102
constraintValidatorContext ,
105
103
validator
106
104
);
107
105
108
106
// We re-evaluate the boolean composition by taking into consideration also the violations
109
107
// from the local constraintValidator
110
- if ( localViolations . isEmpty () ) {
108
+ if ( ! violatedLocalConstraintValidatorContext . isPresent () ) {
111
109
compositionResult .setAtLeastOneTrue ( true );
112
110
}
113
111
else {
114
112
compositionResult .setAllTrue ( false );
115
113
}
116
114
}
117
115
else {
118
- localViolations = Collections . emptySet ();
116
+ violatedLocalConstraintValidatorContext = Optional . empty ();
119
117
}
120
118
121
- if ( !passesCompositionTypeRequirement ( constraintViolations , compositionResult ) ) {
119
+ if ( !passesCompositionTypeRequirement ( violatedConstraintValidatorContexts , compositionResult ) ) {
122
120
prepareFinalConstraintViolations (
123
- validationContext , valueContext , constraintViolations , localViolations
121
+ validationContext , valueContext , violatedConstraintValidatorContexts , violatedLocalConstraintValidatorContext
124
122
);
125
123
}
126
124
}
127
125
128
- private <T > boolean mainConstraintNeedsEvaluation (ValidationContext <T > executionContext ,
129
- Set < ConstraintViolation < T >> constraintViolations ) {
126
+ private <T > boolean mainConstraintNeedsEvaluation (ValidationContext <T > validationContext ,
127
+ Collection < ConstraintValidatorContextImpl > violatedConstraintValidatorContexts ) {
130
128
// we are dealing with a composing constraint with no validator for the main constraint
131
129
if ( !descriptor .getComposingConstraints ().isEmpty () && descriptor .getMatchingConstraintValidatorDescriptors ().isEmpty () ) {
132
130
return false ;
133
131
}
134
132
135
- if ( constraintViolations .isEmpty () ) {
133
+ if ( violatedConstraintValidatorContexts .isEmpty () ) {
136
134
return true ;
137
135
}
138
136
@@ -142,7 +140,7 @@ private <T> boolean mainConstraintNeedsEvaluation(ValidationContext<T> execution
142
140
}
143
141
144
142
// explicit fail fast mode
145
- if ( executionContext .isFailFastModeEnabled () ) {
143
+ if ( validationContext .isFailFastModeEnabled () ) {
146
144
return false ;
147
145
}
148
146
@@ -153,33 +151,32 @@ private <T> boolean mainConstraintNeedsEvaluation(ValidationContext<T> execution
153
151
* Before the final constraint violations can be reported back we need to check whether we have a composing
154
152
* constraint whose result should be reported as single violation.
155
153
*
156
- * @param executionContext meta data about top level validation
154
+ * @param validationContext meta data about top level validation
157
155
* @param valueContext meta data for currently validated value
158
- * @param constraintViolations used to accumulate constraint violations
159
- * @param localViolations set of constraint violations of top level constraint
156
+ * @param violatedConstraintValidatorContexts used to accumulate constraint validator contexts that cause constraint violations
157
+ * @param localConstraintValidatorContext an optional of constraint violations of top level constraint
158
+ *
160
159
*/
161
- private <T > void prepareFinalConstraintViolations (ValidationContext <T > executionContext ,
160
+ private <T > void prepareFinalConstraintViolations (ValidationContext <T > validationContext ,
162
161
ValueContext <?, ?> valueContext ,
163
- Set < ConstraintViolation < T >> constraintViolations ,
164
- Set < ConstraintViolation < T >> localViolations ) {
162
+ Collection < ConstraintValidatorContextImpl > violatedConstraintValidatorContexts ,
163
+ Optional < ConstraintValidatorContextImpl > localConstraintValidatorContext ) {
165
164
if ( reportAsSingleViolation () ) {
166
165
// We clear the current violations list anyway
167
- constraintViolations .clear ();
166
+ violatedConstraintValidatorContexts .clear ();
168
167
169
168
// But then we need to distinguish whether the local ConstraintValidator has reported
170
169
// violations or not (or if there is no local ConstraintValidator at all).
171
170
// If not we create a violation
172
171
// using the error message in the annotation declaration at top level.
173
- if ( localViolations .isEmpty () ) {
174
- final String message = getDescriptor ().getMessageTemplate ();
175
- ConstraintViolationCreationContext constraintViolationCreationContext = new ConstraintViolationCreationContext (
176
- message ,
177
- valueContext .getPropertyPath ()
178
- );
179
- ConstraintViolation <T > violation = executionContext .createConstraintViolation (
180
- valueContext , constraintViolationCreationContext , descriptor
181
- );
182
- constraintViolations .add ( violation );
172
+ if ( !localConstraintValidatorContext .isPresent () ) {
173
+ violatedConstraintValidatorContexts .add ( new ConstraintValidatorContextImpl (
174
+ validationContext .getParameterNames (),
175
+ validationContext .getClockProvider (),
176
+ valueContext .getPropertyPath (),
177
+ descriptor ,
178
+ validationContext .getConstraintValidatorPayload ()
179
+ ) );
183
180
}
184
181
}
185
182
@@ -191,28 +188,30 @@ private <T> void prepareFinalConstraintViolations(ValidationContext<T> execution
191
188
// as checked in test CustomErrorMessage.java
192
189
// If no violations have been reported from the local ConstraintValidator, or no such validator exists,
193
190
// then we just add an empty list.
194
- constraintViolations .addAll ( localViolations );
191
+ if ( localConstraintValidatorContext .isPresent () ) {
192
+ violatedConstraintValidatorContexts .add ( localConstraintValidatorContext .get () );
193
+ }
195
194
}
196
195
197
196
/**
198
197
* Validates all composing constraints recursively.
199
198
*
200
- * @param executionContext Meta data about top level validation
199
+ * @param validationContext Meta data about top level validation
201
200
* @param valueContext Meta data for currently validated value
202
- * @param constraintViolations Used to accumulate constraint violations
201
+ * @param violatedConstraintValidatorContexts Used to accumulate constraint validator contexts that cause constraint violations
203
202
*
204
203
* @return Returns an instance of {@code CompositionResult} relevant for boolean composition of constraints
205
204
*/
206
- private <T > CompositionResult validateComposingConstraints (ValidationContext <T > executionContext ,
205
+ private <T > CompositionResult validateComposingConstraints (ValidationContext <T > validationContext ,
207
206
ValueContext <?, ?> valueContext ,
208
- Set < ConstraintViolation < T >> constraintViolations ) {
207
+ Collection < ConstraintValidatorContextImpl > violatedConstraintValidatorContexts ) {
209
208
CompositionResult compositionResult = new CompositionResult ( true , false );
210
209
for ( ConstraintTree <?> tree : children ) {
211
- Set < ConstraintViolation < T >> tmpViolations = newHashSet ( 5 );
212
- tree .validateConstraints ( executionContext , valueContext , tmpViolations );
213
- constraintViolations .addAll ( tmpViolations );
210
+ List < ConstraintValidatorContextImpl > tmpConstraintValidatorContexts = new ArrayList <> ( 5 );
211
+ tree .validateConstraints ( validationContext , valueContext , tmpConstraintValidatorContexts );
212
+ violatedConstraintValidatorContexts .addAll ( tmpConstraintValidatorContexts );
214
213
215
- if ( tmpViolations .isEmpty () ) {
214
+ if ( tmpConstraintValidatorContexts .isEmpty () ) {
216
215
compositionResult .setAtLeastOneTrue ( true );
217
216
// no need to further validate constraints, because at least one validation passed
218
217
if ( descriptor .getCompositionType () == OR ) {
@@ -222,15 +221,15 @@ private <T> CompositionResult validateComposingConstraints(ValidationContext<T>
222
221
else {
223
222
compositionResult .setAllTrue ( false );
224
223
if ( descriptor .getCompositionType () == AND
225
- && ( executionContext .isFailFastModeEnabled () || descriptor .isReportAsSingleViolation () ) ) {
224
+ && ( validationContext .isFailFastModeEnabled () || descriptor .isReportAsSingleViolation () ) ) {
226
225
break ;
227
226
}
228
227
}
229
228
}
230
229
return compositionResult ;
231
230
}
232
231
233
- private boolean passesCompositionTypeRequirement (Set <?> constraintViolations , CompositionResult compositionResult ) {
232
+ private boolean passesCompositionTypeRequirement (Collection <?> constraintViolations , CompositionResult compositionResult ) {
234
233
CompositionType compositionType = getDescriptor ().getCompositionType ();
235
234
boolean passedValidation = false ;
236
235
switch ( compositionType ) {
0 commit comments