Skip to content

Commit 3787839

Browse files
authored
Merge pull request #3 from SentryMan/andThen
slightly refactor annotation validator
2 parents 7ee83a9 + 500bf09 commit 3787839

File tree

11 files changed

+178
-65
lines changed

11 files changed

+178
-65
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.avaje.validation;
2+
3+
import java.lang.reflect.Type;
4+
import java.util.Map;
5+
import java.util.Objects;
6+
import java.util.Set;
7+
8+
import io.avaje.validation.core.MessageInterpolator;
9+
10+
public interface AnnotationValidationAdapter<T> {
11+
12+
void validate(T type, Set<ConstraintViolation> violations);
13+
14+
default AnnotationValidationAdapter<T> init(Map<String, String> annotationValueMap) {
15+
return this;
16+
}
17+
18+
default AnnotationValidationAdapter<T> andThen(AnnotationValidationAdapter<? super T> after) {
19+
Objects.requireNonNull(after);
20+
return (t, v) -> {
21+
validate(t, v);
22+
after.validate(t, v);
23+
};
24+
}
25+
/** Factory for creating a ValidationAdapter. */
26+
public interface Factory {
27+
28+
/**
29+
* Create and return a ValidationAdapter given the type and annotations or return null.
30+
*
31+
* <p>Returning null means that the adapter could be created by another factory.
32+
*/
33+
AnnotationValidationAdapter<?> create(
34+
Type annotationType, Validator context, MessageInterpolator interpolator);
35+
}
36+
}

