Skip to content

Commit 42e9c17

Browse files
committed
Moved table conversion logic from java to core
1 parent 0847bed commit 42e9c17

File tree

14 files changed

+92
-95
lines changed

14 files changed

+92
-95
lines changed

clojure/src/main/java/cucumber/runtime/clojure/ClojureStepDefinition.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ public List<Argument> matchedArguments(Step step) {
3535
return new JdkPatternArgumentMatcher(pattern).argumentsFrom(step.getName());
3636
}
3737

38+
@Override
39+
public Class getTypeForTableList(int argIndex) {
40+
return null;
41+
}
42+
3843
public String getLocation() {
3944
return location.getFileName() + ":" + location.getLineNumber();
4045
}
@@ -60,9 +65,4 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
6065
public String getPattern() {
6166
return pattern.pattern();
6267
}
63-
64-
@Override
65-
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
66-
return new Table(rows);
67-
}
6868
}

core/src/main/java/cucumber/runtime/Runtime.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import cucumber.resources.Resources;
44
import cucumber.runtime.converters.LocalizedXStreams;
5+
import cucumber.table.CamelCaseHeaderMapper;
6+
import cucumber.table.TableHeaderMapper;
57
import gherkin.formatter.Argument;
68
import gherkin.formatter.model.Step;
79

@@ -13,6 +15,7 @@ public class Runtime {
1315
private final List<Step> undefinedSteps = new ArrayList<Step>();
1416
private final List<Backend> backends;
1517
private final LocalizedXStreams localizedXStreams = new LocalizedXStreams();
18+
private final TableHeaderMapper tableHeaderMapper = new CamelCaseHeaderMapper();
1619

1720
public Runtime(Backend... backends) {
1821
this.backends = asList(backends);
@@ -41,7 +44,7 @@ private List<StepDefinitionMatch> stepDefinitionMatches(String uri, Step step) {
4144
for (StepDefinition stepDefinition : backend.getStepDefinitions()) {
4245
List<Argument> arguments = stepDefinition.matchedArguments(step);
4346
if (arguments != null) {
44-
result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams));
47+
result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams, tableHeaderMapper));
4548
}
4649
}
4750
}

core/src/main/java/cucumber/runtime/StepDefinition.java

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package cucumber.runtime;
22

3-
import cucumber.table.TableConverter;
43
import gherkin.formatter.Argument;
5-
import gherkin.formatter.model.Row;
64
import gherkin.formatter.model.Step;
75

86
import java.util.List;
@@ -16,18 +14,14 @@ public interface StepDefinition {
1614
List<Argument> matchedArguments(Step step);
1715

1816
/**
19-
* Returns a different representation of {@code table} for the argument at position {@code argIndex}. This allows
20-
* step definitions to accept a higher level argument type. If the implementation does not know how to transform
21-
* a table, the table itself should be returned.
22-
*
23-
*
24-
* @param argIndex index of the argument
25-
* @param rows
26-
* @param tableConverter
27-
* @return the {@link Object} associated with the argument
28-
* at argIndex or table if there's none
17+
* Cucumber will try to convert each Gherkin step table into a {@link List} of objects. The header row is used to
18+
* identify property names of the objects, and each row underneath will be converted to an object.
19+
*
20+
* If this method returns null, Cucumber will convert the rows into an instance of {@link cucumber.table.Table}.
21+
*
22+
* @return the kind of object Cucumber should instantiate for each row, or null if no conversion should happen.
2923
*/
30-
Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter);
24+
Class getTypeForTableList(int argIndex);
3125

