Permalink
Browse files

added @Transpose annotation processing

  • Loading branch information...
1 parent c5a83f9 commit aba756a7112e84ea5389920b26ae02f7d9876583 Roberto Lo Giacco committed Nov 22, 2013
@@ -63,6 +63,12 @@ public DataTable(List<DataTableRow> gherkinRows, TableConverter tableConverter)
}
this.raw = Collections.unmodifiableList(raw);
}
+
+ private DataTable(List<DataTableRow> gherkinRows, List<List<String>> raw, TableConverter tableConverter) {
+ this.gherkinRows = gherkinRows;
+ this.tableConverter = tableConverter;
+ this.raw = Collections.unmodifiableList(raw);
+ }
/**
* Converts the table to a 2D array.
@@ -74,7 +80,11 @@ public DataTable(List<DataTableRow> gherkinRows, TableConverter tableConverter)
}
public <T> T convert(Type type) {
- return tableConverter.convert(type, this);
+ return tableConverter.convert(type, this, false);
+ }
+
+ public <T> T convert(Type type, boolean transposed) {
+ return tableConverter.convert(type, this, transposed);
}
/**
@@ -100,7 +110,23 @@ public DataTable(List<DataTableRow> gherkinRows, TableConverter tableConverter)
* @return a list of objects
*/
public <T> List<T> asList(Type type) {
- List<T> result = tableConverter.toList(type, this);
+ List<T> result = tableConverter.toList(type, this, false);
+ return result;
+ }
+
+ /**
+ * Converts the table to a List of objects. The first column is used to identifies the fields/properties
+ * of the objects.
+ * <p/>
+ * Backends that support generic types can declare a parameter as a List of a type, and Cucumber will
+ * do the conversion automatically.
+ *
+ * @param type the type of the result (should be a {@link List} generic type)
+ * @param <T> the type of each object
+ * @return a list of objects
+ */
+ public <T> List<T> asTransposedList(Type type) {
+ List<T> result = tableConverter.toList(type, this, true);
return result;
}
@@ -208,7 +234,7 @@ public DataTable transpose() {
row.add(gherkinRow.getCells().get(j));
}
}
- return DataTable.create(transposed);
+ return new DataTable(this.gherkinRows, transposed, this.tableConverter);
}
@Override
@@ -0,0 +1,39 @@
+package cucumber.api;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * <p>
+ * This annotation can be specified on step definition method parameters to give Cucumber a hint
+ * to transpose a DataTable into an object or list of objects.
+ *
+ * For example, if you have the following Gherkin step with a table
+ * </p>
+ * <pre>
+ * Given the user is
+ * | firstname | Roberto |
+ * | lastname | Lo Giacco |
+ * | nationality | Italian |
+ * </pre>
+ * <p>
+ * Then the following Java Step Definition would convert that into an User object:
+ * </p>
+ * <pre>
+ * &#064;Given("^the user is$")
+ * public void the_user_is(@Transpose User user) {
+ * this.user = user;
+ * }
+ * </pre>
+ * <p>
+ *
+ * This annotation also works for data tables that are transformed to a list of beans.
+ * </p>
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Transpose {
+ boolean value() default true;
+}
@@ -4,6 +4,7 @@
import cucumber.api.Format;
import cucumber.api.Transform;
import cucumber.api.Transformer;
+import cucumber.api.Transpose;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.converters.SingleValueConverter;
import cucumber.runtime.xstream.LocalizedXStreams;
@@ -24,6 +25,7 @@
private final Type type;
private final String format;
private final String delimiter;
+ private final boolean transposed;
private final Transformer transformer;
public static List<ParameterInfo> fromMethod(Method method) {
@@ -33,6 +35,7 @@
for (int i = 0; i < genericParameterTypes.length; i++) {
String format = null;
String delimiter = DEFAULT_DELIMITER;
+ boolean transposed = false;
Transformer transformer = null;
for (Annotation annotation : annotations[i]) {
if (annotation instanceof Format) {
@@ -41,6 +44,9 @@
if (annotation instanceof Delimiter) {
delimiter = ((Delimiter) annotation).value();
}
+ if (annotation instanceof Transpose) {
+ transposed = ((Transpose) annotation).value();
+ }
if (annotation instanceof Transform) {
try {
transformer = ((Transform) annotation).value().newInstance();
@@ -51,15 +57,20 @@
}
}
}
- result.add(new ParameterInfo(genericParameterTypes[i], format, delimiter, transformer));
+ result.add(new ParameterInfo(genericParameterTypes[i], format, delimiter, transposed, transformer));
}
return result;
}
public ParameterInfo(Type type, String format, String delimiter, Transformer transformer) {
+ this(type, format, delimiter, false, transformer);
+ }
+
+ public ParameterInfo(Type type, String format, String delimiter, boolean transposed, Transformer transformer) {
this.type = type;
this.format = format;
this.delimiter = delimiter;
+ this.transposed = transposed;
this.transformer = transformer;
}
@@ -78,6 +89,10 @@ public ParameterInfo(Type type, String format, String delimiter, Transformer tra
public Type getType() {
return type;
}
+
+ public boolean isTransposed() {
+ return transposed;
+ }
@Override
public String toString() {
@@ -82,7 +82,7 @@ private ParameterInfo getParameterType(int n, Type argumentType) {
ParameterInfo parameterInfo = stepDefinition.getParameterType(n, argumentType);
if (parameterInfo == null) {
// Some backends return null because they don't know
- parameterInfo = new ParameterInfo(argumentType, null, null, null);
+ parameterInfo = new ParameterInfo(argumentType, null, null, false, null);
}
return parameterInfo;
}
@@ -91,7 +91,7 @@ private Object tableArgument(Step step, int argIndex, LocalizedXStreams.Localize
ParameterInfo parameterInfo = getParameterType(argIndex, DataTable.class);
DataTable table = new DataTable(step.getRows(), new TableConverter(xStream, parameterInfo));
Type type = parameterInfo.getType();
- return table.convert(type);
+ return table.convert(type, parameterInfo.isTransposed());
}
private CucumberException arityMismatch(int parameterCount) {
@@ -44,7 +44,7 @@ public TableConverter(LocalizedXStreams.LocalizedXStream xStream, ParameterInfo
this.parameterInfo = parameterInfo;
}
- public <T> T convert(Type type, DataTable dataTable) {
+ public <T> T convert(Type type, DataTable dataTable, boolean transposed) {
try {
xStream.setParameterType(parameterInfo);
if (type == null || (type instanceof Class && ((Class) type).isAssignableFrom(DataTable.class))) {
@@ -55,6 +55,10 @@ public TableConverter(LocalizedXStreams.LocalizedXStream xStream, ParameterInfo
if (itemType == null) {
throw new CucumberException("Not a List type: " + type);
}
+
+ if (transposed) {
+ dataTable = dataTable.transpose();
+ }
Type listItemType = listItemType(itemType);
if (listItemType == null) {
@@ -153,9 +157,16 @@ public TableConverter(LocalizedXStreams.LocalizedXStream xStream, ParameterInfo
*/
public <T> List<T> toList(final Type type, DataTable dataTable) {
if (type == null) {
- return convert(new GenericListType(new GenericListType(String.class)), dataTable);
+ return convert(new GenericListType(new GenericListType(String.class)), dataTable, false);
+ }
+ return convert(new GenericListType(type), dataTable, false);
+ }
+
+ public <T> List<T> toList(final Type type, DataTable dataTable, boolean transposed) {
+ if (type == null) {
+ return convert(new GenericListType(new GenericListType(String.class)), dataTable, transposed);
}
- return convert(new GenericListType(type), dataTable);
+ return convert(new GenericListType(type), dataTable, transposed);
}
/**
@@ -3,6 +3,7 @@
import cucumber.api.DataTable;
import cucumber.api.Format;
import cucumber.api.Transformer;
+import cucumber.api.Transpose;
import cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter;
import cucumber.deps.com.thoughtworks.xstream.converters.javabean.JavaBeanConverter;
import cucumber.runtime.StepDefinition;
@@ -55,19 +56,35 @@ public void listOfPrimitiveContainers(List<PrimitiveContainer> primitiveContaine
public void listOfPojos(@Format("yyyy-MM-dd") List<UserPojo> listOfPojos) {
this.listOfPojos = listOfPojos;
}
+
+ public void listOfPojosTransposed(@Transpose @Format("yyyy-MM-dd") List<UserPojo> listOfPojos) {
+ this.listOfPojos = listOfPojos;
+ }
public void listOfBeans(@Format("yyyy-MM-dd") List<UserBean> listOfBeans) {
this.listOfBeans = listOfBeans;
}
+
+ public void listOfBeansTransposed(@Transpose @Format("yyyy-MM-dd") List<UserBean> listOfBeans) {
+ this.listOfBeans = listOfBeans;
+ }
public void listOfUsersWithNameField(@Format("yyyy-MM-dd") List<UserWithNameField> listOfUsersWithNameField) {
this.listOfUsersWithNameField = listOfUsersWithNameField;
}
+
+ public void listOfUsersTransposedWithNameField(@Transpose @Format("yyyy-MM-dd") List<UserWithNameField> listOfUsersWithNameField) {
+ this.listOfUsersWithNameField = listOfUsersWithNameField;
+ }
public void listOfListOfDoubles(List<List<Double>> listOfListOfDoubles) {
this.listOfListOfDoubles = listOfListOfDoubles;
}
+ public void listOfListOfDoublesTransposed(@Transpose List<List<Double>> listOfListOfDoubles) {
+ this.listOfListOfDoubles = listOfListOfDoubles;
+ }
+
public void listOfMapsOfStringToString(List<Map<String, String>> listOfMapsOfStringToString) {
this.listOfMapsOfStringToString = listOfMapsOfStringToString;
}
@@ -98,6 +115,15 @@ public void transforms_to_list_of_pojos() throws Throwable {
assertEquals(sidsDeathcal().getTime(), stepDefs.listOfPojos.get(0).deathCal.getTime());
assertNull(stepDefs.listOfPojos.get(1).deathCal);
}
+
+ @Test
+ public void transforms_to_list_of_pojos_transposed() throws Throwable {
+ Method m = StepDefs.class.getMethod("listOfPojosTransposed", List.class);
+ StepDefs stepDefs = runStepDef(m, transposedListOfDatesAndCalWithHeader());
+ assertEquals(sidsBirthday(), stepDefs.listOfPojos.get(0).birthDate);
+ assertEquals(sidsDeathcal().getTime(), stepDefs.listOfPojos.get(0).deathCal.getTime());
+ assertNull(stepDefs.listOfPojos.get(1).deathCal);
+ }
@Test
public void assigns_null_to_objects_when_empty_except_boolean_special_case() throws Throwable {
@@ -125,6 +151,13 @@ public void transforms_to_list_of_beans() throws Throwable {
StepDefs stepDefs = runStepDef(m, listOfDatesWithHeader());
assertEquals(sidsBirthday(), stepDefs.listOfBeans.get(0).getBirthDate());
}
+
+ @Test
+ public void transforms_to_list_of_beans_transposed() throws Throwable {
+ Method m = StepDefs.class.getMethod("listOfBeansTransposed", List.class);
+ StepDefs stepDefs = runStepDef(m, transposedListOfDatesWithHeader());
+ assertEquals(sidsBirthday(), stepDefs.listOfBeans.get(0).getBirthDate());
+ }
@Test
public void converts_table_to_list_of_class_with_special_fields() throws Throwable {
@@ -134,6 +167,15 @@ public void converts_table_to_list_of_class_with_special_fields() throws Throwab
assertEquals("Sid", stepDefs.listOfUsersWithNameField.get(0).name.first);
assertEquals("Vicious", stepDefs.listOfUsersWithNameField.get(0).name.last);
}
+
+ @Test
+ public void converts_table_to_list_of_class_with_special_fields_transposed() throws Throwable {
+ Method m = StepDefs.class.getMethod("listOfUsersTransposedWithNameField", List.class);
+ StepDefs stepDefs = runStepDef(m, transposedListOfDatesAndNamesWithHeader());
+ assertEquals(sidsBirthday(), stepDefs.listOfUsersWithNameField.get(0).birthDate);
+ assertEquals("Sid", stepDefs.listOfUsersWithNameField.get(0).name.first);
+ assertEquals("Vicious", stepDefs.listOfUsersWithNameField.get(0).name.last);
+ }
@Test
public void transforms_to_list_of_single_values() throws Throwable {
@@ -143,6 +185,13 @@ public void transforms_to_list_of_single_values() throws Throwable {
}
@Test
+ public void transforms_to_list_of_single_values_transposed() throws Throwable {
+ Method m = StepDefs.class.getMethod("listOfListOfDoublesTransposed", List.class);
+ StepDefs stepDefs = runStepDef(m, transposedListOfDoublesWithoutHeader());
+ assertEquals("[[100.5, 99.5], [0.5, -0.5], [1000.0, 999.0]]", stepDefs.listOfListOfDoubles.toString());
+ }
+
+ @Test
public void transforms_to_list_of_map_of_string_to_string() throws Throwable {
Method m = StepDefs.class.getMethod("listOfMapsOfStringToString", List.class);
StepDefs stepDefs = runStepDef(m, listOfDatesWithHeader());
@@ -205,6 +254,33 @@ private StepDefs runStepDef(Method method, List<DataTableRow> rows) throws Throw
rows.add(new DataTableRow(NO_COMMENTS, asList("1000", "999"), 2));
return rows;
}
+
+ private List<DataTableRow> transposedListOfDatesWithHeader() {
+ List<DataTableRow> rows = new ArrayList<DataTableRow>();
+ rows.add(new DataTableRow(NO_COMMENTS, asList("Birth Date", "1957-05-10"), 1));
+ return rows;
+ }
+
+ private List<DataTableRow> transposedListOfDatesAndCalWithHeader() {
+ List<DataTableRow> rows = new ArrayList<DataTableRow>();
+ rows.add(new DataTableRow(NO_COMMENTS, asList("Birth Date", "1957-05-10", ""), 1));
+ rows.add(new DataTableRow(NO_COMMENTS, asList("Death Cal", "1979-02-02", ""), 2));
+ return rows;
+ }
+
+ private List<DataTableRow> transposedListOfDatesAndNamesWithHeader() {
+ List<DataTableRow> rows = new ArrayList<DataTableRow>();
+ rows.add(new DataTableRow(NO_COMMENTS, asList("Birth Date", "1957-05-10"), 1));
+ rows.add(new DataTableRow(NO_COMMENTS, asList("Name", "Sid Vicious"), 2));
+ return rows;
+ }
+
+ private List<DataTableRow> transposedListOfDoublesWithoutHeader() {
+ List<DataTableRow> rows = new ArrayList<DataTableRow>();
+ rows.add(new DataTableRow(NO_COMMENTS, asList("100.5", "0.5", "1000"), 1));
+ rows.add(new DataTableRow(NO_COMMENTS, asList("99.5", "-0.5", "999"), 2));
+ return rows;
+ }
private Date sidsBirthday() {
Calendar sidsBirthday = Calendar.getInstance();

0 comments on commit aba756a

Please sign in to comment.