/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.internal.engine.valuehandling;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintTypes;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectPropertyPaths;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.pathWith;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.internal.engine.path.NodeImpl;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class PropertyPathAndStringRepresentationTest {
private Validator validator;
@BeforeClass
public void setupValidator() {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
@Test
public void testMapInvalidKeyTypeArgument() {
DemographicStatistics statictics = new DemographicStatistics();
statictics.put( null, 2 );
Set<ConstraintViolation<DemographicStatistics>> constraintViolations = validator.validate( statictics );
assertCorrectPropertyPaths( constraintViolations, "inhabitantsPerAddress<K>[].<map key>" ); // the key is null, thus the '[]'
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "inhabitantsPerAddress" )
.containerElement( NodeImpl.MAP_KEY_NODE_NAME, true, null, null, Map.class, 0 )
);
}
@Test
public void testMapInvalidKeyCascadedValidation() {
Address invalidAddress = new Address( null, new City( "Lyon" ) );
DemographicStatistics statictics = new DemographicStatistics();
statictics.put( invalidAddress, 2 );
Set<ConstraintViolation<DemographicStatistics>> constraintViolations = validator.validate( statictics );
assertCorrectPropertyPaths( constraintViolations, "inhabitantsPerAddress<K>[null, Lyon].street" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "inhabitantsPerAddress" )
.property( "street", true, invalidAddress, null, Map.class, 0 )
);
invalidAddress = new Address( "rue Garibaldi", new City( "L" ) );
statictics = new DemographicStatistics();
statictics.put( invalidAddress, 2 );
constraintViolations = validator.validate( statictics );
assertCorrectPropertyPaths( constraintViolations, "inhabitantsPerAddress<K>[rue Garibaldi, L].city.name" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "inhabitantsPerAddress" )
.property( "city", true, invalidAddress, null, Map.class, 0 )
.property( "name" )
);
}
@Test
public void testMapInvalidKeyClassLevelConstraint() {
Address invalidAddress = new Address( "rue Garibaldi", new City( "Lyon" ), "75003" );
DemographicStatistics statictics = new DemographicStatistics();
statictics.put( invalidAddress, 2 );
Set<ConstraintViolation<DemographicStatistics>> constraintViolations = validator.validate( statictics );
assertCorrectPropertyPaths( constraintViolations, "inhabitantsPerAddress<K>[rue Garibaldi, Lyon]" );
assertCorrectConstraintTypes( constraintViolations, ValidLyonZipCode.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "inhabitantsPerAddress" )
.bean( true, invalidAddress, null, Map.class, 0 )
);
}
@Test
public void testMapInvalidValueTypeArgument() {
City city = new City( "Lyon" );
State state = new State();
state.put( city, null );
Set<ConstraintViolation<State>> constraintViolations = validator.validate( state );
assertCorrectPropertyPaths( constraintViolations, "addressesPerCity[Lyon].<map value>" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addressesPerCity" )
.containerElement( NodeImpl.MAP_VALUE_NODE_NAME, true, city, null, Map.class, 1 )
);
}
@Test
public void testMapInvalidValueCascadedValidation() {
City city = new City( "Lyon" );
Address invalidAddress = new Address( null, new City( "Lyon" ) );
State state = new State();
state.put( city, invalidAddress );
Set<ConstraintViolation<State>> constraintViolations = validator.validate( state );
assertCorrectPropertyPaths( constraintViolations, "addressesPerCity[Lyon].street" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addressesPerCity" )
.property( "street", true, city, null, Map.class, 1 )
);
invalidAddress = new Address( "rue Garibaldi", new City( "L" ) );
state = new State();
state.put( city, invalidAddress );
constraintViolations = validator.validate( state );
assertCorrectPropertyPaths( constraintViolations, "addressesPerCity[Lyon].city.name" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addressesPerCity" )
.property( "city", true, city, null, Map.class, 1 )
.property( "name" )
);
}
@Test
public void testMapInvalidValueClassLevelConstraint() {
City city = new City( "Lyon" );
Address invalidAddress = new Address( "rue Garibaldi", new City( "Lyon" ), "75003" );
State state = new State();
state.put( city, invalidAddress );
Set<ConstraintViolation<State>> constraintViolations = validator.validate( state );
assertCorrectPropertyPaths( constraintViolations, "addressesPerCity[Lyon]" );
assertCorrectConstraintTypes( constraintViolations, ValidLyonZipCode.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addressesPerCity" )
.bean( true, city, null, Map.class, 1 )
);
}
@Test
public void testListTypeArgument() {
Block block = new Block();
block.add( null );
Set<ConstraintViolation<Block>> constraintViolations = validator.validate( block );
assertCorrectPropertyPaths( constraintViolations, "addresses[0].<list element>" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addresses" )
.containerElement( NodeImpl.LIST_ELEMENT_NODE_NAME, true, null, 0, List.class, 0 )
);
}
@Test
public void testListInvalidCascadedValidation() {
Block block = new Block();
block.add( new Address( null, new City( "Lyon" ) ) );
Set<ConstraintViolation<Block>> constraintViolations = validator.validate( block );
assertCorrectPropertyPaths( constraintViolations, "addresses[0].street" );
assertCorrectConstraintTypes( constraintViolations, NotNull.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addresses" )
.property( "street", true, null, 0, List.class, 0 )
);
block = new Block();
block.add( new Address( "rue Garibaldi", new City( "L" ) ) );
constraintViolations = validator.validate( block );
assertCorrectPropertyPaths( constraintViolations, "addresses[0].city.name" );
assertCorrectConstraintTypes( constraintViolations, Size.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addresses" )
.property( "city", true, null, 0, List.class, 0 )
.property( "name" )
);
}
@Test
public void testListKeyClassLevelConstraint() {
Block block = new Block();
block.add( new Address( "rue Garibaldi", new City( "Lyon" ), "75003" ) );
Set<ConstraintViolation<Block>> constraintViolations = validator.validate( block );
assertCorrectPropertyPaths( constraintViolations, "addresses[0]" );
assertCorrectConstraintTypes( constraintViolations, ValidLyonZipCode.class );
assertThat( constraintViolations ).containsOnlyPaths(
pathWith()
.property( "addresses" )
.bean( true, null, 0, List.class, 0 )
);
}
private static class DemographicStatistics {
private Map<@NotNull @Valid Address, @NotNull Integer> inhabitantsPerAddress = new HashMap<>();
public void put(Address address, Integer count) {
inhabitantsPerAddress.put( address, count );
}
}
private static class Block {
private List<@NotNull @Valid Address> addresses = new ArrayList<>();
public void add(Address address) {
addresses.add( address );
}
}
private static class State {
private Map<City, @NotNull @Valid Address> addressesPerCity = new HashMap<>();
public void put(City city, Address address) {
addressesPerCity.put( city, address );
}
}
@ValidLyonZipCode
private static class Address {
@NotNull
private String street;
@Valid
private City city;
private String zipCode;
public Address(String street, City city) {
this.street = street;
this.city = city;
}
public Address(String street, City city, String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
@Override
public String toString() {
return street + ", " + city;
}
}
private static class City {
@Size(min = 3)
private String name;
public City(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = { ValidLyonZipCodeValidator.class })
@Documented
public @interface ValidLyonZipCode {
String message() default "{org.hibernate.validator.test.internal.engine.valuehandling.ValidLyonZipCode.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public static class ValidLyonZipCodeValidator implements ConstraintValidator<ValidLyonZipCode, Address> {
@Override
public boolean isValid(Address address, ConstraintValidatorContext context) {
if ( address == null || address.zipCode == null || address.city == null || !"Lyon".equals( address.city.name ) ) {
return true;
}
return address.zipCode.length() == 5 && address.zipCode.startsWith( "6900" );
}
}
}