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 1b9a16a7027b772f2550ad3984c9251ea0528007..06d15d5336902fbab3dc14af2dff8286fabf07e5 100644 --- a/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java +++ b/src/main/java/edu/unl/cse/csv_io/CSVReaderWriter.java @@ -61,8 +61,6 @@ public class CSVReaderWriter { return destination; } - // I'd like to replace this with something that uses openCSV, but it works, and openCSV doesn't seem to have - // writers that take map<string,string> public static boolean writeCSV(String filename, Collection<Map<String, String>> data) { boolean wroteFile = true; ClassLoader classLoader = CSVReaderWriter.class.getClassLoader(); @@ -114,6 +112,60 @@ public class CSVReaderWriter { return fieldArray; } + public static String createDelimitedCollection(Collection<String> collection, String delimiter) { + if (delimiter.equals("\"")) { + throw new IllegalArgumentException("The double-quote character \" cannot be a delimiter."); + } + StringBuilder delimitedCollection = new StringBuilder(); + int tokensRemaining = collection.size(); + for (String datum : collection) { + if (datum.contains(delimiter)) { + delimitedCollection.append("\"").append(datum).append("\""); + } else { + delimitedCollection.append(datum); + } + if (--tokensRemaining > 0) { + delimitedCollection.append(delimiter); + } + } + return delimitedCollection.toString(); + } + + public static List<String> createFreeList(String delimitedCollection, String delimiter) { + return (List<String>) createFreeCollection(delimitedCollection, delimiter, new LinkedList<>()); + } + + public static Set<String> createFreeSet(String delimitedCollection, String delimiter) { + return (Set<String>) createFreeCollection(delimitedCollection, delimiter, new HashSet<>()); + } + + static Collection<String> createFreeCollection(String delimitedCollection, String delimiter, + Collection<String> collection) { + // NOTE: if there are 2n+1 quotes, where n>0, then the delimitedCollection is ambiguous + // In practice, this code treats the final quote as the floating quote (but do not guarantee it will do so) + // If this isn't what you want, then don't pass an ambiguous delimitedCollection + if (delimiter.equals("\"")) { + throw new IllegalArgumentException("The double-quote character \" cannot be a delimiter."); + } + List<String> preliminaryCollection = new LinkedList<>(); + if (!Objects.equals(delimitedCollection, "")) { + String[] splitOnQuotedSegments = delimitedCollection.split(" (?=\")|(?<=\") "); + for (String segment : splitOnQuotedSegments) { + String trimmedSegment = segment.trim(); // use .strip() if we move to Java 11 + if (!trimmedSegment.equals("")) { + if (trimmedSegment.startsWith("\"") && trimmedSegment.endsWith("\"")) { + preliminaryCollection.add(trimmedSegment.substring(1, trimmedSegment.length() - 1)); + } else { + String[] splitOnDelimiter = trimmedSegment.split(delimiter); + preliminaryCollection.addAll(Arrays.asList(splitOnDelimiter)); + } + } + } + } + collection.addAll(preliminaryCollection); + return collection; + } + /* LEGACY METHODS */ public static Set<Map<String, String>> readCSV(String filename) { return readCSVasSet(filename); 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 c7e541889495bb01d11147d0941d870e12be4491..64f27c3a66b5eb28cb5514cad035bc1f844ac922 100644 --- a/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java +++ b/src/test/java/edu/unl/cse/csv_io/CSVReaderWriterTest.java @@ -217,4 +217,135 @@ public class CSVReaderWriterTest { assertTrue(expectedCSVStringOption1.equals(output) || expectedCSVStringOption2.equals(output)); assertThat(output, either(is(expectedCSVStringOption1)).or(is(expectedCSVStringOption2))); } + + @Test + public void testCreateDelimitedCollection() { + // Input + String[] inputArray = {"foo", "bar", "baz plugh"}; + List<String> inputList = Arrays.asList(inputArray); + // Oracle + String expectedOutput = "foo bar \"baz plugh\""; + // Output + String actualOutput = CSVReaderWriter.createDelimitedCollection(inputList, " "); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateDelimitedCollectionFromTrivialList() { + // Input + List<String> input = new ArrayList<>(1); + input.add("foo"); + // Oracle + String expectedOutput = "foo"; + // Output + String actualOutput = CSVReaderWriter.createDelimitedCollection(input, " "); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateDelimitedCollectionFromVacuousList() { + // Input + List<String> input = new ArrayList<>(); + // Oracle + String expectedOutput = ""; + // Output + String actualOutput = CSVReaderWriter.createDelimitedCollection(input, " "); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateFreeCollectionSimpleCase() { + // Input + String input = "one two three four"; + // Oracle + String[] expectedOutputArray = {"one", "two", "three", "four"}; + Collection<String> expectedOutput = Arrays.asList(expectedOutputArray); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateFreeCollectionTrivialCase() { + // Input + String input = "one"; + // Oracle + String[] expectedOutputArray = {"one"}; + Collection<String> expectedOutput = Arrays.asList(expectedOutputArray); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateFreeCollectionEmptyString() { + // Input + String input = ""; + // Oracle + Collection<String> expectedOutput = new ArrayList<>(); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + assertNotNull(actualOutput); + assertEquals(0, actualOutput.size()); + } + + @Test + public void testCreateFreeCollectionNullString() { + // Input + String input = ""; + // Oracle + Collection<String> expectedOutput = new ArrayList<>(); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + assertNotNull(actualOutput); + assertEquals(0, actualOutput.size()); + } + + @Test + public void testCreateFreeCollectionQuotedSubstring() { + // Input + String input = "one \"two\" three four \"five\" six"; + // Oracle + String[] expectedOutputArray = {"one", "two", "three", "four", "five", "six"}; + Collection<String> expectedOutput = Arrays.asList(expectedOutputArray); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateFreeCollectionQuotedDelimiter() { + // Input + String input = "one \"two three\" \"four five\" six"; + // Oracle + String[] expectedOutputArray = {"one", "two three", "four five", "six"}; + Collection<String> expectedOutput = Arrays.asList(expectedOutputArray); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + } + + @Test + public void testCreateFreeCollectionSingleQuote() { + // Input + String input = "one two three\" four five six"; + // Oracle + String[] expectedOutputArray = {"one", "two", "three\"", "four", "five", "six"}; + Collection<String> expectedOutput = Arrays.asList(expectedOutputArray); + // Output + Collection<String> actualOutput = CSVReaderWriter.createFreeCollection(input, " ", new ArrayList<>()); + // Compare + assertEquals(expectedOutput, actualOutput); + } } \ No newline at end of file