3226
/**
3327
* The source line where the step definition is defined.

core/src/main/java/cucumber/runtime/StepDefinitionMatch.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
import com.thoughtworks.xstream.converters.ConverterLookup;
44
import com.thoughtworks.xstream.converters.SingleValueConverter;
55
import cucumber.runtime.converters.LocalizedXStreams;
6+
import cucumber.table.Table;
67
import cucumber.table.TableConverter;
8+
import cucumber.table.TableHeaderMapper;
79
import gherkin.formatter.Argument;
810
import gherkin.formatter.model.Match;
11+
import gherkin.formatter.model.Row;
912
import gherkin.formatter.model.Step;
1013

1114
import java.lang.reflect.InvocationTargetException;
15+
import java.util.ArrayList;
1216
import java.util.List;
1317
import java.util.Locale;
1418

@@ -19,21 +23,24 @@ public class StepDefinitionMatch extends Match {
1923
private final String uri;
2024
private final Step step;
2125
private final LocalizedXStreams localizedXStreams;
26+
private final TableHeaderMapper tableHeaderMapper;
2227

23-
public StepDefinitionMatch(List<Argument> arguments, StepDefinition stepDefinition, String uri, Step step, LocalizedXStreams localizedXStreams) {
28+
public StepDefinitionMatch(List<Argument> arguments, StepDefinition stepDefinition, String uri, Step step, LocalizedXStreams localizedXStreams, TableHeaderMapper tableHeaderMapper) {
2429
super(arguments, stepDefinition.getLocation());
2530
this.stepDefinition = stepDefinition;
2631
this.uri = uri;
2732
this.step = step;
2833
this.localizedXStreams = localizedXStreams;
34+
this.tableHeaderMapper = tableHeaderMapper;
2935
}
3036

3137
public void runStep(Locale locale) throws Throwable {
3238
if (locale == null) {
3339
throw new NullPointerException("null Locale!");
3440
}
3541
try {
36-
stepDefinition.execute(transformedArgs(stepDefinition.getParameterTypes(), step, locale));
42+
Object[] args = transformedArgs(stepDefinition.getParameterTypes(), step, locale);
43+
stepDefinition.execute(args);
3744
} catch (CucumberException e) {
3845
throw e;
3946
} catch (InvocationTargetException t) {
@@ -70,9 +77,39 @@ private Object[] transformedArgs(Class<?>[] parameterTypes, Step step, Locale lo
7077
}
7178

7279
private Object tableArgument(Step step, TableConverter tableConverter, int argIndex) {
73-
return stepDefinition.tableArgument(argIndex, step.getRows(), tableConverter);
80+
Class listType = stepDefinition.getTypeForTableList(argIndex);
81+
if(listType != null) {
82+
return tableConverter.convert(listType, attributeNames(step.getRows()), attributeValues(step.getRows()));
83+
} else {
84+
return new Table(step.getRows());
85+
}
86+
}
87+
88+
private List<List<String>> attributeValues(List<Row> rows) {
89+
List<List<String>> attributeValues = new ArrayList<List<String>>();
90+
List<Row> valueRows = rows.subList(1, rows.size());
91+
for (Row valueRow : valueRows) {
92+
attributeValues.add(toStrings(valueRow));
93+
}
94+
return attributeValues;
7495
}
7596

97+
private List<String> attributeNames(List<Row> rows) {
98+
List<String> strings = new ArrayList<String>();
99+
for (String string : rows.get(0).getCells()) {
100+
strings.add(tableHeaderMapper.map(string));
101+
}
102+
return strings;
103+
}
104+
105+
private List<String> toStrings(Row row) {
106+
List<String> strings = new ArrayList<String>();
107+
for (String string : row.getCells()) {
108+
strings.add(string);
109+
}
110+
return strings;
111+
}
112+
76113
public Throwable filterStacktrace(Throwable error, StackTraceElement stepLocation) {
77114
StackTraceElement[] stackTraceElements = error.getStackTrace();
78115
if (error.getCause() != null && error.getCause() != error) {

java/src/main/java/cucumber/table/java/JavaBeanPropertyHeaderMapper.java renamed to core/src/main/java/cucumber/table/CamelCaseHeaderMapper.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
package cucumber.table.java;
2-
3-
import cucumber.table.TableHeaderMapper;
1+
package cucumber.table;
42

53
import java.util.regex.Pattern;
64

7-
public class JavaBeanPropertyHeaderMapper implements TableHeaderMapper {
5+
public class CamelCaseHeaderMapper implements TableHeaderMapper {
86

97
private static final String WHITESPACE = " ";
108
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
@@ -21,8 +19,8 @@ public String map(String originalHeaderName) {
2119

2220
private String join(String[] splitted) {
2321
StringBuilder sb = new StringBuilder();
24-
for (int i = 0; i < splitted.length; i++) {
25-
sb.append(splitted[i]);
22+
for (String s : splitted) {
23+
sb.append(s);
2624
}
2725
return sb.toString();
2826
}

core/src/test/java/cucumber/runtime/StepDefinitionMatchTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void shouldConvertParameters() throws Throwable {
2323
when(stepWithoutDocStringOrTable.getDocString()).thenReturn(null);
2424
when(stepWithoutDocStringOrTable.getRows()).thenReturn(null);
2525

26-
StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(arguments, stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams());
26+
StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(arguments, stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams(), null);
2727
stepDefinitionMatch.runStep(Locale.ENGLISH);
2828
Object[] args = {5};
2929
verify(stepDefinition).execute(args);

java/src/test/java/cucumber/table/java/JavaBeanPropertyHeaderMapperTest.java renamed to core/src/test/java/cucumber/table/CamelCaseHeaderMapperTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
package cucumber.table.java;
1+
package cucumber.table;
22

33
import org.junit.Test;
44

55
import static org.junit.Assert.assertEquals;
66

7-
public class JavaBeanPropertyHeaderMapperTest {
7+
public class CamelCaseHeaderMapperTest {
88
@Test
99
public void testTransformToJavaPropertyName() {
10-
JavaBeanPropertyHeaderMapper mapper = new JavaBeanPropertyHeaderMapper();
10+
CamelCaseHeaderMapper mapper = new CamelCaseHeaderMapper();
1111
assertEquals("Transformed Name", "userName", mapper.map("User Name"));
1212
assertEquals("Transformed Name", "birthDate", mapper.map(" Birth Date\t"));
1313
assertEquals("Transformed Name", "email", mapper.map("email"));

groovy/src/main/java/cucumber/runtime/groovy/GroovyStepDefinition.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import gherkin.formatter.model.Step;
1010
import groovy.lang.Closure;
1111

12+
import java.lang.reflect.ParameterizedType;
13+
import java.lang.reflect.Type;
1214
import java.util.List;
1315
import java.util.regex.Pattern;
1416

@@ -31,6 +33,11 @@ public List<Argument> matchedArguments(Step step) {
3133
return argumentMatcher.argumentsFrom(step.getName());
3234
}
3335

36+
@Override
37+
public Class getTypeForTableList(int argIndex) {
38+
return null;
39+
}
40+
3441
public String getLocation() {
3542
return location.getFileName() + ":" + location.getLineNumber();
3643
}
@@ -51,9 +58,4 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
5158
public String getPattern() {
5259
return pattern.pattern();
5360
}
54-
55-
@Override
56-
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
57-
return new Table(rows);
58-
}
5961
}

ioke/src/main/java/cucumber/runtime/ioke/IokeStepDefinition.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
import cucumber.runtime.CucumberException;
44
import cucumber.runtime.StepDefinition;
5-
import cucumber.table.Table;
6-
import cucumber.table.TableConverter;
75
import gherkin.formatter.Argument;
8-
import gherkin.formatter.model.Row;
96
import gherkin.formatter.model.Step;
107
import ioke.lang.IokeObject;
118
import ioke.lang.Runtime;
@@ -47,6 +44,11 @@ public List<Argument> matchedArguments(Step step) {
4744
}
4845
}
4946

47+
@Override
48+
public Class getTypeForTableList(int argIndex) {
49+
return null;
50+
}
51+
5052
public String getLocation() {
5153
return location;
5254
}
@@ -74,9 +76,4 @@ public void execute(Object[] args) throws Throwable {
7476
public boolean isDefinedAt(StackTraceElement stackTraceElement) {
7577
return stackTraceElement.getClassName().equals(IokeBackend.class.getName());
7678
}
77-
78-
@Override
79-
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
80-
return new Table(rows);
81-
}
8279
}

java/src/main/java/cucumber/runtime/java/JavaStepDefinition.java

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,12 @@
33
import cucumber.runtime.CucumberException;
44
import cucumber.runtime.JdkPatternArgumentMatcher;
55
import cucumber.runtime.StepDefinition;
6-
import cucumber.table.Table;
7-
import cucumber.table.TableConverter;
8-
import cucumber.table.java.JavaBeanPropertyHeaderMapper;
96
import gherkin.formatter.Argument;
10-
import gherkin.formatter.model.Row;
117
import gherkin.formatter.model.Step;
128

139
import java.lang.reflect.Method;
1410
import java.lang.reflect.ParameterizedType;
1511
import java.lang.reflect.Type;
16-
import java.util.ArrayList;
1712
import java.util.List;
1813
import java.util.regex.Pattern;
1914

@@ -25,7 +20,6 @@ public class JavaStepDefinition implements StepDefinition {
2520
private final ObjectFactory objectFactory;
2621
private final JdkPatternArgumentMatcher argumentMatcher;
2722
private final Pattern pattern;
28-
private final JavaBeanPropertyHeaderMapper mapper = new JavaBeanPropertyHeaderMapper();
2923

3024
public JavaStepDefinition(Pattern pattern, Method method, ObjectFactory objectFactory) {
3125
this.pattern = pattern;
@@ -67,39 +61,13 @@ public String getPattern() {
6761
}
6862

6963
@Override
70-
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
64+
public Class getTypeForTableList(int argIndex) {
7165
Type genericParameterType = method.getGenericParameterTypes()[argIndex];
7266
if (genericParameterType instanceof ParameterizedType) {
7367
Type[] parameters = ((ParameterizedType) genericParameterType).getActualTypeArguments();
74-
Class<?> itemType = (Class<?>) parameters[0];
75-
return tableConverter.convert(itemType, attributeNames(rows), attributeValues(rows));
68+
return (Class<?>) parameters[0];
7669
} else {
77-
return new Table(rows);
70+
return null;
7871
}
7972
}
80-
81-
private List<List<String>> attributeValues(List<Row> rows) {
82-
List<List<String>> attributeValues = new ArrayList<List<String>>();
83-
List<Row> valueRows = rows.subList(1, rows.size());
84-
for (Row valueRow : valueRows) {
85-
attributeValues.add(toStrings(valueRow));
86-
}
87-
return attributeValues;
88-
}
89-
90-
private List<String> attributeNames(List<Row> rows) {
91-
List<String> strings = new ArrayList<String>();
92-
for (String string : rows.get(0).getCells()) {
93-
strings.add(mapper.map(string));
94-
}
95-
return strings;
96-
}
97-
98-
private List<String> toStrings(Row row) {
99-
List<String> strings = new ArrayList<String>();
100-
for (String string : row.getCells()) {
101-
strings.add(string);
102-
}
103-
return strings;
104-
}
10573
}

0 commit comments

Comments
 (0)