Skip to content

Commit 42476f4

Browse files
committed
CSVStrategy is now immutable (SANDBOX-279)
git-svn-id: https://svn.apache.org/repos/asf/commons/sandbox/csv/trunk@1199827 13f79535-47bb-0310-9956-ffa450edef68
1 parent fc4ccb4 commit 42476f4

5 files changed

Lines changed: 126 additions & 136 deletions

File tree

src/main/java/org/apache/commons/csv/CSVParser.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Token reset() {
122122
* @param input a Reader containing "csv-formatted" input
123123
*/
124124
public CSVParser(Reader input) {
125-
this(input, (CSVStrategy) CSVStrategy.DEFAULT_STRATEGY.clone());
125+
this(input, CSVStrategy.DEFAULT_STRATEGY);
126126
}
127127

128128
/**
@@ -260,7 +260,7 @@ Token nextToken(Token tkn) throws IOException {
260260
c = in.readAgain();
261261

262262
// empty line detection: eol AND (last char was EOL or beginning)
263-
while (strategy.getIgnoreEmptyLines() && eol
263+
while (strategy.isEmptyLinesIgnored() && eol
264264
&& (lastChar == '\n'
265265
|| lastChar == '\r'
266266
|| lastChar == ExtendedBufferedReader.UNDEFINED)
@@ -286,7 +286,7 @@ Token nextToken(Token tkn) throws IOException {
286286
// important: make sure a new char gets consumed in each iteration
287287
while (!tkn.isReady && tkn.type != TT_EOF) {
288288
// ignore whitespaces at beginning of a token
289-
while (strategy.getIgnoreLeadingWhitespaces() && isWhitespace(c) && !eol) {
289+
while (strategy.isLeadingSpacesIgnored() && isWhitespace(c) && !eol) {
290290
wsBuf.append((char) c);
291291
c = in.read();
292292
eol = isEndOfLine(c);
@@ -316,7 +316,7 @@ Token nextToken(Token tkn) throws IOException {
316316
} else {
317317
// next token must be a simple token
318318
// add removed blanks when not ignoring whitespace chars...
319-
if (!strategy.getIgnoreLeadingWhitespaces()) {
319+
if (!strategy.isLeadingSpacesIgnored()) {
320320
tkn.content.append(wsBuf);
321321
}
322322
simpleTokenLexer(tkn, c);
@@ -359,7 +359,7 @@ private Token simpleTokenLexer(Token tkn, int c) throws IOException {
359359
tkn.type = TT_TOKEN;
360360
tkn.isReady = true;
361361
break;
362-
} else if (c == '\\' && strategy.getUnicodeEscapeInterpretation() && in.lookAhead() == 'u') {
362+
} else if (c == '\\' && strategy.isUnicodeEscapesInterpreted() && in.lookAhead() == 'u') {
363363
// interpret unicode escaped chars (like \u0070 -> p)
364364
tkn.content.append((char) unicodeEscapeLexer(c));
365365
} else if (c == strategy.getEscape()) {
@@ -371,7 +371,7 @@ private Token simpleTokenLexer(Token tkn, int c) throws IOException {
371371
c = in.read();
372372
}
373373

374-
if (strategy.getIgnoreTrailingWhitespaces()) {
374+
if (strategy.isTrailingSpacesIgnored()) {
375375
tkn.content.trimTrailingWhitespace();
376376
}
377377

@@ -400,7 +400,7 @@ private Token encapsulatedTokenLexer(Token tkn, int c) throws IOException {
400400
for (; ;) {
401401
c = in.read();
402402

403-
if (c == '\\' && strategy.getUnicodeEscapeInterpretation() && in.lookAhead() == 'u') {
403+
if (c == '\\' && strategy.isUnicodeEscapesInterpreted() && in.lookAhead() == 'u') {
404404
tkn.content.append((char) unicodeEscapeLexer(c));
405405
} else if (c == strategy.getEscape()) {
406406
tkn.content.append((char) readEscape(c));

src/main/java/org/apache/commons/csv/CSVPrinter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public CSVPrinter(Writer out, CSVStrategy strategy) {
5858
* Output a blank line
5959
*/
6060
public void println() throws IOException {
61-
out.write(strategy.getPrinterNewline());
61+
out.write(strategy.getLineSeparator());
6262
newLine = true;
6363
}
6464

src/main/java/org/apache/commons/csv/CSVStrategy.java

Lines changed: 91 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,15 @@
2626
*/
2727
public class CSVStrategy implements Cloneable, Serializable {
2828

29-
private char delimiter;
30-
private char encapsulator;
31-
private char commentStart;
32-
private char escape;
33-
private boolean ignoreLeadingWhitespaces;
34-
private boolean ignoreTrailingWhitespaces;
35-
private boolean interpretUnicodeEscapes;
36-
private boolean ignoreEmptyLines;
37-
38-
// controls for output
39-
private String printerNewline = "\n";
29+
private char delimiter = ',';
30+
private char encapsulator = '"';
31+
private char commentStart = COMMENTS_DISABLED;
32+
private char escape = ESCAPE_DISABLED;
33+
private boolean leadingSpacesIgnored = true;
34+
private boolean trailingSpacesIgnored = true;
35+
private boolean unicodeEscapesInterpreted = false;
36+
private boolean emptyLinesIgnored = true;
37+
private String lineSeparator = "\n";
4038

4139
// -2 is used to signal disabled, because it won't be confused with
4240
// an EOF signal (-1), and because \ufffe in UTF-16 would be
@@ -46,11 +44,22 @@ public class CSVStrategy implements Cloneable, Serializable {
4644
public static final char ESCAPE_DISABLED = (char) -2;
4745
public static final char ENCAPSULATOR_DISABLED = (char) -2;
4846

47+
/** Standard comma separated format. */
4948
public static final CSVStrategy DEFAULT_STRATEGY = new CSVStrategy(',', '"', COMMENTS_DISABLED, ESCAPE_DISABLED, true, true, false, true);
49+
50+
/** Excel file format (using a comma as the value delimiter). */
5051
public static final CSVStrategy EXCEL_STRATEGY = new CSVStrategy(',', '"', COMMENTS_DISABLED, ESCAPE_DISABLED, false, false, false, false);
52+
53+
/** Tabulation delimited format. */
5154
public static final CSVStrategy TDF_STRATEGY = new CSVStrategy('\t', '"', COMMENTS_DISABLED, ESCAPE_DISABLED, true, true, false, true);
5255

5356

57+
/**
58+
* Creates a CSVStrategy with the default parameters.
59+
*/
60+
public CSVStrategy() {
61+
}
62+
5463
public CSVStrategy(char delimiter, char encapsulator, char commentStart) {
5564
this(delimiter, encapsulator, commentStart, ESCAPE_DISABLED, true, true, false, true);
5665
}
@@ -62,103 +71,129 @@ public CSVStrategy(char delimiter, char encapsulator, char commentStart) {
6271
* @param encapsulator a char used as value encapsulation marker
6372
* @param commentStart a char used for comment identification
6473
* @param escape a char used to escape special characters in values
65-
* @param ignoreLeadingWhitespaces TRUE when leading whitespaces should be ignored
66-
* @param ignoreTrailingWhitespaces TRUE when trailing whitespaces should be ignored
67-
* @param interpretUnicodeEscapes TRUE when unicode escapes should be interpreted
68-
* @param ignoreEmptyLines TRUE when the parser should skip emtpy lines
74+
* @param leadingSpacesIgnored TRUE when leading whitespaces should be ignored
75+
* @param trailingSpacesIgnored TRUE when trailing whitespaces should be ignored
76+
* @param unicodeEscapesInterpreted TRUE when unicode escapes should be interpreted
77+
* @param emptyLinesIgnored TRUE when the parser should skip emtpy lines
6978
*/
7079
public CSVStrategy(
7180
char delimiter,
7281
char encapsulator,
7382
char commentStart,
7483
char escape,
75-
boolean ignoreLeadingWhitespaces,
76-
boolean ignoreTrailingWhitespaces,
77-
boolean interpretUnicodeEscapes,
78-
boolean ignoreEmptyLines) {
84+
boolean leadingSpacesIgnored,
85+
boolean trailingSpacesIgnored,
86+
boolean unicodeEscapesInterpreted,
87+
boolean emptyLinesIgnored) {
7988
this.delimiter = delimiter;
8089
this.encapsulator = encapsulator;
8190
this.commentStart = commentStart;
8291
this.escape = escape;
83-
this.ignoreLeadingWhitespaces = ignoreLeadingWhitespaces;
84-
this.ignoreTrailingWhitespaces = ignoreTrailingWhitespaces;
85-
this.interpretUnicodeEscapes = interpretUnicodeEscapes;
86-
this.ignoreEmptyLines = ignoreEmptyLines;
87-
}
88-
89-
public void setDelimiter(char delimiter) {
90-
this.delimiter = delimiter;
92+
this.leadingSpacesIgnored = leadingSpacesIgnored;
93+
this.trailingSpacesIgnored = trailingSpacesIgnored;
94+
this.unicodeEscapesInterpreted = unicodeEscapesInterpreted;
95+
this.emptyLinesIgnored = emptyLinesIgnored;
9196
}
9297

9398
public char getDelimiter() {
94-
return this.delimiter;
99+
return delimiter;
95100
}
96101

97-
public void setEncapsulator(char encapsulator) {
98-
this.encapsulator = encapsulator;
102+
public CSVStrategy withDelimiter(char delimiter) {
103+
CSVStrategy strategy = (CSVStrategy) clone();
104+
this.delimiter = delimiter;
105+
return strategy;
99106
}
100107

101108
public char getEncapsulator() {
102-
return this.encapsulator;
109+
return encapsulator;
103110
}
104111

105-
public void setCommentStart(char commentStart) {
106-
this.commentStart = commentStart;
112+
public CSVStrategy withEncapsulator(char encapsulator) {
113+
CSVStrategy strategy = (CSVStrategy) clone();
114+
strategy.encapsulator = encapsulator;
115+
return strategy;
107116
}
108117

109118
public char getCommentStart() {
110-
return this.commentStart;
119+
return commentStart;
120+
}
121+
122+
public CSVStrategy withCommentStart(char commentStart) {
123+
CSVStrategy strategy = (CSVStrategy) clone();
124+
strategy.commentStart = commentStart;
125+
return strategy;
111126
}
112127

113128
public boolean isCommentingDisabled() {
114129
return this.commentStart == COMMENTS_DISABLED;
115130
}
116131

117-
public void setEscape(char escape) {
118-
this.escape = escape;
132+
public char getEscape() {
133+
return escape;
119134
}
120135

121-
public char getEscape() {
122-
return this.escape;
136+
public CSVStrategy withEscape(char escape) {
137+
CSVStrategy strategy = (CSVStrategy) clone();
138+
strategy.escape = escape;
139+
return strategy;
140+
}
141+
142+
public boolean isLeadingSpacesIgnored() {
143+
return leadingSpacesIgnored;
144+
}
145+
146+
public CSVStrategy withLeadingSpacesIgnored(boolean leadingSpacesIgnored) {
147+
CSVStrategy strategy = (CSVStrategy) clone();
148+
strategy.leadingSpacesIgnored = leadingSpacesIgnored;
149+
return strategy;
123150
}
124151

125-
public void setIgnoreLeadingWhitespaces(boolean ignoreLeadingWhitespaces) {
126-
this.ignoreLeadingWhitespaces = ignoreLeadingWhitespaces;
152+
public boolean isTrailingSpacesIgnored() {
153+
return trailingSpacesIgnored;
127154
}
128155

129-
public boolean getIgnoreLeadingWhitespaces() {
130-
return this.ignoreLeadingWhitespaces;
156+
public CSVStrategy withTrailingSpacesIgnored(boolean trailingSpacesIgnored) {
157+
CSVStrategy strategy = (CSVStrategy) clone();
158+
strategy.trailingSpacesIgnored = trailingSpacesIgnored;
159+
return strategy;
131160
}
132161

133-
public void setIgnoreTrailingWhitespaces(boolean ignoreTrailingWhitespaces) {
134-
this.ignoreTrailingWhitespaces = ignoreTrailingWhitespaces;
162+
public boolean isUnicodeEscapesInterpreted() {
163+
return unicodeEscapesInterpreted;
135164
}
136165

137-
public boolean getIgnoreTrailingWhitespaces() {
138-
return this.ignoreTrailingWhitespaces;
166+
public CSVStrategy withUnicodeEscapesInterpreted(boolean unicodeEscapesInterpreted) {
167+
CSVStrategy strategy = (CSVStrategy) clone();
168+
strategy.unicodeEscapesInterpreted = unicodeEscapesInterpreted;
169+
return strategy;
139170
}
140171

141-
public void setUnicodeEscapeInterpretation(boolean interpretUnicodeEscapes) {
142-
this.interpretUnicodeEscapes = interpretUnicodeEscapes;
172+
public boolean isEmptyLinesIgnored() {
173+
return emptyLinesIgnored;
143174
}
144175

145-
public boolean getUnicodeEscapeInterpretation() {
146-
return this.interpretUnicodeEscapes;
176+
public CSVStrategy withEmptyLinesIgnored(boolean emptyLinesIgnored) {
177+
CSVStrategy strategy = (CSVStrategy) clone();
178+
strategy.emptyLinesIgnored = emptyLinesIgnored;
179+
return strategy;
147180
}
148181

149-
public boolean getIgnoreEmptyLines() {
150-
return this.ignoreEmptyLines;
182+
public String getLineSeparator() {
183+
return lineSeparator;
151184
}
152185

153-
public String getPrinterNewline() {
154-
return this.printerNewline;
186+
public CSVStrategy withLineSeparator(String lineSeparator) {
187+
CSVStrategy strategy = (CSVStrategy) clone();
188+
strategy.lineSeparator = lineSeparator;
189+
return strategy;
155190
}
156191

157-
public Object clone() {
192+
protected Object clone() {
158193
try {
159194
return super.clone();
160195
} catch (CloneNotSupportedException e) {
161-
throw new RuntimeException(e); // impossible
196+
throw (Error) new InternalError().initCause(e);
162197
}
163198
}
164199
}

src/test/java/org/apache/commons/csv/CSVParserTest.java

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,8 @@ public void testNextToken2() throws IOException {
9494
*
9595
*/
9696
String code = "1,2,3,\na,b x,c\n#foo\n\nd,e,\n\n";
97-
CSVStrategy strategy = (CSVStrategy) CSVStrategy.DEFAULT_STRATEGY.clone();
98-
// strategy.setIgnoreEmptyLines(false);
99-
strategy.setCommentStart('#');
100-
97+
CSVStrategy strategy = CSVStrategy.DEFAULT_STRATEGY.withCommentStart('#');
98+
10199
TestCSVParser parser = new TestCSVParser(new StringReader(code), strategy);
102100

103101

@@ -123,8 +121,7 @@ public void testNextToken3() throws IOException {
123121
* \,,
124122
*/
125123
String code = "a,\\,,b\n\\,,";
126-
CSVStrategy strategy = (CSVStrategy) CSVStrategy.DEFAULT_STRATEGY.clone();
127-
strategy.setCommentStart('#');
124+
CSVStrategy strategy = CSVStrategy.DEFAULT_STRATEGY.withCommentStart('#');
128125
TestCSVParser parser = new TestCSVParser(new StringReader(code), strategy);
129126

130127
assertEquals(CSVParser.TT_TOKEN + ";a;", parser.testNextToken());
@@ -520,8 +517,7 @@ public void testDefaultStrategy() throws IOException {
520517

521518
public void testUnicodeEscape() throws IOException {
522519
String code = "abc,\\u0070\\u0075\\u0062\\u006C\\u0069\\u0063";
523-
CSVParser parser = new CSVParser(new StringReader(code));
524-
parser.getStrategy().setUnicodeEscapeInterpretation(true);
520+
CSVParser parser = new CSVParser(new StringReader(code), CSVStrategy.DEFAULT_STRATEGY.withUnicodeEscapesInterpreted(true));
525521
String[] data = parser.getLine();
526522
assertEquals(2, data.length);
527523
assertEquals("abc", data[0]);

0 commit comments

Comments
 (0)