Skip to content

Commit

Permalink
Implemented fix for issue cucumber#433 - DataTable.toTable( List<Stri…
Browse files Browse the repository at this point in the history
…ng[]> ),

toTable( List<Map<String,Object>> ).
Added ArrayOfSingleValueWriter and MapWriter.
  • Loading branch information
Nicholas Albion committed Dec 5, 2012
1 parent 97b099c commit bc5c6d9
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 15 deletions.
44 changes: 29 additions & 15 deletions core/src/main/java/cucumber/runtime/table/TableConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -170,22 +172,34 @@ public DataTable toTable(List<?> objects, String... columnNames) {

List<String> header = null;
List<List<String>> valuesList = new ArrayList<List<String>>();
for (Object object : objects) {
CellWriter writer;
if (isListOfSingleValue(object)) {
// XStream needs this
object = new ArrayList<Object>((List<Object>) object);
writer = new ListOfSingleValueWriter();
} else {
writer = new ComplexTypeWriter(asList(columnNames));
}
xStream.marshal(object, writer);
if (header == null) {
header = writer.getHeader();
}
List<String> values = writer.getValues();
valuesList.add(values);
boolean firstRow = true;

for (Object object : objects) {
CellWriter writer;
if (isListOfSingleValue(object)) {
// XStream needs this
object = new ArrayList<Object>((List<Object>) 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<String> 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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> columnNames;
private final List<String> values = new ArrayList<String>();

public ArrayOfSingleValueWriter(List<String> columnNames) {
this.columnNames = columnNames;
}

@Override
public List<String> getHeader() {
return columnNames;
}

@Override
public List<String> getValues() {
return values;
}

@Override
public void startNode(String name) {
if (nodeDepth > 1) {
throw new CucumberException("Can only convert List<List<T>> 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();
}
}
75 changes: 75 additions & 0 deletions core/src/main/java/cucumber/runtime/xstream/MapWriter.java
Original file line number Diff line number Diff line change
@@ -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&lt;String, Object> as the List item
*
* @author Nicholas Albion
*/
public class MapWriter extends CellWriter {
private String key;
private final List<String> columnNames;
private final Map<String, Object> values = new LinkedHashMap<String, Object>();
private final List<String> fieldValues = new ArrayList<String>();

public MapWriter(List<String> columnNames) {
this.columnNames = columnNames;
}

@Override
public List<String> getHeader() {
return columnNames;
}

@Override
public List<String> getValues() {
if (columnNames.size() > 0) {
List<String> fieldValues = new ArrayList<String>(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() {
}
}
35 changes: 35 additions & 0 deletions core/src/test/java/cucumber/runtime/table/ToDataTableTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -125,6 +127,39 @@ public void converts_list_of_single_value_to_table() {
List<List<Double>> actual = tc.toList(listOfDoubleType, table);
assertEquals(lists, actual);
}

@Test
public void convert_list_of_arrays_to_table() {
List<Object[]> 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<String, Object> vicious = new LinkedHashMap<String, Object>();
vicious.put("name", "Sid Vicious");
vicious.put("birthDate", "10/05/1957");
vicious.put("credits", 1000);
Map<String, Object> zappa = new LinkedHashMap<String, Object>();
zappa.put("name", "Frank Zappa");
zappa.put("birthDate", "21/12/1940");
zappa.put("credits", 3000);

List<Map<String, Object>> 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 {
Expand Down

0 comments on commit bc5c6d9

Please sign in to comment.