Skip to content

Commit

Permalink
Moved table conversion logic from java to core
Browse files Browse the repository at this point in the history
  • Loading branch information
aslakhellesoy committed Sep 3, 2011
1 parent 0847bed commit 42e9c17
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 95 deletions.
Expand Up @@ -35,6 +35,11 @@ public List<Argument> matchedArguments(Step step) {
return new JdkPatternArgumentMatcher(pattern).argumentsFrom(step.getName());
}

@Override
public Class getTypeForTableList(int argIndex) {
return null;
}

public String getLocation() {
return location.getFileName() + ":" + location.getLineNumber();
}
Expand All @@ -60,9 +65,4 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
public String getPattern() {
return pattern.pattern();
}

@Override
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
return new Table(rows);
}
}
5 changes: 4 additions & 1 deletion core/src/main/java/cucumber/runtime/Runtime.java
Expand Up @@ -2,6 +2,8 @@

import cucumber.resources.Resources;
import cucumber.runtime.converters.LocalizedXStreams;
import cucumber.table.CamelCaseHeaderMapper;
import cucumber.table.TableHeaderMapper;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Step;

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

public Runtime(Backend... backends) {
this.backends = asList(backends);
Expand Down Expand Up @@ -41,7 +44,7 @@ private List<StepDefinitionMatch> stepDefinitionMatches(String uri, Step step) {
for (StepDefinition stepDefinition : backend.getStepDefinitions()) {
List<Argument> arguments = stepDefinition.matchedArguments(step);
if (arguments != null) {
result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams));
result.add(new StepDefinitionMatch(arguments, stepDefinition, uri, step, localizedXStreams, tableHeaderMapper));
}
}
}
Expand Down
20 changes: 7 additions & 13 deletions core/src/main/java/cucumber/runtime/StepDefinition.java
@@ -1,8 +1,6 @@
package cucumber.runtime;

import cucumber.table.TableConverter;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Row;
import gherkin.formatter.model.Step;

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

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

