4848import java .util .Set ;
4949import java .util .concurrent .ConcurrentHashMap ;
5050import java .util .stream .Collectors ;
51- import java .util .stream .Stream ;
5251
5352/**
5453 * The implementation of {@link ConstraintValidatorContext}.
@@ -83,7 +82,7 @@ public final class DefaultConstraintValidatorContext<R> implements ConstraintVal
8382 private Object executableReturnValue ;
8483 private List <Class <?>> currentGroups ;
8584 private Map <Class <?>, Class <?>> convertedGroups = Collections .emptyMap ();
86- private Set < ConstraintViolation < R >> currentViolations = new LinkedHashSet <>() ;
85+ private boolean hasCurrentViolations = false ;
8786
8887 DefaultConstraintValidatorContext (DefaultValidator defaultValidator , BeanIntrospection <R > beanIntrospection , R rootBean , BeanValidationContext validationContext ) {
8988 this (defaultValidator , beanIntrospection , validationContext , rootBean , null , new ValidationPath (), new LinkedHashSet <>(), null , Collections .emptyList ());
@@ -144,15 +143,20 @@ private static void sanityCheckGroups(List<Class<?>> groups) {
144143 }
145144 }
146145
147- public boolean hasDefaultGroup () {
146+ private static boolean hasDefaultGroup (List < Class <?>> definedGroups ) {
148147 return definedGroups .equals (DEFAULT_GROUPS );
149148 }
150149
151150 public boolean containsGroup (Collection <Class <?>> constraintGroups ) {
152151 if (currentGroups .contains (Default .class ) && rootClass != null && constraintGroups .contains (rootClass )) {
153152 return true ;
154153 }
155- return currentGroups .stream ().anyMatch (constraintGroups ::contains );
154+ for (Class <?> group : currentGroups ) {
155+ if (constraintGroups .contains (group )) {
156+ return true ;
157+ }
158+ }
159+ return false ;
156160 }
157161
158162 public Object [] getExecutableParameterValues () {
@@ -186,9 +190,9 @@ public ValidationCloseable withExecutableReturnValue(Object executableReturnValu
186190
187191 public GroupsValidation withGroupSequence (@ NonNull ValidationGroup validationGroup ) {
188192 List <Class <?>> prevGroups = currentGroups ;
189- Set < ConstraintViolation < R >> prevViolations = currentViolations ;
193+ boolean prevViolations = hasCurrentViolations ;
190194 currentGroups = validationGroup .groups ();
191- currentViolations = new LinkedHashSet <>() ;
195+ hasCurrentViolations = false ;
192196
193197 return new GroupsValidation () {
194198
@@ -200,13 +204,13 @@ public boolean isFailed() {
200204 if (validationGroup .isRedefinedDefaultGroupSequence ()) {
201205 return !overallViolations .isEmpty ();
202206 }
203- return ! currentViolations . isEmpty () ;
207+ return hasCurrentViolations ;
204208 }
205209
206210 @ Override
207211 public void close () {
208212 currentGroups = prevGroups ;
209- currentViolations = prevViolations ;
213+ hasCurrentViolations = prevViolations ;
210214 }
211215 };
212216 }
@@ -226,87 +230,128 @@ public ValidationCloseable convertGroups(@NonNull AnnotationMetadata annotationM
226230 av -> av .classValue ("to" ).orElseThrow ())
227231 );
228232 convertedGroups .putAll (newConvertGroups );
229- currentGroups = prevGroups .stream ().<Class <?>>map (this :: convertGroup ).toList ();
233+ currentGroups = prevGroups .stream ().<Class <?>>map (c -> convertGroup ( convertedGroups , c ) ).toList ();
230234 return () -> {
231235 convertedGroups = prevConvertedGroups ;
232236 currentGroups = prevGroups ;
233237 };
234238 }
235239
240+ List <DefaultConstraintValidatorContext .ValidationGroup > findGroupSequences (@ Nullable Object bean ) {
241+ if (bean == null ) {
242+ return findGroupSequences ();
243+ } else {
244+ BeanIntrospection <?> beanIntrospection = defaultValidator .getBeanIntrospection (bean );
245+ if (beanIntrospection == null ) {
246+ return findGroupSequences ();
247+ } else {
248+ return findGroupSequences (beanIntrospection );
249+ }
250+ }
251+ }
252+
236253 public List <ValidationGroup > findGroupSequences (BeanIntrospection <?> beanIntrospection ) {
237- if (hasDefaultGroup ()) {
254+ FindGroupContext ctx = new FindGroupContext (defaultValidator , convertedGroups , definedGroups );
255+ if (ctx .isDefault ()) {
256+ return defaultValidator .findGroupSequencesCache .computeIfAbsent (beanIntrospection , bi -> List .copyOf (findGroupSequences (ctx , bi )));
257+ } else {
258+ return findGroupSequences (ctx , beanIntrospection );
259+ }
260+ }
261+
262+ private static List <ValidationGroup > findGroupSequences (FindGroupContext ctx , BeanIntrospection <?> beanIntrospection ) {
263+ if (hasDefaultGroup (ctx .definedGroups )) {
238264 Class <Object >[] classGroupSequence = beanIntrospection .classValues (GroupSequence .class );
239265 if (classGroupSequence .length > 0 ) {
240266 if (Arrays .stream (classGroupSequence ).noneMatch (c -> c == beanIntrospection .getBeanType ())) {
241267 throw new GroupDefinitionException ("Group sequence is missing default group defined by the class of: " + beanIntrospection .getBeanType ());
242268 }
243- return Arrays .stream (classGroupSequence )
244- .flatMap (group -> {
245- if (group == beanIntrospection .getBeanType ()) {
246- return Stream .of (new ValidationGroup (true , true , List .of (Default .class )));
247- }
248- return findGroupSequence (Collections .singletonList (group ), new HashSet <>()).stream ();
249- })
250- .toList ();
269+ List <ValidationGroup > dest = new ArrayList <>();
270+ for (Class <Object > group : classGroupSequence ) {
271+ if (group == beanIntrospection .getBeanType ()) {
272+ dest .add (new ValidationGroup (true , true , List .of (Default .class )));
273+ } else {
274+ findGroups (ctx , dest , List .of (group ), new HashSet <>());
275+ }
276+ }
277+ return dest ;
251278 }
252279 }
253- return findGroupSequence ( definedGroups , new HashSet <>() );
280+ return findGroupSequences ( ctx );
254281 }
255282
256283 public List <ValidationGroup > findGroupSequences () {
257- return findGroupSequence (definedGroups , new HashSet <>());
284+ FindGroupContext ctx = new FindGroupContext (defaultValidator , convertedGroups , definedGroups );
285+ if (ctx .isDefault ()) {
286+ return defaultValidator .findGroupSequencesCache .computeIfAbsent (null , ignored -> List .copyOf (findGroupSequences (ctx )));
287+ } else {
288+ return findGroupSequences (ctx );
289+ }
258290 }
259291
260- private List <ValidationGroup > findGroupSequence (List <Class <?>> groups , Set <Class <?>> processedGroups ) {
261- return findGroups (groups , processedGroups ).stream ().toList ();
292+ private static List <ValidationGroup > findGroupSequences (FindGroupContext ctx ) {
293+ List <ValidationGroup > dest = new ArrayList <>();
294+ findGroups (ctx , dest , ctx .definedGroups , new HashSet <>());
295+ return dest ;
262296 }
263297
264- private List <ValidationGroup > findGroups ( Class <?> group , Set <Class <?>> processedGroups ) {
265- if (convertedGroups != null ) {
266- group = convertGroup (group );
298+ private static void findGroups ( FindGroupContext ctx , List <ValidationGroup > dest , Class <?> group , Set <Class <?>> processedGroups ) {
299+ if (ctx . convertedGroups != null ) {
300+ group = convertGroup (ctx . convertedGroups , group );
267301 }
268302 if (!processedGroups .add (group )) {
269303 throw new GroupDefinitionException ("Cyclical group: " + group );
270304 }
271305 Class <?> finalGroup = group ;
272306 List <Class <?>> groupSequence = GROUP_SEQUENCES .computeIfAbsent (group , ignore -> {
273- return defaultValidator .getBeanIntrospector ().findIntrospection (finalGroup ).stream ()
307+ return ctx . defaultValidator .getBeanIntrospector ().findIntrospection (finalGroup ).stream ()
274308 .<Class <?>>flatMap (introspection -> Arrays .stream (introspection .classValues (GroupSequence .class )))
275309 .toList ();
276310 });
277311 if (groupSequence .isEmpty ()) {
278- return List .of (new ValidationGroup (false , false , List .of (group )));
312+ dest .add (new ValidationGroup (false , false , List .of (group )));
313+ return ;
314+ }
315+ int start = dest .size ();
316+ for (Class <?> g : groupSequence ) {
317+ findGroups (ctx , dest , g , processedGroups );
318+ }
319+ for (int i = start ; i < groupSequence .size (); i ++) {
320+ ValidationGroup vg = dest .get (i );
321+ dest .set (i , new ValidationGroup (true , true , vg .groups ));
279322 }
280- return groupSequence .stream ()
281- .flatMap (g -> findGroups (g , processedGroups ).stream ().map (vg -> new ValidationGroup (true , true , vg .groups ))).toList ();
282323 }
283324
284- private Class <?> convertGroup (Class <?> group ) {
325+ private static Class <?> convertGroup (Map < Class <?>, Class <?>> convertedGroups , Class <?> group ) {
285326 Class <?> newGroup = convertedGroups .get (group );
286327 if (newGroup == null ) {
287328 return group ;
288329 }
289330 return newGroup ;
290331 }
291332
292- private List <ValidationGroup > findGroups (List <Class <?>> groupSequence , Set <Class <?>> processedGroups ) {
293- List <ValidationGroup > innerGroups = groupSequence .stream ().flatMap (g -> findGroups (g , processedGroups ).stream ()).toList ();
294- if (innerGroups .stream ().noneMatch (validationGroup -> validationGroup .isSequence )) {
295- return List .of (
296- new ValidationGroup (
297- false ,
298- false ,
299- innerGroups .stream ().flatMap (validationGroup -> validationGroup .groups .stream ()).toList ()
300- )
301- );
333+ private static void findGroups (FindGroupContext ctx , List <ValidationGroup > dest , List <Class <?>> groupSequence , Set <Class <?>> processedGroups ) {
334+ int start = dest .size ();
335+ for (Class <?> g : groupSequence ) {
336+ findGroups (ctx , dest , g , processedGroups );
337+ }
338+ boolean anySequence = false ;
339+ for (int i = start ; i < groupSequence .size () && !anySequence ; i ++) {
340+ anySequence |= dest .get (i ).isSequence ;
341+ }
342+ if (!anySequence ) {
343+ List <ValidationGroup > subList = dest .subList (start , dest .size ());
344+ List <Class <?>> copy = new ArrayList <>();
345+ for (ValidationGroup validationGroup : subList ) {
346+ copy .addAll (validationGroup .groups );
347+ }
348+ subList .clear ();
349+ dest .add (new ValidationGroup (false , false , copy ));
302350 }
303- return innerGroups ;
304351 }
305352
306353 public void addViolation (DefaultConstraintViolation <R > violation ) {
307- if (currentViolations != null ) {
308- currentViolations .add (violation );
309- }
354+ hasCurrentViolations = true ;
310355 overallViolations .add (violation );
311356 }
312357
@@ -394,4 +439,14 @@ interface ValidationCloseable extends AutoCloseable {
394439 record ValidationGroup (boolean isSequence , boolean isRedefinedDefaultGroupSequence ,
395440 List <Class <?>> groups ) {
396441 }
442+
443+ private record FindGroupContext (
444+ DefaultValidator defaultValidator ,
445+ Map <Class <?>, Class <?>> convertedGroups ,
446+ List <Class <?>> definedGroups
447+ ) {
448+ boolean isDefault () {
449+ return definedGroups == DEFAULT_GROUPS && convertedGroups .isEmpty ();
450+ }
451+ }
397452}
0 commit comments