Skip to content

Commit 9779b8b

Browse files
committed
SpringValidatorAdapter skips value retrieval for Set field without index
Issue: SPR-16177 (cherry picked from commit 3091fee)
1 parent 2c8a6bb commit 9779b8b

File tree

2 files changed

+185
-2
lines changed

2 files changed

+185
-2
lines changed

spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ protected MessageSourceResolvable getResolvableField(String objectName, String f
263263
*/
264264
protected Object getRejectedValue(String field, ConstraintViolation<Object> violation, BindingResult bindingResult) {
265265
Object invalidValue = violation.getInvalidValue();
266-
if (!"".equals(field) && (invalidValue == violation.getLeafBean() ||
267-
(!field.contains("[]") && (field.contains("[") || field.contains("."))))) {
266+
if (!"".equals(field) && !field.contains("[]") &&
267+
(invalidValue == violation.getLeafBean() || field.contains("[") || field.contains("."))) {
268268
// Possibly a bean constraint with property path: retrieve the actual property value.
269269
// However, explicitly avoid this for "address[]" style paths that we can't handle.
270270
invalidValue = bindingResult.getRawFieldValue(field);

spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,22 @@
2121
import java.lang.annotation.Repeatable;
2222
import java.lang.annotation.Retention;
2323
import java.lang.annotation.Target;
24+
import java.lang.reflect.Field;
25+
import java.util.ArrayList;
26+
import java.util.Arrays;
27+
import java.util.LinkedHashSet;
28+
import java.util.LinkedList;
29+
import java.util.List;
2430
import java.util.Locale;
31+
import java.util.Set;
2532
import javax.validation.Constraint;
2633
import javax.validation.ConstraintValidator;
2734
import javax.validation.ConstraintValidatorContext;
2835
import javax.validation.Payload;
36+
import javax.validation.Valid;
2937
import javax.validation.Validation;
3038
import javax.validation.Validator;
39+
import javax.validation.constraints.NotNull;
3140
import javax.validation.constraints.Pattern;
3241
import javax.validation.constraints.Size;
3342

@@ -138,6 +147,43 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMe
138147
is("Email required"));
139148
}
140149

150+
@Test // SPR-16177
151+
public void testWithList() {
152+
Parent parent = new Parent();
153+
parent.setName("Parent whit list");
154+
parent.getChildList().addAll(createChildren(parent));
155+
156+
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
157+
validatorAdapter.validate(parent, errors);
158+
159+
assertTrue(errors.getErrorCount() > 0);
160+
}
161+
162+
@Test // SPR-16177
163+
public void testWithSet() {
164+
Parent parent = new Parent();
165+
parent.setName("Parent whith set");
166+
parent.getChildSet().addAll(createChildren(parent));
167+
168+
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");
169+
validatorAdapter.validate(parent, errors);
170+
171+
assertTrue(errors.getErrorCount() > 0);
172+
}
173+
174+
private List<Child> createChildren(Parent parent) {
175+
Child child1 = new Child();
176+
child1.setName("Child1");
177+
child1.setAge(null);
178+
child1.setParent(parent);
179+
180+
Child child2 = new Child();
181+
child2.setName(null);
182+
child2.setAge(17);
183+
child2.setParent(parent);
184+
185+
return Arrays.asList(child1, child2);
186+
}
141187

142188
@Same(field = "password", comparingField = "confirmPassword")
143189
@Same(field = "email", comparingField = "confirmEmail")
@@ -255,4 +301,141 @@ public boolean isValid(Object value, ConstraintValidatorContext context) {
255301
}
256302
}
257303

304+
305+
public static class Parent {
306+
307+
private Integer id;
308+
309+
@NotNull
310+
private String name;
311+
312+
@Valid
313+
private Set<Child> childSet = new LinkedHashSet<>();
314+
315+
@Valid
316+
private List<Child> childList = new LinkedList<>();
317+
318+
public Integer getId() {
319+
return id;
320+
}
321+
322+
public void setId(Integer id) {
323+
this.id = id;
324+
}
325+
326+
public String getName() {
327+
return name;
328+
}
329+
330+
public void setName(String name) {
331+
this.name = name;
332+
}
333+
334+
public Set<Child> getChildSet() {
335+
return childSet;
336+
}
337+
338+
public void setChildSet(Set<Child> childSet) {
339+
this.childSet = childSet;
340+
}
341+
342+
public List<Child> getChildList() {
343+
return childList;
344+
}
345+
346+
public void setChildList(List<Child> childList) {
347+
this.childList = childList;
348+
}
349+
}
350+
351+
352+
@AnythingValid
353+
public static class Child {
354+
355+
private Integer id;
356+
357+
@javax.validation.constraints.NotNull
358+
private String name;
359+
360+
@javax.validation.constraints.NotNull
361+
private Integer age;
362+
363+
@javax.validation.constraints.NotNull
364+
private Parent parent;
365+
366+
public Integer getId() {
367+
return id;
368+
}
369+
370+
public void setId(Integer id) {
371+
this.id = id;
372+
}
373+
374+
public String getName() {
375+
return name;
376+
}
377+
378+
public void setName(String name) {
379+
this.name = name;
380+
}
381+
382+
public Integer getAge() {
383+
return age;
384+
}
385+
386+
public void setAge(Integer age) {
387+
this.age = age;
388+
}
389+
390+
public Parent getParent() {
391+
return parent;
392+
}
393+
394+
public void setParent(Parent parent) {
395+
this.parent = parent;
396+
}
397+
}
398+
399+
400+
@Constraint(validatedBy = AnythingValidator.class)
401+
@Retention(RUNTIME)
402+
public @interface AnythingValid {
403+
404+
String message() default "{AnythingValid.message}";
405+
406+
Class<?>[] groups() default {};
407+
408+
Class<? extends Payload>[] payload() default {};
409+
}
410+
411+
412+
public static class AnythingValidator implements ConstraintValidator<AnythingValid, Object> {
413+
414+
private static final String ID = "id";
415+
416+
@Override
417+
public void initialize(AnythingValid constraintAnnotation) {
418+
}
419+
420+
@Override
421+
public boolean isValid(Object value, ConstraintValidatorContext context) {
422+
List<Field> fieldsErros = new ArrayList<>();
423+
Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {
424+
f.setAccessible(true);
425+
try {
426+
if (!f.getName().equals(ID) && f.get(value) == null) {
427+
fieldsErros.add(f);
428+
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())
429+
.addNode(f.getName())
430+
.addConstraintViolation();
431+
}
432+
} catch (IllegalAccessException ex) {
433+
throw new IllegalStateException(ex);
434+
}
435+
436+
});
437+
return fieldsErros.isEmpty();
438+
}
439+
}
440+
258441
}

0 commit comments

Comments
 (0)