| 
21 | 21 | import java.lang.annotation.Repeatable;  | 
22 | 22 | import java.lang.annotation.Retention;  | 
23 | 23 | import java.lang.annotation.Target;  | 
 | 24 | +import java.lang.reflect.Field;  | 
 | 25 | +import java.util.ArrayList;  | 
24 | 26 | import java.util.Arrays;  | 
25 | 27 | import java.util.HashMap;  | 
 | 28 | +import java.util.LinkedHashSet;  | 
 | 29 | +import java.util.LinkedList;  | 
26 | 30 | import java.util.List;  | 
27 | 31 | import java.util.Locale;  | 
28 | 32 | import java.util.Map;  | 
 | 33 | +import java.util.Set;  | 
29 | 34 | import javax.validation.Constraint;  | 
30 | 35 | import javax.validation.ConstraintValidator;  | 
31 | 36 | import javax.validation.ConstraintValidatorContext;  | 
@@ -148,6 +153,44 @@ public void testApplyMessageSourceResolvableToStringArgumentValueWithAlwaysUseMe  | 
148 | 153 | 				is("Email required"));  | 
149 | 154 | 	}  | 
150 | 155 | 
 
  | 
 | 156 | +	@Test  // SPR-16177  | 
 | 157 | +	public void testWithList() {  | 
 | 158 | +		Parent parent = new Parent();  | 
 | 159 | +		parent.setName("Parent whit list");  | 
 | 160 | +		parent.getChildList().addAll(createChildren(parent));  | 
 | 161 | + | 
 | 162 | +		BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");  | 
 | 163 | +		validatorAdapter.validate(parent, errors);  | 
 | 164 | + | 
 | 165 | +		assertTrue(errors.getErrorCount() > 0);  | 
 | 166 | +	}  | 
 | 167 | + | 
 | 168 | +	@Test  // SPR-16177  | 
 | 169 | +	public void testWithSet() {  | 
 | 170 | +		Parent parent = new Parent();  | 
 | 171 | +		parent.setName("Parent whith set");  | 
 | 172 | +		parent.getChildSet().addAll(createChildren(parent));  | 
 | 173 | + | 
 | 174 | +		BeanPropertyBindingResult errors = new BeanPropertyBindingResult(parent, "parent");  | 
 | 175 | +		validatorAdapter.validate(parent, errors);  | 
 | 176 | + | 
 | 177 | +		assertTrue(errors.getErrorCount() > 0);  | 
 | 178 | +	}  | 
 | 179 | + | 
 | 180 | +	private List<Child> createChildren(Parent parent) {  | 
 | 181 | +		Child child1 = new Child();  | 
 | 182 | +		child1.setName("Child1");  | 
 | 183 | +		child1.setAge(null);  | 
 | 184 | +		child1.setParent(parent);  | 
 | 185 | + | 
 | 186 | +		Child child2 = new Child();  | 
 | 187 | +		child2.setName(null);  | 
 | 188 | +		child2.setAge(17);  | 
 | 189 | +		child2.setParent(parent);  | 
 | 190 | + | 
 | 191 | +		return Arrays.asList(child1, child2);  | 
 | 192 | +	}  | 
 | 193 | + | 
