diff --git a/CHANGELOG.md b/CHANGELOG.md index 88aad4b570..736afc04c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [Core] Update dependency io.cucumber:html-formatter to v22.1.0 - [Core] Update dependency io.cucumber:junit-xml-formatter to v0.11.0 - [Core] Update dependency io.cucumber:pretty-formatter to v2.4.1 +- [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) ### Fixed - [Core] Add OS version to `Meta` message ([#3108](https://github.com/cucumber/cucumber-jvm/pull/3108)) diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultParameterTransformerDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultParameterTransformerDefinition.java index ddc9248701..4cbac1736c 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultParameterTransformerDefinition.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/DefaultParameterTransformerDefinition.java @@ -3,9 +3,15 @@ import io.cucumber.cucumberexpressions.ParameterByTypeTransformer; import org.apiguardian.api.API; +import java.util.Locale; + @API(status = API.Status.STABLE) public interface DefaultParameterTransformerDefinition extends Located { ParameterByTypeTransformer parameterByTypeTransformer(); + default ParameterByTypeTransformer parameterByTypeTransformer(Locale locale) { + return this.parameterByTypeTransformer(); + } + } diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java index fbdbd66d52..26a1f4a461 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -277,7 +277,7 @@ void prepareGlue(Locale locale) throws DuplicateStepDefinitionException { if (defaultParameterTransformers.size() == 1) { DefaultParameterTransformerDefinition definition = defaultParameterTransformers.get(0); - ParameterByTypeTransformer transformer = definition.parameterByTypeTransformer(); + ParameterByTypeTransformer transformer = definition.parameterByTypeTransformer(locale); stepTypeRegistry.setDefaultParameterTransformer(transformer); } else if (defaultParameterTransformers.size() > 1) { throw new DuplicateDefaultParameterTransformers(defaultParameterTransformers); 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..7d8d2fdbf6 100644 --- a/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java +++ b/cucumber-java/src/main/java/io/cucumber/java/DefaultParameterTransformer.java @@ -14,6 +14,8 @@ * * * @see io.cucumber.cucumberexpressions.ParameterByTypeTransformer 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..59609c587c 100644 --- a/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java +++ b/cucumber-java/src/main/java/io/cucumber/java/JavaDefaultParameterTransformerDefinition.java @@ -6,6 +6,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Locale; import static io.cucumber.java.InvalidMethodSignatureException.builder; @@ -13,12 +14,10 @@ class JavaDefaultParameterTransformerDefinition extends AbstractGlueDefinition implements DefaultParameterTransformerDefinition { private final Lookup lookup; - private final ParameterByTypeTransformer transformer; JavaDefaultParameterTransformerDefinition(Method method, Lookup lookup) { super(requireValidMethod(method), lookup); this.lookup = lookup; - this.transformer = this::execute; } private static Method requireValidMethod(Method method) { @@ -28,7 +27,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 +39,19 @@ 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) { + if (method.getParameterTypes().length == 2) { + return Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method, fromValue, toValueType); + } else { + return Invoker.invoke(this, lookup.getInstance(method.getDeclaringClass()), method, fromValue, toValueType, + locale); + } } private static InvalidMethodSignatureException createInvalidSignatureException(Method method) { @@ -52,12 +59,19 @@ 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() { - return transformer; + return this.parameterByTypeTransformer(null); + } + + @Override + public ParameterByTypeTransformer parameterByTypeTransformer(Locale locale) { + return (fromValue, toValueType) -> execute(fromValue, toValueType, locale); } } 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..bbad1f86ba 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,34 @@ 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(Locale.ENGLISH).transform("something", String.class); + 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(Locale.ENGLISH).transform("something", String.class); + 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 +72,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(Locale.ENGLISH).transform("something", + String.class); + 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(Locale.ENGLISH).transform("something", + String.class); + 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 +112,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(Object 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 +121,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, + Method fourArg = JavaDefaultParameterTransformerDefinitionTest.class.getMethod("four_arguments", String.class, Type.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 +155,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 +175,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"; + } + }