Skip to content

Commit 72c9980

Browse files
authored
Fix Boolean Nulls/Refactor Time Adapters/Adapter Tests (#55)
* fix boolean nulls and add more tests * Update FuturePastAdapter.java * futurePast Tests * Update FuturePastAdapterTest.java * Update FuturePastAdapterTest.java * Revert "Update FuturePastAdapterTest.java" This reverts commit 0c070da. * Update FuturePastAdapterTest.java * refactor futurepast adapter * Create EmailTest.java * fix pattern * Update CoreAdapterBuilder.java * Update CoreAdapterBuilder.java * Update DValidator.java * number adapters * Update FuturePastAdapterTest.java * Revert "Update FuturePastAdapterTest.java" This reverts commit 7e963ad. * hmm * maybe it's the temporal thing? * Update BasicTest.java * Update BasicTest.java * long actually generates correctly * 90% coverage on adapters * reflection free array length * Update FuturePastAdapterTest.java
1 parent b3278d3 commit 72c9980

21 files changed

+1201
-119
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66

77
import java.lang.annotation.Annotation;
88
import java.lang.reflect.Type;
9-
import java.util.Iterator;
9+
import java.time.Clock;
10+
import java.time.Duration;
1011
import java.util.Locale;
1112
import java.util.Map;
1213
import java.util.ResourceBundle;
1314
import java.util.ServiceLoader;
15+
import java.util.function.Supplier;
1416

1517
public interface Validator {
1618

@@ -27,7 +29,7 @@ public interface Validator {
2729
void validate(Object any, Locale locale) throws ConstraintViolationException;
2830

2931
static Builder builder() {
30-
final Iterator<Bootstrap> bootstrapService = ServiceLoader.load(Bootstrap.class).iterator();
32+
final var bootstrapService = ServiceLoader.load(Bootstrap.class).iterator();
3133
if (bootstrapService.hasNext()) {
3234
return bootstrapService.next().builder();
3335
}
@@ -61,6 +63,10 @@ interface Builder {
6163
/** Adds additional Locales for this validator */
6264
Builder addLocales(Locale... locales);
6365

66+
Builder clockProvider(Supplier<Clock> clockSupplier);
67+
68+
Builder temporalTolerance(Duration temporalTolerance);
69+
6470
/** Add a AdapterBuilder which provides a ValidationAdapter to use for the given type. */
6571
Builder add(Type type, AdapterBuilder builder);
6672

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
import java.lang.annotation.Annotation;
44
import java.lang.reflect.Type;
5+
import java.time.Clock;
6+
import java.time.Duration;
57
import java.util.ArrayList;
68
import java.util.List;
79
import java.util.Map;
810
import java.util.concurrent.ConcurrentHashMap;
11+
import java.util.function.Supplier;
912

1013
import io.avaje.validation.adapter.ValidationAdapter;
1114
import io.avaje.validation.adapter.ValidationContext;
1215
import io.avaje.validation.core.adapters.BasicAdapters;
16+
import io.avaje.validation.core.adapters.FuturePastAdapterFactory;
1317
import io.avaje.validation.core.adapters.NumberAdapters;
1418

1519
/** Builds and caches the ValidationAdapter adapters for DValidator. */
@@ -23,12 +27,15 @@ final class CoreAdapterBuilder {
2327
CoreAdapterBuilder(
2428
DValidator context,
2529
List<ValidationContext.AdapterFactory> userFactories,
26-
List<ValidationContext.AnnotationFactory> userAnnotationFactories) {
30+
List<ValidationContext.AnnotationFactory> userAnnotationFactories,
31+
Supplier<Clock> clockSupplier,
32+
Duration temporalTolerance) {
2733
this.context = context;
2834
this.factories.addAll(userFactories);
2935
this.annotationFactories.addAll(userAnnotationFactories);
3036
this.annotationFactories.add(BasicAdapters.FACTORY);
3137
this.annotationFactories.add(NumberAdapters.FACTORY);
38+
this.annotationFactories.add(new FuturePastAdapterFactory(clockSupplier, temporalTolerance));
3239
}
3340

3441
/** Return the adapter from cache if exists else return null. */
@@ -52,7 +59,7 @@ <T> ValidationAdapter<T> annotationAdapter(Class<? extends Annotation> cls, Map<
5259
<T> ValidationAdapter<T> build(Type type, Object cacheKey) {
5360
// Ask each factory to create the validation adapter.
5461
for (final ValidationContext.AdapterFactory factory : factories) {
55-
final ValidationAdapter<T> result = (ValidationAdapter<T>) factory.create(type, context);
62+
final var result = (ValidationAdapter<T>) factory.create(type, context);
5663
if (result != null) {
5764
return result;
5865
}

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

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77

88
import java.lang.annotation.Annotation;
99
import java.lang.reflect.Type;
10+
import java.time.Clock;
11+
import java.time.Duration;
1012
import java.util.ArrayList;
1113
import java.util.Collections;
12-
import java.util.HashMap;
1314
import java.util.List;
1415
import java.util.Locale;
1516
import java.util.Map;
1617
import java.util.ResourceBundle;
1718
import java.util.ServiceLoader;
1819
import java.util.concurrent.ConcurrentHashMap;
20+
import java.util.function.Supplier;
1921

2022
import io.avaje.lang.Nullable;
2123
import io.avaje.validation.Validator;
@@ -40,12 +42,17 @@ final class DValidator implements Validator, ValidationContext {
4042
List<String> bundleNames,
4143
List<ResourceBundle> bundles,
4244
MessageInterpolator interpolator,
43-
LocaleResolver localeResolver) {
45+
LocaleResolver localeResolver,
46+
Supplier<Clock> clockSupplier,
47+
Duration temporalTolerance) {
4448
this.localeResolver = localeResolver;
45-
final var defaultResourceBundle = new DResourceBundleManager(bundleNames, bundles, localeResolver);
49+
final var defaultResourceBundle =
50+
new DResourceBundleManager(bundleNames, bundles, localeResolver);
4651
this.templateLookup = new DTemplateLookup(defaultResourceBundle);
4752
this.interpolator = interpolator;
48-
this.builder = new CoreAdapterBuilder(this, factories, annotationFactories);
53+
this.builder =
54+
new CoreAdapterBuilder(
55+
this, factories, annotationFactories, clockSupplier, temporalTolerance);
4956
}
5057

5158
MessageInterpolator interpolator() {
@@ -138,6 +145,8 @@ static final class DBuilder implements Validator.Builder {
138145
private final List<ResourceBundle> bundles = new ArrayList<>();
139146
private final List<Locale> otherLocals = new ArrayList<>();
140147
private Locale defaultLocal = Locale.getDefault();
148+
private Supplier<Clock> clockSupplier = Clock::systemDefaultZone;
149+
private Duration temporalTolerance = Duration.ZERO;
141150

142151
@Override
143152
public Builder add(Type type, AdapterBuilder builder) {
@@ -201,6 +210,18 @@ public Builder addLocales(Locale... locals) {
201210
return this;
202211
}
203212

213+
@Override
214+
public Builder clockProvider(Supplier<Clock> clockSupplier) {
215+
this.clockSupplier = clockSupplier;
216+
return this;
217+
}
218+
219+
@Override
220+
public Builder temporalTolerance(Duration temporalTolerance) {
221+
this.temporalTolerance = temporalTolerance;
222+
return this;
223+
}
224+
204225
private void registerComponents() {
205226
// first register all user defined ValidatorComponent
206227
for (final ValidatorComponent next : ServiceLoader.load(ValidatorComponent.class)) {
@@ -220,7 +241,15 @@ public DValidator build() {
220241
ServiceLoader.load(MessageInterpolator.class)
221242
.findFirst()
222243
.orElseGet(BasicMessageInterpolator::new);
223-
return new DValidator(factories, afactories, bundleNames, bundles, interpolator, localeResolver);
244+
return new DValidator(
245+
factories,
246+
afactories,
247+
bundleNames,
248+
bundles,
249+
interpolator,
250+
localeResolver,
251+
clockSupplier,
252+
temporalTolerance);
224253
}
225254

226255
private static <T> AnnotationFactory newAnnotationAdapterFactory(Type type, ValidationAdapter<T> adapter) {

validator/src/main/java/io/avaje/validation/core/adapters/BasicAdapters.java

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.avaje.validation.core.adapters;
22

3-
import java.lang.reflect.Array;
43
import java.util.Collection;
54
import java.util.List;
65
import java.util.Map;
@@ -15,23 +14,20 @@
1514
public final class BasicAdapters {
1615
private BasicAdapters() {}
1716

18-
public static final ValidationContext.AnnotationFactory FACTORY = (annotationType, context, attributes) ->
19-
switch (annotationType.getSimpleName()) {
20-
case "Email" -> new EmailAdapter(context.message(attributes), attributes);
21-
case "Null" -> new NullableAdapter(context.message(attributes), true);
22-
case "NotNull", "NonNull" -> new NullableAdapter(context.message(attributes), false);
23-
case "AssertTrue" -> new AssertBooleanAdapter(context.message(attributes), Boolean.FALSE);
24-
case "AssertFalse" -> new AssertBooleanAdapter(context.message(attributes), Boolean.TRUE);
25-
case "NotBlank" -> new NotBlankAdapter(context.message(attributes));
26-
case "NotEmpty" -> new NotEmptyAdapter(context.message(attributes));
27-
case "Past" -> new FuturePastAdapter(context.message(attributes), true, false);
28-
case "PastOrPresent" -> new FuturePastAdapter(context.message(attributes), true, true);
29-
case "Future" -> new FuturePastAdapter(context.message(attributes), false, false);
30-
case "FutureOrPresent" -> new FuturePastAdapter(context.message(attributes), false, true);
31-
case "Pattern" -> new PatternAdapter(context.message(attributes), attributes);
32-
case "Size" -> new SizeAdapter(context.message(attributes), attributes);
33-
default -> null;
34-
};
17+
public static final ValidationContext.AnnotationFactory FACTORY =
18+
(annotationType, context, attributes) ->
19+
switch (annotationType.getSimpleName()) {
20+
case "Email" -> new EmailAdapter(context.message(attributes), attributes);
21+
case "Null" -> new NullableAdapter(context.message(attributes), true);
22+
case "NotNull", "NonNull" -> new NullableAdapter(context.message(attributes), false);
23+
case "AssertTrue" -> new AssertBooleanAdapter(context.message(attributes), Boolean.TRUE);
24+
case "AssertFalse" -> new AssertBooleanAdapter(context.message(attributes), Boolean.FALSE);
25+
case "NotBlank" -> new NotBlankAdapter(context.message(attributes));
26+
case "NotEmpty" -> new NotEmptyAdapter(context.message(attributes));
27+
case "Pattern" -> new PatternAdapter(context.message(attributes), attributes);
28+
case "Size" -> new SizeAdapter(context.message(attributes), attributes);
29+
default -> null;
30+
};
3531

3632
private static final class PatternAdapter implements ValidationAdapter<CharSequence> {
3733

@@ -54,7 +50,11 @@ private static final class PatternAdapter implements ValidationAdapter<CharSeque
5450

5551
@Override
5652
public boolean validate(CharSequence value, ValidationRequest req, String propertyName) {
57-
if (value == null || pattern.test(value.toString())) {
53+
if (value == null) {
54+
return true;
55+
}
56+
57+
if (pattern.test(value.toString())) {
5858
req.addViolation(message, propertyName);
5959
return false;
6060
}
@@ -99,7 +99,7 @@ public boolean validate(Object value, ValidationRequest req, String propertyName
9999
return len > 0;
100100
}
101101
} else if (value.getClass().isArray()) {
102-
final var len = Array.getLength(value);
102+
final var len = arrayLength(value);
103103
if (len > max || len < min) {
104104
req.addViolation(message, propertyName);
105105
return len > 0;
@@ -170,7 +170,7 @@ public boolean validate(Object value, ValidationRequest req, String propertyName
170170
return false;
171171
}
172172
} else if (value.getClass().isArray()) {
173-
final var len = Array.getLength(value);
173+
final var len = arrayLength(value);
174174
if (len == 0) {
175175
req.addViolation(message, propertyName);
176176
return false;
@@ -193,7 +193,11 @@ private static final class AssertBooleanAdapter implements ValidationAdapter<Boo
193193

194194
@Override
195195
public boolean validate(Boolean type, ValidationRequest req, String propertyName) {
196-
if (assertBool.equals(type)) {
196+
if (!assertBool.booleanValue() && type == null) {
197+
return true;
198+
}
199+
200+
if (!assertBool.equals(type)) {
197201
req.addViolation(message, propertyName);
198202
return false;
199203
}
@@ -220,4 +224,27 @@ public boolean validate(Object value, ValidationRequest req, String propertyName
220224
return true;
221225
}
222226
}
227+
228+
private static int arrayLength(Object array) {
229+
230+
if (array instanceof int[] arr) {
231+
return arr.length;
232+
} else if (array instanceof boolean[] arr) {
233+
return arr.length;
234+
} else if (array instanceof byte[] arr) {
235+
return arr.length;
236+
} else if (array instanceof char[] arr) {
237+
return arr.length;
238+
} else if (array instanceof short[] arr) {
239+
return arr.length;
240+
} else if (array instanceof float[] arr) {
241+
return arr.length;
242+
} else if (array instanceof double[] arr) {
243+
return arr.length;
244+
} else if (array instanceof long[] arr) {
245+
return arr.length;
246+
} else {
247+
return ((Object[]) array).length;
248+
}
249+
}
223250
}

0 commit comments

Comments
 (0)