From 0c05094178db43f8bf86483f40ff18f31affad91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gasterst=C3=A4dt?= <157380642+antagoony@users.noreply.github.com> Date: Tue, 25 Nov 2025 19:51:22 +0100 Subject: [PATCH 1/2] create a ParameterByTypeTransformer that is aware of Feature's Locale --- CHANGELOG.md | 1 + .../LocaleParameterByTypeTransformer.java | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 java/src/main/java/io/cucumber/cucumberexpressions/LocaleParameterByTypeTransformer.java diff --git a/CHANGELOG.md b/CHANGELOG.md index bf483c16..9a58badb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [Java] Add OSGi metadata ### Added - [Java] class `KeyboardFriendlyDecimalFormatSymbols` is now `public` and can be used when creating a custom `Locale`-aware type transformation method ([#376](https://github.com/cucumber/cucumber-expressions/issues/376) [antagoony](https://github.com/antagoony)) +- [Java] interface `LocaleParameterByTypeTransformer` to support `Locale`-aware type transformation ([#376](https://github.com/cucumber/cucumber-expressions/issues/376) [antagoony](https://github.com/antagoony)) ### Removed - [Python] Remove support for end-of-life Python 3.8 and 3.9 ([#359](https://github.com/cucumber/cucumber-expressions/pull/359)) diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/LocaleParameterByTypeTransformer.java b/java/src/main/java/io/cucumber/cucumberexpressions/LocaleParameterByTypeTransformer.java new file mode 100644 index 00000000..ddab7ccc --- /dev/null +++ b/java/src/main/java/io/cucumber/cucumberexpressions/LocaleParameterByTypeTransformer.java @@ -0,0 +1,33 @@ +package io.cucumber.cucumberexpressions; + +import java.lang.reflect.Type; +import java.util.Locale; +import org.apiguardian.api.API; + +/** + * This extension of {@link ParameterByTypeTransformer} provides an additional + * transform method that consumes a {@link Locale locale}. Intentionally, this + * will be the locale information specified within the feature file containing + * the currently processed scenario. + * + * @see KeyboardFriendlyDecimalFormatSymbols + */ +@API(status = API.Status.EXPERIMENTAL) +@FunctionalInterface +public interface LocaleParameterByTypeTransformer extends ParameterByTypeTransformer { + + /** + * Similar to {@link #transform(String, Type)} but, in addition, consumes a + * {@link Locale locale}. This locale information can be ignored, or can be + * considered in case the transformation is aware of localized values. For + * example, numbers {@linkplain KeyboardFriendlyDecimalFormatSymbols may use + * localized decimal symbols}. + * + * @implNote The default implementation ignores the {@code locale} and + * delegates to {@link #transform(String, Type)} + */ + default Object transform(final String fromValue, final Type toValueType, final Locale locale) throws Throwable { + return this.transform(fromValue, toValueType); + } + +} From 89ee8a180c87600a1ba396e4148d3ca9dd9ad74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Gasterst=C3=A4dt?= <157380642+antagoony@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:23:32 +0100 Subject: [PATCH 2/2] adapt Locale-aware type transformation --- .../BuiltInParameterTransformer.java | 2 +- .../CucumberExpression.java | 5 ++-- .../ParameterTypeRegistry.java | 24 +++++++++++++++---- .../RegularExpression.java | 5 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/BuiltInParameterTransformer.java b/java/src/main/java/io/cucumber/cucumberexpressions/BuiltInParameterTransformer.java index 5c0b7a1e..eea47e99 100644 --- a/java/src/main/java/io/cucumber/cucumberexpressions/BuiltInParameterTransformer.java +++ b/java/src/main/java/io/cucumber/cucumberexpressions/BuiltInParameterTransformer.java @@ -9,7 +9,7 @@ import static java.util.Objects.requireNonNull; -final class BuiltInParameterTransformer implements ParameterByTypeTransformer { +final class BuiltInParameterTransformer implements LocaleParameterByTypeTransformer { private final NumberParser numberParser; diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/CucumberExpression.java b/java/src/main/java/io/cucumber/cucumberexpressions/CucumberExpression.java index c0ea6615..0be9aa3d 100644 --- a/java/src/main/java/io/cucumber/cucumberexpressions/CucumberExpression.java +++ b/java/src/main/java/io/cucumber/cucumberexpressions/CucumberExpression.java @@ -155,8 +155,9 @@ public List> match(String text, Type... typeHints) { ParameterType parameterType = parameterTypes.get(i); Type type = i < typeHints.length ? typeHints[i] : String.class; if (parameterType.isAnonymous()) { - ParameterByTypeTransformer defaultTransformer = parameterTypeRegistry.getDefaultParameterTransformer(); - parameterTypes.set(i, parameterType.deAnonymize(type, arg -> defaultTransformer.transform(arg, type))); + LocaleParameterByTypeTransformer defaultTransformer = parameterTypeRegistry.getDefaultParameterTransformer(); + parameterTypes.set(i, parameterType.deAnonymize(type, arg -> defaultTransformer.transform(arg, type, + parameterTypeRegistry.getLocale()))); } } diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/ParameterTypeRegistry.java b/java/src/main/java/io/cucumber/cucumberexpressions/ParameterTypeRegistry.java index 5c6a3261..964d261c 100644 --- a/java/src/main/java/io/cucumber/cucumberexpressions/ParameterTypeRegistry.java +++ b/java/src/main/java/io/cucumber/cucumberexpressions/ParameterTypeRegistry.java @@ -47,14 +47,16 @@ public final class ParameterTypeRegistry { * To maintain consistency with `datatable` we don't use the mutable default * transformer to handle build in in conversions yet. */ - private final ParameterByTypeTransformer internalParameterTransformer; - private ParameterByTypeTransformer defaultParameterTransformer; + private final LocaleParameterByTypeTransformer internalParameterTransformer; + private LocaleParameterByTypeTransformer defaultParameterTransformer; + private Locale locale; public ParameterTypeRegistry(Locale locale) { this(new BuiltInParameterTransformer(locale), locale); + this.locale = locale; } - private ParameterTypeRegistry(ParameterByTypeTransformer defaultParameterTransformer, Locale locale) { + private ParameterTypeRegistry(LocaleParameterByTypeTransformer defaultParameterTransformer, Locale locale) { this.internalParameterTransformer = defaultParameterTransformer; this.defaultParameterTransformer = defaultParameterTransformer; @@ -161,11 +163,21 @@ public void defineParameterType(ParameterType parameterType) { } } - ParameterByTypeTransformer getDefaultParameterTransformer() { + LocaleParameterByTypeTransformer getDefaultParameterTransformer() { return defaultParameterTransformer; } public void setDefaultParameterTransformer(ParameterByTypeTransformer defaultParameterTransformer) { + final LocaleParameterByTypeTransformer localeParameterTransformer; + if (defaultParameterTransformer instanceof LocaleParameterByTypeTransformer) { + localeParameterTransformer = (LocaleParameterByTypeTransformer) defaultParameterTransformer; + } else { + localeParameterTransformer = defaultParameterTransformer::transform; + } + setDefaultParameterTransformer(localeParameterTransformer); + } + + public void setDefaultParameterTransformer(LocaleParameterByTypeTransformer defaultParameterTransformer) { this.defaultParameterTransformer = defaultParameterTransformer; } @@ -186,6 +198,10 @@ ParameterType lookupByRegexp(String parameterTypeRegexp, Pattern expressi return (ParameterType) parameterTypes.first(); } + Locale getLocale() { + return locale; + } + Collection> getParameterTypes() { return parameterTypeByName.values(); } diff --git a/java/src/main/java/io/cucumber/cucumberexpressions/RegularExpression.java b/java/src/main/java/io/cucumber/cucumberexpressions/RegularExpression.java index 53c53f3a..58dd4799 100644 --- a/java/src/main/java/io/cucumber/cucumberexpressions/RegularExpression.java +++ b/java/src/main/java/io/cucumber/cucumberexpressions/RegularExpression.java @@ -36,7 +36,7 @@ public List> match(String text, Type... typeHints) { return null; } - final ParameterByTypeTransformer defaultTransformer = parameterTypeRegistry.getDefaultParameterTransformer(); + final LocaleParameterByTypeTransformer defaultTransformer = parameterTypeRegistry.getDefaultParameterTransformer(); final List> parameterTypes = new ArrayList<>(); int typeHintIndex = 0; for (GroupBuilder groupBuilder : treeRegexp.getGroupBuilder().getChildren()) { @@ -63,7 +63,8 @@ public List> match(String text, Type... typeHints) { // Either from createAnonymousParameterType or lookupByRegexp if (parameterType.isAnonymous()) { - parameterType = parameterType.deAnonymize(typeHint, arg -> defaultTransformer.transform(arg, typeHint)); + parameterType = parameterType.deAnonymize(typeHint, arg -> defaultTransformer.transform(arg, typeHint, + parameterTypeRegistry.getLocale())); } parameterTypes.add(parameterType);