Skip to content

Commit 85491dd

Browse files
authored
Merge pull request #50 from SentryMan/bundling
Configurable Local/ResourceBundles
2 parents c5ed895 + 806816e commit 85491dd

31 files changed

+180
-96
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ build/
99
*.class
1010
.DS_Store
1111
*.factorypath
12+
validator-generator/.factorypath

blackbox-test/src/test/java/example/avaje/ACustomerMessageTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class ACustomerMessageTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/avaje/AMyEmailTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class AMyEmailTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/avaje/AMyMinNumbersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class AMyMinNumbersTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
final BigDecimal valid = new BigDecimal("20");
2121

blackbox-test/src/test/java/example/avaje/AMyNumbersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class AMyNumbersTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
@Test
2121
void valid() {

blackbox-test/src/test/java/example/avaje/AMyPatternTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class AMyPatternTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/avaje/ANumsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class ANumsTest {
1616

17-
final Validator validator = Validator.builder().build();
17+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1818

1919
@Test
2020
void valid() {

blackbox-test/src/test/java/example/avaje/APastFutureTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class APastFutureTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
@Test
2121
void valid() {

blackbox-test/src/test/java/example/jakarta/AllSortsBeanTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
class AllSortsBeanTest {
1414

15-
final Validator validator = Validator.builder().build();
15+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1616

1717
protected ConstraintViolation one(AllSortsBean pojo, Locale locale) {
1818
try {

blackbox-test/src/test/java/example/jakarta/JCustomerMessageTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class JCustomerMessageTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/jakarta/JMyEmailTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class JMyEmailTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/jakarta/JMyMinNumbersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class JMyMinNumbersTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
final BigDecimal valid = new BigDecimal("20");
2121

blackbox-test/src/test/java/example/jakarta/JMyNumbersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class JMyNumbersTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
@Test
2121
void valid() {

blackbox-test/src/test/java/example/jakarta/JMyPatternTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
class JMyPatternTest {
1515

16-
final Validator validator = Validator.builder().build();
16+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1717

1818
@Test
1919
void valid() {

blackbox-test/src/test/java/example/jakarta/JNumsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class JNumsTest {
1717

18-
final Validator validator = Validator.builder().build();
18+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1919

2020
@Test
2121
void valid() {

blackbox-test/src/test/java/example/jakarta/JPastFutureTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
class JPastFutureTest {
1616

17-
final Validator validator = Validator.builder().build();
17+
final Validator validator = Validator.builder().addLocales(Locale.GERMAN).build();
1818

1919
@Test
2020
void valid() {

validator-generator/.factorypath

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

validator-generator/pom.xml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,10 @@
8585

8686
<build>
8787
<plugins>
88-
8988
<plugin>
9089
<groupId>org.apache.maven.plugins</groupId>
9190
<artifactId>maven-compiler-plugin</artifactId>
9291
<configuration>
93-
<source>${java.version}</source>
94-
<target>${java.version}</target>
9592
<annotationProcessorPaths>
9693
<path>
9794
<groupId>io.avaje</groupId>

validator-generator/src/test/java/io/avaje/validation/generator/ValidatorProcessorTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import javax.tools.ToolProvider;
2323

2424
import org.junit.jupiter.api.AfterEach;
25-
import org.junit.jupiter.api.Disabled;
2625
import org.junit.jupiter.api.Test;
2726

2827
class ValidatorProcessorTest {
@@ -39,10 +38,9 @@ void deleteGeneratedFiles() throws IOException {
3938
}
4039
}
4140

42-
@Disabled
4341
@Test
4442
void testGeneration() throws Exception {
45-
final String source = Paths.get("src").toAbsolutePath().toString();
43+
final String source = Paths.get("src/test/java/io/avaje/validation/generator/models/valid").toAbsolutePath().toString();
4644

4745
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
4846
final StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);

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

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,20 @@
88
import java.lang.reflect.Type;
99
import java.util.Iterator;
1010
import java.util.Locale;
11+
import java.util.ResourceBundle;
1112
import java.util.ServiceLoader;
1213

1314
public interface Validator {
1415

15-
/**
16-
* Validate the object using the default locale.
17-
*/
16+
/** Validate the object using the default locale. */
1817
void validate(Object any) throws ConstraintViolationException;
1918

2019
/**
2120
* Validate the object with a given locale.
2221
*
23-
* <p>If the locale is not one of the supported locales then the
24-
* default locale will be used.
22+
* <p>If the locale is not one of the supported locales then the default locale will be used.
2523
*
26-
* <p>This is expected to be used when the Validator is configured
27-
* to support multiple locales.
24+
* <p>This is expected to be used when the Validator is configured to support multiple locales.
2825
*/
2926
void validate(Object any, Locale locale) throws ConstraintViolationException;
3027

@@ -45,6 +42,26 @@ interface Builder {
4542
/** Add a AnnotationValidationAdapter to use for the given type. */
4643
<T> Builder add(Class<Annotation> type, ValidationAdapter<T> adapter);
4744

45+
/**
46+
* Lookup ResourceBundles with the given name and for error message interpolation
47+
*
48+
* @param bundleName the name of the bundleFiles
49+
*/
50+
Builder addResourceBundles(String... bundleName);
51+
52+
/**
53+
* Add ResourceBundle for error message interpolation
54+
*
55+
* @param bundleName the name of the bundleFiles
56+
*/
57+
Builder addResourceBundles(ResourceBundle... bundle);
58+
59+
/** Set Default Locale for this validator, if not set, will use Locale.getDefault() */
60+
Builder setDefaultLocale(Locale defaultLocale);
61+
62+
/** Adds an additional Locales for this validator */
63+
Builder addLocales(Locale... locales);
64+
4865
/** Add a AdapterBuilder which provides a ValidationAdapter to use for the given type. */
4966
Builder add(Type type, AdapterBuilder builder);
5067

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Map;
55
import java.util.Objects;
66

7+
@FunctionalInterface
78
public interface ValidationAdapter<T> {
89

910
/** Return true if validation should recurse */

validator/src/main/java/io/avaje/validation/adapter/ValidationContext.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ interface Message {
3636
/**
3737
* Factory for creating a ValidationAdapter for a given type.
3838
*/
39+
@FunctionalInterface
3940
interface AdapterFactory {
4041

4142
/**
@@ -49,6 +50,7 @@ interface AdapterFactory {
4950
/**
5051
* Factory for creating a ValidationAdapter for a given annotation.
5152
*/
53+
@FunctionalInterface
5254
interface AnnotationFactory {
5355

5456
/**

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,11 @@
1414

1515
/** Builds and caches the ValidationAdapter adapters for DValidator. */
1616
final class CoreAdapterBuilder {
17-
17+
public static final ValidationAdapter NOOP = (type, req, propertyName) -> true;
1818
private final DValidator context;
1919
private final List<ValidationContext.AdapterFactory> factories = new ArrayList<>();
2020
private final List<ValidationContext.AnnotationFactory> annotationFactories = new ArrayList<>();
2121
private final Map<Object, ValidationAdapter<?>> adapterCache = new ConcurrentHashMap<>();
22-
private final MessageInterpolator interpolator;
2322

2423
CoreAdapterBuilder(
2524
DValidator context,
@@ -30,7 +29,6 @@ final class CoreAdapterBuilder {
3029
this.annotationFactories.addAll(userAnnotationFactories);
3130
this.annotationFactories.add(BasicAdapters.FACTORY);
3231
this.annotationFactories.add(NumberAdapters.FACTORY);
33-
interpolator = context.interpolator();
3432
}
3533

3634
/** Return the adapter from cache if exists else return null. */
@@ -76,6 +74,6 @@ <T> ValidationAdapter<T> buildAnnotation(Class<? extends Annotation> cls, Map<St
7674
}
7775
}
7876
// unknown annotations have noop
79-
return NoopAnnotationValidator.INSTANCE;
77+
return NOOP;
8078
}
8179
}

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

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

3-
import io.avaje.lang.Nullable;
4-
5-
import java.util.Collections;
3+
import java.util.Collection;
64
import java.util.HashSet;
75
import java.util.Locale;
86
import java.util.Set;
97

8+
import io.avaje.lang.Nullable;
9+
1010
final class DLocaleResolver implements LocaleResolver {
1111

1212
private final Locale defaultLocale;
1313
private final Set<Locale> otherLocales = new HashSet<>();
1414

15-
DLocaleResolver(Locale defaultLocale, Locale... others) {
15+
DLocaleResolver(Locale defaultLocale, Collection<Locale> others) {
1616
this.defaultLocale = defaultLocale;
17-
Collections.addAll(otherLocales, others);
17+
otherLocales.addAll(others);
1818
}
1919

2020
@Override
Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,72 @@
11
package io.avaje.validation.core;
22

3-
import io.avaje.lang.Nullable;
3+
import static java.util.ResourceBundle.getBundle;
4+
5+
import java.util.ArrayList;
6+
import java.util.HashMap;
7+
import java.util.List;
8+
import java.util.Locale;
9+
import java.util.Map;
10+
import java.util.ResourceBundle;
411

5-
import java.util.*;
12+
import io.avaje.lang.Nullable;
613

714
final class DResourceBundleManager {
815

9-
private final Map<Locale, ResourceBundle> map = new HashMap<>();
16+
private final Map<Locale, List<ResourceBundle>> map = new HashMap<>();
17+
private static final List<ResourceBundle> EMPTY = List.of();
18+
private static final String DEFAULT_BUNDLE = "io.avaje.validation.Messages";
19+
20+
DResourceBundleManager(
21+
List<String> names, List<ResourceBundle> providedBundles, LocaleResolver localeResolver) {
1022

11-
DResourceBundleManager(String name, LocaleResolver localeResolver) {
12-
map.put(localeResolver.defaultLocale(), bundle(name, localeResolver.defaultLocale()));
13-
for (Locale locale : localeResolver.otherLocales()) {
14-
map.put(locale, bundle(name, locale));
23+
for (final var name : names) {
24+
25+
addBundle(name, localeResolver.defaultLocale());
26+
27+
for (final Locale locale : localeResolver.otherLocales()) {
28+
29+
addBundle(name, locale);
30+
}
31+
}
32+
33+
for (final var bundle : providedBundles) {
34+
map.compute(
35+
bundle.getLocale(),
36+
(local, list) -> {
37+
if (list == null) list = new ArrayList<>();
38+
39+
list.add(bundle);
40+
return list;
41+
});
42+
}
43+
// since default is added last, it will be the last place messages will be resolved
44+
addBundle(DEFAULT_BUNDLE, localeResolver.defaultLocale());
45+
46+
for (final Locale locale : localeResolver.otherLocales()) {
47+
addBundle(DEFAULT_BUNDLE, locale);
1548
}
1649
}
1750

18-
private static ResourceBundle bundle(String name, Locale locale) {
19-
return ResourceBundle.getBundle(name, locale);
51+
private void addBundle(final String name, final Locale locale) {
52+
map.compute(
53+
locale,
54+
(local, list) -> {
55+
if (list == null) list = new ArrayList<>();
56+
list.add(getBundle(name, local));
57+
return list;
58+
});
2059
}
2160

2261
@Nullable
2362
public String message(String template, Locale resolvedLocale) {
24-
ResourceBundle bundle = map.get(resolvedLocale);
25-
boolean exists = bundle.containsKey(template);
26-
return !exists ? null : bundle.getString(template);
63+
64+
for (final var bundle : map.getOrDefault(resolvedLocale, EMPTY)) {
65+
66+
if (bundle.containsKey(template)) {
67+
return bundle.getString(template);
68+
}
69+
}
70+
return null;
2771
}
2872
}

0 commit comments

Comments
 (0)