validator/src/main/java/io/avaje/validation/ValidationAdapter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
public interface ValidationAdapter<T> {
2222

23-
/** */
2423
void validate(T value, Set<ConstraintViolation> violations);
2524

2625
/** Factory for creating a ValidationAdapter. */

validator/src/main/java/io/avaje/validation/Validator.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import java.util.ServiceLoader;
88
import java.util.Set;
99

10-
import io.avaje.validation.core.AnnotationValidationAdapter;
1110
import io.avaje.validation.core.DefaultBootstrap;
1211
import io.avaje.validation.spi.Bootstrap;
1312

validator/src/main/java/io/avaje/validation/core/AnnotationValidationAdapter.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

validator/src/main/java/io/avaje/validation/core/CoreAdapterBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323
import java.util.concurrent.ConcurrentHashMap;
2424

25+
import io.avaje.validation.AnnotationValidationAdapter;
2526
import io.avaje.validation.ValidationAdapter;
2627

2728
/** Builds and caches the ValidationAdapter adapters for DValidator. */

validator/src/main/java/io/avaje/validation/core/DValidator.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
import java.util.Set;
1717
import java.util.concurrent.ConcurrentHashMap;
1818

19+
import io.avaje.validation.AnnotationValidationAdapter;
20+
import io.avaje.validation.AnnotationValidationAdapter.Factory;
21+
import io.avaje.validation.ConstraintViolation;
1922
import io.avaje.validation.ValidationAdapter;
2023
import io.avaje.validation.ValidationType;
2124
import io.avaje.validation.Validator;
2225
import io.avaje.validation.ValidatorComponent;
23-
import io.avaje.validation.core.AnnotationValidationAdapter.Factory;
24-
import io.avaje.validation.ConstraintViolation;
2526

2627
/** Default implementation of Validator. */
2728
final class DValidator implements Validator {
@@ -83,11 +84,9 @@ public <T> ValidationAdapter<T> adapter(Class<T> cls) {
8384
}
8485

8586
@Override
86-
public <T> AnnotationValidationAdapter<T> annotationAdapter(Class<? extends Annotation> cls) {
87-
final AnnotationValidationAdapter<T> result = builder.annotationAdapter(cls);
88-
if (result != null) {
89-
return result;
90-
}
87+
public <T> AnnotationValidationAdapter<T> annotationAdapter(
88+
Class<? extends Annotation> cls) {
89+
9190
return builder.annotationAdapter(cls);
9291
}
9392

validator/src/main/java/io/avaje/validation/core/JakartaTypeAdapters.java

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,88 @@
1515
*/
1616
package io.avaje.validation.core;
1717

18+
import java.time.LocalDate;
19+
import java.time.LocalTime;
20+
import java.time.temporal.TemporalAccessor;
1821
import java.util.Map;
22+
import java.util.Set;
1923

24+
import io.avaje.validation.AnnotationValidationAdapter;
2025
import io.avaje.validation.ConstraintViolation;
2126
import jakarta.validation.constraints.AssertTrue;
27+
import jakarta.validation.constraints.NotBlank;
28+
import jakarta.validation.constraints.Past;
29+
2230
final class JakartaTypeAdapters {
2331

2432
@SuppressWarnings({"unchecked", "rawtypes"})
2533
static final AnnotationValidationAdapter.Factory FACTORY =
26-
(type, jsonb, interpolator) -> {
27-
if (type == AssertTrue.class) return new AssertTrueAdapter(interpolator);
34+
(annotationType, validator, interpolator) -> {
35+
if (annotationType == AssertTrue.class) return new AssertTrueAdapter(interpolator);
36+
if (annotationType == NotBlank.class) return new NotBlankAdapter(interpolator);
37+
if (annotationType == Past.class) return new PastAdapter(interpolator);
2838
return null;
2939
};
3040

41+
private static final class PastAdapter implements AnnotationValidationAdapter<TemporalAccessor> {
42+
43+
private String message;
44+
private final MessageInterpolator interpolator;
45+
46+
public PastAdapter(MessageInterpolator interpolator) {
47+
this.interpolator = interpolator;
48+
}
49+
50+
@Override
51+
public AnnotationValidationAdapter<TemporalAccessor> init(
52+
Map<String, String> annotationValueMap) {
53+
message = interpolator.interpolate(annotationValueMap.get("message"));
54+
return this;
55+
}
56+
57+
@Override
58+
public void validate(TemporalAccessor temporalAccessor, Set<ConstraintViolation> violations) {
59+
60+
if (temporalAccessor == null) {
61+
violations.add(new ConstraintViolation(message));
62+
return;
63+
}
64+
if (temporalAccessor instanceof LocalDate) {
65+
if (LocalDate.from(temporalAccessor).isAfter(LocalDate.now())) {
66+
violations.add(new ConstraintViolation(message));
67+
}
68+
} else if (temporalAccessor instanceof LocalTime) {
69+
final LocalTime localTime = (LocalTime) temporalAccessor;
70+
// handle LocalTime
71+
72+
// TODO do the rest of them
73+
}
74+
}
75+
}
76+
77+
private static final class NotBlankAdapter implements AnnotationValidationAdapter<String> {
78+
79+
private String message;
80+
private final MessageInterpolator interpolator;
81+
82+
public NotBlankAdapter(MessageInterpolator interpolator) {
83+
this.interpolator = interpolator;
84+
}
85+
86+
@Override
87+
public AnnotationValidationAdapter<String> init(Map<String, String> annotationValueMap) {
88+
message = interpolator.interpolate(annotationValueMap.get("message"));
89+
return this;
90+
}
91+
92+
@Override
93+
public void validate(String str, Set<ConstraintViolation> violations) {
94+
if (str == null || str.isBlank()) {
95+
violations.add(new ConstraintViolation(message));
96+
}
97+
}
98+
}
99+
31100
private static final class AssertTrueAdapter implements AnnotationValidationAdapter<Boolean> {
32101

33102
private String message;
@@ -37,17 +106,17 @@ public AssertTrueAdapter(MessageInterpolator interpolator) {
37106
this.interpolator = interpolator;
38107
}
39108

40-
@Override
109+
@Override
41110
public AnnotationValidationAdapter<Boolean> init(Map<String, String> annotationValueMap) {
42111
message = interpolator.interpolate(annotationValueMap.get("message"));
43112
return this;
44113
}
45114

46115
@Override
47-
public ConstraintViolation validate(Boolean type) {
48-
if (type) return null;
49-
50-
return new ConstraintViolation(message);
116+
public void validate(Boolean type, Set<ConstraintViolation> violations) {
117+
if (Boolean.FALSE.equals(type)) {
118+
violations.add(new ConstraintViolation(message));
119+
}
51120
}
52121
}
53122
}
Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
package io.avaje.validation.core;
22

3-
import java.util.Map;
3+
import java.util.Set;
44

5+
import io.avaje.validation.AnnotationValidationAdapter;
56
import io.avaje.validation.ConstraintViolation;
6-
//TODO Create an avaje config interpolator
7-
public class NoopAnnotationValidator<T> implements AnnotationValidationAdapter<T> {
8-
9-
@Override
10-
public AnnotationValidationAdapter<T> init(Map<String, String> annotationValueMap) {
117

12-
return this;
13-
}
8+
public class NoopAnnotationValidator<T> implements AnnotationValidationAdapter<T> {
149

1510
@Override
16-
public ConstraintViolation validate(Object type) {
17-
return null;
11+
public void validate(T type, Set<ConstraintViolation> violations) {
12+
// NOOP
1813
}
1914
}

validator/src/test/java/io/avaje/validation/core/AuthProvider$PojoAdapter.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,40 @@
33
import java.util.Map;
44
import java.util.Set;
55

6+
import io.avaje.validation.AnnotationValidationAdapter;
7+
import io.avaje.validation.ConstraintViolation;
68
import io.avaje.validation.ValidationAdapter;
79
import io.avaje.validation.Validator;
8-
import io.avaje.validation.ConstraintViolation;
910
import jakarta.validation.constraints.AssertTrue;
11+
import jakarta.validation.constraints.NotBlank;
12+
import jakarta.validation.constraints.NotNull;
13+
import jakarta.validation.constraints.Past;
1014

1115
public final class AuthProvider$PojoAdapter implements ValidationAdapter<Pojo> {
1216

1317
private final AnnotationValidationAdapter<Boolean> booleanAdapter;
18+
private final AnnotationValidationAdapter<String> strAdapter;
19+
private final AnnotationValidationAdapter<Object> dateAdapter;
1420

1521
public AuthProvider$PojoAdapter(Validator jsonb) {
1622
this.booleanAdapter =
1723
jsonb.<Boolean>annotationAdapter(AssertTrue.class).init(Map.of("message", "not true"));
24+
25+
this.strAdapter =
26+
jsonb
27+
.<String>annotationAdapter(NotNull.class)
28+
.init(Map.of("message", "null"))
29+
.andThen(
30+
jsonb.<String>annotationAdapter(NotBlank.class).init(Map.of("message", "empty")));
31+
32+
this.dateAdapter =
33+
jsonb.annotationAdapter(Past.class).init(Map.of("message", "not in the past"));
1834
}
1935

2036
@Override
2137
public void validate(Pojo value, Set<ConstraintViolation> violations) {
22-
23-
violations.add(booleanAdapter.validate(value.bool));
38+
booleanAdapter.validate(value.bool, violations);
39+
strAdapter.validate(value.str, violations);
40+
dateAdapter.validate(value.date, violations);
2441
}
2542
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
package io.avaje.validation.core;
22

3+
import java.time.LocalDate;
4+
35
public class Pojo {
46
boolean bool = false;
7+
String str = "";
8+
LocalDate date;
9+
10+
public Pojo(boolean bool, String str, LocalDate date) {
11+
this.bool = bool;
12+
this.str = str;
13+
this.date = date;
14+
}
515
}

validator/src/test/java/io/avaje/validation/core/ValidatorTest.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,42 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44

5+
import java.time.LocalDate;
6+
57
import org.junit.jupiter.api.Test;
68

79
import io.avaje.validation.Validator;
810

911
class ValidatorTest {
1012

13+
private final Validator validator =
14+
Validator.builder().add(Pojo.class, AuthProvider$PojoAdapter::new).build();
15+
1116
@Test
12-
void testSuccess() {
17+
void testAllFail() {
1318

14-
final var result =
15-
Validator.builder()
16-
.add(Pojo.class, AuthProvider$PojoAdapter::new)
17-
.build()
18-
.validate(new Pojo());
19-
assertThat(result).isNotEmpty();
19+
final var result = validator.validate(new Pojo(false, null, LocalDate.now().plusDays(3)));
20+
assertThat(result).hasSize(3);
2021
}
2122

2223
@Test
23-
void testFail() {
24-
final var pojo = new Pojo();
25-
pojo.bool = true;
24+
void testStrDate() {
25+
26+
final var result = validator.validate(new Pojo(true, " ", LocalDate.now().plusDays(3)));
27+
assertThat(result).hasSize(2);
28+
}
29+
30+
@Test
31+
void testStrOnly() {
32+
33+
final var result = validator.validate(new Pojo(true, " ", LocalDate.now().minusDays(3)));
34+
assertThat(result).hasSize(1);
35+
}
36+
37+
@Test
38+
void testSuccess() {
2639
final var result =
27-
Validator.builder().add(Pojo.class, AuthProvider$PojoAdapter::new).build().validate(pojo);
40+
validator.validate(new Pojo(true, " success ", LocalDate.now().minusDays(3)));
2841

2942
assertThat(result).isEmpty();
3043
}

0 commit comments

Comments
 (0)