Skip to content

Commit 7cbe9ac

Browse files
authored
Merge pull request eugenp#7949 from martinvw/feature/BAEL-3306-validations-for-enum-types
[BAEL-3306] Validations for Enum types
2 parents 05d2f24 + bf34353 commit 7cbe9ac

15 files changed

+558
-0
lines changed

javaxval/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ This module contains articles about Bean Validation.
99
- [Difference Between @NotNull, @NotEmpty, and @NotBlank Constraints in Bean Validation](https://www.baeldung.com/java-bean-validation-not-null-empty-blank)
1010
- [Javax BigDecimal Validation](https://www.baeldung.com/javax-bigdecimal-validation)
1111
- [Grouping Javax Validation Constraints](https://www.baeldung.com/javax-validation-groups)
12+
- [Javax Validations for Enums](https://www.baeldung.com/javax-validations-for-enums/)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.baeldung.javaxval.enums;
2+
3+
import java.util.Arrays;
4+
5+
import javax.validation.ConstraintValidator;
6+
import javax.validation.ConstraintValidatorContext;
7+
8+
import org.baeldung.javaxval.enums.constraints.CustomerTypeSubset;
9+
import org.baeldung.javaxval.enums.demo.CustomerType;
10+
11+
public class CustomerTypeSubSetValidator implements ConstraintValidator<CustomerTypeSubset, CustomerType> {
12+
private CustomerType[] subset;
13+
14+
@Override
15+
public void initialize(CustomerTypeSubset constraint) {
16+
this.subset = constraint.anyOf();
17+
}
18+
19+
@Override
20+
public boolean isValid(CustomerType value, ConstraintValidatorContext context) {
21+
return value == null || Arrays.asList(subset)
22+
.contains(value);
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.baeldung.javaxval.enums;
2+
3+
import java.util.regex.Matcher;
4+
import java.util.regex.Pattern;
5+
import java.util.regex.PatternSyntaxException;
6+
7+
import javax.validation.ConstraintValidator;
8+
import javax.validation.ConstraintValidatorContext;
9+
10+
import org.baeldung.javaxval.enums.constraints.EnumNamePattern;
11+
12+
public class EnumNamePatternValidator implements ConstraintValidator<EnumNamePattern, Enum<?>> {
13+
private Pattern pattern;
14+
15+
@Override
16+
public void initialize(EnumNamePattern constraintAnnotation) {
17+
try {
18+
pattern = Pattern.compile(constraintAnnotation.regexp());
19+
} catch (PatternSyntaxException e) {
20+
throw new IllegalArgumentException("Given regex is invalid", e);
21+
}
22+
}
23+
24+
@Override
25+
public boolean isValid(Enum<?> value, ConstraintValidatorContext context) {
26+
if (value == null) {
27+
return true;
28+
}
29+
30+
Matcher m = pattern.matcher(value.name());
31+
return m.matches();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.baeldung.javaxval.enums;
2+
3+
import java.lang.annotation.Annotation;
4+
import java.util.Arrays;
5+
6+
import javax.validation.ConstraintValidator;
7+
import javax.validation.ConstraintValidatorContext;
8+
9+
public abstract class EnumSubSetValidator<T extends Annotation, U> implements ConstraintValidator<T, U> {
10+
private U[] subset;
11+
12+
protected void initialize(U[] subset) {
13+
this.subset = subset;
14+
}
15+
16+
@Override
17+
public boolean isValid(U value, ConstraintValidatorContext context) {
18+
if (value == null) {
19+
return true;
20+
}
21+
22+
return Arrays.asList(subset)
23+
.contains(value);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.baeldung.javaxval.enums;
2+
3+
import org.baeldung.javaxval.enums.constraints.CustomerTypeSubset;
4+
import org.baeldung.javaxval.enums.demo.CustomerType;
5+
6+
public class InheritedCustomerTypeSubSetValidator extends EnumSubSetValidator<CustomerTypeSubset, CustomerType> {
7+
@Override
8+
public void initialize(CustomerTypeSubset constraint) {
9+
super.initialize(constraint.anyOf());
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.baeldung.javaxval.enums;
2+
3+
import java.util.List;
4+
import java.util.stream.Collectors;
5+
import java.util.stream.Stream;
6+
7+
import javax.validation.ConstraintValidator;
8+
import javax.validation.ConstraintValidatorContext;
9+
10+
import org.baeldung.javaxval.enums.constraints.ValueOfEnum;
11+
12+
public class ValueOfEnumValidator implements ConstraintValidator<ValueOfEnum, CharSequence> {
13+
private List<String> acceptedValues;
14+
15+
@Override
16+
public void initialize(ValueOfEnum annotation) {
17+
acceptedValues = Stream.of(annotation.enumClass()
18+
.getEnumConstants())
19+
.map(Enum::name)
20+
.collect(Collectors.toList());
21+
}
22+
23+
@Override
24+
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
25+
if (value == null) {
26+
return true;
27+
}
28+
29+
return acceptedValues.contains(value.toString());
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.baeldung.javaxval.enums.constraints;
2+
3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4+
import static java.lang.annotation.ElementType.CONSTRUCTOR;
5+
import static java.lang.annotation.ElementType.FIELD;
6+
import static java.lang.annotation.ElementType.METHOD;
7+
import static java.lang.annotation.ElementType.PARAMETER;
8+
import static java.lang.annotation.ElementType.TYPE_USE;
9+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10+
11+
import java.lang.annotation.Documented;
12+
import java.lang.annotation.Retention;
13+
import java.lang.annotation.Target;
14+
15+
import javax.validation.Constraint;
16+
import javax.validation.Payload;
17+
18+
import org.baeldung.javaxval.enums.CustomerTypeSubSetValidator;
19+
import org.baeldung.javaxval.enums.demo.CustomerType;
20+
21+
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
22+
@Retention(RUNTIME)
23+
@Documented
24+
@Constraint(validatedBy = CustomerTypeSubSetValidator.class)
25+
public @interface CustomerTypeSubset {
26+
/**
27+
* @return subset of CustomerType enum
28+
*/
29+
CustomerType[] anyOf();
30+
31+
/**
32+
* @return the error message template
33+
*/
34+
String message() default "must be any of {anyOf}";
35+
36+
/**
37+
* @return the groups the constraint belongs to
38+
*/
39+
Class<?>[] groups() default {};
40+
41+
/**
42+
* @return the payload associated to the constraint
43+
*/
44+
Class<? extends Payload>[] payload() default {};
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.baeldung.javaxval.enums.constraints;
2+
3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4+
import static java.lang.annotation.ElementType.CONSTRUCTOR;
5+
import static java.lang.annotation.ElementType.FIELD;
6+
import static java.lang.annotation.ElementType.METHOD;
7+
import static java.lang.annotation.ElementType.PARAMETER;
8+
import static java.lang.annotation.ElementType.TYPE_USE;
9+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10+
11+
import java.lang.annotation.Documented;
12+
import java.lang.annotation.Retention;
13+
import java.lang.annotation.Target;
14+
15+
import javax.validation.Constraint;
16+
import javax.validation.Payload;
17+
18+
import org.baeldung.javaxval.enums.EnumNamePatternValidator;
19+
20+
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
21+
@Retention(RUNTIME)
22+
@Documented
23+
@Constraint(validatedBy = EnumNamePatternValidator.class)
24+
public @interface EnumNamePattern {
25+
26+
/**
27+
* @return the regular expression to match
28+
*/
29+
String regexp();
30+
31+
/**
32+
* @return the error message template
33+
*/
34+
String message() default "must match \"{regexp}\"";
35+
36+
/**
37+
* @return the groups the constraint belongs to
38+
*/
39+
Class<?>[] groups() default {};
40+
41+
/**
42+
* @return the payload associated to the constraint
43+
*/
44+
Class<? extends Payload>[] payload() default {};
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package org.baeldung.javaxval.enums.constraints;
2+
3+
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
4+
import static java.lang.annotation.ElementType.CONSTRUCTOR;
5+
import static java.lang.annotation.ElementType.FIELD;
6+
import static java.lang.annotation.ElementType.METHOD;
7+
import static java.lang.annotation.ElementType.PARAMETER;
8+
import static java.lang.annotation.ElementType.TYPE_USE;
9+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
10+
11+
import java.lang.annotation.Documented;
12+
import java.lang.annotation.Retention;
13+
import java.lang.annotation.Target;
14+
15+
import javax.validation.Constraint;
16+
import javax.validation.Payload;
17+
18+
import org.baeldung.javaxval.enums.ValueOfEnumValidator;
19+
20+
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
21+
@Retention(RUNTIME)
22+
@Documented
23+
@Constraint(validatedBy = ValueOfEnumValidator.class)
24+
public @interface ValueOfEnum {
25+
/**
26+
* @return class containing enum values to which this String should match
27+
*/
28+
Class<? extends Enum<?>> enumClass();
29+
30+
/**
31+
* @return the error message template
32+
*/
33+
String message() default "must be any of enum {enumClass}";
34+
35+
/**
36+
* @return the groups the constraint belongs to
37+
*/
38+
Class<?>[] groups() default {};
39+
40+
/**
41+
* @return the payload associated to the constraint
42+
*/
43+
Class<? extends Payload>[] payload() default {};
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package org.baeldung.javaxval.enums.demo;
2+
3+
import javax.validation.constraints.NotNull;
4+
5+
import org.baeldung.javaxval.enums.constraints.CustomerTypeSubset;
6+
import org.baeldung.javaxval.enums.constraints.EnumNamePattern;
7+
import org.baeldung.javaxval.enums.constraints.ValueOfEnum;
8+
9+
public class Customer {
10+
@ValueOfEnum(enumClass = CustomerType.class)
11+
private String customerTypeString;
12+
13+
@NotNull
14+
@CustomerTypeSubset(anyOf = { CustomerType.NEW, CustomerType.OLD })
15+
private CustomerType customerTypeOfSubset;
16+
17+
@EnumNamePattern(regexp = "NEW|DEFAULT")
18+
private CustomerType customerTypeMatchesPattern;
19+
20+
public Customer() {
21+
}
22+
23+
public Customer(String customerTypeString, CustomerType customerTypeOfSubset, CustomerType customerTypeMatchesPattern) {
24+
this.customerTypeString = customerTypeString;
25+
this.customerTypeOfSubset = customerTypeOfSubset;
26+
this.customerTypeMatchesPattern = customerTypeMatchesPattern;
27+
}
28+
29+
public String getCustomerTypeString() {
30+
return customerTypeString;
31+
}
32+
33+
public void setCustomerTypeString(String customerTypeString) {
34+
this.customerTypeString = customerTypeString;
35+
}
36+
37+
public CustomerType getCustomerTypeOfSubset() {
38+
return customerTypeOfSubset;
39+
}
40+
41+
public void setCustomerTypeOfSubset(CustomerType customerTypeOfSubset) {
42+
this.customerTypeOfSubset = customerTypeOfSubset;
43+
}
44+
45+
public CustomerType getCustomerTypeMatchesPattern() {
46+
return customerTypeMatchesPattern;
47+
}
48+
49+
public void setCustomerTypeMatchesPattern(CustomerType customerTypeMatchesPattern) {
50+
this.customerTypeMatchesPattern = customerTypeMatchesPattern;
51+
}
52+
53+
public static class Builder {
54+
private String customerTypeString;
55+
private CustomerType customerTypeOfSubset = CustomerType.NEW;
56+
private CustomerType customerTypeMatchesPattern;
57+
58+
public Builder withCustomerTypeString(String customerTypeString) {
59+
this.customerTypeString = customerTypeString;
60+
return this;
61+
}
62+
63+
public Builder withCustomerTypeOfSubset(CustomerType customerTypeOfSubset) {
64+
this.customerTypeOfSubset = customerTypeOfSubset;
65+
return this;
66+
}
67+
68+
public Builder withCustomerTypeMatchesPattern(CustomerType customerTypeMatchesPattern) {
69+
this.customerTypeMatchesPattern = customerTypeMatchesPattern;
70+
return this;
71+
}
72+
73+
public Customer build() {
74+
return new Customer(customerTypeString, customerTypeOfSubset, customerTypeMatchesPattern);
75+
}
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package org.baeldung.javaxval.enums.demo;
2+
3+
public enum CustomerType {
4+
NEW, OLD, DEFAULT
5+
}

0 commit comments

Comments
 (0)