From 5f07781f275f8dc5e2e0ea2e321603412a847b9d Mon Sep 17 00:00:00 2001 From: Christopher Bohn <bohn@unl.edu> Date: Sun, 19 Jan 2020 11:13:02 -0600 Subject: [PATCH] Completed refactor - Writing to CSV is now done with openCSV calls, and client is responsible for lifecycle of injected dependencies (closes #1) - Because of the openCSV calls, commas within a field are now allowed on output, as is enclosing a field in quotes (closes #3) - Because reading had already been rewritten using openCSV calls, reading already could handle quoted fields that contained commas; we simply didn't have a test for it until now (closes #2) --- .../edu/unl/cse/csv_io/CSVReaderWriter.java | 24 +++---- .../unl/cse/csv_io/CSVReaderWriterTest.java | 66 ++++++++++++++++++- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java b/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java index f81437c..1b9a16a 100644 --- a/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java +++ b/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java @@ -43,16 +43,16 @@ public class CSVReaderWriter { return csvList; } - private static Collection<Map<String, String>> parseCSV(InputStreamReader inputStreamReader, + private static Collection<Map<String, String>> parseCSV(Reader reader, Collection<Map<String, String>> destination) { Map<String, String> line; try { - CSVReaderHeaderAware csvReader = new CSVReaderHeaderAwareBuilder(inputStreamReader).build(); + CSVReaderHeaderAware csvReader = new CSVReaderHeaderAwareBuilder(reader).build(); while ((line = csvReader.readMap()) != null) { destination.add(line); } } catch (NullPointerException ignored) { - // CSVReaderHeaderAwareBuilder.build() throws NullPointerException if inputStreamReader is empty + // CSVReaderHeaderAwareBuilder.build() throws NullPointerException if reader is empty } catch (IOException ioException) { System.err.println("Error reading CSV file. " + ioException); } catch (CsvValidationException csvValidationException) { @@ -69,8 +69,8 @@ public class CSVReaderWriter { URL resource = classLoader.getResource("csv/" + filename); if (resource != null) { File file = new File(resource.getPath()); - try (OutputStream outputStream = new FileOutputStream(file)) { - placeCSVonStream(data, outputStream); + try (FileWriter fileWriter = new FileWriter(file)) { + placeCSVonWriter(data, fileWriter); } catch (FileNotFoundException fileNotFoundException) { System.err.println("Could not open " + filename + "; probably due to a bad pathname. " + fileNotFoundException); wroteFile = false; @@ -85,13 +85,7 @@ public class CSVReaderWriter { return wroteFile; } - static void placeCSVonStream(Collection<Map<String, String>> data, OutputStream outputStream) throws IOException { - OutputStreamWriter writer = new OutputStreamWriter(outputStream); - placeCSVonWriter(data, writer); - writer.close(); - } - - static void placeCSVonWriter(Collection<Map<String, String>> data, OutputStreamWriter writer) { + static void placeCSVonWriter(Collection<Map<String, String>> data, Writer writer) { CSVWriter csvWriter = new CSVWriter(writer); List<String[]> allLines = new LinkedList<>(); String[] fieldNames = getFieldNames(data); @@ -128,6 +122,12 @@ public class CSVReaderWriter { static Set<Map<String, String>> parseCSV(InputStream inputStream) { return (Set<Map<String, String>>) parseCSV(new InputStreamReader(inputStream), new HashSet<>()); } + + static void placeCSVonStream(Collection<Map<String, String>> data, OutputStream outputStream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(outputStream); + placeCSVonWriter(data, writer); + writer.close(); + } /* public static void main(String[] args) { diff --git a/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java b/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java index 693c460..c7e5418 100644 --- a/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java +++ b/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java @@ -14,6 +14,7 @@ import static org.hamcrest.CoreMatchers.*; public class CSVReaderWriterTest { private static InputStream inputStream; private static OutputStream outputStream; + private static Writer writer; private static void placeCSVStringOnInputStream(String[][] data) { String CSVString = createCSVString(data); @@ -35,10 +36,13 @@ public class CSVReaderWriterTest { @Before public void setUp() { outputStream = new ByteArrayOutputStream(); + writer = new OutputStreamWriter(outputStream); } @After - public void tearDown() { + public void tearDown() throws IOException { + writer.close(); + outputStream.close(); } @Test @@ -125,6 +129,36 @@ public class CSVReaderWriterTest { assertEquals(expectedResultSize, result.size()); } + @Test + public void testReadingWithComma() { + // Input + String[] headers = {"header1", "header2"}; + String[][] rows = {{"datum1", "datum2"}, {"datum3", "datum4"}, {"\"datum5, datum6\"", "datum7 datum8"}}; + String[][] data = {headers, rows[0], rows[1], rows[2]}; + // Output + placeCSVStringOnInputStream(data); + Set<Map<String, String>> result; + result = CSVReaderWriter.parseCSV(inputStream); + // Oracle -- Header + Set<String> expectedKeys = new HashSet<>(Arrays.asList(headers)); + // Oracle -- Rows + String[][] parsedRows = {rows[0], rows[1], {"datum5, datum6", "datum7 datum8"}}; + Map<String, String>[] expectedRows = new Map[3]; + for (int i = 0; i < expectedRows.length; i++) { + expectedRows[i] = new HashMap<>(); + for (int j = 0; j < headers.length; j++) { + expectedRows[i].put(headers[j], parsedRows[i][j]); + } + } + // Compare + assertNotNull(result); + Map<String, String> aRow = (Map<String, String>) result.toArray()[0]; + assertEquals(expectedKeys, aRow.keySet()); + for (Map<String, String> expectedRow : expectedRows) { + assertTrue(result.contains(expectedRow)); + } + } + // We probably should test malformed CSVs, but this is good enough for students' starter code @Test @@ -153,4 +187,34 @@ public class CSVReaderWriterTest { assertTrue(expectedCSVStringOption1.equals(output) || expectedCSVStringOption2.equals(output)); assertThat(output, either(is(expectedCSVStringOption1)).or(is(expectedCSVStringOption2))); } + + @Test + public void testWritingWithComma() throws IOException { + // Input + String[] headers = {"header1", "header2"}; + String[][] rows = {{"datum1", "datum2"}, {"datum3", "datum4"}, {"datum5, datum6", "datum7 datum8"}}; + Map<String, String>[] inputRows = new Map[3]; + for (int i = 0; i < inputRows.length; i++) { + inputRows[i] = new HashMap<>(2); + inputRows[i].put(headers[0], rows[i][0]); + inputRows[i].put(headers[1], rows[i][1]); + } + Set<Map<String, String>> input = new HashSet<>(Arrays.asList(inputRows)); + // Oracle + String[] formattedRow2 = {"\"datum5, datum6\"", "datum7 datum8"}; + String[][] data = {headers, rows[0], rows[1], formattedRow2}; + String expectedCSVStringOption1 = createCSVString(data); + Collections.reverse(Arrays.asList(headers)); + Collections.reverse(Arrays.asList(rows[0])); + Collections.reverse(Arrays.asList(rows[1])); + Collections.reverse(Arrays.asList(formattedRow2)); + String expectedCSVStringOption2 = createCSVString(data); + // Output + CSVReaderWriter.placeCSVonWriter(input, writer); + writer.flush(); + String output = outputStream.toString(); + // Compare + assertTrue(expectedCSVStringOption1.equals(output) || expectedCSVStringOption2.equals(output)); + assertThat(output, either(is(expectedCSVStringOption1)).or(is(expectedCSVStringOption2))); + } } \ No newline at end of file -- GitLab