/**
* The source line where the step definition is defined.
Expand Down
43 changes: 40 additions & 3 deletions core/src/main/java/cucumber/runtime/StepDefinitionMatch.java
Expand Up @@ -3,12 +3,16 @@
import com.thoughtworks.xstream.converters.ConverterLookup;
import com.thoughtworks.xstream.converters.SingleValueConverter;
import cucumber.runtime.converters.LocalizedXStreams;
import cucumber.table.Table;
import cucumber.table.TableConverter;
import cucumber.table.TableHeaderMapper;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Match;
import gherkin.formatter.model.Row;
import gherkin.formatter.model.Step;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

Expand All @@ -19,21 +23,24 @@ public class StepDefinitionMatch extends Match {
private final String uri;
private final Step step;
private final LocalizedXStreams localizedXStreams;
private final TableHeaderMapper tableHeaderMapper;

public StepDefinitionMatch(List<Argument> arguments, StepDefinition stepDefinition, String uri, Step step, LocalizedXStreams localizedXStreams) {
public StepDefinitionMatch(List<Argument> arguments, StepDefinition stepDefinition, String uri, Step step, LocalizedXStreams localizedXStreams, TableHeaderMapper tableHeaderMapper) {
super(arguments, stepDefinition.getLocation());
this.stepDefinition = stepDefinition;
this.uri = uri;
this.step = step;
this.localizedXStreams = localizedXStreams;
this.tableHeaderMapper = tableHeaderMapper;
}

public void runStep(Locale locale) throws Throwable {
if (locale == null) {
throw new NullPointerException("null Locale!");
}
try {
stepDefinition.execute(transformedArgs(stepDefinition.getParameterTypes(), step, locale));
Object[] args = transformedArgs(stepDefinition.getParameterTypes(), step, locale);
stepDefinition.execute(args);
} catch (CucumberException e) {
throw e;
} catch (InvocationTargetException t) {
Expand Down Expand Up @@ -70,9 +77,39 @@ private Object[] transformedArgs(Class<?>[] parameterTypes, Step step, Locale lo
}

private Object tableArgument(Step step, TableConverter tableConverter, int argIndex) {
return stepDefinition.tableArgument(argIndex, step.getRows(), tableConverter);
Class listType = stepDefinition.getTypeForTableList(argIndex);
if(listType != null) {
return tableConverter.convert(listType, attributeNames(step.getRows()), attributeValues(step.getRows()));
} else {
return new Table(step.getRows());
}
}

private List<List<String>> attributeValues(List<Row> rows) {
List<List<String>> attributeValues = new ArrayList<List<String>>();
List<Row> valueRows = rows.subList(1, rows.size());
for (Row valueRow : valueRows) {
attributeValues.add(toStrings(valueRow));
}
return attributeValues;
}

private List<String> attributeNames(List<Row> rows) {
List<String> strings = new ArrayList<String>();
for (String string : rows.get(0).getCells()) {
strings.add(tableHeaderMapper.map(string));
}
return strings;
}

private List<String> toStrings(Row row) {
List<String> strings = new ArrayList<String>();
for (String string : row.getCells()) {
strings.add(string);
}
return strings;
}

public Throwable filterStacktrace(Throwable error, StackTraceElement stepLocation) {
StackTraceElement[] stackTraceElements = error.getStackTrace();
if (error.getCause() != null && error.getCause() != error) {
Expand Down
@@ -1,10 +1,8 @@
package cucumber.table.java;

import cucumber.table.TableHeaderMapper;
package cucumber.table;

import java.util.regex.Pattern;

public class JavaBeanPropertyHeaderMapper implements TableHeaderMapper {
public class CamelCaseHeaderMapper implements TableHeaderMapper {

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

private String join(String[] splitted) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < splitted.length; i++) {
sb.append(splitted[i]);
for (String s : splitted) {
sb.append(s);
}
return sb.toString();
}
Expand Down
Expand Up @@ -23,7 +23,7 @@ public void shouldConvertParameters() throws Throwable {
when(stepWithoutDocStringOrTable.getDocString()).thenReturn(null);
when(stepWithoutDocStringOrTable.getRows()).thenReturn(null);

StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(arguments, stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams());
StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(arguments, stepDefinition, "some.feature", stepWithoutDocStringOrTable, new LocalizedXStreams(), null);
stepDefinitionMatch.runStep(Locale.ENGLISH);
Object[] args = {5};
verify(stepDefinition).execute(args);
Expand Down
@@ -1,13 +1,13 @@
package cucumber.table.java;
package cucumber.table;

import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class JavaBeanPropertyHeaderMapperTest {
public class CamelCaseHeaderMapperTest {
@Test
public void testTransformToJavaPropertyName() {
JavaBeanPropertyHeaderMapper mapper = new JavaBeanPropertyHeaderMapper();
CamelCaseHeaderMapper mapper = new CamelCaseHeaderMapper();
assertEquals("Transformed Name", "userName", mapper.map("User Name"));
assertEquals("Transformed Name", "birthDate", mapper.map(" Birth Date\t"));
assertEquals("Transformed Name", "email", mapper.map("email"));
Expand Down
Expand Up @@ -9,6 +9,8 @@
import gherkin.formatter.model.Step;
import groovy.lang.Closure;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.regex.Pattern;

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

@Override
public Class getTypeForTableList(int argIndex) {
return null;
}

public String getLocation() {
return location.getFileName() + ":" + location.getLineNumber();
}
Expand All @@ -51,9 +58,4 @@ public boolean isDefinedAt(StackTraceElement stackTraceElement) {
public String getPattern() {
return pattern.pattern();
}

@Override
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
return new Table(rows);
}
}
13 changes: 5 additions & 8 deletions ioke/src/main/java/cucumber/runtime/ioke/IokeStepDefinition.java
Expand Up @@ -2,10 +2,7 @@

import cucumber.runtime.CucumberException;
import cucumber.runtime.StepDefinition;
import cucumber.table.Table;
import cucumber.table.TableConverter;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Row;
import gherkin.formatter.model.Step;
import ioke.lang.IokeObject;
import ioke.lang.Runtime;
Expand Down Expand Up @@ -47,6 +44,11 @@ public List<Argument> matchedArguments(Step step) {
}
}

@Override
public Class getTypeForTableList(int argIndex) {
return null;
}

public String getLocation() {
return location;
}
Expand Down Expand Up @@ -74,9 +76,4 @@ public void execute(Object[] args) throws Throwable {
public boolean isDefinedAt(StackTraceElement stackTraceElement) {
return stackTraceElement.getClassName().equals(IokeBackend.class.getName());
}

@Override
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
return new Table(rows);
}
}
38 changes: 3 additions & 35 deletions java/src/main/java/cucumber/runtime/java/JavaStepDefinition.java
Expand Up @@ -3,17 +3,12 @@
import cucumber.runtime.CucumberException;
import cucumber.runtime.JdkPatternArgumentMatcher;
import cucumber.runtime.StepDefinition;
import cucumber.table.Table;
import cucumber.table.TableConverter;
import cucumber.table.java.JavaBeanPropertyHeaderMapper;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Row;
import gherkin.formatter.model.Step;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

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

public JavaStepDefinition(Pattern pattern, Method method, ObjectFactory objectFactory) {
this.pattern = pattern;
Expand Down Expand Up @@ -67,39 +61,13 @@ public String getPattern() {
}

@Override
public Object tableArgument(int argIndex, List<Row> rows, TableConverter tableConverter) {
public Class getTypeForTableList(int argIndex) {
Type genericParameterType = method.getGenericParameterTypes()[argIndex];
if (genericParameterType instanceof ParameterizedType) {
Type[] parameters = ((ParameterizedType) genericParameterType).getActualTypeArguments();
Class<?> itemType = (Class<?>) parameters[0];
return tableConverter.convert(itemType, attributeNames(rows), attributeValues(rows));
return (Class<?>) parameters[0];
} else {
return new Table(rows);
return null;
}
}

private List<List<String>> attributeValues(List<Row> rows) {
List<List<String>> attributeValues = new ArrayList<List<String>>();
List<Row> valueRows = rows.subList(1, rows.size());
for (Row valueRow : valueRows) {
attributeValues.add(toStrings(valueRow));
}
return attributeValues;
}

private List<String> attributeNames(List<Row> rows) {
List<String> strings = new ArrayList<String>();
for (String string : rows.get(0).getCells()) {
strings.add(mapper.map(string));
}
return strings;
}

private List<String> toStrings(Row row) {
List<String> strings = new ArrayList<String>();
for (String string : row.getCells()) {
strings.add(string);
}
return strings;
}
}
Expand Up @@ -3,6 +3,7 @@
import cucumber.runtime.StepDefinition;
import cucumber.runtime.StepDefinitionMatch;
import cucumber.runtime.converters.LocalizedXStreams;
import cucumber.table.CamelCaseHeaderMapper;
import cucumber.table.java.User;
import gherkin.formatter.Argument;
import gherkin.formatter.model.Comment;
Expand Down Expand Up @@ -39,7 +40,7 @@ public void shouldExecuteWithAListOfUsers() throws Throwable {
Step stepWithRows = new Step(NO_COMMENTS, "Given", "something that wants users", 10);
stepWithRows.setMultilineArg(rowsList());

StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(NO_ARGS, stepDefinition, "some.feature", stepWithRows, new LocalizedXStreams());
StepDefinitionMatch stepDefinitionMatch = new StepDefinitionMatch(NO_ARGS, stepDefinition, "some.feature", stepWithRows, new LocalizedXStreams(), new CamelCaseHeaderMapper());
stepDefinitionMatch.runStep(Locale.UK);

assertEquals(asList(new User("Sid Vicious", sidsBirthday(), 1000)), stepDefs.users);
Expand Down

0 comments on commit 42e9c17

Please sign in to comment.