diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index 11953049..5cd8b708 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -30,6 +30,8 @@ import java.io.Reader; import java.io.Serializable; import java.io.StringWriter; +import org.apache.commons.csv.CSVFormat.CSVFormatBuilder; + /** * The format specification of a CSV file. * @@ -41,13 +43,6 @@ public class CSVFormat implements Serializable { private static final long serialVersionUID = 1L; - /** - * Constant char to use for disabling comments, escapes and encapsulation. The value -2 is used because it - * won't be confused with an EOF signal (-1), and because the Unicode value {@code FFFE} would be encoded as two chars - * (using surrogates) and thus there should never be a collision with a real text char. - */ - private static final char DISABLED = '\ufffe'; - private final char delimiter; private final Character quoteChar; private final Quote quotePolicy; @@ -58,11 +53,6 @@ public class CSVFormat implements Serializable { private final String recordSeparator; // for outputs private final String[] header; - /** - * Starting format; used for creating other formats. - */ - private static final CSVFormat PRISTINE = new CSVFormat(DISABLED, null, null, null, null, false, false, null, null); - /** * Comma separated format as defined by RFC 4180. * */ public static final CSVFormat RFC4180 = - PRISTINE - .withDelimiter(COMMA) - .withQuoteChar(DOUBLE_QUOTE) - .withRecordSeparator(CRLF); + defaults() + .withIgnoreEmptyLines(false) + .build(); /** * Standard comma separated format, as for {@link #RFC4180} but allowing blank lines. @@ -87,8 +76,8 @@ public class CSVFormat implements Serializable { * */ public static final CSVFormat DEFAULT = // TODO rename to something more meaningful - RFC4180 - .withIgnoreEmptyLines(true); + defaults() + .build(); /** * Excel file format (using a comma as the value delimiter). Note that the actual value delimiter used by Excel is @@ -108,19 +97,16 @@ public class CSVFormat implements Serializable { * Note: this is currently the same as RFC4180 */ public static final CSVFormat EXCEL = - PRISTINE - .withDelimiter(COMMA) - .withQuoteChar(DOUBLE_QUOTE) - .withRecordSeparator(CRLF); + defaults() + .withIgnoreEmptyLines(false) + .build(); /** Tab-delimited format, with quote; leading and trailing spaces ignored. */ public static final CSVFormat TDF = - PRISTINE + defaults() .withDelimiter(TAB) - .withQuoteChar(DOUBLE_QUOTE) .withIgnoreSurroundingSpaces(true) - .withIgnoreEmptyLines(true) - .withRecordSeparator(CRLF); + .build(); /** * Default MySQL format used by the SELECT INTO OUTFILE and LOAD DATA INFILE operations. This is @@ -131,20 +117,36 @@ public class CSVFormat implements Serializable { * http://dev.mysql.com/doc/refman/5.1/en/load-data.html */ public static final CSVFormat MYSQL = - PRISTINE + defaults() .withDelimiter(TAB) + .withQuoteChar(null) .withEscape(ESCAPE) - .withRecordSeparator(LF); + .withIgnoreEmptyLines(false) + .withRecordSeparator(LF) + .build(); /** - * Creates a basic CSV format. + * Factory method for creating CSV formats. * * @param delimiter * the char used for value separation, must not be a line break character * @throws IllegalArgumentException if the delimiter is a line break character */ - public CSVFormat(char delimiter){ - this(delimiter, null, null, null, null, false, false, null, null); + public static CSVFormatBuilder newFormat(char delimiter) { + return new CSVFormatBuilder(delimiter); + } + + /** + * Standard comma separated format, as for {@link #RFC4180} but allowing blank lines. + * + */ + public static CSVFormatBuilder defaults() { + return new CSVFormatBuilder(COMMA, DOUBLE_QUOTE, null, null, null, false, true, CRLF, null); } /** @@ -170,8 +172,7 @@ public class CSVFormat implements Serializable { * the header * @throws IllegalArgumentException if the delimiter is a line break character */ - // package protected for use by test code - CSVFormat(final char delimiter, final Character quoteChar, final Quote quotePolicy, final Character commentStart, final Character escape, final + private CSVFormat(final char delimiter, final Character quoteChar, final Quote quotePolicy, final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces, final boolean ignoreEmptyLines, final String lineSeparator, final String[] header) { if (isLineBreak(delimiter)) { @@ -212,39 +213,6 @@ public class CSVFormat implements Serializable { return c == LF || c == CR; } - /** - * Verifies the consistency of the parameters and throws an IllegalStateException if necessary. - * - * @throws IllegalStateException - */ - void validate() throws IllegalStateException { - if (quoteChar != null && delimiter == quoteChar.charValue()) { - throw new IllegalStateException("The quoteChar character and the delimiter cannot be the same ('" + quoteChar + "')"); - } - - if (escape != null && delimiter == escape.charValue()) { - throw new IllegalStateException("The escape character and the delimiter cannot be the same ('" + escape + "')"); - } - - if (commentStart != null && delimiter == commentStart.charValue()) { - throw new IllegalStateException("The comment start character and the delimiter cannot be the same ('" + commentStart + - "')"); - } - - if (quoteChar != null && quoteChar.equals(commentStart)) { - throw new IllegalStateException("The comment start character and the quoteChar cannot be the same ('" + commentStart + - "')"); - } - - if (escape != null && escape.equals(commentStart)) { - throw new IllegalStateException("The comment start and the escape character cannot be the same ('" + commentStart + "')"); - } - - if (escape == null && quotePolicy == Quote.NONE) { - throw new IllegalStateException("No quotes mode set but no escape character is set"); - } - } - /** * Returns the character delimiting the values (typically ';', ',' or '\t'). * @@ -254,20 +222,6 @@ public class CSVFormat implements Serializable { return delimiter; } - /** - * Returns a copy of this format using the specified delimiter character. - * - * @param delimiter - * the delimiter character - * @return A copy of this format using the specified delimiter character - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withDelimiter(final char delimiter) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Returns the character used to encapsulate values containing special characters. * @@ -277,36 +231,6 @@ public class CSVFormat implements Serializable { return quoteChar; } - /** - * Returns a copy of this format using the specified quoteChar character. - * - * @param quoteChar - * the quoteChar character - * @return A copy of this format using the specified quoteChar character - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withQuoteChar(final char quoteChar) { - return withQuoteChar(Character.valueOf(quoteChar)); - } - - /** - * Returns a copy of this format using the specified quoteChar character. - * - * @param quoteChar - * the quoteChar character - * @return A copy of this format using the specified quoteChar character - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withQuoteChar(final Character quoteChar) { - if (isLineBreak(quoteChar)) { - throw new IllegalArgumentException("The quoteChar cannot be a line break"); - } - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Returns whether an quoteChar has been defined. * @@ -325,40 +249,6 @@ public class CSVFormat implements Serializable { return commentStart; } - /** - * Returns a copy of this format using the specified character as the comment start marker. - * - * Note that the comment introducer character is only recognised at the start of a line. - * - * @param commentStart - * the comment start marker - * @return A copy of this format using the specified character as the comment start marker - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withCommentStart(final char commentStart) { - return withCommentStart(Character.valueOf(commentStart)); - } - - /** - * Returns a copy of this format using the specified character as the comment start marker. - * - * Note that the comment introducer character is only recognised at the start of a line. - * - * @param commentStart - * the comment start marker - * @return A copy of this format using the specified character as the comment start marker - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withCommentStart(final Character commentStart) { - if (isLineBreak(commentStart)) { - throw new IllegalArgumentException("The comment start character cannot be a line break"); - } - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Specifies whether comments are supported by this format. * @@ -379,36 +269,6 @@ public class CSVFormat implements Serializable { return escape; } - /** - * Returns a copy of this format using the specified escape character. - * - * @param escape - * the escape character - * @return A copy of this format using the specified escape character - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withEscape(final char escape) { - return withEscape(Character.valueOf(escape)); - } - - /** - * Returns a copy of this format using the specified escape character. - * - * @param escape - * the escape character - * @return A copy of this format using the specified escape character - * @throws IllegalArgumentException - * thrown if the specified character is a line break - */ - public CSVFormat withEscape(final Character escape) { - if (isLineBreak(escape)) { - throw new IllegalArgumentException("The escape character cannot be a line break"); - } - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Returns whether escape are being processed. * @@ -428,19 +288,6 @@ public class CSVFormat implements Serializable { return ignoreSurroundingSpaces; } - /** - * Returns a copy of this format with the specified trimming behavior. - * - * @param ignoreSurroundingSpaces - * the trimming behavior, true to remove the surrounding spaces, false to leave the - * spaces as is. - * @return A copy of this format with the specified trimming behavior. - */ - public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Specifies whether empty lines between records are ignored when parsing input. * @@ -451,19 +298,6 @@ public class CSVFormat implements Serializable { return ignoreEmptyLines; } - /** - * Returns a copy of this format with the specified empty line skipping behavior. - * - * @param ignoreEmptyLines - * the empty line skipping behavior, true to ignore the empty lines between the records, - * false to translate empty lines to empty records. - * @return A copy of this format with the specified empty line skipping behavior. - */ - public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Returns the line separator delimiting output records. * @@ -473,73 +307,10 @@ public class CSVFormat implements Serializable { return recordSeparator; } - /** - * Returns a copy of this format using the specified output record separator. - * - * @param recordSeparator - * the record separator to use for output. - * - * @return A copy of this format using the specified output record separator - */ - public CSVFormat withRecordSeparator(final char recordSeparator) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, String.valueOf(recordSeparator), header); - } - - /** - * Returns a copy of this format using the specified output record separator. - * - * @param recordSeparator - * the record separator to use for output. - * - * @return A copy of this format using the specified output record separator - */ - public CSVFormat withRecordSeparator(final String recordSeparator) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - - /** - * Returns a copy of this format using the specified output quote policy. - * - * @param quotePolicy - * the quote policy to use for output. - * - * @return A copy of this format using the specified output line separator - */ - public CSVFormat withQuotePolicy(final Quote quotePolicy) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - String[] getHeader() { return header; } - /** - * Returns a copy of this format using the specified header. The header can either be parsed automatically from the - * input file with: - * - *
-     * CSVFormat format = aformat.withHeader();
-     * 
- * - * or specified manually with: - * - *
-     * CSVFormat format = aformat.withHeader("name", "email", "phone");
-     * 
- * - * @param header - * the header, null if disabled, empty if parsed automatically, user specified otherwise. - * - * @return A copy of this format using the specified header - */ - public CSVFormat withHeader(final String... header) { - return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); - } - /** * Parses the specified content. * @@ -600,5 +371,307 @@ public class CSVFormat implements Serializable { public Quote getQuotePolicy() { return quotePolicy; } + + public static class CSVFormatBuilder { + private char delimiter; + private Character quoteChar; + private Quote quotePolicy; + private Character commentStart; + private Character escape; + private boolean ignoreSurroundingSpaces; // Should leading/trailing spaces be ignored around values? + private boolean ignoreEmptyLines; + private String recordSeparator; // for outputs + private String[] header; + + /** + * Creates a customized CSV format. + * + * @param delimiter + * the char used for value separation, must not be a line break character + * @param quoteChar + * the char used as value encapsulation marker + * @param quotePolicy + * the quote policy + * @param commentStart + * the char used for comment identification + * @param escape + * the char used to escape special characters in values + * @param ignoreSurroundingSpaces + * true when whitespaces enclosing values should be ignored + * @param ignoreEmptyLines + * true when the parser should skip empty lines + * @param recordSeparator + * the line separator to use for output + * @param header + * the header + * @throws IllegalArgumentException if the delimiter is a line break character + */ + // package protected for use by test code + CSVFormatBuilder(final char delimiter, final Character quoteChar, final Quote quotePolicy, final Character commentStart, final Character escape, final + boolean ignoreSurroundingSpaces, final boolean ignoreEmptyLines, final String lineSeparator, + final String[] header) { + if (isLineBreak(delimiter)) { + throw new IllegalArgumentException("The delimiter cannot be a line break"); + } + this.delimiter = delimiter; + this.quoteChar = quoteChar; + this.quotePolicy = quotePolicy; + this.commentStart = commentStart; + this.escape = escape; + this.ignoreSurroundingSpaces = ignoreSurroundingSpaces; + this.ignoreEmptyLines = ignoreEmptyLines; + this.recordSeparator = lineSeparator; + this.header = header; + } + + /** + * Creates a basic CSVFormatBuilder. + * + * @param delimiter + * the char used for value separation, must not be a line break character + * @throws IllegalArgumentException if the delimiter is a line break character + */ + private CSVFormatBuilder(char delimiter){ + this(delimiter, null, null, null, null, false, false, null, null); + } + + public CSVFormat build() { + validate(); + return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape, + ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, header); + } + + /** + * Verifies the consistency of the parameters and throws an IllegalStateException if necessary. + * + * @throws IllegalStateException + */ + private void validate() throws IllegalStateException { + if (quoteChar != null && delimiter == quoteChar.charValue()) { + throw new IllegalStateException("The quoteChar character and the delimiter cannot be the same ('" + quoteChar + "')"); + } + + if (escape != null && delimiter == escape.charValue()) { + throw new IllegalStateException("The escape character and the delimiter cannot be the same ('" + escape + "')"); + } + + if (commentStart != null && delimiter == commentStart.charValue()) { + throw new IllegalStateException("The comment start character and the delimiter cannot be the same ('" + commentStart + + "')"); + } + + if (quoteChar != null && quoteChar.equals(commentStart)) { + throw new IllegalStateException("The comment start character and the quoteChar cannot be the same ('" + commentStart + + "')"); + } + + if (escape != null && escape.equals(commentStart)) { + throw new IllegalStateException("The comment start and the escape character cannot be the same ('" + commentStart + "')"); + } + + if (escape == null && quotePolicy == Quote.NONE) { + throw new IllegalStateException("No quotes mode set but no escape character is set"); + } + } + + /** + * Sets the delimiter of the format to the specified character. + * + * @param delimiter + * the delimiter character + * @return This builder with the specified character as delimiter + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withDelimiter(final char delimiter) { + if (isLineBreak(delimiter)) { + throw new IllegalArgumentException("The delimiter cannot be a line break"); + } + this.delimiter = delimiter; + return this; + } + + /** + * Sets the quoteChar of the format to the specified character. + * + * @param quoteChar + * the quoteChar character + * @return This builder with the specified character as quoteChar + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withQuoteChar(final char quoteChar) { + return withQuoteChar(Character.valueOf(quoteChar)); + } + + /** + * Sets the quoteChar of the format to the specified character. + * + * @param quoteChar + * the quoteChar character + * @return This builder with the specified character as quoteChar + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withQuoteChar(final Character quoteChar) { + if (isLineBreak(quoteChar)) { + throw new IllegalArgumentException("The quoteChar cannot be a line break"); + } + this.quoteChar = quoteChar; + return this; + } + + /** + * Sets the comment start marker of the format to the specified character. + * + * Note that the comment introducer character is only recognised at the start of a line. + * + * @param commentStart + * the comment start marker + * @return This builder with the specified character as the comment start marker + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withCommentStart(final char commentStart) { + return withCommentStart(Character.valueOf(commentStart)); + } + + /** + * Sets the comment start marker of the format to the specified character. + * + * Note that the comment introducer character is only recognised at the start of a line. + * + * @param commentStart + * the comment start marker + * @return This builder with the specified character as the comment start marker + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withCommentStart(final Character commentStart) { + if (isLineBreak(commentStart)) { + throw new IllegalArgumentException("The comment start character cannot be a line break"); + } + this.commentStart = commentStart; + return this; + } + + /** + * Sets the escape character of the format to the specified character. + * + * @param escape + * the escape character + * @return This builder with the specified character as the escape character + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withEscape(final char escape) { + return withEscape(Character.valueOf(escape)); + } + + /** + * Sets the escape character of the format to the specified character. + * + * @param escape + * the escape character + * @return This builder with the specified character as the escape character + * @throws IllegalArgumentException + * thrown if the specified character is a line break + */ + public CSVFormatBuilder withEscape(final Character escape) { + if (isLineBreak(escape)) { + throw new IllegalArgumentException("The escape character cannot be a line break"); + } + this.escape = escape; + return this; + } + + /** + * Sets the header of the format. The header can either be parsed automatically from the + * input file with: + * + *
+         * CSVFormat format = aformat.withHeader();
+         * 
+ * + * or specified manually with: + * + *
+         * CSVFormat format = aformat.withHeader("name", "email", "phone");
+         * 
+ * + * @param header + * the header, null if disabled, empty if parsed automatically, user specified otherwise. + * + * @return This builder with the specified header + */ + public CSVFormatBuilder withHeader(final String... header) { + this.header = header; + return this; + } + + /** + * Sets the trimming behavior of the format. + * + * @param ignoreSurroundingSpaces + * the trimming behavior, true to remove the surrounding spaces, false to leave the + * spaces as is. + * @return This builder with the specified trimming behavior. + */ + public CSVFormatBuilder withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) { + this.ignoreSurroundingSpaces = ignoreSurroundingSpaces; + return this; + } + + /** + * Sets the empty line skipping behavior of the format. + * + * @param ignoreEmptyLines + * the empty line skipping behavior, true to ignore the empty lines between the records, + * false to translate empty lines to empty records. + * @return This builder with the specified empty line skipping behavior. + */ + public CSVFormatBuilder withIgnoreEmptyLines(final boolean ignoreEmptyLines) { + this.ignoreEmptyLines = ignoreEmptyLines; + return this; + } + + /** + * Sets the record separator of the format to the specified character. + * + * @param recordSeparator + * the record separator to use for output. + * + * @return This builder with the the specified output record separator + */ + public CSVFormatBuilder withRecordSeparator(final char recordSeparator) { + return withRecordSeparator(String.valueOf(recordSeparator)); + } + + /** + * Sets the record separator of the format to the specified String. + * + * @param recordSeparator + * the record separator to use for output. + * + * @return This builder with the the specified output record separator + */ + public CSVFormatBuilder withRecordSeparator(final String recordSeparator) { + this.recordSeparator = recordSeparator; + return this; + } + + /** + * Sets the output quote policy of the format to the specified value. + * + * @param quotePolicy + * the quote policy to use for output. + * + * @return This builder with the specified quote policy + */ + public CSVFormatBuilder withQuotePolicy(final Quote quotePolicy) { + this.quotePolicy = quotePolicy; + return this; + } + } } diff --git a/src/main/java/org/apache/commons/csv/CSVParser.java b/src/main/java/org/apache/commons/csv/CSVParser.java index 3a3de34e..d4c49b6b 100644 --- a/src/main/java/org/apache/commons/csv/CSVParser.java +++ b/src/main/java/org/apache/commons/csv/CSVParser.java @@ -103,7 +103,6 @@ public class CSVParser implements Iterable { * thrown if the parameters of the format are inconsistent */ public CSVParser(final Reader input, final CSVFormat format) throws IOException { - format.validate(); this.lexer = new CSVLexer(format, new ExtendedBufferedReader(input)); this.headerMap = initializeHeader(format); } diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index f6384c1f..1411bb59 100644 --- a/src/main/java/org/apache/commons/csv/CSVPrinter.java +++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java @@ -59,7 +59,6 @@ public class CSVPrinter implements Flushable, Closeable { public CSVPrinter(final Appendable out, final CSVFormat format) { this.out = out; this.format = format == null ? CSVFormat.DEFAULT : format; - this.format.validate(); } // ====================================================== diff --git a/src/test/java/org/apache/commons/csv/CSVFileParserTest.java b/src/test/java/org/apache/commons/csv/CSVFileParserTest.java index a5d3ac06..900873bb 100644 --- a/src/test/java/org/apache/commons/csv/CSVFileParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFileParserTest.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.apache.commons.csv.CSVFormat.CSVFormatBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -89,17 +90,18 @@ public class CSVFileParserTest { assertTrue(testName+" require 1 param", split.length >= 1); // first line starts with csv data file name final BufferedReader csvFile = new BufferedReader(new FileReader(new File(BASE, split[0]))); - CSVFormat fmt = new CSVFormat(',').withQuoteChar('"'); + CSVFormatBuilder builder = CSVFormat.newFormat(',').withQuoteChar('"'); + CSVFormat fmt = builder.build(); boolean checkComments = false; for(int i=1; i < split.length; i++) { final String option = split[i]; final String[] option_parts = option.split("=",2); if ("IgnoreEmpty".equalsIgnoreCase(option_parts[0])){ - fmt = fmt.withIgnoreEmptyLines(Boolean.parseBoolean(option_parts[1])); + fmt = builder.withIgnoreEmptyLines(Boolean.parseBoolean(option_parts[1])).build(); } else if ("IgnoreSpaces".equalsIgnoreCase(option_parts[0])) { - fmt = fmt.withIgnoreSurroundingSpaces(Boolean.parseBoolean(option_parts[1])); + fmt = builder.withIgnoreSurroundingSpaces(Boolean.parseBoolean(option_parts[1])).build(); } else if ("CommentStart".equalsIgnoreCase(option_parts[0])) { - fmt = fmt.withCommentStart(option_parts[1].charAt(0)); + fmt = builder.withCommentStart(option_parts[1].charAt(0)).build(); } else if ("CheckComments".equalsIgnoreCase(option_parts[0])) { checkComments = true; } else { diff --git a/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java b/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java new file mode 100644 index 00000000..06fa2f3c --- /dev/null +++ b/src/test/java/org/apache/commons/csv/CSVFormatBuilderTest.java @@ -0,0 +1,152 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.csv; + +import static org.apache.commons.csv.Constants.*; +import static org.junit.Assert.*; + +import org.apache.commons.csv.CSVFormat.CSVFormatBuilder; +import org.junit.Before; +import org.junit.Test; + +/** + * + * + * @version $Id$ + */ +public class CSVFormatBuilderTest { + + private CSVFormatBuilder builder; + + @Before + public void setUp() throws Exception { + builder = new CSVFormatBuilder('+', '!', null, '#', '!', true, true, CRLF, null); + } + + @Test + public void testDelimiter() { + assertEquals('?', builder.withDelimiter('?').build().getDelimiter()); + } + + @Test(expected = IllegalArgumentException.class) + public void testNewFormatLFThrowsException() { + CSVFormat.newFormat(LF); + } + + @Test(expected = IllegalArgumentException.class) + public void testNewFormatCRThrowsException() { + CSVFormat.newFormat(CR); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithDelimiterLFThrowsException() { + builder.withDelimiter(LF).build(); + } + + @Test(expected = IllegalStateException.class) + public void testDelimiterSameAsEscapeThrowsException() { + builder.withDelimiter('!').withEscape('!').build(); + } + + @Test(expected = IllegalStateException.class) + public void testDelimiterSameAsCommentStartThrowsException() { + builder.withDelimiter('!').withCommentStart('!').build(); + } + + @Test + public void testQuoteChar() { + assertEquals('?', builder.withQuoteChar('?').build().getQuoteChar().charValue()); + } + + @Test(expected = IllegalStateException.class) + public void testQuoteCharSameAsCommentStartThrowsException() { + builder.withQuoteChar('!').withCommentStart('!').build(); + } + + @Test(expected = IllegalStateException.class) + public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType() { + // Cannot assume that callers won't use different Character objects + builder.withQuoteChar(new Character('!')).withCommentStart('!').build(); + } + + @Test(expected = IllegalStateException.class) + public void testQuoteCharSameAsDelimiterThrowsException() { + builder.withQuoteChar('!').withDelimiter('!').build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithQuoteLFThrowsException() { + builder.withQuoteChar(LF).build(); + } + + @Test + public void testQuotePolicy() { + assertEquals(Quote.ALL, builder.withQuotePolicy(Quote.ALL).build().getQuotePolicy()); + } + + @Test(expected = IllegalStateException.class) + public void testQuotePolicyNoneWithoutEscapeThrowsException() { + CSVFormat.newFormat('!').withQuotePolicy(Quote.NONE).build(); + } + + @Test + public void testCommentStart() { + assertEquals('?', builder.withCommentStart('?').build().getCommentStart().charValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithCommentStartCRThrowsException() { + builder.withCommentStart(CR).build(); + } + + @Test + public void testRecoardSeparator() { + assertEquals("?", builder.withRecordSeparator("?").build().getRecordSeparator()); + } + + @Test + public void testEscape() { + assertEquals('?', builder.withEscape('?').build().getEscape().charValue()); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithEscapeCRThrowsExceptions() { + builder.withEscape(CR).build(); + } + + @Test(expected = IllegalStateException.class) + public void testEscapeSameAsCommentStartThrowsException() { + builder.withEscape('!').withCommentStart('!').build(); + } + + @Test(expected = IllegalStateException.class) + public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType() { + // Cannot assume that callers won't use different Character objects + builder.withEscape(new Character('!')).withCommentStart(new Character('!')).build(); + } + + @Test + public void testIgnoreSurroundingSpaces() { + assertFalse(builder.withIgnoreSurroundingSpaces(false).build().getIgnoreSurroundingSpaces()); + } + + @Test + public void testIgnoreEmptyLines() { + assertFalse(builder.withIgnoreEmptyLines(false).build().getIgnoreEmptyLines()); + } +} diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index ae4f9a50..a6584bc9 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -29,6 +29,7 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import org.apache.commons.csv.CSVFormat.CSVFormatBuilder; import org.junit.Test; /** @@ -38,46 +39,6 @@ import org.junit.Test; */ public class CSVFormatTest { - @Test - public void testImmutalibity() { - final CSVFormat format = new CSVFormat('!', '!', Quote.MINIMAL, '!', '!', true, true, CRLF, null); - - format.withDelimiter('?'); - format.withQuoteChar('?'); - format.withQuotePolicy(Quote.ALL); - format.withCommentStart('?'); - format.withRecordSeparator("?"); - format.withEscape('?'); - format.withIgnoreSurroundingSpaces(false); - format.withIgnoreEmptyLines(false); - - assertEquals('!', format.getDelimiter()); - assertEquals('!', format.getQuoteChar().charValue()); - assertEquals('!', format.getCommentStart().charValue()); - assertEquals('!', format.getEscape().charValue()); - assertEquals(CRLF, format.getRecordSeparator()); - - assertTrue(format.getIgnoreSurroundingSpaces()); - assertTrue(format.getIgnoreEmptyLines()); - - assertEquals(Quote.MINIMAL, format.getQuotePolicy()); - } - - @Test - public void testMutators() { - final CSVFormat format = new CSVFormat('!', '!', null, '!', '!', true, true, CRLF, null); - - assertEquals('?', format.withDelimiter('?').getDelimiter()); - assertEquals('?', format.withQuoteChar('?').getQuoteChar().charValue()); - assertEquals(Quote.ALL, format.withQuotePolicy(Quote.ALL).getQuotePolicy()); - assertEquals('?', format.withCommentStart('?').getCommentStart().charValue()); - assertEquals("?", format.withRecordSeparator("?").getRecordSeparator()); - assertEquals('?', format.withEscape('?').getEscape().charValue()); - - assertFalse(format.withIgnoreSurroundingSpaces(false).getIgnoreSurroundingSpaces()); - assertFalse(format.withIgnoreEmptyLines(false).getIgnoreEmptyLines()); - } - @Test public void testFormat() { final CSVFormat format = CSVFormat.DEFAULT; @@ -87,116 +48,6 @@ public class CSVFormatTest { assertEquals("\"x,y\",z", format.format("x,y", "z")); } - @Test - public void testValidation() { - try { - new CSVFormat('\n'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - try { - new CSVFormat('\r'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - final CSVFormat format = CSVFormat.DEFAULT; - - try { - format.withDelimiter('\n'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - try { - format.withEscape('\r'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - try { - format.withQuoteChar('\n'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - try { - format.withCommentStart('\r'); - fail(); - } catch (final IllegalArgumentException e) { - // expected - } - - try { - format.withDelimiter('!').withEscape('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - try { - format.withDelimiter('!').withCommentStart('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - try { - format.withQuoteChar('!').withCommentStart('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - // Cannot assume that callers won't use different Character objects - try { - format.withQuoteChar(new Character('!')).withCommentStart('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - format.withQuoteChar(null).withCommentStart(null).validate(); - - try { - format.withEscape('!').withCommentStart('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - // Cannot assume that callers won't use different Character objects - try { - format.withEscape(new Character('!')).withCommentStart(new Character('!')).validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - format.withEscape(null).withCommentStart(null).validate(); - - - try { - format.withQuoteChar('!').withDelimiter('!').validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - - try { - format.withQuoteChar('!').withQuotePolicy(Quote.NONE).validate(); - fail(); - } catch (final IllegalStateException e) { - // expected - } - } - @SuppressWarnings("boxing") // no need to worry about boxing here @Test public void testSerialization() throws Exception { diff --git a/src/test/java/org/apache/commons/csv/CSVLexerTest.java b/src/test/java/org/apache/commons/csv/CSVLexerTest.java index c4493b50..786e2b77 100644 --- a/src/test/java/org/apache/commons/csv/CSVLexerTest.java +++ b/src/test/java/org/apache/commons/csv/CSVLexerTest.java @@ -51,7 +51,7 @@ public class CSVLexerTest { @Test public void testNextToken1() throws IOException { final String code = "abc,def, hijk, lmnop, qrst,uv ,wxy ,z , ,"; - final Lexer parser = getLexer(code, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(true)); + final Lexer parser = getLexer(code, CSVFormat.defaults().withIgnoreSurroundingSpaces(true).build()); assertTokenEquals(TOKEN, "abc", parser.nextToken(new Token())); assertTokenEquals(TOKEN, "def", parser.nextToken(new Token())); assertTokenEquals(TOKEN, "hijk", parser.nextToken(new Token())); @@ -83,7 +83,7 @@ public class CSVLexerTest { "\n"+ "\n"+ "# Final comment\n"; // 7 - final CSVFormat format = CSVFormat.DEFAULT.withCommentStart('#'); + final CSVFormat format = CSVFormat.defaults().withCommentStart('#').build(); assertTrue("Should ignore empty lines", format.getIgnoreEmptyLines()); final Lexer parser = getLexer(code, format); @@ -126,7 +126,7 @@ public class CSVLexerTest { "\n"+ // 6b "\n"+ // 6c "# Final comment\n"; // 7 - final CSVFormat format = CSVFormat.DEFAULT.withCommentStart('#').withIgnoreEmptyLines(false); + final CSVFormat format = CSVFormat.defaults().withCommentStart('#').withIgnoreEmptyLines(false).build(); assertFalse("Should not ignore empty lines", format.getIgnoreEmptyLines()); final Lexer parser = getLexer(code, format); @@ -187,7 +187,7 @@ public class CSVLexerTest { * \,, */ final String code = "a,\\,,b\\\\\n\\,,\\\nc,d\\\r\ne"; - final CSVFormat format = CSVFormat.DEFAULT.withEscape('\\').withIgnoreEmptyLines(false); + final CSVFormat format = CSVFormat.defaults().withEscape('\\').withIgnoreEmptyLines(false).build(); assertTrue(format.isEscaping()); final Lexer parser = getLexer(code, format); @@ -204,7 +204,7 @@ public class CSVLexerTest { @Test public void testNextToken3BadEscaping() throws IOException { final String code = "a,b,c\\"; - final CSVFormat format = CSVFormat.DEFAULT.withEscape('\\'); + final CSVFormat format = CSVFormat.defaults().withEscape('\\').build(); assertTrue(format.isEscaping()); final Lexer parser = getLexer(code, format); @@ -226,7 +226,7 @@ public class CSVLexerTest { * a, " foo " ,b */ final String code = "a,\"foo\",b\na, \" foo\",b\na,\"foo \" ,b\na, \" foo \" ,b"; - final Lexer parser = getLexer(code, CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(true)); + final Lexer parser = getLexer(code, CSVFormat.defaults().withIgnoreSurroundingSpaces(true).build()); assertTokenEquals(TOKEN, "a", parser.nextToken(new Token())); assertTokenEquals(TOKEN, "foo", parser.nextToken(new Token())); assertTokenEquals(EORECORD, "b", parser.nextToken(new Token())); @@ -264,7 +264,7 @@ public class CSVLexerTest { * ;; */ final String code = "a;'b and '' more\n'\n!comment;;;;\n;;"; - final CSVFormat format = CSVFormat.DEFAULT.withDelimiter(';').withQuoteChar('\'').withCommentStart('!'); + final CSVFormat format = CSVFormat.defaults().withDelimiter(';').withQuoteChar('\'').withCommentStart('!').build(); final Lexer parser = getLexer(code, format); assertTokenEquals(TOKEN, "a", parser.nextToken(new Token())); assertTokenEquals(EORECORD, "b and ' more\n", parser.nextToken(new Token())); diff --git a/src/test/java/org/apache/commons/csv/CSVParserTest.java b/src/test/java/org/apache/commons/csv/CSVParserTest.java index 3a7edf9f..5522d3ce 100644 --- a/src/test/java/org/apache/commons/csv/CSVParserTest.java +++ b/src/test/java/org/apache/commons/csv/CSVParserTest.java @@ -71,7 +71,7 @@ public class CSVParserTest { @Test public void testGetLine() throws IOException { - final CSVParser parser = new CSVParser(new StringReader(CSVINPUT), CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(true)); + final CSVParser parser = new CSVParser(new StringReader(CSVINPUT), CSVFormat.defaults().withIgnoreSurroundingSpaces(true).build()); for (final String[] re : RESULT) { assertArrayEquals(re, parser.nextRecord().values()); } @@ -81,7 +81,7 @@ public class CSVParserTest { @Test public void testGetRecords() throws IOException { - final CSVParser parser = new CSVParser(new StringReader(CSVINPUT), CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(true)); + final CSVParser parser = new CSVParser(new StringReader(CSVINPUT), CSVFormat.defaults().withIgnoreSurroundingSpaces(true).build()); final List records = parser.getRecords(); assertEquals(RESULT.length, records.size()); assertTrue(records.size() > 0); @@ -312,8 +312,8 @@ public class CSVParserTest { }; - final CSVFormat format = new CSVFormat(',').withQuoteChar('\'').withEscape('/') - .withIgnoreEmptyLines(true).withRecordSeparator(CRLF); + final CSVFormat format = CSVFormat.newFormat(',').withQuoteChar('\'').withEscape('/') + .withIgnoreEmptyLines(true).withRecordSeparator(CRLF).build(); final CSVParser parser = new CSVParser(code, format); final List records = parser.getRecords(); @@ -342,8 +342,8 @@ public class CSVParserTest { }; - final CSVFormat format = new CSVFormat(',').withEscape('/') - .withIgnoreEmptyLines(true).withRecordSeparator(CRLF); + final CSVFormat format = CSVFormat.newFormat(',').withEscape('/') + .withIgnoreEmptyLines(true).withRecordSeparator(CRLF).build(); final CSVParser parser = new CSVParser(code, format); final List records = parser.getRecords(); @@ -381,7 +381,7 @@ public class CSVParserTest { {"\n", " ", "#"}, }; - format = CSVFormat.DEFAULT.withCommentStart('#'); + format = CSVFormat.defaults().withCommentStart('#').build(); parser = new CSVParser(code, format); records = parser.getRecords(); @@ -481,7 +481,7 @@ public class CSVParserTest { public void testHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); - final Iterator records = CSVFormat.DEFAULT.withHeader().parse(in).iterator(); + final Iterator records = CSVFormat.defaults().withHeader().build().parse(in).iterator(); for (int i = 0; i < 2; i++) { assertTrue(records.hasNext()); @@ -498,7 +498,7 @@ public class CSVParserTest { public void testHeaderComment() throws Exception { final Reader in = new StringReader("# comment\na,b,c\n1,2,3\nx,y,z"); - final Iterator records = CSVFormat.DEFAULT.withCommentStart('#').withHeader().parse(in).iterator(); + final Iterator records = CSVFormat.defaults().withCommentStart('#').withHeader().build().parse(in).iterator(); for (int i = 0; i < 2; i++) { assertTrue(records.hasNext()); @@ -515,7 +515,7 @@ public class CSVParserTest { public void testProvidedHeader() throws Exception { final Reader in = new StringReader("a,b,c\n1,2,3\nx,y,z"); - final Iterator records = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in).iterator(); + final Iterator records = CSVFormat.defaults().withHeader("A", "B", "C").build().parse(in).iterator(); for (int i = 0; i < 3; i++) { assertTrue(records.hasNext()); @@ -536,7 +536,7 @@ public class CSVParserTest { public void testMappedButNotSetAsOutlook2007ContactExport() throws Exception { final Reader in = new StringReader("a,b,c\n1,2\nx,y,z"); - final Iterator records = CSVFormat.DEFAULT.withHeader("A", "B", "C").parse(in).iterator(); + final Iterator records = CSVFormat.defaults().withHeader("A", "B", "C").build().parse(in).iterator(); // header record assertTrue(records.hasNext()); @@ -578,7 +578,7 @@ public class CSVParserTest { } public void testGetHeaderMap() throws Exception { - final CSVParser parser = new CSVParser("a,b,c\n1,2,3\nx,y,z", CSVFormat.DEFAULT.withHeader("A", "B", "C")); + final CSVParser parser = new CSVParser("a,b,c\n1,2,3\nx,y,z", CSVFormat.defaults().withHeader("A", "B", "C").build()); final Map headerMap = parser.getHeaderMap(); final Iterator columnNames = headerMap.keySet().iterator(); // Headers are iterated in column order. @@ -622,7 +622,7 @@ public class CSVParserTest { @Test public void testGetRecordWithMultiiLineValues() throws Exception { final CSVParser parser = new CSVParser("\"a\r\n1\",\"a\r\n2\"" + CRLF + "\"b\r\n1\",\"b\r\n2\"" + CRLF + "\"c\r\n1\",\"c\r\n2\"", - CSVFormat.DEFAULT.withRecordSeparator(CRLF)); + CSVFormat.defaults().withRecordSeparator(CRLF).build()); CSVRecord record; assertEquals(0, parser.getRecordNumber()); assertEquals(0, parser.getLineNumber()); @@ -654,7 +654,7 @@ public class CSVParserTest { } private void validateRecordNumbers(String lineSeparator) throws IOException { - final CSVParser parser = new CSVParser("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator)); + final CSVParser parser = new CSVParser("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.defaults().withRecordSeparator(lineSeparator).build()); CSVRecord record; assertEquals(0, parser.getRecordNumber()); assertNotNull(record = parser.nextRecord()); @@ -671,7 +671,7 @@ public class CSVParserTest { } private void validateLineNumbers(String lineSeparator) throws IOException { - final CSVParser parser = new CSVParser("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.DEFAULT.withRecordSeparator(lineSeparator)); + final CSVParser parser = new CSVParser("a" + lineSeparator + "b" + lineSeparator + "c", CSVFormat.defaults().withRecordSeparator(lineSeparator).build()); assertEquals(0, parser.getLineNumber()); assertNotNull(parser.nextRecord()); assertEquals(1, parser.getLineNumber()); diff --git a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java index f1e5f863..c5a61040 100644 --- a/src/test/java/org/apache/commons/csv/CSVPrinterTest.java +++ b/src/test/java/org/apache/commons/csv/CSVPrinterTest.java @@ -220,7 +220,7 @@ public class CSVPrinterTest { @Test public void testMultiLineComment() throws IOException { final StringWriter sw = new StringWriter(); - final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentStart('#')); + final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.defaults().withCommentStart('#').build()); printer.printComment("This is a comment\non multiple lines"); assertEquals("# This is a comment" + recordSeparator + "# on multiple lines" + recordSeparator, sw.toString()); @@ -293,7 +293,7 @@ public class CSVPrinterTest { @Test public void testQuoteAll() throws IOException { final StringWriter sw = new StringWriter(); - final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuotePolicy(Quote.ALL)); + final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.defaults().withQuotePolicy(Quote.ALL).build()); printer.printRecord("a", "b\nc", "d"); assertEquals("\"a\",\"b\nc\",\"d\"" + recordSeparator, sw.toString()); } @@ -301,7 +301,7 @@ public class CSVPrinterTest { @Test public void testQuoteNonNumeric() throws IOException { final StringWriter sw = new StringWriter(); - final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withQuotePolicy(Quote.NON_NUMERIC)); + final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.defaults().withQuotePolicy(Quote.NON_NUMERIC).build()); printer.printRecord("a", "b\nc", Integer.valueOf(1)); assertEquals("\"a\",\"b\nc\",1" + recordSeparator, sw.toString()); } @@ -317,7 +317,7 @@ public class CSVPrinterTest { @Test public void testSingleLineComment() throws IOException { final StringWriter sw = new StringWriter(); - final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withCommentStart('#')); + final CSVPrinter printer = new CSVPrinter(sw, CSVFormat.defaults().withCommentStart('#').build()); printer.printComment("This is a comment"); assertEquals("# This is a comment" + recordSeparator, sw.toString()); diff --git a/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java b/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java index ee2a23a5..6212ec84 100644 --- a/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java +++ b/src/test/java/org/apache/commons/csv/perf/PerformanceTest.java @@ -66,7 +66,7 @@ public class PerformanceTest { } private long parse(final Reader in, boolean traverseColumns) throws IOException { - final CSVFormat format = CSVFormat.DEFAULT.withIgnoreSurroundingSpaces(false); + final CSVFormat format = CSVFormat.defaults().withIgnoreSurroundingSpaces(false).build(); long recordCount = 0; for (final CSVRecord record : format.parse(in)) { recordCount++;