151 | 194 | 	@Test  // SPR-15839  | 
152 | 195 | 	public void testListElementConstraint() {  | 
153 | 196 | 		BeanWithListElementConstraint bean = new BeanWithListElementConstraint();  | 
@@ -308,6 +351,143 @@ public boolean isValid(Object value, ConstraintValidatorContext context) {  | 
308 | 351 | 	}  | 
309 | 352 | 
 
  | 
310 | 353 | 
 
  | 
 | 354 | +	public static class Parent {  | 
 | 355 | + | 
 | 356 | +		private Integer id;  | 
 | 357 | + | 
 | 358 | +		@NotNull  | 
 | 359 | +		private String name;  | 
 | 360 | + | 
 | 361 | +		@Valid  | 
 | 362 | +		private Set<Child> childSet = new LinkedHashSet<>();  | 
 | 363 | + | 
 | 364 | +		@Valid  | 
 | 365 | +		private List<Child> childList = new LinkedList<>();  | 
 | 366 | + | 
 | 367 | +		public Integer getId() {  | 
 | 368 | +			return id;  | 
 | 369 | +		}  | 
 | 370 | + | 
 | 371 | +		public void setId(Integer id) {  | 
 | 372 | +			this.id = id;  | 
 | 373 | +		}  | 
 | 374 | + | 
 | 375 | +		public String getName() {  | 
 | 376 | +			return name;  | 
 | 377 | +		}  | 
 | 378 | + | 
 | 379 | +		public void setName(String name) {  | 
 | 380 | +			this.name = name;  | 
 | 381 | +		}  | 
 | 382 | + | 
 | 383 | +		public Set<Child> getChildSet() {  | 
 | 384 | +			return childSet;  | 
 | 385 | +		}  | 
 | 386 | + | 
 | 387 | +		public void setChildSet(Set<Child> childSet) {  | 
 | 388 | +			this.childSet = childSet;  | 
 | 389 | +		}  | 
 | 390 | + | 
 | 391 | +		public List<Child> getChildList() {  | 
 | 392 | +			return childList;  | 
 | 393 | +		}  | 
 | 394 | + | 
 | 395 | +		public void setChildList(List<Child> childList) {  | 
 | 396 | +			this.childList = childList;  | 
 | 397 | +		}  | 
 | 398 | +	}  | 
 | 399 | + | 
 | 400 | + | 
 | 401 | +	@AnythingValid  | 
 | 402 | +	public static class Child {  | 
 | 403 | + | 
 | 404 | +		private Integer id;  | 
 | 405 | + | 
 | 406 | +		@javax.validation.constraints.NotNull  | 
 | 407 | +		private String name;  | 
 | 408 | + | 
 | 409 | +		@javax.validation.constraints.NotNull  | 
 | 410 | +		private Integer age;  | 
 | 411 | + | 
 | 412 | +		@javax.validation.constraints.NotNull  | 
 | 413 | +		private Parent parent;  | 
 | 414 | + | 
 | 415 | +		public Integer getId() {  | 
 | 416 | +			return id;  | 
 | 417 | +		}  | 
 | 418 | + | 
 | 419 | +		public void setId(Integer id) {  | 
 | 420 | +			this.id = id;  | 
 | 421 | +		}  | 
 | 422 | + | 
 | 423 | +		public String getName() {  | 
 | 424 | +			return name;  | 
 | 425 | +		}  | 
 | 426 | + | 
 | 427 | +		public void setName(String name) {  | 
 | 428 | +			this.name = name;  | 
 | 429 | +		}  | 
 | 430 | + | 
 | 431 | +		public Integer getAge() {  | 
 | 432 | +			return age;  | 
 | 433 | +		}  | 
 | 434 | + | 
 | 435 | +		public void setAge(Integer age) {  | 
 | 436 | +			this.age = age;  | 
 | 437 | +		}  | 
 | 438 | + | 
 | 439 | +		public Parent getParent() {  | 
 | 440 | +			return parent;  | 
 | 441 | +		}  | 
 | 442 | + | 
 | 443 | +		public void setParent(Parent parent) {  | 
 | 444 | +			this.parent = parent;  | 
 | 445 | +		}  | 
 | 446 | +	}  | 
 | 447 | + | 
 | 448 | + | 
 | 449 | +	@Constraint(validatedBy = AnythingValidator.class)  | 
 | 450 | +	@Retention(RUNTIME)  | 
 | 451 | +	public @interface AnythingValid {  | 
 | 452 | + | 
 | 453 | +		String message() default "{AnythingValid.message}";  | 
 | 454 | + | 
 | 455 | +		Class<?>[] groups() default {};  | 
 | 456 | + | 
 | 457 | +		Class<? extends Payload>[] payload() default {};  | 
 | 458 | +	}  | 
 | 459 | + | 
 | 460 | + | 
 | 461 | +	public static class AnythingValidator implements ConstraintValidator<AnythingValid, Object> {  | 
 | 462 | + | 
 | 463 | +		private static final String ID = "id";  | 
 | 464 | + | 
 | 465 | +		@Override  | 
 | 466 | +		public void initialize(AnythingValid constraintAnnotation) {  | 
 | 467 | +		}  | 
 | 468 | + | 
 | 469 | +		@Override  | 
 | 470 | +		public boolean isValid(Object value, ConstraintValidatorContext context) {  | 
 | 471 | +			List<Field> fieldsErros = new ArrayList<>();  | 
 | 472 | +			Arrays.asList(value.getClass().getDeclaredFields()).forEach(f -> {  | 
 | 473 | +				f.setAccessible(true);  | 
 | 474 | +				try {  | 
 | 475 | +					if (!f.getName().equals(ID) && f.get(value) == null) {  | 
 | 476 | +						fieldsErros.add(f);  | 
 | 477 | +						context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate())  | 
 | 478 | +								.addPropertyNode(f.getName())  | 
 | 479 | +								.addConstraintViolation();  | 
 | 480 | +					}  | 
 | 481 | +				} catch (IllegalAccessException ex) {  | 
 | 482 | +					throw new IllegalStateException(ex);  | 
 | 483 | +				}  | 
 | 484 | + | 
 | 485 | +			});  | 
 | 486 | +			return fieldsErros.isEmpty();  | 
 | 487 | +		}  | 
 | 488 | +	}  | 
 | 489 | + | 
 | 490 | + | 
311 | 491 | 	public class BeanWithListElementConstraint {  | 
312 | 492 | 
 
  | 
313 | 493 | 		@Valid  | 
 | 
0 commit comments