From bc5c6d98bc3c61d81574eabbc8545c4dc7171e37 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Wed, 5 Dec 2012 18:03:14 +1100 Subject: [PATCH] Implemented fix for issue #433 - DataTable.toTable( List ), toTable( List> ). Added ArrayOfSingleValueWriter and MapWriter. --- .../runtime/table/TableConverter.java | 44 +++++++---- .../xstream/ArrayOfSingleValueWriter.java | 62 +++++++++++++++ .../cucumber/runtime/xstream/MapWriter.java | 75 +++++++++++++++++++ .../runtime/table/ToDataTableTest.java | 35 +++++++++ 4 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 core/src/main/java/cucumber/runtime/xstream/ArrayOfSingleValueWriter.java create mode 100644 core/src/main/java/cucumber/runtime/xstream/MapWriter.java diff --git a/core/src/main/java/cucumber/runtime/table/TableConverter.java b/core/src/main/java/cucumber/runtime/table/TableConverter.java index 649f8e84b6..5ff1bf802e 100644 --- a/core/src/main/java/cucumber/runtime/table/TableConverter.java +++ b/core/src/main/java/cucumber/runtime/table/TableConverter.java @@ -7,11 +7,13 @@ import cucumber.deps.com.thoughtworks.xstream.io.HierarchicalStreamReader; import cucumber.runtime.CucumberException; import cucumber.runtime.ParameterInfo; +import cucumber.runtime.xstream.ArrayOfSingleValueWriter; import cucumber.runtime.xstream.CellWriter; import cucumber.runtime.xstream.ComplexTypeWriter; import cucumber.runtime.xstream.ListOfComplexTypeReader; import cucumber.runtime.xstream.ListOfSingleValueWriter; import cucumber.runtime.xstream.LocalizedXStreams; +import cucumber.runtime.xstream.MapWriter; import gherkin.formatter.model.Comment; import gherkin.formatter.model.DataTableRow; import gherkin.util.Mapper; @@ -170,22 +172,34 @@ public DataTable toTable(List objects, String... columnNames) { List header = null; List> valuesList = new ArrayList>(); - for (Object object : objects) { - CellWriter writer; - if (isListOfSingleValue(object)) { - // XStream needs this - object = new ArrayList((List) object); - writer = new ListOfSingleValueWriter(); - } else { - writer = new ComplexTypeWriter(asList(columnNames)); - } - xStream.marshal(object, writer); - if (header == null) { - header = writer.getHeader(); - } - List values = writer.getValues(); - valuesList.add(values); + boolean firstRow = true; + + for (Object object : objects) { + CellWriter writer; + if (isListOfSingleValue(object)) { + // XStream needs this + object = new ArrayList((List) object); + writer = new ListOfSingleValueWriter(); + } else if( object instanceof Map ) { + writer = new MapWriter(asList(columnNames)); + } else if( object.getClass().isArray() ) { + writer = new ArrayOfSingleValueWriter(asList(columnNames)); + } else { + writer = new ComplexTypeWriter(asList(columnNames)); + } + xStream.marshal(object, writer); + if (header == null) { + header = writer.getHeader(); + } + + List values = writer.getValues(); + if( !(firstRow && values.equals(header)) ) { + // Don't include the header twice + valuesList.add(values); + } + firstRow = false; } + return createDataTable(header, valuesList); } finally { xStream.unsetParameterInfo(); diff --git a/core/src/main/java/cucumber/runtime/xstream/ArrayOfSingleValueWriter.java b/core/src/main/java/cucumber/runtime/xstream/ArrayOfSingleValueWriter.java new file mode 100644 index 0000000000..2c637eabb5 --- /dev/null +++ b/core/src/main/java/cucumber/runtime/xstream/ArrayOfSingleValueWriter.java @@ -0,0 +1,62 @@ +package cucumber.runtime.xstream; + +import java.util.ArrayList; +import java.util.List; + +import cucumber.runtime.CucumberException; + +/** + * Based on {@link ListOfSingleValueWriter} but supports {@link #getHeader()} + * @author Nicholas Albion + */ +public class ArrayOfSingleValueWriter extends CellWriter { + private int nodeDepth; + private final List columnNames; + private final List values = new ArrayList(); + + public ArrayOfSingleValueWriter(List columnNames) { + this.columnNames = columnNames; + } + + @Override + public List getHeader() { + return columnNames; + } + + @Override + public List getValues() { + return values; + } + + @Override + public void startNode(String name) { + if (nodeDepth > 1) { + throw new CucumberException("Can only convert List> to a table when T is a single value (primitive, string, date etc)."); + } + nodeDepth++; + } + + @Override + public void addAttribute(String name, String value) { + } + + @Override + public void setValue(String value) { + values.add(value == null ? "" : value); + } + + @Override + public void endNode() { + nodeDepth--; + } + + @Override + public void flush() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/src/main/java/cucumber/runtime/xstream/MapWriter.java b/core/src/main/java/cucumber/runtime/xstream/MapWriter.java new file mode 100644 index 0000000000..b5ce0c1b22 --- /dev/null +++ b/core/src/main/java/cucumber/runtime/xstream/MapWriter.java @@ -0,0 +1,75 @@ +package cucumber.runtime.xstream; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Supports Map<String, Object> as the List item + * + * @author Nicholas Albion + */ +public class MapWriter extends CellWriter { + private String key; + private final List columnNames; + private final Map values = new LinkedHashMap(); + private final List fieldValues = new ArrayList(); + + public MapWriter(List columnNames) { + this.columnNames = columnNames; + } + + @Override + public List getHeader() { + return columnNames; + } + + @Override + public List getValues() { + if (columnNames.size() > 0) { + List fieldValues = new ArrayList(columnNames.size()); + for (String columnName : columnNames) { + Object value = values.get(columnName); + fieldValues.add( value == null ? "" : value.toString() ); + } + + return fieldValues; + } else { + return fieldValues; + } + } + + @Override + public void setValue(String value) { + if( key == null ) { + key = value; + } else { + values.put(key, value); + fieldValues.add(value == null ? "" : value); + key = null; + } + } + + @Override + public void flush() { + throw new UnsupportedOperationException(); + } + + @Override + public void close() { + throw new UnsupportedOperationException(); + } + + @Override + public void startNode(String name) { + } + + @Override + public void addAttribute(String name, String value) { + } + + @Override + public void endNode() { + } +} diff --git a/core/src/test/java/cucumber/runtime/table/ToDataTableTest.java b/core/src/test/java/cucumber/runtime/table/ToDataTableTest.java index 141ab19df8..04f5f81c35 100644 --- a/core/src/test/java/cucumber/runtime/table/ToDataTableTest.java +++ b/core/src/test/java/cucumber/runtime/table/ToDataTableTest.java @@ -9,8 +9,10 @@ import java.lang.reflect.Type; import java.util.Date; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; @@ -125,6 +127,39 @@ public void converts_list_of_single_value_to_table() { List> actual = tc.toList(listOfDoubleType, table); assertEquals(lists, actual); } + + @Test + public void convert_list_of_arrays_to_table() { + List arrays = asList( new Object[]{"name", "birthDate", "credits"}, // DataTable.diff() passes the whole table including the top row as "raw" + new Object[]{"Sid Vicious", "10/05/1957", 1000}, + new Object[]{"Frank Zappa", "21/12/1940", 3000} ); + DataTable table = tc.toTable( arrays, "name", "credits", "birthDate" ); +// TODO: order of columns should not matter assertEquals( personTable(), table ); + + arrays = asList( new Object[]{"name", "birthDate", "credits"}, // DataTable.diff() passes the whole table including the top row as "raw" + new Object[]{"Sid Vicious", "10/05/1957", 1000}, + new Object[]{"Frank Zappa", "21/12/1940", 3000} ); + table = tc.toTable( arrays, "name", "birthDate", "credits" ); +// TODO: why does assertEquals() throw? assertEquals( personTable(), table ); + personTable().diff( arrays ); + } + + @Test + public void convert_list_of_maps_to_table() { + Map vicious = new LinkedHashMap(); + vicious.put("name", "Sid Vicious"); + vicious.put("birthDate", "10/05/1957"); + vicious.put("credits", 1000); + Map zappa = new LinkedHashMap(); + zappa.put("name", "Frank Zappa"); + zappa.put("birthDate", "21/12/1940"); + zappa.put("credits", 3000); + + List> maps = asList( vicious, zappa ); + DataTable table = tc.toTable( maps, vicious.keySet().toArray(new String[]{}) ); +// TODO: why does assertEquals() throw? assertEquals( personTable(), table ); + personTable().diff( maps ); + } // No setters public static class UserPojo {