diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbdcf85fd..640d9c3299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - [Core] Upload Cucumber Reports with Gzip encoding ([#3115](https://github.com/cucumber/cucumber-jvm/pull/3115)) +- [Java] Any custom method declared as `@DefaultParameterTransformer` can now have a `Locale` argument to optionally consider the locale declaration of the current Feature ([cucumber/cucumber-expressions#376](https://github.com/cucumber/cucumber-expressions/issues/376) Stefan Gasterstädt) ## [7.32.0] - 2025-11-21 ### Changed diff --git a/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java b/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java index 1986fe5fa9..c0e5507f38 100644 --- a/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java +++ b/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java @@ -14,8 +14,11 @@ * * + * @see io.cucumber.cucumberexpressions.LocaleParameterByTypeTransformer * @see io.cucumber.cucumberexpressions.ParameterByTypeTransformer * @see io.cucumber.cucumberexpressions.ParameterType */ diff --git a/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java b/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java index 873e03562d..f1b1faa11c 100644 --- a/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java +++ b/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java @@ -2,10 +2,11 @@ import io.cucumber.core.backend.DefaultParameterTransformerDefinition; import io.cucumber.core.backend.Lookup; -import io.cucumber.cucumberexpressions.ParameterByTypeTransformer; +import io.cucumber.cucumberexpressions.LocaleParameterByTypeTransformer; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Locale; import static io.cucumber.java.InvalidMethodSignatureException.builder; @@ -13,12 +14,22 @@ class JavaDefaultParameterTransformerDefinition extends AbstractGlueDefinition implements DefaultParameterTransformerDefinition { private final Lookup lookup; - private final ParameterByTypeTransformer transformer; + private final LocaleParameterByTypeTransformer transformer; JavaDefaultParameterTransformerDefinition(Method method, Lookup lookup) { super(requireValidMethod(method), lookup); this.lookup = lookup; - this.transformer = this::execute; + this.transformer = new LocaleParameterByTypeTransformer() { + @Override + public Object transform(String fromValue, Type toValueType) throws Throwable { + return this.transform(fromValue, toValueType, null); + } + + @Override + public Object transform(String fromValue, Type toValueType, Locale locale) throws Throwable { + return JavaDefaultParameterTransformerDefinition.this.execute(fromValue, toValueType, locale); + } + }; } private static Method requireValidMethod(Method method) { @@ -28,7 +39,7 @@ private static Method requireValidMethod(Method method) { } Class[] parameterTypes = method.getParameterTypes(); - if (parameterTypes.length != 2) { + if ((parameterTypes.length != 2) && (parameterTypes.length != 3)) { throw createInvalidSignatureException(method); } @@ -40,11 +51,20 @@ private static Method requireValidMethod(Method method) { throw createInvalidSignatureException(method); } + if ((parameterTypes.length == 3) && !Locale.class.equals(parameterTypes[2])) { + throw createInvalidSignatureException(method); + } return method; } - private Object execute(String fromValue, Type toValueType) { - return Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method, fromValue, toValueType); + private Object execute(String fromValue, Type toValueType, Locale locale) { + Object[] args; + if (method.getParameterTypes().length == 3) { + args = new Object[] { fromValue, toValueType, locale }; + } else { + args = new Object[] { fromValue, toValueType }; + } + return invokeMethod(args); } private static InvalidMethodSignatureException createInvalidSignatureException(Method method) { @@ -52,11 +72,13 @@ private static InvalidMethodSignatureException createInvalidSignatureException(M .addAnnotation(DefaultParameterTransformer.class) .addSignature("public Object defaultDataTableEntry(String fromValue, Type toValueType)") .addSignature("public Object defaultDataTableEntry(Object fromValue, Type toValueType)") + .addSignature("public Object defaultDataTableEntry(String fromValue, Type toValueType, Locale locale)") + .addSignature("public Object defaultDataTableEntry(Object fromValue, Type toValueType, Locale locale)") .build(); } @Override - public ParameterByTypeTransformer parameterByTypeTransformer() { + public LocaleParameterByTypeTransformer parameterByTypeTransformer() { return transformer; } diff --git a/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java b/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java index fe7b184eec..13b06b6c5b 100644 --- a/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java +++ b/cucumber-java/src/test/java/io/cucumber/java/JavaDefaultParameterTransformerDefinitionTest.java @@ -5,6 +5,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Locale; import java.util.Map; import static org.hamcrest.CoreMatchers.startsWith; @@ -33,10 +34,36 @@ void can_transform_string_to_type() throws Throwable { assertThat(transformed, is("transform_string_to_type")); } + @Test + void can_transform_string_to_type_ignoring_locale() throws Throwable { + Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_string_to_type", + String.class, Type.class); + JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method, + lookup); + Object transformed = definition.parameterByTypeTransformer().transform("something", String.class, + Locale.ENGLISH); + assertThat(transformed, is("transform_string_to_type")); + } + public Object transform_string_to_type(String fromValue, Type toValueType) { return "transform_string_to_type"; } + @Test + void can_transform_string_to_type_using_locale() throws Throwable { + Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod( + "transform_string_to_type_with_locale", String.class, Type.class, Locale.class); + JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method, + lookup); + Object transformed = definition.parameterByTypeTransformer().transform("something", String.class, + Locale.ENGLISH); + assertThat(transformed, is("transform_string_to_type_with_locale_en")); + } + + public Object transform_string_to_type_with_locale(String fromValue, Type toValueType, Locale locale) { + return "transform_string_to_type_with_locale_" + locale.getLanguage(); + } + @Test void can_transform_object_to_type() throws Throwable { Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_object_to_type", @@ -47,10 +74,36 @@ void can_transform_object_to_type() throws Throwable { assertThat(transformed, is("transform_object_to_type")); } + @Test + void can_transform_object_to_type_ignoring_locale() throws Throwable { + Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transform_object_to_type", + Object.class, Type.class); + JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method, + lookup); + String transformed = (String) definition.parameterByTypeTransformer().transform("something", String.class, + Locale.ENGLISH); + assertThat(transformed, is("transform_object_to_type")); + } + public Object transform_object_to_type(Object fromValue, Type toValueType) { return "transform_object_to_type"; } + @Test + void can_transform_object_to_type_using_locale() throws Throwable { + Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod( + "transform_object_to_type_with_locale", Object.class, Type.class, Locale.class); + JavaDefaultParameterTransformerDefinition definition = new JavaDefaultParameterTransformerDefinition(method, + lookup); + String transformed = (String) definition.parameterByTypeTransformer().transform("something", String.class, + Locale.ENGLISH); + assertThat(transformed, is("transform_object_to_type_with_locale_en")); + } + + public Object transform_object_to_type_with_locale(Object fromValue, Type toValueType, Locale locale) { + return "transform_object_to_type_with_locale_" + locale.getLanguage(); + } + @Test void must_have_non_void_return() throws Throwable { Method method = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("transforms_string_to_void", @@ -61,6 +114,8 @@ void must_have_non_void_return() throws Throwable { "A @DefaultParameterTransformer annotated method must have one of these signatures:\n" + " * public Object defaultDataTableEntry(String fromValue, Type toValueType)\n" + " * public Object defaultDataTableEntry(Object fromValue, Type toValueType)\n" + + " * public Object defaultDataTableEntry(String fromValue, Type toValueType, Locale locale)\n" + + " * public Object defaultDataTableEntry(Object fromValue, Type toValueType, Locale locale)\n" + "at io.cucumber.java.JavaDefaultParameterTransformerDefinitionTest.transforms_string_to_void(java.lang.String,java.lang.reflect.Type)")); } @@ -68,28 +123,32 @@ public void transforms_string_to_void(String fromValue, Type toValueType) { } @Test - void must_have_two_arguments() throws Throwable { + void must_have_two_or_three_arguments() throws Throwable { Method oneArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("one_argument", String.class); assertThrows(InvalidMethodSignatureException.class, () -> new JavaDefaultParameterTransformerDefinition(oneArg, lookup)); - Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("three_arguments", String.class, - Type.class, Object.class); + Method fourArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("four_arguments", String.class, + Type.class, Locale.class, Object.class); assertThrows(InvalidMethodSignatureException.class, - () -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup)); + () -> new JavaDefaultParameterTransformerDefinition(fourArg, lookup)); } public Object one_argument(String fromValue) { - return "one_arguments"; + return "one_argument"; } - public Object three_arguments(String fromValue, Type toValueType, Object extra) { - return "three_arguments"; + public Object four_arguments(String fromValue, Type toValueType, Locale locale, Object extra) { + return "four_arguments"; } @Test void must_have_string_or_object_as_from_value() throws Throwable { - Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value", Map.class, + Method twoArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value", Map.class, Type.class); + assertThrows(InvalidMethodSignatureException.class, + () -> new JavaDefaultParameterTransformerDefinition(twoArg, lookup)); + Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("map_as_from_value_with_locale", + Map.class, Type.class, Locale.class); assertThrows(InvalidMethodSignatureException.class, () -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup)); } @@ -98,10 +157,18 @@ public Object map_as_from_value(Map fromValue, Type toValueType) return "map_as_from_value"; } + public Object map_as_from_value_with_locale(Map fromValue, Type toValueType, Locale locale) { + return "map_as_from_value_with_locale"; + } + @Test void must_have_type_as_to_value_type() throws Throwable { - Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("object_as_to_value_type", + Method twoArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("object_as_to_value_type", String.class, Object.class); + assertThrows(InvalidMethodSignatureException.class, + () -> new JavaDefaultParameterTransformerDefinition(twoArg, lookup)); + Method threeArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod( + "object_as_to_value_type_with_locale", String.class, Object.class, Locale.class); assertThrows(InvalidMethodSignatureException.class, () -> new JavaDefaultParameterTransformerDefinition(threeArg, lookup)); } @@ -110,4 +177,8 @@ public Object object_as_to_value_type(String fromValue, Object toValueType) { return "object_as_to_value_type"; } + public Object object_as_to_value_type_with_locale(String fromValue, Object toValueType, Locale locale) { + return "object_as_to_value_type_with_locale"; + } + }