Skip to content

Commit 2c43b79

Browse files
committed
HV-1749 Introduce a LocaleResolver contract
1 parent 2312530 commit 2c43b79

File tree

15 files changed

+519
-37
lines changed

15 files changed

+519
-37
lines changed

engine/src/main/java/org/hibernate/validator/BaseHibernateValidatorConfiguration.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.hibernate.validator.cfg.ConstraintMapping;
2323
import org.hibernate.validator.constraints.ParameterScriptAssert;
2424
import org.hibernate.validator.constraints.ScriptAssert;
25+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
2526
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
2627
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
2728
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
@@ -131,6 +132,15 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
131132
@Incubating
132133
String PROPERTY_NODE_NAME_PROVIDER_CLASSNAME = "hibernate.validator.property_node_name_provider";
133134

135+
/**
136+
* Property for configuring the locale resolver, allowing to select an implementation of {@link LocaleResolver}
137+
* which will be used for locale resolution when interpolating a message.
138+
*
139+
* @since 6.1.1
140+
*/
141+
@Incubating
142+
String LOCALE_RESOLVER_CLASSNAME = "hibernate.validator.locale_resolver";
143+
134144
/**
135145
* <p>
136146
* Returns the {@link ResourceBundleLocator} used by the
@@ -371,4 +381,16 @@ public interface BaseHibernateValidatorConfiguration<S extends BaseHibernateVali
371381
*/
372382
@Incubating
373383
S defaultLocale(Locale defaultLocale);
384+
385+
/**
386+
* Allows setting a locale resolver, defining how the locale will be resolved when interpolating the message of a constraint violation.
387+
*
388+
* @param localeResolver the {@link LocaleResolver} to be used
389+
*
390+
* @return {@code this} following the chaining method pattern
391+
*
392+
* @since 6.1.1
393+
*/
394+
@Incubating
395+
S localeResolver(LocaleResolver localeResolver);
374396
}

engine/src/main/java/org/hibernate/validator/internal/engine/AbstractConfigurationImpl.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.hibernate.validator.internal.xml.config.ValidationXmlParser;
5858
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
5959
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
60+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
6061
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
6162
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
6263
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
@@ -112,9 +113,8 @@ public abstract class AbstractConfigurationImpl<T extends BaseHibernateValidator
112113
private Duration temporalValidationTolerance;
113114
private Object constraintValidatorPayload;
114115
private GetterPropertySelectionStrategy getterPropertySelectionStrategy;
115-
116-
// the default locale
117116
private Locale defaultLocale = Locale.getDefault();
117+
private LocaleResolver localeResolver;
118118

