Skip to content

Commit d533408

Browse files
committed
Wrap match result in optional to avoid nullable in API
1 parent 7827c6e commit d533408

File tree

7 files changed

+132
-90
lines changed

7 files changed

+132
-90
lines changed

java/src/main/java/io/cucumber/cucumberexpressions/ParameterTypeRegistry.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,13 @@ private ParameterTypeRegistry(ParameterByTypeTransformer defaultParameterTransfo
139139
}
140140

141141
public void defineParameterType(ParameterType<?> parameterType) {
142-
if (parameterType.getName() != null) {
143-
if (parameterTypeByName.containsKey(parameterType.getName())) {
144-
if (parameterType.getName().isEmpty()) {
145-
throw new DuplicateTypeNameException("The anonymous parameter type has already been defined");
146-
}
147-
throw new DuplicateTypeNameException(String.format("There is already a parameter type with name %s", parameterType.getName()));
142+
if (parameterTypeByName.containsKey(parameterType.getName())) {
143+
if (parameterType.getName().isEmpty()) {
144+
throw new DuplicateTypeNameException("The anonymous parameter type has already been defined");
148145
}
149-
parameterTypeByName.put(parameterType.getName(), parameterType);
146+
throw new DuplicateTypeNameException(String.format("There is already a parameter type with name %s", parameterType.getName()));
150147
}
148+
parameterTypeByName.put(parameterType.getName(), parameterType);
151149

152150
for (String parameterTypeRegexp : parameterType.getRegexps()) {
153151
if (!parameterTypesByRegexp.containsKey(parameterTypeRegexp)) {

java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionGeneratorTest.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.Date;
1212
import java.util.List;
1313
import java.util.Locale;
14+
import java.util.Optional;
1415

1516
import static java.util.Arrays.asList;
1617
import static java.util.Collections.singletonList;
@@ -34,20 +35,20 @@ public void documents_expression_generation() {
3435

3536
@Test
3637
public void generates_expression_for_no_args() {
37-
assertExpression("hello", Collections.<String>emptyList(), "hello");
38+
assertExpression("hello", Collections.emptyList(), "hello");
3839
}
3940

4041
@Test
4142
public void generates_expression_with_escaped_left_parenthesis() {
4243
assertExpression(
43-
"\\(iii)", Collections.<String>emptyList(),
44+
"\\(iii)", Collections.emptyList(),
4445
"(iii)");
4546
}
4647

4748
@Test
4849
public void generates_expression_with_escaped_left_curly_brace() {
4950
assertExpression(
50-
"\\{iii}", Collections.<String>emptyList(),
51+
"\\{iii}", Collections.emptyList(),
5152
"{iii}");
5253
}
5354

@@ -131,7 +132,7 @@ public void does_not_suggest_parameter_type_when_surrounded_by_alphanum() {
131132
false
132133
));
133134
assertExpression(
134-
"I like muppets", Collections.<String>emptyList(),
135+
"I like muppets", Collections.emptyList(),
135136
"I like muppets");
136137
}
137138

@@ -318,11 +319,11 @@ private void assertExpression(String expectedExpression, List<String> expectedAr
318319

319320
// Check that the generated expression matches the text
320321
CucumberExpression cucumberExpression = new CucumberExpression(generatedExpression.getSource(), parameterTypeRegistry);
321-
List<Argument<?>> match = cucumberExpression.match(text);
322-
if (match == null) {
322+
Optional<List<Argument<?>>> match = cucumberExpression.match(text);
323+
if (match.isEmpty()) {
323324
fail(String.format("Expected text '%s' to match generated expression '%s'", text, generatedExpression.getSource()));
324325
}
325-
assertEquals(expectedArgumentNames.size(), match.size());
326+
assertEquals(expectedArgumentNames.size(), match.get().size());
326327
}
327328

328329
}

java/src/test/java/io/cucumber/cucumberexpressions/CucumberExpressionTest.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
import java.nio.file.Paths;
2323
import java.util.ArrayList;
2424
import java.util.Arrays;
25+
import java.util.Collection;
2526
import java.util.Comparator;
2627
import java.util.List;
2728
import java.util.Locale;
2829
import java.util.Map;
30+
import java.util.Optional;
2931
import java.util.stream.Collectors;
3032

3133
import static java.nio.file.Files.newDirectoryStream;
@@ -60,8 +62,9 @@ static List<Path> acceptance_tests_pass() throws IOException {
6062
void acceptance_tests_pass(@ConvertWith(Converter.class) Expectation expectation) {
6163
if (expectation.exception == null) {
6264
CucumberExpression expression = new CucumberExpression(expectation.expression, parameterTypeRegistry);
63-
List<Argument<?>> match = expression.match(requireNonNull(expectation.text));
64-
List<?> values = match == null ? null : match.stream()
65+
Optional<List<Argument<?>>> match = expression.match(requireNonNull(expectation.text));
66+
List<?> values = match.isEmpty() ? null : match.stream()
67+
.flatMap(Collection::stream)
6568
.map(Argument::getValue)
6669
.collect(Collectors.toList());
6770

@@ -99,9 +102,9 @@ void matches_anonymous_parameter_type_with_hint() {
99102
void documents_match_arguments() {
100103
String expr = "I have {int} cuke(s)";
101104
Expression expression = new CucumberExpression(expr, parameterTypeRegistry);
102-
List<Argument<?>> args = expression.match("I have 7 cukes");
105+
Optional<List<Argument<?>>> args = expression.match("I have 7 cukes");
103106
assertNotNull(args);
104-
assertEquals(7, args.get(0).getValue());
107+
assertEquals(7, args.get().get(0).getValue());
105108
}
106109

107110
@Test
@@ -179,16 +182,15 @@ private List<?> match(String expr, String text, Locale locale, Type... typeHints
179182
@Nullable
180183
private List<?> match(String expr, String text, ParameterTypeRegistry parameterTypeRegistry, Type... typeHints) {
181184
CucumberExpression expression = new CucumberExpression(expr, parameterTypeRegistry);
182-
List<Argument<?>> args = expression.match(text, typeHints);
183-
if (args == null) {
185+
Optional<List<Argument<?>>> match = expression.match(text, typeHints);
186+
if (match.isEmpty()) {
184187
return null;
185188
} else {
186-
List<Object> list = new ArrayList<>();
187-
for (Argument<?> arg : args) {
188-
Object value = arg.getValue();
189-
list.add(value);
190-
}
191-
return list;
189+
return match.stream()
190+
.flatMap(Collection::stream)
191+
.map(Argument::getValue)
192+
.map(Object.class::cast)
193+
.toList();
192194
}
193195
}
194196

java/src/test/java/io/cucumber/cucumberexpressions/EnumParameterTypeTest.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package io.cucumber.cucumberexpressions;
22

3+
import org.assertj.core.api.AbstractObjectAssert;
4+
import org.assertj.core.api.InstanceOfAssertFactories;
35
import org.junit.jupiter.api.Test;
46

7+
import java.util.List;
58
import java.util.Locale;
9+
import java.util.Optional;
610

711
import static org.assertj.core.api.Assertions.assertThat;
812

@@ -21,9 +25,16 @@ void converts_to_enum() {
2125

2226
var expression = new CucumberExpression("I am {Mood}", registry);
2327
var args = expression.match("I am happy");
24-
assertThat(args).singleElement()
25-
.extracting(Argument::getValue)
26-
.isEqualTo(Mood.happy);
28+
asserThatSingleArgumentValue(args).isEqualTo(Mood.happy);
29+
}
30+
31+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
32+
private static AbstractObjectAssert<?, Object> asserThatSingleArgumentValue(Optional<List<Argument<?>>> match) {
33+
return assertThat(match).get()
34+
.asInstanceOf(InstanceOfAssertFactories.LIST)
35+
.map(Argument.class::cast)
36+
.singleElement()
37+
.extracting(Argument::getValue);
2738
}
2839

2940
}

java/src/test/java/io/cucumber/cucumberexpressions/GenericParameterTypeTest.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package io.cucumber.cucumberexpressions;
22

3+
import org.assertj.core.api.AbstractObjectAssert;
4+
import org.assertj.core.api.InstanceOfAssertFactories;
35
import org.jspecify.annotations.Nullable;
46
import org.junit.jupiter.api.Test;
57

68
import java.util.List;
79
import java.util.Locale;
10+
import java.util.Optional;
811

912
import static java.util.Arrays.asList;
1013
import static java.util.Collections.singletonList;
@@ -19,16 +22,23 @@ public void transforms_to_a_list_of_string() {
1922
parameterTypeRegistry.defineParameterType(new ParameterType<>(
2023
"stringlist",
2124
singletonList(".*"),
22-
new TypeReference<List<String>>() {}.getType(),
25+
new TypeReference<List<String>>() {
26+
}.getType(),
2327
(@Nullable String arg) -> asList(requireNonNull(arg).split(",")),
2428
false,
2529
false)
2630
);
2731
var expression = new CucumberExpression("I have {stringlist} yay", parameterTypeRegistry);
2832
var args = expression.match("I have three,blind,mice yay");
29-
assertThat(args).singleElement()
30-
.extracting(Argument::getValue)
31-
.isEqualTo(asList("three", "blind", "mice"));
33+
asserThatSingleArgumentValue(args).isEqualTo(asList("three", "blind", "mice"));
3234
}
3335

36+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
37+
private static AbstractObjectAssert<?, Object> asserThatSingleArgumentValue(Optional<List<Argument<?>>> match) {
38+
return assertThat(match).get()
39+
.asInstanceOf(InstanceOfAssertFactories.LIST)
40+
.map(Argument.class::cast)
41+
.singleElement()
42+
.extracting(Argument::getValue);
43+
}
3444
}

java/src/test/java/io/cucumber/cucumberexpressions/ParameterTypeRegistryTest.java

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package io.cucumber.cucumberexpressions;
22

3+
import org.assertj.core.api.AbstractObjectAssert;
4+
import org.assertj.core.api.InstanceOfAssertFactories;
5+
import org.jspecify.annotations.Nullable;
36
import org.junit.jupiter.api.Test;
47
import org.junit.jupiter.api.function.Executable;
5-
import org.junit.jupiter.params.shadow.de.siegmar.fastcsv.util.Nullable;
68

79
import java.math.BigDecimal;
10+
import java.util.List;
811
import java.util.Locale;
12+
import java.util.Optional;
913
import java.util.regex.Pattern;
1014

1115
import static org.assertj.core.api.Assertions.assertThat;
@@ -89,7 +93,7 @@ public void throws_ambiguous_exception_on_lookup_when_no_parameter_types_are_pre
8993

9094
@Test
9195
public void does_not_allow_anonymous_parameter_type_to_be_registered() {
92-
Executable testMethod = () -> registry.defineParameterType(new ParameterType<>("", ".*", Object.class, (Transformer<Object>) arg -> arg));
96+
Executable testMethod = () -> registry.defineParameterType(new ParameterType<>("", ".*", Object.class, (@Nullable String arg) -> arg));
9397

9498
var exception = assertThrows(DuplicateTypeNameException.class, testMethod);
9599
assertThat(exception).hasMessage("The anonymous parameter type has already been defined");
@@ -100,72 +104,81 @@ public void parse_decimal_numbers_in_english() {
100104
ExpressionFactory factory = new ExpressionFactory(new ParameterTypeRegistry(Locale.ENGLISH));
101105
Expression expression = factory.createExpression("{bigdecimal}");
102106

103-
assertThat(expression.match("")).isNull();
104-
assertThat(expression.match(".")).isNull();
105-
assertThat(expression.match(",")).isNull();
106-
assertThat(expression.match("-")).isNull();
107-
assertThat(expression.match("E")).isNull();
108-
assertThat(expression.match("1,")).isNull();
109-
assertThat(expression.match(",1")).isNull();
110-
assertThat(expression.match("1.")).isNull();
111-
112-
assertThat(expression.match("1")).singleElement().extracting(Argument::getValue).isEqualTo(BigDecimal.ONE);
113-
assertThat(expression.match("-1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1"));
114-
assertThat(expression.match("1.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1.1"));
115-
assertThat(expression.match("1,000")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000"));
116-
assertThat(expression.match("1,000,0")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("10000"));
117-
assertThat(expression.match("1,000.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000.1"));
118-
assertThat(expression.match("1,000,10")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("100010"));
119-
assertThat(expression.match("1,0.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("10.1"));
120-
assertThat(expression.match("1,000,000.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000000.1"));
121-
assertThat(expression.match("-1.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1.1"));
122-
123-
assertThat(expression.match(".1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("0.1"));
124-
assertThat(expression.match("-.1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-0.1"));
125-
assertThat(expression.match("-.10000001")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-0.10000001"));
107+
assertThat(expression.match("")).isEmpty();
108+
assertThat(expression.match(".")).isEmpty();
109+
assertThat(expression.match(",")).isEmpty();
110+
assertThat(expression.match("-")).isEmpty();
111+
assertThat(expression.match("E")).isEmpty();
112+
assertThat(expression.match("1,")).isEmpty();
113+
assertThat(expression.match(",1")).isEmpty();
114+
assertThat(expression.match("1.")).isEmpty();
115+
116+
asserThatSingleArgumentValue(expression.match("1")).isEqualTo(BigDecimal.ONE);
117+
asserThatSingleArgumentValue(expression.match("-1")).isEqualTo(new BigDecimal("-1"));
118+
asserThatSingleArgumentValue(expression.match("1.1")).isEqualTo(new BigDecimal("1.1"));
119+
asserThatSingleArgumentValue(expression.match("1,000")).isEqualTo(new BigDecimal("1000"));
120+
asserThatSingleArgumentValue(expression.match("1,000,0")).isEqualTo(new BigDecimal("10000"));
121+
asserThatSingleArgumentValue(expression.match("1,000.1")).isEqualTo(new BigDecimal("1000.1"));
122+
asserThatSingleArgumentValue(expression.match("1,000,10")).isEqualTo(new BigDecimal("100010"));
123+
asserThatSingleArgumentValue(expression.match("1,0.1")).isEqualTo(new BigDecimal("10.1"));
124+
asserThatSingleArgumentValue(expression.match("1,000,000.1")).isEqualTo(new BigDecimal("1000000.1"));
125+
asserThatSingleArgumentValue(expression.match("-1.1")).isEqualTo(new BigDecimal("-1.1"));
126+
127+
asserThatSingleArgumentValue(expression.match(".1")).isEqualTo(new BigDecimal("0.1"));
128+
asserThatSingleArgumentValue(expression.match("-.1")).isEqualTo(new BigDecimal("-0.1"));
129+
asserThatSingleArgumentValue(expression.match("-.10000001")).isEqualTo(new BigDecimal("-0.10000001"));
126130
// precision 1 with scale -1, can not be expressed as a decimal
127-
assertThat(expression.match("1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1E1"));
128-
assertThat(expression.match(".1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1"));
129-
assertThat(expression.match("E1")).isNull();
130-
assertThat(expression.match("-.1E-1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-0.01"));
131-
assertThat(expression.match("-.1E-2")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-0.001"));
132-
assertThat(expression.match("-.1E+1")).isNull();
133-
assertThat(expression.match("-.1E+2")).isNull();
134-
assertThat(expression.match("-.1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1"));
135-
assertThat(expression.match("-.10E2")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-10"));
131+
asserThatSingleArgumentValue(expression.match("1E1")).isEqualTo(new BigDecimal("1E1"));
132+
asserThatSingleArgumentValue(expression.match(".1E1")).isEqualTo(new BigDecimal("1"));
133+
assertThat(expression.match("E1")).isEmpty();
134+
asserThatSingleArgumentValue(expression.match("-.1E-1")).isEqualTo(new BigDecimal("-0.01"));
135+
asserThatSingleArgumentValue(expression.match("-.1E-2")).isEqualTo(new BigDecimal("-0.001"));
136+
assertThat(expression.match("-.1E+1")).isEmpty();
137+
assertThat(expression.match("-.1E+2")).isEmpty();
138+
asserThatSingleArgumentValue(expression.match("-.1E1")).isEqualTo(new BigDecimal("-1"));
139+
asserThatSingleArgumentValue(expression.match("-.10E2")).isEqualTo(new BigDecimal("-10"));
136140
}
137141

138142
@Test
139143
public void parse_decimal_numbers_in_german() {
140144
ExpressionFactory factory = new ExpressionFactory(new ParameterTypeRegistry(Locale.GERMAN));
141145
Expression expression = factory.createExpression("{bigdecimal}");
142146

143-
assertThat(expression.match("1.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000.1"));
144-
assertThat(expression.match("1.000.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000000.1"));
145-
assertThat(expression.match("-1,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1.1"));
146-
assertThat(expression.match("-,1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1"));
147+
asserThatSingleArgumentValue(expression.match("1.000,1")).isEqualTo(new BigDecimal("1000.1"));
148+
asserThatSingleArgumentValue(expression.match("1.000.000,1")).isEqualTo(new BigDecimal("1000000.1"));
149+
asserThatSingleArgumentValue(expression.match("-1,1")).isEqualTo(new BigDecimal("-1.1"));
150+
asserThatSingleArgumentValue(expression.match("-,1E1")).isEqualTo(new BigDecimal("-1"));
147151
}
148152

149153
@Test
150154
public void parse_decimal_numbers_in_canadian_french() {
151155
ExpressionFactory factory = new ExpressionFactory(new ParameterTypeRegistry(Locale.CANADA_FRENCH));
152156
Expression expression = factory.createExpression("{bigdecimal}");
153157

154-
assertThat(expression.match("1.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000.1"));
155-
assertThat(expression.match("1.000.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000000.1"));
156-
assertThat(expression.match("-1,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1.1"));
157-
assertThat(expression.match("-,1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1"));
158+
asserThatSingleArgumentValue(expression.match("1.000,1")).isEqualTo(new BigDecimal("1000.1"));
159+
asserThatSingleArgumentValue(expression.match("1.000.000,1")).isEqualTo(new BigDecimal("1000000.1"));
160+
asserThatSingleArgumentValue(expression.match("-1,1")).isEqualTo(new BigDecimal("-1.1"));
161+
asserThatSingleArgumentValue(expression.match("-,1E1")).isEqualTo(new BigDecimal("-1"));
158162
}
159163

160164
@Test
161165
public void parse_decimal_numbers_in_norwegian() {
162166
ExpressionFactory factory = new ExpressionFactory(new ParameterTypeRegistry(Locale.forLanguageTag("no")));
163167
Expression expression = factory.createExpression("{bigdecimal}");
164168

165-
assertThat(expression.match("1.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000.1"));
166-
assertThat(expression.match("1.000.000,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("1000000.1"));
167-
assertThat(expression.match("-1,1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1.1"));
168-
assertThat(expression.match("-,1E1")).singleElement().extracting(Argument::getValue).isEqualTo(new BigDecimal("-1"));
169+
asserThatSingleArgumentValue(expression.match("1.000,1")).isEqualTo(new BigDecimal("1000.1"));
170+
asserThatSingleArgumentValue(expression.match("1.000.000,1")).isEqualTo(new BigDecimal("1000000.1"));
171+
asserThatSingleArgumentValue(expression.match("-1,1")).isEqualTo(new BigDecimal("-1.1"));
172+
asserThatSingleArgumentValue(expression.match("-,1E1")).isEqualTo(new BigDecimal("-1"));
173+
}
174+
175+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
176+
private static AbstractObjectAssert<?, Object> asserThatSingleArgumentValue(Optional<List<Argument<?>>> match) {
177+
return assertThat(match).get()
178+
.asInstanceOf(InstanceOfAssertFactories.LIST)
179+
.map(Argument.class::cast)
180+
.singleElement()
181+
.extracting(Argument::getValue);
169182
}
170183

171184
public static class Name {

0 commit comments

Comments
 (0)