From ea446f003ef96b79f83237b392ac1a973c205957 Mon Sep 17 00:00:00 2001 From: nmahendru Date: Thu, 10 Aug 2017 14:12:55 -0700 Subject: [PATCH 1/3] CSV-214: adding cache for the line ending information --- .../org/apache/commons/csv/CSVParser.java | 11 ++++++++++- .../java/org/apache/commons/csv/Lexer.java | 19 +++++++++++++++++++ .../org/apache/commons/csv/CSVParserTest.java | 18 ++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index efc0d86293..d17c523eb4 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -358,7 +358,16 @@ public CSVParser(final Reader reader, final CSVFormat format, final long charact this.characterOffset = characterOffset; this.recordNumber = recordNumber - 1; } - + /** + * Return the line ending information cached in the internal Lexer object + *

+ * Once you have parsed atleast one line then the internal lexer object would have cached the line ending + * value. This is useful for modifying and writing back files. + *

+ */ + public String getLineEndingFromLexer(){ + return lexer.getLineEnding(); + } private void addRecordValue(final boolean lastRecord) { final String input = this.reusableToken.content.toString(); final String inputClean = this.format.getTrim() ? input.trim() : input; diff --git a/src/main/java/org/apache/commons/csv/Lexer.java b/src/main/java/org/apache/commons/csv/Lexer.java index 0329c356a6..520fe9dfe1 100644 --- a/src/main/java/org/apache/commons/csv/Lexer.java +++ b/src/main/java/org/apache/commons/csv/Lexer.java @@ -57,6 +57,14 @@ final class Lexer implements Closeable { /** The input stream */ private final ExtendedBufferedReader reader; + private String lineEnding; + private boolean isLESet = false; + public String getLineEnding(){ + return lineEnding; + } + private void setLineEnding(String input){ + lineEnding = input; + } Lexer(final CSVFormat format, final ExtendedBufferedReader reader) { this.reader = reader; @@ -374,7 +382,18 @@ boolean readEndOfLine(int ch) throws IOException { if (ch == CR && reader.lookAhead() == LF) { // note: does not change ch outside of this method! ch = reader.read(); + //save the CRLF state here + if(!isLESet) { + setLineEnding(Constants.CRLF); + isLESet = true; + } } + //save LF state here. + if(!isLESet && ch == LF) { + setLineEnding(Character.toString(Constants.LF)); + isLESet = true; + } + return ch == LF || ch == CR; } diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index c547b0d94b..9e3a520878 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -234,6 +234,24 @@ public void testCarriageReturnLineFeedEndings() throws IOException { assertEquals(4, records.size()); } } + @Test + public void testLineEndingsCacheWindows() throws IOException { + final String code = "foo\r\nbaar,\r\nhello,world\r\n,kanu"; + final CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT); + final List records = parser.getRecords(); + assertEquals(4, records.size()); + assertEquals("\r\n",parser.getLineEndingFromLexer()); + parser.close(); + } + @Test + public void testLineEndingsCacheLinux() throws IOException { + final String code = "foo\nbaar,\nhello,world\n,kanu"; + final CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT); + final List records = parser.getRecords(); + assertEquals(4, records.size()); + assertEquals("\n",parser.getLineEndingFromLexer()); + parser.close(); + } @Test(expected = NoSuchElementException.class) public void testClose() throws Exception { From 0d54fa09857cc0a3e3627789c0d486ed215712e0 Mon Sep 17 00:00:00 2001 From: nmahendru Date: Tue, 15 Aug 2017 13:37:32 -0700 Subject: [PATCH 2/3] CSV-215 CSVRecord Mutability --- .../org/apache/commons/csv/CSVParser.java | 11 +----- .../org/apache/commons/csv/CSVRecord.java | 36 +++++++++++++++++++ .../org/apache/commons/csv/CSVParserTest.java | 18 ---------- .../org/apache/commons/csv/CSVRecordTest.java | 22 ++++++++++++ 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 7ca4672c29..4580f41b18 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -358,16 +358,7 @@ public CSVParser(final Reader reader, final CSVFormat format, final long charact this.characterOffset = characterOffset; this.recordNumber = recordNumber - 1; } - /** - * Return the line ending information cached in the internal Lexer object - *

- * Once you have parsed atleast one line then the internal lexer object would have cached the line ending - * value. This is useful for modifying and writing back files. - *

- */ - public String getLineEndingFromLexer(){ - return lexer.getLineEnding(); - } + private void addRecordValue(final boolean lastRecord) { final String input = this.reusableToken.content.toString(); final String inputClean = this.format.getTrim() ? input.trim() : input; diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index d2cbe6ac03..432537ca2d 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -25,6 +25,11 @@ import java.util.Map; import java.util.Map.Entry; +class ImmutableRecordCantBeSetException extends Exception{ + public ImmutableRecordCantBeSetException(String message){ + super(message); + } +} /** * A CSV record parsed from a CSV file. * @@ -49,6 +54,8 @@ public final class CSVRecord implements Serializable, Iterable { /** The values of the record */ private final String[] values; + /** Determines the mutability of the record*/ + private boolean isMutable; CSVRecord(final String[] values, final Map mapping, final String comment, final long recordNumber, final long characterPosition) { @@ -57,6 +64,35 @@ public final class CSVRecord implements Serializable, Iterable { this.mapping = mapping; this.comment = comment; this.characterPosition = characterPosition; + /** By default records are immutable*/ + this.isMutable = false; + + } + /** + * Make this instance Mutable. + * */ + public void makeCSVRecordMutable(){ + this.isMutable = true; + } + public boolean isCSVRecordMutable(){ + return this.isMutable; + } + /** + * Sets a value by index. + * + * @param index index + * a column index (0-based) + * @param value + * a string value to replace the current data + * @return nothing + * + * @throws ImmutableRecordCantBeSetException incase it's called on an immutable instance. + */ + public void set(int index, String value) throws ImmutableRecordCantBeSetException { + if(!isMutable){ + throw new ImmutableRecordCantBeSetException("Attempt to change Immutable CSV Record"); + } + values[index] = value; } /** diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 7d5b9bb637..8a1b56794e 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -234,24 +234,6 @@ public void testCarriageReturnLineFeedEndings() throws IOException { assertEquals(4, records.size()); } } - @Test - public void testLineEndingsCacheWindows() throws IOException { - final String code = "foo\r\nbaar,\r\nhello,world\r\n,kanu"; - final CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT); - final List records = parser.getRecords(); - assertEquals(4, records.size()); - assertEquals("\r\n",parser.getLineEndingFromLexer()); - parser.close(); - } - @Test - public void testLineEndingsCacheLinux() throws IOException { - final String code = "foo\nbaar,\nhello,world\n,kanu"; - final CSVParser parser = CSVParser.parse(code, CSVFormat.DEFAULT); - final List records = parser.getRecords(); - assertEquals(4, records.size()); - assertEquals("\n",parser.getLineEndingFromLexer()); - parser.close(); - } @Test public void testFirstEndOfLineCrLf() throws IOException { diff --git a/src/test/java/org/apache/commons/csv/CSVRecordTest.java b/src/test/java/org/apache/commons/csv/CSVRecordTest.java index 6347cc51a4..a0399558f4 100644 --- a/src/test/java/org/apache/commons/csv/CSVRecordTest.java +++ b/src/test/java/org/apache/commons/csv/CSVRecordTest.java @@ -178,6 +178,28 @@ public void testToMapWithNoHeader() throws Exception { assertTrue("Map is empty.", map.isEmpty()); } } + @Test + public void testSettingRecordMutable() throws Exception{ + try(final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))){ + final CSVRecord shortRec = parser.iterator().next(); + shortRec.makeCSVRecordMutable(); + assertTrue(shortRec.isCSVRecordMutable()); + } + } + @Test + public void testDefaultImmutability() throws Exception{ + try(final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))){ + final CSVRecord shortRec = parser.iterator().next(); + assertFalse(shortRec.isCSVRecordMutable()); + } + } + @Test(expected = ImmutableRecordCantBeSetException.class) + public void testImmutableRecordSetException() throws Exception{ + try(final CSVParser parser = CSVParser.parse("a,b", CSVFormat.newFormat(','))){ + final CSVRecord shortRec = parser.iterator().next(); + shortRec.set(0,"new value"); + } + } private void validateMap(final Map map, final boolean allowsNulls) { assertTrue(map.containsKey("first")); From cbfee0a38196beef7c9cab239eae746000866a6b Mon Sep 17 00:00:00 2001 From: nmahendru Date: Tue, 15 Aug 2017 16:08:02 -0700 Subject: [PATCH 3/3] Correcting the doc error --- src/main/java/org/apache/commons/csv/CSVRecord.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index 432537ca2d..02537d3ad8 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -84,7 +84,6 @@ public boolean isCSVRecordMutable(){ * a column index (0-based) * @param value * a string value to replace the current data - * @return nothing * * @throws ImmutableRecordCantBeSetException incase it's called on an immutable instance. */