119119
protected AbstractConfigurationImpl(BootstrapState state) {
120120
this();
@@ -235,6 +235,19 @@ public T propertyNodeNameProvider(PropertyNodeNameProvider propertyNodeNameProvi
235235
return thisAsT();
236236
}
237237

238+
@Override
239+
public T localeResolver(LocaleResolver localeResolver) {
240+
if ( LOG.isDebugEnabled() ) {
241+
if ( localeResolver != null ) {
242+
LOG.debug( "Setting custom LocaleResolver of type " + localeResolver.getClass()
243+
.getName() );
244+
}
245+
}
246+
this.localeResolver = localeResolver;
247+
248+
return thisAsT();
249+
}
250+
238251
@Override
239252
public T addValueExtractor(ValueExtractor<?> extractor) {
240253
Contracts.assertNotNull( extractor, MESSAGES.parameterMustNotBeNull( "extractor" ) );
@@ -489,6 +502,10 @@ public PropertyNodeNameProvider getPropertyNodeNameProvider() {
489502
return validationBootstrapParameters.getPropertyNodeNameProvider();
490503
}
491504

505+
public LocaleResolver getLocaleResolver() {
506+
return localeResolver;
507+
}
508+
492509
public ScriptEvaluatorFactory getScriptEvaluatorFactory() {
493510
return scriptEvaluatorFactory;
494511
}
@@ -527,7 +544,7 @@ public ClassLoader getExternalClassLoader() {
527544
public final MessageInterpolator getDefaultMessageInterpolator() {
528545
if ( defaultMessageInterpolator == null ) {
529546
defaultMessageInterpolator = new ResourceBundleMessageInterpolator( getDefaultResourceBundleLocator(), getAllLocalesToInitialize(),
530-
defaultLocale );
547+
defaultLocale, ValidatorFactoryConfigurationHelper.determineLocaleResolver( this, this.getProperties(), externalClassLoader ) );
531548
}
532549

533550
return defaultMessageInterpolator;
@@ -717,7 +734,8 @@ private MessageInterpolator getDefaultMessageInterpolatorConfiguredWithClassLoad
717734
userResourceBundleLocator,
718735
contributorResourceBundleLocator,
719736
getAllLocalesToInitialize(),
720-
defaultLocale
737+
defaultLocale,
738+
ValidatorFactoryConfigurationHelper.determineLocaleResolver( this, this.getProperties(), externalClassLoader )
721739
);
722740
}
723741
finally {

engine/src/main/java/org/hibernate/validator/internal/engine/PredefinedScopeValidatorFactoryImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowMultipleCascadedValidationOnReturnValues;
1010
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowOverridingMethodAlterParameterConstraint;
1111
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineAllowParallelMethodsDefineParameterConstraints;
12+
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
1213
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintMappings;
1314
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineConstraintValidatorPayload;
1415
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineExternalClassLoader;
@@ -18,7 +19,6 @@
1819
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineTraversableResolverResultCacheEnabled;
1920
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.logValidatorFactoryScopedConfiguration;
2021
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.registerCustomConstraintValidators;
21-
import static org.hibernate.validator.internal.engine.ValidatorFactoryConfigurationHelper.determineBeanMetaDataClassNormalizer;
2222
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
2323

2424
import java.lang.invoke.MethodHandles;

engine/src/main/java/org/hibernate/validator/internal/engine/ValidatorFactoryConfigurationHelper.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.hibernate.validator.cfg.ConstraintMapping;
2626
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
2727
import org.hibernate.validator.internal.engine.constraintdefinition.ConstraintDefinitionContribution;
28+
import org.hibernate.validator.internal.engine.messageinterpolation.DefaultLocaleResolver;
2829
import org.hibernate.validator.internal.engine.scripting.DefaultScriptEvaluatorFactory;
2930
import org.hibernate.validator.internal.metadata.DefaultBeanMetaDataClassNormalizer;
3031
import org.hibernate.validator.internal.metadata.core.ConstraintHelper;
@@ -39,6 +40,7 @@
3940
import org.hibernate.validator.internal.util.privilegedactions.NewInstance;
4041
import org.hibernate.validator.metadata.BeanMetaDataClassNormalizer;
4142
import org.hibernate.validator.spi.cfg.ConstraintMappingContributor;
43+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
4244
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
4345
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
4446
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
@@ -318,6 +320,32 @@ static PropertyNodeNameProvider determinePropertyNodeNameProvider(AbstractConfig
318320
return new DefaultPropertyNodeNameProvider();
319321
}
320322

323+
static LocaleResolver determineLocaleResolver(AbstractConfigurationImpl<?> hibernateSpecificConfig, Map<String, String> properties,
324+
ClassLoader externalClassLoader) {
325+
if ( hibernateSpecificConfig.getLocaleResolver() != null ) {
326+
LOG.usingLocaleResolver( hibernateSpecificConfig.getLocaleResolver().getClass() );
327+
328+
return hibernateSpecificConfig.getLocaleResolver();
329+
}
330+
331+
String localeResolverFqcn = properties.get( HibernateValidatorConfiguration.LOCALE_RESOLVER_CLASSNAME );
332+
if ( localeResolverFqcn != null ) {
333+
try {
334+
@SuppressWarnings("unchecked")
335+
Class<? extends LocaleResolver> clazz = (Class<? extends LocaleResolver>) run( LoadClass.action( localeResolverFqcn, externalClassLoader ) );
336+
LocaleResolver localeResolver = run( NewInstance.action( clazz, "locale resolver class" ) );
337+
LOG.usingLocaleResolver( clazz );
338+
339+
return localeResolver;
340+
}
341+
catch (Exception e) {
342+
throw LOG.getUnableToInstantiateLocaleResolverClassException( localeResolverFqcn, e );
343+
}
344+
}
345+
346+
return new DefaultLocaleResolver();
347+
}
348+
321349
static void registerCustomConstraintValidators(Set<DefaultConstraintMapping> constraintMappings,
322350
ConstraintHelper constraintHelper) {
323351
Set<Class<?>> definedConstraints = newHashSet();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.internal.engine.messageinterpolation;
8+
9+
import java.util.Locale;
10+
11+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
12+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext;
13+
14+
public class DefaultLocaleResolver implements LocaleResolver {
15+
16+
@Override
17+
public Locale resolve(LocaleResolverContext context) {
18+
return context.getDefaultLocale();
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Hibernate Validator, declare and validate application constraints
3+
*
4+
* License: Apache License, Version 2.0
5+
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
6+
*/
7+
package org.hibernate.validator.internal.engine.messageinterpolation;
8+
9+
import java.util.Locale;
10+
import java.util.Set;
11+
12+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext;
13+
14+
public class DefaultLocaleResolverContext implements LocaleResolverContext {
15+
16+
private final Set<Locale> supportedLocales;
17+
18+
private final Locale defaultLocale;
19+
20+
public DefaultLocaleResolverContext(Set<Locale> supportedLocales, Locale defaultLocale) {
21+
this.supportedLocales = supportedLocales;
22+
this.defaultLocale = defaultLocale;
23+
}
24+
25+
@Override
26+
public Set<Locale> getSupportedLocales() {
27+
return supportedLocales;
28+
}
29+
30+
@Override
31+
public Locale getDefaultLocale() {
32+
return defaultLocale;
33+
}
34+
}

engine/src/main/java/org/hibernate/validator/internal/util/logging/Log.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.hibernate.validator.internal.util.logging.formatter.ObjectArrayFormatter;
6464
import org.hibernate.validator.internal.util.logging.formatter.TypeFormatter;
6565
import org.hibernate.validator.internal.xml.mapping.ContainerElementTypePath;
66+
import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
6667
import org.hibernate.validator.spi.nodenameprovider.PropertyNodeNameProvider;
6768
import org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy;
6869
import org.hibernate.validator.spi.scripting.ScriptEvaluationException;
@@ -900,4 +901,11 @@ ConstraintDefinitionException getConstraintValidatorDefinitionConstraintMismatch
900901
+ " To solve this, compile your code with the '-parameters' flag."
901902
)
902903
void missingParameterMetadataWithSyntheticOrImplicitParameters(@FormatWith(ExecutableFormatter.class) Executable executable);
904+
905+
@LogMessage(level = DEBUG)
906+
@Message(id = 255, value = "Using %s as locale resolver.")
907+
void usingLocaleResolver(@FormatWith(ClassObjectFormatter.class) Class<? extends LocaleResolver> localeResolverClass);
908+
909+
@Message(id = 256, value = "Unable to instantiate locale resolver class %s.")
910+
ValidationException getUnableToInstantiateLocaleResolverClassException(String localeResolverClassName, @Cause Exception e);
903911
}

0 commit comments

Comments
 (0)