This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-csv.git
The following commit(s) were added to refs/heads/master by this push: new 6a2376a Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73. 6a2376a is described below commit 6a2376a906b3b6507533b33139f57d06ded1d7a4 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sun Jul 4 13:19:43 2021 -0400 Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73. --- src/changes/changes.xml | 1 + .../java/org/apache/commons/csv/CSVFormat.java | 1618 ++++++++++++-------- .../java/org/apache/commons/csv/CSVPrinter.java | 2 +- .../java/org/apache/commons/csv/CSVRecord.java | 2 +- .../java/org/apache/commons/csv/CSVFormatTest.java | 310 +++- 5 files changed, 1290 insertions(+), 643 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 7893d12..77a5bf3 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -63,6 +63,7 @@ <action type="add" dev="ggregory" due-to="Gary Gregory">Add CSVParser#stream().</action> <action issue="CSV-184" type="add" dev="ggregory" due-to="Gaurav Agarwal, M. Steiger, Gary Gregory">Make the method CSVRecord.putIn(Map) public.</action> <action type="add" dev="ggregory" due-to="dota17">Add test cases for CSVRecord with get(Enum) and toString. #54.</action> + <action type="add" dev="ggregory" due-to="Gary Gregory, dota17">Add and use CSVFormat.Builder, deprecated CSVFormat#with methods, based on #73.</action> <!-- UPDATE --> <action type="update" dev="ggregory" due-to="Gary Gregory">Update org.junit.jupiter:junit-jupiter from 5.6.0 to 5.7.0, #84 #109</action> <action type="update" dev="ggregory" due-to="Gary Gregory">Update tests from Apache Commons Lang 3.9 to 3.12.0.</action> diff --git a/src/main/java/org/apache/commons/csv/CSVFormat.java b/src/main/java/org/apache/commons/csv/CSVFormat.java index a95cccf..9b50ee6 100644 --- a/src/main/java/org/apache/commons/csv/CSVFormat.java +++ b/src/main/java/org/apache/commons/csv/CSVFormat.java @@ -89,7 +89,7 @@ import java.util.Set; * <h2>Defining formats</h2> * * <p> - * You can extend a format by calling the {@code with} methods. For example: + * You can extend a format by calling the {@code set} methods. For example: * </p> * * <pre> @@ -107,11 +107,11 @@ import java.util.Set; * </pre> * * <p> - * Calling {@link #withHeader(String...)} lets you use the given names to address values in a {@link CSVRecord}, and - * assumes that your CSV source does not contain a first record that also defines column names. + * Calling {@link Builder#setHeader(String...)} lets you use the given names to address values in a {@link CSVRecord}, and assumes that your CSV source does not + * contain a first record that also defines column names. * * If it does, then you are overriding this metadata with your names and you should skip the first record by calling - * {@link #withSkipHeaderRecord(boolean)} with {@code true}. + * {@link Builder#setSkipHeaderRecord(boolean)} with {@code true}. * </p> * * <h2>Parsing</h2> @@ -132,8 +132,8 @@ import java.util.Set; * <h2>Referencing columns safely</h2> * * <p> - * If your source contains a header record, you can simplify your code and safely reference columns, by using - * {@link #withHeader(String...)} with no arguments: + * If your source contains a header record, you can simplify your code and safely reference columns, by using {@link Builder#setHeader(String...)} with no + * arguments: * </p> * * <pre> @@ -163,6 +163,505 @@ import java.util.Set; public final class CSVFormat implements Serializable { /** + * Builds CSVFormat instances. + * + * @since 1.9.0 + */ + public static class Builder { + + /** + * Creates a new default builder. + * + * @return a copy of the builder + */ + public static Builder create() { + return new Builder(CSVFormat.DEFAULT); + } + + /** + * Creates a new builder for the given format. + * + * @param csvFormat the source format. + * @return a copy of the builder + */ + public static Builder create(final CSVFormat csvFormat) { + return new Builder(csvFormat); + } + + private boolean allowDuplicateHeaderNames; + + private boolean allowMissingColumnNames; + + private boolean autoFlush; + + private Character commentMarker; + + private char delimiter; + + private Character escapeCharacter; + + private String[] headers; + + private String[] headerComments; + + private boolean ignoreEmptyLines; + + private boolean ignoreHeaderCase; + + private boolean ignoreSurroundingSpaces; + + private String nullString; + + private Character quoteCharacter; + + private String quotedNullString; + + private QuoteMode quoteMode; + + private String recordSeparator; + + private boolean skipHeaderRecord; + + private boolean trailingDelimiter; + + private boolean trim; + + private Builder(final CSVFormat csvFormat) { + this.delimiter = csvFormat.delimiter; + this.quoteCharacter = csvFormat.quoteCharacter; + this.quoteMode = csvFormat.quoteMode; + this.commentMarker = csvFormat.commentMarker; + this.escapeCharacter = csvFormat.escapeCharacter; + this.ignoreSurroundingSpaces = csvFormat.ignoreSurroundingSpaces; + this.allowMissingColumnNames = csvFormat.allowMissingColumnNames; + this.ignoreEmptyLines = csvFormat.ignoreEmptyLines; + this.recordSeparator = csvFormat.recordSeparator; + this.nullString = csvFormat.nullString; + this.headerComments = csvFormat.headerComments; + this.headers = csvFormat.header; + this.skipHeaderRecord = csvFormat.skipHeaderRecord; + this.ignoreHeaderCase = csvFormat.ignoreHeaderCase; + this.trailingDelimiter = csvFormat.trailingDelimiter; + this.trim = csvFormat.trim; + this.autoFlush = csvFormat.autoFlush; + this.quotedNullString = csvFormat.quotedNullString; + this.allowDuplicateHeaderNames = csvFormat.allowDuplicateHeaderNames; + } + + /** + * Builds a new CSVFormat instance. + * + * @return a new CSVFormat instance. + */ + public CSVFormat build() { + return new CSVFormat(this); + } + + /** + * Sets the duplicate header names behavior, true to allow, false to disallow. + * + * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow. + * @return This instance. + */ + public Builder setAllowDuplicateHeaderNames(final boolean allowDuplicateHeaderNames) { + this.allowDuplicateHeaderNames = allowDuplicateHeaderNames; + return this; + } + + /** + * Sets the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an + * {@link IllegalArgumentException} to be thrown. + * + * @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to + * cause an {@link IllegalArgumentException} to be thrown. + * @return This instance. + */ + public Builder setAllowMissingColumnNames(final boolean allowMissingColumnNames) { + this.allowMissingColumnNames = allowMissingColumnNames; + return this; + } + + /** + * Sets whether to flush on close. + * + * @param autoFlush whether to flush on close. + * @return This instance. + */ + public Builder setAutoFlush(final boolean autoFlush) { + this.autoFlush = autoFlush; + return this; + } + + /** + * Sets the comment start marker, use {@code null} to disable. + * + * Note that the comment start character is only recognized at the start of a line. + * + * @param commentMarker the comment start marker, use {@code null} to disable. + * @return This instance. + * @throws IllegalArgumentException thrown if the specified character is a line break + */ + public Builder setCommentMarker(final char commentMarker) { + setCommentMarker(Character.valueOf(commentMarker)); + return this; + } + + /** + * Sets the comment start marker, use {@code null} to disable. + * + * Note that the comment start character is only recognized at the start of a line. + * + * @param commentMarker the comment start marker, use {@code null} to disable. + * @return This instance. + * @throws IllegalArgumentException thrown if the specified character is a line break + */ + public Builder setCommentMarker(final Character commentMarker) { + if (isLineBreak(commentMarker)) { + throw new IllegalArgumentException("The comment start marker character cannot be a line break"); + } + this.commentMarker = commentMarker; + return this; + } + + /** + * Sets the delimiter character. + * + * @param delimiter the delimiter character. + * @return This instance. + */ + public Builder setDelimiter(final char delimiter) { + if (isLineBreak(delimiter)) { + throw new IllegalArgumentException("The delimiter cannot be a line break"); + } + this.delimiter = delimiter; + return this; + } + + /** + * Sets the escape character. + * + * @param escapeCharacter the escape character. + * @return This instance. + * @throws IllegalArgumentException thrown if the specified character is a line break + */ + public Builder setEscape(final char escapeCharacter) { + setEscape(Character.valueOf(escapeCharacter)); + return this; + } + + /** + * Sets the escape character. + * + * @param escapeCharacter the escape character. + * @return This instance. + * @throws IllegalArgumentException thrown if the specified character is a line break + */ + public Builder setEscape(final Character escapeCharacter) { + if (isLineBreak(escapeCharacter)) { + throw new IllegalArgumentException("The escape character cannot be a line break"); + } + this.escapeCharacter = escapeCharacter; + return this; + } + + /** + * Sets the header defined by the given {@link Enum} class. + * + * <p> + * Example: + * </p> + * + * <pre> + * public enum HeaderEnum { + * Name, Email, Phone + * } + * + * Builder builder = builder.setHeader(HeaderEnum.class); + * </pre> + * <p> + * The header is also used by the {@link CSVPrinter}. + * </p> + * + * @param headerEnum the enum defining the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. + * @return This instance. + */ + public Builder setHeader(final Class<? extends Enum<?>> headerEnum) { + String[] header = null; + if (headerEnum != null) { + final Enum<?>[] enumValues = headerEnum.getEnumConstants(); + header = new String[enumValues.length]; + for (int i = 0; i < enumValues.length; i++) { + header[i] = enumValues[i].name(); + } + } + return setHeader(header); + } + + /** + * Sets the header from the result set metadata. The header can either be parsed automatically from the input file with: + * + * <pre> + * builder.setHeader(); + * </pre> + * + * or specified manually with: + * + * <pre> + * builder.setHeader(resultSet); + * </pre> + * <p> + * The header is also used by the {@link CSVPrinter}. + * </p> + * + * @param resultSet the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. + * @return This instance. + * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. + */ + public Builder setHeader(final ResultSet resultSet) throws SQLException { + return setHeader(resultSet != null ? resultSet.getMetaData() : null); + } + + /** + * Sets the header from the result set metadata. The header can either be parsed automatically from the input file with: + * + * <pre> + * builder.setHeader(); + * </pre> + * + * or specified manually with: + * + * <pre> + * builder.setHeader(resultSetMetaData); + * </pre> + * <p> + * The header is also used by the {@link CSVPrinter}. + * </p> + * + * @param resultSetMetaData the metaData for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. + * @return This instance. + * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. + */ + public Builder setHeader(final ResultSetMetaData resultSetMetaData) throws SQLException { + String[] labels = null; + if (resultSetMetaData != null) { + final int columnCount = resultSetMetaData.getColumnCount(); + labels = new String[columnCount]; + for (int i = 0; i < columnCount; i++) { + labels[i] = resultSetMetaData.getColumnLabel(i + 1); + } + } + return setHeader(labels); + } + + /** + * Sets the header to the given values. The header can either be parsed automatically from the input file with: + * + * <pre> + * builder.setHeader(); + * </pre> + * + * or specified manually with: + * + * <pre> + * builder.setHeader("name", "email", "phone"); + * </pre> + * <p> + * The header is also used by the {@link CSVPrinter}. + * </p> + * + * @param header the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. + * @return This instance. + */ + public Builder setHeader(final String... header) { + this.headers = CSVFormat.clone(header); + return this; + } + + /** + * Sets the header comments set to the given values. The comments will be printed first, before the headers. This setting is ignored by the parser. + * + * <pre> + * builder.setHeaderComments("Generated by Apache Commons CSV.", Instant.now()); + * </pre> + * + * @param headerComments the headerComments which will be printed by the Printer before the actual CSV data. + * @return This instance. + */ + public Builder setHeaderComments(final Object... headerComments) { + this.headerComments = CSVFormat.clone(toStringArray(headerComments)); + return this; + } + + /** + * Sets the header comments set to the given values. The comments will be printed first, before the headers. This setting is ignored by the parser. + * + * <pre> + * Builder.setHeaderComments("Generated by Apache Commons CSV.", Instant.now()); + * </pre> + * + * @param headerComments the headerComments which will be printed by the Printer before the actual CSV data. + * @return This instance. + */ + public Builder setHeaderComments(final String... headerComments) { + this.headerComments = CSVFormat.clone(headerComments); + return this; + } + + /** + * Sets the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate empty lines to empty + * records. + * + * @param ignoreEmptyLines the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate + * empty lines to empty records. + * @return This instance. + */ + public Builder setIgnoreEmptyLines(final boolean ignoreEmptyLines) { + this.ignoreEmptyLines = ignoreEmptyLines; + return this; + } + + /** + * Sets the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is. + * + * @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is. + * @return This instance. + */ + public Builder setIgnoreHeaderCase(final boolean ignoreHeaderCase) { + this.ignoreHeaderCase = ignoreHeaderCase; + return this; + } + + /** + * Sets the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is. + * + * @param ignoreSurroundingSpaces the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is. + * @return This instance. + */ + public Builder setIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) { + this.ignoreSurroundingSpaces = ignoreSurroundingSpaces; + return this; + } + + /** + * Sets the String to convert to and from {@code null}. No substitution occurs if {@code null}. + * + * <ul> + * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li> + * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li> + * </ul> + * + * @param nullString the String to convert to and from {@code null}. No substitution occurs if {@code null}. + * @return This instance. + */ + public Builder setNullString(final String nullString) { + this.nullString = nullString; + this.quotedNullString = quoteCharacter + nullString + quoteCharacter; + return this; + } + + /** + * Sets the quote character. + * + * @param quoteCharacter the quote character. + * @return This instance. + */ + public Builder setQuote(final char quoteCharacter) { + setQuote(Character.valueOf(quoteCharacter)); + return this; + } + + /** + * Sets the quote character, use {@code null} to disable. + * + * @param quoteCharacter the quote character, use {@code null} to disable. + * @return This instance. + */ + public Builder setQuote(final Character quoteCharacter) { + if (isLineBreak(quoteCharacter)) { + throw new IllegalArgumentException("The quoteChar cannot be a line break"); + } + this.quoteCharacter = quoteCharacter; + return this; + } + + /** + * Sets the quote policy to use for output. + * + * @param quoteMode the quote policy to use for output. + * @return This instance. + */ + public Builder setQuoteMode(final QuoteMode quoteMode) { + this.quoteMode = quoteMode; + return this; + } + + /** + * Sets the record separator to use for output. + * + * <p> + * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r' + * and "\r\n" + * </p> + * + * @param recordSeparator the record separator to use for output. + * @return This instance. + */ + public Builder setRecordSeparator(final char recordSeparator) { + this.recordSeparator = String.valueOf(recordSeparator); + return this; + } + + /** + * Sets the record separator to use for output. + * + * <p> + * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r' + * and "\r\n" + * </p> + * + * @param recordSeparator the record separator to use for output. + * @return This instance. + */ + public Builder setRecordSeparator(final String recordSeparator) { + this.recordSeparator = recordSeparator; + return this; + } + + /** + * Sets whether to skip the header record. + * + * @param skipHeaderRecord whether to skip the header record. + * @return This instance. + */ + public Builder setSkipHeaderRecord(final boolean skipHeaderRecord) { + this.skipHeaderRecord = skipHeaderRecord; + return this; + } + + /** + * Sets whether to add a trailing delimiter. + * + * @param trailingDelimiter whether to add a trailing delimiter. + * @return This instance. + */ + public Builder setTrailingDelimiter(final boolean trailingDelimiter) { + this.trailingDelimiter = trailingDelimiter; + return this; + } + + /** + * Sets whether to trim leading and trailing blanks. + * + * @param trim whether to trim leading and trailing blanks. + * @return This instance. + */ + public Builder setTrim(final boolean trim) { + this.trim = trim; + return this; + } + } + + /** * Predefines formats. * * @since 1.2 @@ -254,24 +753,24 @@ public final class CSVFormat implements Serializable { * Standard Comma Separated Value format, as for {@link #RFC4180} but allowing empty lines. * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator("\r\n")}</li> - * <li>{@code withIgnoreEmptyLines(true)}</li> - * <li>{@code withAllowDuplicateHeaderNames(true)}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator("\r\n")}</li> + * <li>{@code setIgnoreEmptyLines(true)}</li> + * <li>{@code setAllowDuplicateHeaderNames(true)}</li> * </ul> * * @see Predefined#Default */ - public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, - null, null, null, false, false, false, false, false, false, true); + public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF, null, null, null, false, false, false, + false, false, false, true); /** - * Excel file format (using a comma as the value delimiter). Note that the actual value delimiter used by Excel is - * locale dependent, it might be necessary to customize this format to accommodate to your regional settings. + * Excel file format (using a comma as the value delimiter). Note that the actual value delimiter used by Excel is locale dependent, it might be necessary + * to customize this format to accommodate to your regional settings. * * <p> * For example for parsing or generating a CSV file on a French system the following format will be used: @@ -282,89 +781,90 @@ public final class CSVFormat implements Serializable { * </pre> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator("\r\n")}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> - * <li>{@code withAllowMissingColumnNames(true)}</li> - * <li>{@code withAllowDuplicateHeaderNames(true)}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator("\r\n")}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> + * <li>{@code setAllowMissingColumnNames(true)}</li> + * <li>{@code setAllowDuplicateHeaderNames(true)}</li> * </ul> * <p> - * Note: This is currently like {@link #RFC4180} plus {@link #withAllowMissingColumnNames(boolean) - * withAllowMissingColumnNames(true)} and {@link #withIgnoreEmptyLines(boolean) withIgnoreEmptyLines(false)}. + * Note: This is currently like {@link #RFC4180} plus {@link Builder#setAllowMissingColumnNames(boolean) Builder#setAllowMissingColumnNames(true)} and + * {@link Builder#setIgnoreEmptyLines(boolean) Builder#setIgnoreEmptyLines(false)}. * </p> * * @see Predefined#Excel */ // @formatter:off - public static final CSVFormat EXCEL = DEFAULT - .withIgnoreEmptyLines(false) - .withAllowMissingColumnNames(); + public static final CSVFormat EXCEL = DEFAULT.builder() + .setIgnoreEmptyLines(false) + .setAllowMissingColumnNames(true) + .build(); // @formatter:on /** * Default Informix CSV UNLOAD format used by the {@code UNLOAD TO file_name} operation. * * <p> - * This is a comma-delimited format with a LF character as the line separator. Values are not quoted and special - * characters are escaped with {@code '\'}. The default NULL string is {@code "\\N"}. + * This is a comma-delimited format with a LF character as the line separator. Values are not quoted and special characters are escaped with {@code '\'}. + * The default NULL string is {@code "\\N"}. * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withEscape('\\')}</li> - * <li>{@code withQuote("\"")}</li> - * <li>{@code withRecordSeparator('\n')}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setEscape('\\')}</li> + * <li>{@code setQuote("\"")}</li> + * <li>{@code setRecordSeparator('\n')}</li> * </ul> * * @see Predefined#MySQL - * @see <a href= - * "http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm"> + * @see <a href= "http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm"> * http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm</a> * @since 1.3 */ // @formatter:off - public static final CSVFormat INFORMIX_UNLOAD = DEFAULT - .withDelimiter(PIPE) - .withEscape(BACKSLASH) - .withQuote(DOUBLE_QUOTE_CHAR) - .withRecordSeparator(LF); + public static final CSVFormat INFORMIX_UNLOAD = DEFAULT.builder() + .setDelimiter(PIPE) + .setEscape(BACKSLASH) + .setQuote(DOUBLE_QUOTE_CHAR) + .setRecordSeparator(LF) + .build(); // @formatter:on /** * Default Informix CSV UNLOAD format used by the {@code UNLOAD TO file_name} operation (escaping is disabled.) * * <p> - * This is a comma-delimited format with a LF character as the line separator. Values are not quoted and special - * characters are escaped with {@code '\'}. The default NULL string is {@code "\\N"}. + * This is a comma-delimited format with a LF character as the line separator. Values are not quoted and special characters are escaped with {@code '\'}. + * The default NULL string is {@code "\\N"}. * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withQuote("\"")}</li> - * <li>{@code withRecordSeparator('\n')}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setQuote("\"")}</li> + * <li>{@code setRecordSeparator('\n')}</li> * </ul> * * @see Predefined#MySQL - * @see <a href= - * "http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm"> + * @see <a href= "http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm"> * http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm</a> * @since 1.3 */ // @formatter:off - public static final CSVFormat INFORMIX_UNLOAD_CSV = DEFAULT - .withDelimiter(COMMA) - .withQuote(DOUBLE_QUOTE_CHAR) - .withRecordSeparator(LF); + public static final CSVFormat INFORMIX_UNLOAD_CSV = DEFAULT.builder() + .setDelimiter(COMMA) + .setQuote(DOUBLE_QUOTE_CHAR) + .setRecordSeparator(LF) + .build(); // @formatter:on /** @@ -374,33 +874,33 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * This is a comma-delimited format. Values are double quoted only if needed and special characters are escaped with - * {@code '"'}. A header line with field names is expected. + * This is a comma-delimited format. Values are double quoted only if needed and special characters are escaped with {@code '"'}. A header line with field + * names is expected. * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withEscape('"')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withQuoteMode(QuoteMode.ALL_NON_NULL)}</li> - * <li>{@code withSkipHeaderRecord(false)}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setEscape('"')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setQuoteMode(QuoteMode.ALL_NON_NULL)}</li> + * <li>{@code setSkipHeaderRecord(false)}</li> * </ul> * * @see Predefined#MongoDBCsv - * @see <a href="https://docs.mongodb.com/manual/reference/program/mongoexport/">MongoDB mongoexport command - * documentation</a> + * @see <a href="https://docs.mongodb.com/manual/reference/program/mongoexport/">MongoDB mongoexport command documentation</a> * @since 1.7 */ // @formatter:off - public static final CSVFormat MONGODB_CSV = DEFAULT - .withDelimiter(COMMA) - .withEscape(DOUBLE_QUOTE_CHAR) - .withQuote(DOUBLE_QUOTE_CHAR) - .withQuoteMode(QuoteMode.MINIMAL) - .withSkipHeaderRecord(false); + public static final CSVFormat MONGODB_CSV = DEFAULT.builder() + .setDelimiter(COMMA) + .setEscape(DOUBLE_QUOTE_CHAR) + .setQuote(DOUBLE_QUOTE_CHAR) + .setQuoteMode(QuoteMode.MINIMAL) + .setSkipHeaderRecord(false) + .build(); // @formatter:off /** @@ -415,14 +915,14 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter('\t')}</li> - * <li>{@code withEscape('"')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withQuoteMode(QuoteMode.ALL_NON_NULL)}</li> - * <li>{@code withSkipHeaderRecord(false)}</li> + * <li>{@code setDelimiter('\t')}</li> + * <li>{@code setEscape('"')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setQuoteMode(QuoteMode.ALL_NON_NULL)}</li> + * <li>{@code setSkipHeaderRecord(false)}</li> * </ul> * * @see Predefined#MongoDBCsv @@ -431,12 +931,13 @@ public final class CSVFormat implements Serializable { * @since 1.7 */ // @formatter:off - public static final CSVFormat MONGODB_TSV = DEFAULT - .withDelimiter(TAB) - .withEscape(DOUBLE_QUOTE_CHAR) - .withQuote(DOUBLE_QUOTE_CHAR) - .withQuoteMode(QuoteMode.MINIMAL) - .withSkipHeaderRecord(false); + public static final CSVFormat MONGODB_TSV = DEFAULT.builder() + .setDelimiter(TAB) + .setEscape(DOUBLE_QUOTE_CHAR) + .setQuote(DOUBLE_QUOTE_CHAR) + .setQuoteMode(QuoteMode.MINIMAL) + .setSkipHeaderRecord(false) + .build(); // @formatter:off /** @@ -448,16 +949,16 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter('\t')}</li> - * <li>{@code withEscape('\\')}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> - * <li>{@code withQuote(null)}</li> - * <li>{@code withRecordSeparator('\n')}</li> - * <li>{@code withNullString("\\N")}</li> - * <li>{@code withQuoteMode(QuoteMode.ALL_NON_NULL)}</li> + * <li>{@code setDelimiter('\t')}</li> + * <li>{@code setEscape('\\')}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> + * <li>{@code setQuote(null)}</li> + * <li>{@code setRecordSeparator('\n')}</li> + * <li>{@code setNullString("\\N")}</li> + * <li>{@code setQuoteMode(QuoteMode.ALL_NON_NULL)}</li> * </ul> * * @see Predefined#MySQL @@ -465,14 +966,15 @@ public final class CSVFormat implements Serializable { * -data.html</a> */ // @formatter:off - public static final CSVFormat MYSQL = DEFAULT - .withDelimiter(TAB) - .withEscape(BACKSLASH) - .withIgnoreEmptyLines(false) - .withQuote(null) - .withRecordSeparator(LF) - .withNullString("\\N") - .withQuoteMode(QuoteMode.ALL_NON_NULL); + public static final CSVFormat MYSQL = DEFAULT.builder() + .setDelimiter(TAB) + .setEscape(BACKSLASH) + .setIgnoreEmptyLines(false) + .setQuote(null) + .setRecordSeparator(LF) + .setNullString("\\N") + .setQuoteMode(QuoteMode.ALL_NON_NULL) + .build(); // @formatter:off /** @@ -485,17 +987,17 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',') // default is {@code FIELDS TERMINATED BY ','}}</li> - * <li>{@code withEscape('\\')}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> - * <li>{@code withQuote('"') // default is {@code OPTIONALLY ENCLOSED BY '"'}}</li> - * <li>{@code withNullString("\\N")}</li> - * <li>{@code withTrim()}</li> - * <li>{@code withSystemRecordSeparator()}</li> - * <li>{@code withQuoteMode(QuoteMode.MINIMAL)}</li> + * <li>{@code setDelimiter(',') // default is {@code FIELDS TERMINATED BY ','}}</li> + * <li>{@code setEscape('\\')}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> + * <li>{@code setQuote('"') // default is {@code OPTIONALLY ENCLOSED BY '"'}}</li> + * <li>{@code setNullString("\\N")}</li> + * <li>{@code setTrim()}</li> + * <li>{@code setSystemRecordSeparator()}</li> + * <li>{@code setQuoteMode(QuoteMode.MINIMAL)}</li> * </ul> * * @see Predefined#Oracle @@ -503,15 +1005,16 @@ public final class CSVFormat implements Serializable { * @since 1.6 */ // @formatter:off - public static final CSVFormat ORACLE = DEFAULT - .withDelimiter(COMMA) - .withEscape(BACKSLASH) - .withIgnoreEmptyLines(false) - .withQuote(DOUBLE_QUOTE_CHAR) - .withNullString("\\N") - .withTrim() - .withSystemRecordSeparator() - .withQuoteMode(QuoteMode.MINIMAL); + public static final CSVFormat ORACLE = DEFAULT.builder() + .setDelimiter(COMMA) + .setEscape(BACKSLASH) + .setIgnoreEmptyLines(false) + .setQuote(DOUBLE_QUOTE_CHAR) + .setNullString("\\N") + .setTrim(true) + .setRecordSeparator(System.lineSeparator()) + .setQuoteMode(QuoteMode.MINIMAL) + .build(); // @formatter:off /** @@ -523,16 +1026,16 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withEscape('"')}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator('\n')}</li> - * <li>{@code withNullString("")}</li> - * <li>{@code withQuoteMode(QuoteMode.ALL_NON_NULL)}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setEscape('"')}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator('\n')}</li> + * <li>{@code setNullString("")}</li> + * <li>{@code setQuoteMode(QuoteMode.ALL_NON_NULL)}</li> * </ul> * * @see Predefined#MySQL @@ -541,14 +1044,15 @@ public final class CSVFormat implements Serializable { * @since 1.5 */ // @formatter:off - public static final CSVFormat POSTGRESQL_CSV = DEFAULT - .withDelimiter(COMMA) - .withEscape(DOUBLE_QUOTE_CHAR) - .withIgnoreEmptyLines(false) - .withQuote(DOUBLE_QUOTE_CHAR) - .withRecordSeparator(LF) - .withNullString(EMPTY) - .withQuoteMode(QuoteMode.ALL_NON_NULL); + public static final CSVFormat POSTGRESQL_CSV = DEFAULT.builder() + .setDelimiter(COMMA) + .setEscape(DOUBLE_QUOTE_CHAR) + .setIgnoreEmptyLines(false) + .setQuote(DOUBLE_QUOTE_CHAR) + .setRecordSeparator(LF) + .setNullString(EMPTY) + .setQuoteMode(QuoteMode.ALL_NON_NULL) + .build(); // @formatter:off /** @@ -560,16 +1064,16 @@ public final class CSVFormat implements Serializable { * </p> * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter('\t')}</li> - * <li>{@code withEscape('\\')}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator('\n')}</li> - * <li>{@code withNullString("\\N")}</li> - * <li>{@code withQuoteMode(QuoteMode.ALL_NON_NULL)}</li> + * <li>{@code setDelimiter('\t')}</li> + * <li>{@code setEscape('\\')}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator('\n')}</li> + * <li>{@code setNullString("\\N")}</li> + * <li>{@code setQuoteMode(QuoteMode.ALL_NON_NULL)}</li> * </ul> * * @see Predefined#MySQL @@ -578,32 +1082,33 @@ public final class CSVFormat implements Serializable { * @since 1.5 */ // @formatter:off - public static final CSVFormat POSTGRESQL_TEXT = DEFAULT - .withDelimiter(TAB) - .withEscape(BACKSLASH) - .withIgnoreEmptyLines(false) - .withQuote(DOUBLE_QUOTE_CHAR) - .withRecordSeparator(LF) - .withNullString("\\N") - .withQuoteMode(QuoteMode.ALL_NON_NULL); + public static final CSVFormat POSTGRESQL_TEXT = DEFAULT.builder() + .setDelimiter(TAB) + .setEscape(BACKSLASH) + .setIgnoreEmptyLines(false) + .setQuote(DOUBLE_QUOTE_CHAR) + .setRecordSeparator(LF) + .setNullString("\\N") + .setQuoteMode(QuoteMode.ALL_NON_NULL) + .build(); // @formatter:off /** * Comma separated format as defined by <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>. * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter(',')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator("\r\n")}</li> - * <li>{@code withIgnoreEmptyLines(false)}</li> + * <li>{@code setDelimiter(',')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator("\r\n")}</li> + * <li>{@code setIgnoreEmptyLines(false)}</li> * </ul> * * @see Predefined#RFC4180 */ - public static final CSVFormat RFC4180 = DEFAULT.withIgnoreEmptyLines(false); + public static final CSVFormat RFC4180 = DEFAULT.builder().setIgnoreEmptyLines(false).build(); private static final long serialVersionUID = 1L; @@ -611,28 +1116,40 @@ public final class CSVFormat implements Serializable { * Tab-delimited format. * * <p> - * Settings are: + * The {@link Builder} settings are: * </p> * <ul> - * <li>{@code withDelimiter('\t')}</li> - * <li>{@code withQuote('"')}</li> - * <li>{@code withRecordSeparator("\r\n")}</li> - * <li>{@code withIgnoreSurroundingSpaces(true)}</li> + * <li>{@code setDelimiter('\t')}</li> + * <li>{@code setQuote('"')}</li> + * <li>{@code setRecordSeparator("\r\n")}</li> + * <li>{@code setIgnoreSurroundingSpaces(true)}</li> * </ul> * * @see Predefined#TDF */ // @formatter:off - public static final CSVFormat TDF = DEFAULT - .withDelimiter(TAB) - .withIgnoreSurroundingSpaces(); + public static final CSVFormat TDF = DEFAULT.builder() + .setDelimiter(TAB) + .setIgnoreSurroundingSpaces(true) + .build(); // @formatter:on /** + * Null-safe clone of an array. + * + * @param <T> The array element type. + * @param values the source array + * @return the cloned array. + */ + @SafeVarargs + static <T> T[] clone(final T... values) { + return values == null ? null : values.clone(); + } + + /** * Returns true if the given character is a line break character. * - * @param c - * the character to check + * @param c the character to check * * @return true if {@code c} is a line break character */ @@ -643,8 +1160,7 @@ public final class CSVFormat implements Serializable { /** * Returns true if the given character is a line break character. * - * @param c - * the character to check, may be null + * @param c the character to check, may be null * * @return true if {@code c} is a line break character (and not null) */ @@ -656,15 +1172,12 @@ public final class CSVFormat implements Serializable { * Creates a new CSV format with the specified delimiter. * * <p> - * Use this method if you want to create a CSVFormat from scratch. All fields but the delimiter will be initialized - * with null/false. + * Use this method if you want to create a CSVFormat from scratch. All fields but the delimiter will be initialized with null/false. * </p> * - * @param delimiter - * the char used for value separation, must not be a line break character + * @param delimiter the char used for value separation, must not be a line break character * @return a new CSV format. - * @throws IllegalArgumentException - * if the delimiter is a line break character + * @throws IllegalArgumentException if the delimiter is a line break character * * @see #DEFAULT * @see #RFC4180 @@ -673,15 +1186,42 @@ public final class CSVFormat implements Serializable { * @see #TDF */ public static CSVFormat newFormat(final char delimiter) { - return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, null, false, false, - false, false, false, false, true); + return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, null, false, false, false, false, false, false, true); + } + + static String[] toStringArray(final Object[] values) { + if (values == null) { + return null; + } + final String[] strings = new String[values.length]; + for (int i = 0; i < values.length; i++) { + final Object value = values[i]; + strings[i] = value == null ? null : value.toString(); + } + return strings; + } + + static CharSequence trim(final CharSequence charSequence) { + if (charSequence instanceof String) { + return ((String) charSequence).trim(); + } + final int count = charSequence.length(); + int len = count; + int pos = 0; + + while (pos < len && charSequence.charAt(pos) <= SP) { + pos++; + } + while (pos < len && charSequence.charAt(len - 1) <= SP) { + len--; + } + return pos > 0 || len < count ? charSequence.subSequence(pos, len) : charSequence; } /** * Gets one of the predefined formats from {@link CSVFormat.Predefined}. * - * @param format - * name + * @param format name * @return one of the predefined formats * @since 1.2 */ @@ -727,51 +1267,56 @@ public final class CSVFormat implements Serializable { private final boolean trim; + private CSVFormat(final Builder builder) { + this.delimiter = builder.delimiter; + this.quoteCharacter = builder.quoteCharacter; + this.quoteMode = builder.quoteMode; + this.commentMarker = builder.commentMarker; + this.escapeCharacter = builder.escapeCharacter; + this.ignoreSurroundingSpaces = builder.ignoreSurroundingSpaces; + this.allowMissingColumnNames = builder.allowMissingColumnNames; + this.ignoreEmptyLines = builder.ignoreEmptyLines; + this.recordSeparator = builder.recordSeparator; + this.nullString = builder.nullString; + this.headerComments = builder.headerComments; + this.header = builder.headers; + this.skipHeaderRecord = builder.skipHeaderRecord; + this.ignoreHeaderCase = builder.ignoreHeaderCase; + this.trailingDelimiter = builder.trailingDelimiter; + this.trim = builder.trim; + this.autoFlush = builder.autoFlush; + this.quotedNullString = builder.quotedNullString; + this.allowDuplicateHeaderNames = builder.allowDuplicateHeaderNames; + validate(); + } + /** * Creates a customized CSV format. * - * @param delimiter - * the char used for value separation, must not be a line break character - * @param quoteChar - * the Character used as value encapsulation marker, may be {@code null} to disable - * @param quoteMode - * the quote mode - * @param commentStart - * the Character used for comment identification, may be {@code null} to disable - * @param escape - * the Character used to escape special characters in values, may be {@code null} to disable - * @param ignoreSurroundingSpaces - * {@code true} when whitespaces enclosing values should be ignored - * @param ignoreEmptyLines - * {@code true} when the parser should skip empty lines - * @param recordSeparator - * the line separator to use for output - * @param nullString - * the line separator to use for output - * @param headerComments - * the comments to be printed by the Printer before the actual CSV data - * @param header - * the header - * @param skipHeaderRecord - * TODO - * @param allowMissingColumnNames - * TODO - * @param ignoreHeaderCase - * TODO - * @param trim - * TODO - * @param trailingDelimiter - * TODO - * @param autoFlush - * @throws IllegalArgumentException - * if the delimiter is a line break character - */ - private CSVFormat(final char delimiter, final Character quoteChar, final QuoteMode quoteMode, - final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces, - final boolean ignoreEmptyLines, final String recordSeparator, final String nullString, - final Object[] headerComments, final String[] header, final boolean skipHeaderRecord, - final boolean allowMissingColumnNames, final boolean ignoreHeaderCase, final boolean trim, - final boolean trailingDelimiter, final boolean autoFlush, final boolean allowDuplicateHeaderNames) { + * @param delimiter the char used for value separation, must not be a line break character. + * @param quoteChar the Character used as value encapsulation marker, may be {@code null} to disable. + * @param quoteMode the quote mode. + * @param commentStart the Character used for comment identification, may be {@code null} to disable. + * @param escape the Character used to escape special characters in values, may be {@code null} to disable. + * @param ignoreSurroundingSpaces {@code true} when whitespaces enclosing values should be ignored. + * @param ignoreEmptyLines {@code true} when the parser should skip empty lines. + * @param recordSeparator the line separator to use for output. + * @param nullString the line separator to use for output. + * @param headerComments the comments to be printed by the Printer before the actual CSV data. + * @param header the header + * @param skipHeaderRecord TODO Doc me. + * @param allowMissingColumnNames TODO Doc me. + * @param ignoreHeaderCase TODO Doc me. + * @param trim TODO Doc me. + * @param trailingDelimiter TODO Doc me. + * @param autoFlush TODO Doc me. + * @throws IllegalArgumentException if the delimiter is a line break character. + */ + private CSVFormat(final char delimiter, final Character quoteChar, final QuoteMode quoteMode, final Character commentStart, final Character escape, + final boolean ignoreSurroundingSpaces, final boolean ignoreEmptyLines, final String recordSeparator, final String nullString, + final Object[] headerComments, final String[] header, final boolean skipHeaderRecord, final boolean allowMissingColumnNames, + final boolean ignoreHeaderCase, final boolean trim, final boolean trailingDelimiter, final boolean autoFlush, + final boolean allowDuplicateHeaderNames) { this.delimiter = delimiter; this.quoteCharacter = quoteChar; this.quoteMode = quoteMode; @@ -783,7 +1328,7 @@ public final class CSVFormat implements Serializable { this.recordSeparator = recordSeparator; this.nullString = nullString; this.headerComments = toStringArray(headerComments); - this.header = header == null ? null : header.clone(); + this.header = clone(header); this.skipHeaderRecord = skipHeaderRecord; this.ignoreHeaderCase = ignoreHeaderCase; this.trailingDelimiter = trailingDelimiter; @@ -794,71 +1339,41 @@ public final class CSVFormat implements Serializable { validate(); } + /** + * Creates a new Builder for this instance. + * + * @return a new Builder. + */ + public Builder builder() { + return Builder.create(this); + } + @Override public boolean equals(final Object obj) { if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if ((obj == null) || (getClass() != obj.getClass())) { return false; } final CSVFormat other = (CSVFormat) obj; - if (delimiter != other.delimiter) { + if ((delimiter != other.delimiter) || (trailingDelimiter != other.trailingDelimiter) || (autoFlush != other.autoFlush) || (trim != other.trim)) { return false; } - if (trailingDelimiter != other.trailingDelimiter) { + if ((allowMissingColumnNames != other.allowMissingColumnNames) || (allowDuplicateHeaderNames != other.allowDuplicateHeaderNames) || + (ignoreHeaderCase != other.ignoreHeaderCase) || (quoteMode != other.quoteMode)) { return false; } - if (autoFlush != other.autoFlush) { + if (!Objects.equals(quoteCharacter, other.quoteCharacter) || !Objects.equals(commentMarker, other.commentMarker) || + !Objects.equals(escapeCharacter, other.escapeCharacter) || !Objects.equals(nullString, other.nullString)) { return false; } - if (trim != other.trim) { + if (!Arrays.equals(header, other.header) || (ignoreSurroundingSpaces != other.ignoreSurroundingSpaces) || + (ignoreEmptyLines != other.ignoreEmptyLines) || (skipHeaderRecord != other.skipHeaderRecord)) { return false; } - if (allowMissingColumnNames != other.allowMissingColumnNames) { - return false; - } - if (allowDuplicateHeaderNames != other.allowDuplicateHeaderNames) { - return false; - } - if (ignoreHeaderCase != other.ignoreHeaderCase) { - return false; - } - if (quoteMode != other.quoteMode) { - return false; - } - if (!Objects.equals(quoteCharacter, other.quoteCharacter)) { - return false; - } - if (!Objects.equals(commentMarker, other.commentMarker)) { - return false; - } - if (!Objects.equals(escapeCharacter, other.escapeCharacter)) { - return false; - } - if (!Objects.equals(nullString, other.nullString)) { - return false; - } - if (!Arrays.equals(header, other.header)) { - return false; - } - if (ignoreSurroundingSpaces != other.ignoreSurroundingSpaces) { - return false; - } - if (ignoreEmptyLines != other.ignoreEmptyLines) { - return false; - } - if (skipHeaderRecord != other.skipHeaderRecord) { - return false; - } - if (!Objects.equals(recordSeparator, other.recordSeparator)) { - return false; - } - if (!Arrays.equals(headerComments, other.headerComments)) { + if (!Objects.equals(recordSeparator, other.recordSeparator) || !Arrays.equals(headerComments, other.headerComments)) { return false; } return true; @@ -867,8 +1382,7 @@ public final class CSVFormat implements Serializable { /** * Formats the specified values. * - * @param values - * the values to format + * @param values the values to format * @return the formatted values */ public String format(final Object... values) { @@ -897,8 +1411,7 @@ public final class CSVFormat implements Serializable { /** * Specifies whether missing column names are allowed when parsing the header line. * - * @return {@code true} if missing column names are allowed when parsing the header line, {@code false} to throw an - * {@link IllegalArgumentException}. + * @return {@code true} if missing column names are allowed when parsing the header line, {@code false} to throw an {@link IllegalArgumentException}. */ public boolean getAllowMissingColumnNames() { return allowMissingColumnNames; @@ -962,8 +1475,7 @@ public final class CSVFormat implements Serializable { /** * Specifies whether empty lines between records are ignored when parsing input. * - * @return {@code true} if empty lines between records are ignored, {@code false} if they are turned into empty - * records. + * @return {@code true} if empty lines between records are ignored, {@code false} if they are turned into empty records. */ public boolean getIgnoreEmptyLines() { return ignoreEmptyLines; @@ -991,8 +1503,7 @@ public final class CSVFormat implements Serializable { /** * Gets the String to convert to and from {@code null}. * <ul> - * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading - * records.</li> + * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li> * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li> * </ul> * @@ -1049,9 +1560,8 @@ public final class CSVFormat implements Serializable { } /** - * Returns whether to trim leading and trailing blanks. - * This is used by {@link #print(Object, Appendable, boolean)} - * Also by {CSVParser#addRecordValue(boolean)} + * Returns whether to trim leading and trailing blanks. This is used by {@link #print(Object, Appendable, boolean)} Also by + * {CSVParser#addRecordValue(boolean)} * * @return whether to trim leading and trailing blanks. */ @@ -1061,10 +1571,9 @@ public final class CSVFormat implements Serializable { @Override public int hashCode() { - return Objects.hash(delimiter, quoteMode, quoteCharacter, commentMarker, escapeCharacter, nullString, - ignoreSurroundingSpaces, ignoreHeaderCase, ignoreEmptyLines, skipHeaderRecord, allowDuplicateHeaderNames, - trim, autoFlush, trailingDelimiter, allowMissingColumnNames, recordSeparator, Arrays.hashCode(header), - Arrays.hashCode(headerComments)); + return Objects.hash(delimiter, quoteMode, quoteCharacter, commentMarker, escapeCharacter, nullString, ignoreSurroundingSpaces, ignoreHeaderCase, + ignoreEmptyLines, skipHeaderRecord, allowDuplicateHeaderNames, trim, autoFlush, trailingDelimiter, allowMissingColumnNames, recordSeparator, + Arrays.hashCode(header), Arrays.hashCode(headerComments)); } /** @@ -1112,11 +1621,9 @@ public final class CSVFormat implements Serializable { * See also the various static parse methods on {@link CSVParser}. * </p> * - * @param in - * the input stream + * @param in the input stream * @return a parser over a stream of {@link CSVRecord}s. - * @throws IOException - * If an I/O error occurs + * @throws IOException If an I/O error occurs */ public CSVParser parse(final Reader in) throws IOException { return new CSVParser(in, this); @@ -1129,11 +1636,9 @@ public final class CSVFormat implements Serializable { * See also {@link CSVPrinter}. * </p> * - * @param out - * the output. + * @param out the output. * @return a printer to an output. - * @throws IOException - * thrown if the optional header cannot be printed. + * @throws IOException thrown if the optional header cannot be printed. */ public CSVPrinter print(final Appendable out) throws IOException { return new CSVPrinter(out, this); @@ -1146,13 +1651,10 @@ public final class CSVFormat implements Serializable { * See also {@link CSVPrinter}. * </p> * - * @param out - * the output. - * @param charset - * A charset. + * @param out the output. + * @param charset A charset. * @return a printer to an output. - * @throws IOException - * thrown if the optional header cannot be printed. + * @throws IOException thrown if the optional header cannot be printed. * @since 1.5 */ @SuppressWarnings("resource") @@ -1162,17 +1664,13 @@ public final class CSVFormat implements Serializable { } /** - * Prints the {@code value} as the next value on the line to {@code out}. The value will be escaped or encapsulated - * as needed. Useful when one wants to avoid creating CSVPrinters. - * Trims the value if {@link #getTrim()} is true - * @param value - * value to output. - * @param out - * where to print the value. - * @param newRecord - * if this a new record. - * @throws IOException - * If an I/O error occurs. + * Prints the {@code value} as the next value on the line to {@code out}. The value will be escaped or encapsulated as needed. Useful when one wants to + * avoid creating CSVPrinters. Trims the value if {@link #getTrim()} is true + * + * @param value value to output. + * @param out where to print the value. + * @param newRecord if this a new record. + * @throws IOException If an I/O error occurs. * @since 1.4 */ public void print(final Object value, final Appendable out, final boolean newRecord) throws IOException { @@ -1200,8 +1698,7 @@ public final class CSVFormat implements Serializable { print(value, charSequence, out, newRecord); } - private void print(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) - throws IOException { + private void print(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) throws IOException { final int offset = 0; final int len = value.length(); if (!newRecord) { @@ -1226,7 +1723,7 @@ public final class CSVFormat implements Serializable { * See also {@link CSVPrinter}. * </p> * - * @param out the output. + * @param out the output. * @param charset A charset. * @return a printer to an output. * @throws IOException thrown if the optional header cannot be printed. @@ -1262,8 +1759,7 @@ public final class CSVFormat implements Serializable { * </p> * * @return a printer to {@link System#out}. - * @throws IOException - * thrown if the optional header cannot be printed. + * @throws IOException thrown if the optional header cannot be printed. * @since 1.5 */ public CSVPrinter printer() throws IOException { @@ -1273,10 +1769,8 @@ public final class CSVFormat implements Serializable { /** * Outputs the trailing delimiter (if set) followed by the record separator (if set). * - * @param out - * where to write - * @throws IOException - * If an I/O error occurs + * @param out where to write + * @throws IOException If an I/O error occurs * @since 1.4 */ public void println(final Appendable out) throws IOException { @@ -1289,20 +1783,16 @@ public final class CSVFormat implements Serializable { } /** - * Prints the given {@code values} to {@code out} as a single record of delimiter separated values followed by the - * record separator. + * Prints the given {@code values} to {@code out} as a single record of delimiter separated values followed by the record separator. * * <p> - * The values will be quoted if needed. Quotes and new-line characters will be escaped. This method adds the record - * separator to the output after printing the record, so there is no need to call {@link #println(Appendable)}. + * The values will be quoted if needed. Quotes and new-line characters will be escaped. This method adds the record separator to the output after printing + * the record, so there is no need to call {@link #println(Appendable)}. * </p> * - * @param out - * where to write. - * @param values - * values to output. - * @throws IOException - * If an I/O error occurs. + * @param out where to write. + * @param values values to output. + * @throws IOException If an I/O error occurs. * @since 1.4 */ public void printRecord(final Appendable out, final Object... values) throws IOException { @@ -1392,8 +1882,7 @@ public final class CSVFormat implements Serializable { * Note: must only be called if quoting is enabled, otherwise will generate NPE */ // the original object is needed so can check for Number - private void printWithQuotes(final Object object, final CharSequence value, final Appendable out, - final boolean newRecord) throws IOException { + private void printWithQuotes(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) throws IOException { boolean quote = false; int start = 0; int pos = 0; @@ -1501,8 +1990,7 @@ public final class CSVFormat implements Serializable { /** * Always use quotes unless QuoteMode is NONE, so we not have to look ahead. * - * @throws IOException - * If an I/O error occurs + * @throws IOException If an I/O error occurs */ private void printWithQuotes(final Reader reader, final Appendable out) throws IOException { @@ -1592,35 +2080,6 @@ public final class CSVFormat implements Serializable { return sb.toString(); } - private String[] toStringArray(final Object[] values) { - if (values == null) { - return null; - } - final String[] strings = new String[values.length]; - for (int i = 0; i < values.length; i++) { - final Object value = values[i]; - strings[i] = value == null ? null : value.toString(); - } - return strings; - } - - private CharSequence trim(final CharSequence charSequence) { - if (charSequence instanceof String) { - return ((String) charSequence).trim(); - } - final int count = charSequence.length(); - int len = count; - int pos = 0; - - while (pos < len && charSequence.charAt(pos) <= SP) { - pos++; - } - while (pos < len && charSequence.charAt(len - 1) <= SP) { - len--; - } - return pos > 0 || len < count ? charSequence.subSequence(pos, len) : charSequence; - } - /** * Verifies the validity and consistency of the attributes, and throws an IllegalArgumentException if necessary. * @@ -1632,28 +2091,23 @@ public final class CSVFormat implements Serializable { } if (quoteCharacter != null && delimiter == quoteCharacter.charValue()) { - throw new IllegalArgumentException( - "The quoteChar character and the delimiter cannot be the same ('" + quoteCharacter + "')"); + throw new IllegalArgumentException("The quoteChar character and the delimiter cannot be the same ('" + quoteCharacter + "')"); } if (escapeCharacter != null && delimiter == escapeCharacter.charValue()) { - throw new IllegalArgumentException( - "The escape character and the delimiter cannot be the same ('" + escapeCharacter + "')"); + throw new IllegalArgumentException("The escape character and the delimiter cannot be the same ('" + escapeCharacter + "')"); } if (commentMarker != null && delimiter == commentMarker.charValue()) { - throw new IllegalArgumentException( - "The comment start character and the delimiter cannot be the same ('" + commentMarker + "')"); + throw new IllegalArgumentException("The comment start character and the delimiter cannot be the same ('" + commentMarker + "')"); } if (quoteCharacter != null && quoteCharacter.equals(commentMarker)) { - throw new IllegalArgumentException( - "The comment start character and the quoteChar cannot be the same ('" + commentMarker + "')"); + throw new IllegalArgumentException("The comment start character and the quoteChar cannot be the same ('" + commentMarker + "')"); } if (escapeCharacter != null && escapeCharacter.equals(commentMarker)) { - throw new IllegalArgumentException( - "The comment start and the escape character cannot be the same ('" + commentMarker + "')"); + throw new IllegalArgumentException("The comment start and the escape character cannot be the same ('" + commentMarker + "')"); } if (escapeCharacter == null && quoteMode == QuoteMode.NONE) { @@ -1665,8 +2119,7 @@ public final class CSVFormat implements Serializable { final Set<String> dupCheck = new HashSet<>(); for (final String hdr : header) { if (!dupCheck.add(hdr)) { - throw new IllegalArgumentException( - "The header contains a duplicate entry: '" + hdr + "' in " + Arrays.toString(header)); + throw new IllegalArgumentException("The header contains a duplicate entry: '" + hdr + "' in " + Arrays.toString(header)); } } } @@ -1677,9 +2130,11 @@ public final class CSVFormat implements Serializable { * * @return a new {@code CSVFormat} that allows duplicate header names * @since 1.7 + * @deprecated Use {@link Builder#setAllowDuplicateHeaderNames(boolean) Builder#setAllowDuplicateHeaderNames(true)} */ + @Deprecated public CSVFormat withAllowDuplicateHeaderNames() { - return withAllowDuplicateHeaderNames(true); + return builder().setAllowDuplicateHeaderNames(true).build(); } /** @@ -1688,54 +2143,51 @@ public final class CSVFormat implements Serializable { * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow. * @return a new {@code CSVFormat} with duplicate header names behavior set to the given value. * @since 1.7 + * @deprecated Use {@link Builder#setAllowDuplicateHeaderNames(boolean)} */ + @Deprecated public CSVFormat withAllowDuplicateHeaderNames(final boolean allowDuplicateHeaderNames) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setAllowDuplicateHeaderNames(allowDuplicateHeaderNames).build(); } /** - * Returns a new {@code CSVFormat} with the missing column names behavior of the format set to {@code true} + * Returns a new {@code CSVFormat} with the missing column names behavior of the format set to {@code true}. * * @return A new CSVFormat that is equal to this but with the specified missing column names behavior. - * @see #withAllowMissingColumnNames(boolean) + * @see Builder#setAllowMissingColumnNames(boolean) * @since 1.1 + * @deprecated Use {@link Builder#setAllowMissingColumnNames(boolean) Builder#setAllowMissingColumnNames(true)} */ + @Deprecated public CSVFormat withAllowMissingColumnNames() { - return this.withAllowMissingColumnNames(true); + return builder().setAllowMissingColumnNames(true).build(); } /** * Returns a new {@code CSVFormat} with the missing column names behavior of the format set to the given value. * - * @param allowMissingColumnNames - * the missing column names behavior, {@code true} to allow missing column names in the header line, - * {@code false} to cause an {@link IllegalArgumentException} to be thrown. + * @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause + * an {@link IllegalArgumentException} to be thrown. * @return A new CSVFormat that is equal to this but with the specified missing column names behavior. + * @deprecated Use {@link Builder#setAllowMissingColumnNames(boolean)} */ + @Deprecated public CSVFormat withAllowMissingColumnNames(final boolean allowMissingColumnNames) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setAllowMissingColumnNames(allowMissingColumnNames).build(); } /** * Returns a new {@code CSVFormat} with whether to flush on close. * - * @param autoFlush - * whether to flush on close. + * @param autoFlush whether to flush on close. * * @return A new CSVFormat that is equal to this but with the specified autoFlush setting. * @since 1.6 + * @deprecated Use {@link Builder#setAutoFlush(boolean)} */ + @Deprecated public CSVFormat withAutoFlush(final boolean autoFlush) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setAutoFlush(autoFlush).build(); } /** @@ -1743,14 +2195,14 @@ public final class CSVFormat implements Serializable { * * Note that the comment start character is only recognized at the start of a line. * - * @param commentMarker - * the comment start marker + * @param commentMarker the comment start marker * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setCommentMarker(char)} */ + @Deprecated public CSVFormat withCommentMarker(final char commentMarker) { - return withCommentMarker(Character.valueOf(commentMarker)); + return builder().setCommentMarker(commentMarker).build(); } /** @@ -1758,71 +2210,53 @@ public final class CSVFormat implements Serializable { * * Note that the comment start character is only recognized at the start of a line. * - * @param commentMarker - * the comment start marker, use {@code null} to disable + * @param commentMarker the comment start marker, use {@code null} to disable * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setCommentMarker(Character)} */ + @Deprecated public CSVFormat withCommentMarker(final Character commentMarker) { - if (isLineBreak(commentMarker)) { - throw new IllegalArgumentException("The comment start marker character cannot be a line break"); - } - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setCommentMarker(commentMarker).build(); } /** * Returns a new {@code CSVFormat} with the delimiter of the format set to the specified character. * - * @param delimiter - * the delimiter character + * @param delimiter the delimiter character * @return A new CSVFormat that is equal to this with the specified character as delimiter - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setDelimiter(char)} */ + @Deprecated public CSVFormat withDelimiter(final char delimiter) { - if (isLineBreak(delimiter)) { - throw new IllegalArgumentException("The delimiter cannot be a line break"); - } - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setDelimiter(delimiter).build(); } /** * Returns a new {@code CSVFormat} with the escape character of the format set to the specified character. * - * @param escape - * the escape character + * @param escape the escape character * @return A new CSVFormat that is equal to his but with the specified character as the escape character - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setEscape(char)} */ + @Deprecated public CSVFormat withEscape(final char escape) { - return withEscape(Character.valueOf(escape)); + return builder().setEscape(escape).build(); } /** * Returns a new {@code CSVFormat} with the escape character of the format set to the specified character. * - * @param escape - * the escape character, use {@code null} to disable + * @param escape the escape character, use {@code null} to disable * @return A new CSVFormat that is equal to this but with the specified character as the escape character - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setEscape(Character)} */ + @Deprecated public CSVFormat withEscape(final Character escape) { - if (isLineBreak(escape)) { - throw new IllegalArgumentException("The escape character cannot be a line break"); - } - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escape, ignoreSurroundingSpaces, - ignoreEmptyLines, recordSeparator, nullString, headerComments, header, skipHeaderRecord, - allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setEscape(escape).build(); } /** @@ -1837,12 +2271,19 @@ public final class CSVFormat implements Serializable { * </pre> * * @return A new CSVFormat that is equal to this but using the first record as header. - * @see #withSkipHeaderRecord(boolean) - * @see #withHeader(String...) + * @see Builder#setSkipHeaderRecord(boolean) + * @see Builder#setHeader(String...) * @since 1.3 + * @deprecated Use {@link Builder#setHeader(String...) Builder#setHeader()}.{@link Builder#setSkipHeaderRecord(boolean) setSkipHeaderRecord(true)}. */ + @Deprecated public CSVFormat withFirstRecordAsHeader() { - return withHeader().withSkipHeaderRecord(); + // @formatter:off + return builder() + .setHeader() + .setSkipHeaderRecord(true) + .build(); + // @formatter:on } /** @@ -1863,30 +2304,21 @@ public final class CSVFormat implements Serializable { * The header is also used by the {@link CSVPrinter}. * </p> * - * @param headerEnum - * the enum defining the header, {@code null} if disabled, empty if parsed automatically, user specified - * otherwise. - * + * @param headerEnum the enum defining the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. * @return A new CSVFormat that is equal to this but with the specified header - * @see #withHeader(String...) - * @see #withSkipHeaderRecord(boolean) + * @see Builder#setHeader(String...) + * @see Builder#setSkipHeaderRecord(boolean) * @since 1.3 + * @deprecated Use {@link Builder#setHeader(Class)} */ + @Deprecated public CSVFormat withHeader(final Class<? extends Enum<?>> headerEnum) { - String[] header = null; - if (headerEnum != null) { - final Enum<?>[] enumValues = headerEnum.getEnumConstants(); - header = new String[enumValues.length]; - for (int i = 0; i < enumValues.length; i++) { - header[i] = enumValues[i].name(); - } - } - return withHeader(header); + return builder().setHeader(headerEnum).build(); } /** - * Returns a new {@code CSVFormat} with the header of the format set from the result set metadata. The header can - * either be parsed automatically from the input file with: + * Returns a new {@code CSVFormat} with the header of the format set from the result set metadata. The header can either be parsed automatically from the + * input file with: * * <pre> * CSVFormat format = aformat.withHeader(); @@ -1901,22 +2333,20 @@ public final class CSVFormat implements Serializable { * The header is also used by the {@link CSVPrinter}. * </p> * - * @param resultSet - * the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user specified - * otherwise. - * + * @param resultSet the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. * @return A new CSVFormat that is equal to this but with the specified header - * @throws SQLException - * SQLException if a database access error occurs or this method is called on a closed result set. + * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. * @since 1.1 + * @deprecated Use {@link Builder#setHeader(ResultSet)} */ + @Deprecated public CSVFormat withHeader(final ResultSet resultSet) throws SQLException { - return withHeader(resultSet != null ? resultSet.getMetaData() : null); + return builder().setHeader(resultSet).build(); } /** - * Returns a new {@code CSVFormat} with the header of the format set from the result set metadata. The header can - * either be parsed automatically from the input file with: + * Returns a new {@code CSVFormat} with the header of the format set from the result set metadata. The header can either be parsed automatically from the + * input file with: * * <pre> * CSVFormat format = aformat.withHeader(); @@ -1931,30 +2361,20 @@ public final class CSVFormat implements Serializable { * The header is also used by the {@link CSVPrinter}. * </p> * - * @param metaData - * the metaData for the header, {@code null} if disabled, empty if parsed automatically, user specified - * otherwise. - * + * @param resultSetMetaData the metaData for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. * @return A new CSVFormat that is equal to this but with the specified header - * @throws SQLException - * SQLException if a database access error occurs or this method is called on a closed result set. + * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. * @since 1.1 + * @deprecated Use {@link Builder#setHeader(ResultSetMetaData)} */ - public CSVFormat withHeader(final ResultSetMetaData metaData) throws SQLException { - String[] labels = null; - if (metaData != null) { - final int columnCount = metaData.getColumnCount(); - labels = new String[columnCount]; - for (int i = 0; i < columnCount; i++) { - labels[i] = metaData.getColumnLabel(i + 1); - } - } - return withHeader(labels); + @Deprecated + public CSVFormat withHeader(final ResultSetMetaData resultSetMetaData) throws SQLException { + return builder().setHeader(resultSetMetaData).build(); } /** - * Returns a new {@code CSVFormat} with the header of the format set to the given values. The header can either be - * parsed automatically from the input file with: + * Returns a new {@code CSVFormat} with the header of the format set to the given values. The header can either be parsed automatically from the input file + * with: * * <pre> * CSVFormat format = aformat.withHeader(); @@ -1969,267 +2389,245 @@ public final class CSVFormat implements Serializable { * The header is also used by the {@link CSVPrinter}. * </p> * - * @param header - * the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. - * + * @param header the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. * @return A new CSVFormat that is equal to this but with the specified header - * @see #withSkipHeaderRecord(boolean) + * @see Builder#setSkipHeaderRecord(boolean) + * @deprecated Use {@link Builder#setHeader(String...)} */ + @Deprecated public CSVFormat withHeader(final String... header) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setHeader(header).build(); } /** - * Returns a new {@code CSVFormat} with the header comments of the format set to the given values. The comments will - * be printed first, before the headers. This setting is ignored by the parser. + * Returns a new {@code CSVFormat} with the header comments of the format set to the given values. The comments will be printed first, before the headers. + * This setting is ignored by the parser. * * <pre> - * CSVFormat format = aformat.withHeaderComments("Generated by Apache Commons CSV 1.1.", new Date()); + * CSVFormat format = aformat.withHeaderComments("Generated by Apache Commons CSV.", Instant.now()); * </pre> * - * @param headerComments - * the headerComments which will be printed by the Printer before the actual CSV data. - * + * @param headerComments the headerComments which will be printed by the Printer before the actual CSV data. * @return A new CSVFormat that is equal to this but with the specified header - * @see #withSkipHeaderRecord(boolean) + * @see Builder#setSkipHeaderRecord(boolean) * @since 1.1 + * @deprecated Use {@link Builder#setHeaderComments(Object...)} */ + @Deprecated public CSVFormat withHeaderComments(final Object... headerComments) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setHeaderComments(headerComments).build(); } /** * Returns a new {@code CSVFormat} with the empty line skipping behavior of the format set to {@code true}. * * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior. - * @since {@link #withIgnoreEmptyLines(boolean)} + * @since {@link Builder#setIgnoreEmptyLines(boolean)} * @since 1.1 + * @deprecated Use {@link Builder#setIgnoreEmptyLines(boolean) Builder#setIgnoreEmptyLines(true)} */ + @Deprecated public CSVFormat withIgnoreEmptyLines() { - return this.withIgnoreEmptyLines(true); + return builder().setIgnoreEmptyLines(true).build(); } /** * Returns a new {@code CSVFormat} with the empty line skipping behavior of the format set to the given value. * - * @param ignoreEmptyLines - * the empty line skipping behavior, {@code true} to ignore the empty lines between the records, - * {@code false} to translate empty lines to empty records. + * @param ignoreEmptyLines the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate empty + * lines to empty records. * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior. + * @deprecated Use {@link Builder#setIgnoreEmptyLines(boolean)} */ + @Deprecated public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setIgnoreEmptyLines(ignoreEmptyLines).build(); } /** * Returns a new {@code CSVFormat} with the header ignore case behavior set to {@code true}. * * @return A new CSVFormat that will ignore case header name. - * @see #withIgnoreHeaderCase(boolean) + * @see Builder#setIgnoreHeaderCase(boolean) * @since 1.3 + * @deprecated Use {@link Builder#setIgnoreHeaderCase(boolean) Builder#setIgnoreHeaderCase(true)} */ + @Deprecated public CSVFormat withIgnoreHeaderCase() { - return this.withIgnoreHeaderCase(true); + return builder().setIgnoreHeaderCase(true).build(); } /** * Returns a new {@code CSVFormat} with whether header names should be accessed ignoring case. * - * @param ignoreHeaderCase - * the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as - * is. + * @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is. * @return A new CSVFormat that will ignore case header name if specified as {@code true} * @since 1.3 + * @deprecated Use {@link Builder#setIgnoreHeaderCase(boolean)} */ + @Deprecated public CSVFormat withIgnoreHeaderCase(final boolean ignoreHeaderCase) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setIgnoreHeaderCase(ignoreHeaderCase).build(); } /** * Returns a new {@code CSVFormat} with the parser trimming behavior of the format set to {@code true}. * * @return A new CSVFormat that is equal to this but with the specified parser trimming behavior. - * @see #withIgnoreSurroundingSpaces(boolean) + * @see Builder#setIgnoreSurroundingSpaces(boolean) * @since 1.1 + * @deprecated Use {@link Builder#setIgnoreSurroundingSpaces(boolean) Builder#setIgnoreSurroundingSpaces(true)} */ + @Deprecated public CSVFormat withIgnoreSurroundingSpaces() { - return this.withIgnoreSurroundingSpaces(true); + return builder().setIgnoreSurroundingSpaces(true).build(); } /** * Returns a new {@code CSVFormat} with the parser trimming behavior of the format set to the given value. * - * @param ignoreSurroundingSpaces the parser trimming behavior, {@code true} to remove the surrounding spaces, - * {@code false} to leave the spaces as is. + * @param ignoreSurroundingSpaces the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is. * @return A new CSVFormat that is equal to this but with the specified trimming behavior. + * @deprecated Use {@link Builder#setIgnoreSurroundingSpaces(boolean)} */ + @Deprecated public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setIgnoreSurroundingSpaces(ignoreSurroundingSpaces).build(); } /** * Returns a new {@code CSVFormat} with conversions to and from null for strings on input and output. * <ul> - * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading - * records.</li> + * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li> * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li> * </ul> * - * @param nullString - * the String to convert to and from {@code null}. No substitution occurs if {@code null} - * + * @param nullString the String to convert to and from {@code null}. No substitution occurs if {@code null} * @return A new CSVFormat that is equal to this but with the specified null conversion string. + * @deprecated Use {@link Builder#setNullString(String)} */ + @Deprecated public CSVFormat withNullString(final String nullString) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setNullString(nullString).build(); } /** * Returns a new {@code CSVFormat} with the quoteChar of the format set to the specified character. * - * @param quoteChar - * the quoteChar character + * @param quoteChar the quote character * @return A new CSVFormat that is equal to this but with the specified character as quoteChar - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setQuote(char)} */ + @Deprecated public CSVFormat withQuote(final char quoteChar) { - return withQuote(Character.valueOf(quoteChar)); + return builder().setQuote(quoteChar).build(); } /** * Returns a new {@code CSVFormat} with the quoteChar of the format set to the specified character. * - * @param quoteChar - * the quoteChar character, use {@code null} to disable + * @param quoteChar the quote character, use {@code null} to disable. * @return A new CSVFormat that is equal to this but with the specified character as quoteChar - * @throws IllegalArgumentException - * thrown if the specified character is a line break + * @throws IllegalArgumentException thrown if the specified character is a line break + * @deprecated Use {@link Builder#setQuote(Character)} */ + @Deprecated public CSVFormat withQuote(final Character quoteChar) { - if (isLineBreak(quoteChar)) { - throw new IllegalArgumentException("The quoteChar cannot be a line break"); - } - return new CSVFormat(delimiter, quoteChar, quoteMode, commentMarker, escapeCharacter, ignoreSurroundingSpaces, - ignoreEmptyLines, recordSeparator, nullString, headerComments, header, skipHeaderRecord, - allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setQuote(quoteChar).build(); } /** * Returns a new {@code CSVFormat} with the output quote policy of the format set to the specified value. * - * @param quoteModePolicy - * the quote policy to use for output. + * @param quoteMode the quote policy to use for output. * * @return A new CSVFormat that is equal to this but with the specified quote policy + * @deprecated Use {@link Builder#setQuoteMode(QuoteMode)} */ - public CSVFormat withQuoteMode(final QuoteMode quoteModePolicy) { - return new CSVFormat(delimiter, quoteCharacter, quoteModePolicy, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + @Deprecated + public CSVFormat withQuoteMode(final QuoteMode quoteMode) { + return builder().setQuoteMode(quoteMode).build(); } /** * Returns a new {@code CSVFormat} with the record separator of the format set to the specified character. * * <p> - * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently - * only works for inputs with '\n', '\r' and "\r\n" + * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r' and + * "\r\n" * </p> * - * @param recordSeparator - * the record separator to use for output. - * + * @param recordSeparator the record separator to use for output. * @return A new CSVFormat that is equal to this but with the specified output record separator + * @deprecated Use {@link Builder#setRecordSeparator(char)} */ + @Deprecated public CSVFormat withRecordSeparator(final char recordSeparator) { - return withRecordSeparator(String.valueOf(recordSeparator)); + return builder().setRecordSeparator(recordSeparator).build(); } /** * Returns a new {@code CSVFormat} with the record separator of the format set to the specified String. * * <p> - * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently - * only works for inputs with '\n', '\r' and "\r\n" + * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r' and + * "\r\n" * </p> * - * @param recordSeparator - * the record separator to use for output. - * + * @param recordSeparator the record separator to use for output. * @return A new CSVFormat that is equal to this but with the specified output record separator - * @throws IllegalArgumentException - * if recordSeparator is none of CR, LF or CRLF + * @throws IllegalArgumentException if recordSeparator is none of CR, LF or CRLF + * @deprecated Use {@link Builder#setRecordSeparator(String)} */ + @Deprecated public CSVFormat withRecordSeparator(final String recordSeparator) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setRecordSeparator(recordSeparator).build(); } /** * Returns a new {@code CSVFormat} with skipping the header record set to {@code true}. * * @return A new CSVFormat that is equal to this but with the specified skipHeaderRecord setting. - * @see #withSkipHeaderRecord(boolean) - * @see #withHeader(String...) + * @see Builder#setSkipHeaderRecord(boolean) + * @see Builder#setHeader(String...) * @since 1.1 + * @deprecated Use {@link Builder#setSkipHeaderRecord(boolean) Builder#setSkipHeaderRecord(true)} */ + @Deprecated public CSVFormat withSkipHeaderRecord() { - return this.withSkipHeaderRecord(true); + return builder().setSkipHeaderRecord(true).build(); } /** * Returns a new {@code CSVFormat} with whether to skip the header record. * - * @param skipHeaderRecord - * whether to skip the header record. - * + * @param skipHeaderRecord whether to skip the header record. * @return A new CSVFormat that is equal to this but with the specified skipHeaderRecord setting. - * @see #withHeader(String...) + * @see Builder#setHeader(String...) + * @deprecated Use {@link Builder#setSkipHeaderRecord(boolean)} */ + @Deprecated public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setSkipHeaderRecord(skipHeaderRecord).build(); } /** - * Returns a new {@code CSVFormat} with the record separator of the format set to the operating system's line - * separator string, typically CR+LF on Windows and LF on Linux. + * Returns a new {@code CSVFormat} with the record separator of the format set to the operating system's line separator string, typically CR+LF on Windows + * and LF on Linux. * * <p> - * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently - * only works for inputs with '\n', '\r' and "\r\n" + * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r' and + * "\r\n" * </p> * * @return A new CSVFormat that is equal to this but with the operating system's line separator string. * @since 1.6 + * @deprecated Use {@link Builder#setRecordSeparator(String) setRecordSeparator(System.lineSeparator())} */ + @Deprecated public CSVFormat withSystemRecordSeparator() { - return withRecordSeparator(System.getProperty("line.separator")); + return builder().setRecordSeparator(System.lineSeparator()).build(); } /** @@ -2237,52 +2635,48 @@ public final class CSVFormat implements Serializable { * * @return A new CSVFormat that is equal to this but with the trailing delimiter setting. * @since 1.3 + * @deprecated Use {@link Builder#setTrailingDelimiter(boolean) Builder#setTrailingDelimiter(true)} */ + @Deprecated public CSVFormat withTrailingDelimiter() { - return withTrailingDelimiter(true); + return builder().setTrailingDelimiter(true).build(); } /** * Returns a new {@code CSVFormat} with whether to add a trailing delimiter. * - * @param trailingDelimiter - * whether to add a trailing delimiter. - * + * @param trailingDelimiter whether to add a trailing delimiter. * @return A new CSVFormat that is equal to this but with the specified trailing delimiter setting. * @since 1.3 + * @deprecated Use {@link Builder#setTrailingDelimiter(boolean)} */ + @Deprecated public CSVFormat withTrailingDelimiter(final boolean trailingDelimiter) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setTrailingDelimiter(trailingDelimiter).build(); } /** - * Returns a new {@code CSVFormat} to trim leading and trailing blanks. - * See {@link #getTrim()} for details of where this is used. + * Returns a new {@code CSVFormat} to trim leading and trailing blanks. See {@link #getTrim()} for details of where this is used. * * @return A new CSVFormat that is equal to this but with the trim setting on. * @since 1.3 + * @deprecated Use {@link Builder#setTrim(boolean) Builder#setTrim(true)} */ + @Deprecated public CSVFormat withTrim() { - return withTrim(true); + return builder().setTrim(true).build(); } /** - * Returns a new {@code CSVFormat} with whether to trim leading and trailing blanks. - * See {@link #getTrim()} for details of where this is used. - * - * @param trim - * whether to trim leading and trailing blanks. + * Returns a new {@code CSVFormat} with whether to trim leading and trailing blanks. See {@link #getTrim()} for details of where this is used. * + * @param trim whether to trim leading and trailing blanks. * @return A new CSVFormat that is equal to this but with the specified trim setting. * @since 1.3 + * @deprecated Use {@link Builder#setTrim(boolean)} */ + @Deprecated public CSVFormat withTrim(final boolean trim) { - return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter, - ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header, - skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, - allowDuplicateHeaderNames); + return builder().setTrim(trim).build(); } } diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java b/src/main/java/org/apache/commons/csv/CSVPrinter.java index effb4c1..b953582 100644 --- a/src/main/java/org/apache/commons/csv/CSVPrinter.java +++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java @@ -233,7 +233,7 @@ public final class CSVPrinter implements Flushable, Closeable { * @since 1.9.0 */ public void printHeaders(final ResultSet resultSet) throws IOException, SQLException { - printRecord((Object[]) format.withHeader(resultSet).getHeader()); + printRecord((Object[]) format.builder().setHeader(resultSet).build().getHeader()); } /** diff --git a/src/main/java/org/apache/commons/csv/CSVRecord.java b/src/main/java/org/apache/commons/csv/CSVRecord.java index 64bb40b..635cb02 100644 --- a/src/main/java/org/apache/commons/csv/CSVRecord.java +++ b/src/main/java/org/apache/commons/csv/CSVRecord.java @@ -109,7 +109,7 @@ public final class CSVRecord implements Serializable, Iterable<String> { * @see #isMapped(String) * @see #isConsistent() * @see #getParser() - * @see CSVFormat#withNullString(String) + * @see CSVFormat.Builder#setNullString(String) */ public String get(final String name) { final Map<String, Integer> headerMap = getHeaderMapRaw(); diff --git a/src/test/java/org/apache/commons/csv/CSVFormatTest.java b/src/test/java/org/apache/commons/csv/CSVFormatTest.java index 9a1f3c0..2c29463 100644 --- a/src/test/java/org/apache/commons/csv/CSVFormatTest.java +++ b/src/test/java/org/apache/commons/csv/CSVFormatTest.java @@ -65,7 +65,7 @@ public class CSVFormatTest { } private static CSVFormat copy(final CSVFormat format) { - return format.withDelimiter(format.getDelimiter()); + return format.builder().setDelimiter(format.getDelimiter()).build(); } private void assertNotEquals(final String name, final String type, final Object left, final Object right) { @@ -78,17 +78,27 @@ public class CSVFormatTest { } @Test - public void testDelimiterSameAsCommentStartThrowsException() { + public void testDelimiterSameAsCommentStartThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withCommentMarker('!')); } @Test - public void testDelimiterSameAsEscapeThrowsException() { + public void testDelimiterSameAsCommentStartThrowsException1() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setCommentMarker('!').build()); + } + + @Test + public void testDelimiterSameAsEscapeThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withDelimiter('!').withEscape('!')); } @Test - public void testDuplicateHeaderElements() { + public void testDelimiterSameAsEscapeThrowsException1() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setDelimiter('!').setEscape('!').build()); + } + + @Test + public void testDuplicateHeaderElements0() { final String[] header = { "A", "A" }; final CSVFormat format = CSVFormat.DEFAULT.withHeader(header); assertEquals(2, format.getHeader().length); @@ -96,16 +106,35 @@ public class CSVFormatTest { } @Test - public void testDuplicateHeaderElementsFalse() { + public void testDuplicateHeaderElements() { + final String[] header = { "A", "A" }; + final CSVFormat format = CSVFormat.DEFAULT.builder().setHeader(header).build(); + assertEquals(2, format.getHeader().length); + assertArrayEquals(header, format.getHeader()); + } + + @Test + public void testDuplicateHeaderElementsFalse0() { assertThrows( IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(false).withHeader("A", "A")); } - public void testDuplicateHeaderElementsTrue() { + @Test + public void testDuplicateHeaderElementsFalse() { + assertThrows( + IllegalArgumentException.class, + () -> CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(false).setHeader("A", "A").build()); + } + + public void testDuplicateHeaderElementsTrue0() { CSVFormat.DEFAULT.withAllowDuplicateHeaderNames(true).withHeader("A", "A"); } + public void testDuplicateHeaderElementsTrue() { + CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("A", "A").build(); + } + @Test public void testEquals() { final CSVFormat right = CSVFormat.DEFAULT; @@ -123,7 +152,7 @@ public class CSVFormatTest { } @Test - public void testEqualsCommentStart() { + public void testEqualsCommentStart0() { final CSVFormat right = CSVFormat.newFormat('\'') .withQuote('"') .withCommentMarker('#') @@ -135,6 +164,20 @@ public class CSVFormatTest { } @Test + public void testEqualsCommentStart() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setQuote('"') + .setCommentMarker('#') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setCommentMarker('!') + .build(); + + assertNotEquals(right, left); + } + + @Test public void testEqualsDelimiter() { final CSVFormat right = CSVFormat.newFormat('!'); final CSVFormat left = CSVFormat.newFormat('?'); @@ -143,7 +186,7 @@ public class CSVFormatTest { } @Test - public void testEqualsEscape() { + public void testEqualsEscape0() { final CSVFormat right = CSVFormat.newFormat('\'') .withQuote('"') .withCommentMarker('#') @@ -156,6 +199,21 @@ public class CSVFormatTest { } @Test + public void testEqualsEscape() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setQuote('"') + .setCommentMarker('#') + .setEscape('+') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setEscape('!') + .build(); + + assertNotEquals(right, left); + } + + @Test public void testEqualsHash() throws Exception { final Method[] methods = CSVFormat.class.getDeclaredMethods(); for (final Method method : methods) { @@ -204,7 +262,7 @@ public class CSVFormatTest { } @Test - public void testEqualsHeader() { + public void testEqualsHeader0() { final CSVFormat right = CSVFormat.newFormat('\'') .withRecordSeparator(CR) .withCommentMarker('#') @@ -221,7 +279,26 @@ public class CSVFormatTest { } @Test - public void testEqualsIgnoreEmptyLines() { + public void testEqualsHeader() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setRecordSeparator(CR) + .setCommentMarker('#') + .setEscape('+') + .setHeader("One", "Two", "Three") + .setIgnoreEmptyLines(true) + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setHeader("Three", "Two", "One") + .build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsIgnoreEmptyLines0() { final CSVFormat right = CSVFormat.newFormat('\'') .withCommentMarker('#') .withEscape('+') @@ -236,7 +313,24 @@ public class CSVFormatTest { } @Test - public void testEqualsIgnoreSurroundingSpaces() { + public void testEqualsIgnoreEmptyLines() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setCommentMarker('#') + .setEscape('+') + .setIgnoreEmptyLines(true) + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setIgnoreEmptyLines(false) + .build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsIgnoreSurroundingSpaces0() { final CSVFormat right = CSVFormat.newFormat('\'') .withCommentMarker('#') .withEscape('+') @@ -250,23 +344,55 @@ public class CSVFormatTest { } @Test + public void testEqualsIgnoreSurroundingSpaces() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setCommentMarker('#') + .setEscape('+') + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setIgnoreSurroundingSpaces(false) + .build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsLeftNoQuoteRightQuote0() { + final CSVFormat left = CSVFormat.newFormat(',').withQuote(null); + final CSVFormat right = left.withQuote('#'); + + assertNotEquals(left, right); + } + + @Test public void testEqualsLeftNoQuoteRightQuote() { - final CSVFormat left = CSVFormat.newFormat(',').withQuote(null); - final CSVFormat right = left.withQuote('#'); + final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).build(); + final CSVFormat right = left.builder().setQuote('#').build(); - assertNotEquals(left, right); + assertNotEquals(left, right); + } + + @Test + public void testEqualsNoQuotes0() { + final CSVFormat left = CSVFormat.newFormat(',').withQuote(null); + final CSVFormat right = left.withQuote(null); + + assertEquals(left, right); } @Test public void testEqualsNoQuotes() { - final CSVFormat left = CSVFormat.newFormat(',').withQuote(null); - final CSVFormat right = left.withQuote(null); + final CSVFormat left = CSVFormat.newFormat(',').builder().setQuote(null).build(); + final CSVFormat right = left.builder().setQuote(null).build(); - assertEquals(left, right); + assertEquals(left, right); } @Test - public void testEqualsNullString() { + public void testEqualsNullString0() { final CSVFormat right = CSVFormat.newFormat('\'') .withRecordSeparator(CR) .withCommentMarker('#') @@ -283,6 +409,25 @@ public class CSVFormatTest { } @Test + public void testEqualsNullString() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setRecordSeparator(CR) + .setCommentMarker('#') + .setEscape('+') + .setIgnoreEmptyLines(true) + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .setNullString("null") + .build(); + final CSVFormat left = right.builder() + .setNullString("---") + .build(); + + assertNotEquals(right, left); + } + + @Test public void testEqualsOne() { final CSVFormat csvFormatOne = CSVFormat.INFORMIX_UNLOAD; @@ -412,7 +557,7 @@ public class CSVFormatTest { } @Test - public void testEqualsQuoteChar() { + public void testEqualsQuoteChar0() { final CSVFormat right = CSVFormat.newFormat('\'').withQuote('"'); final CSVFormat left = right.withQuote('!'); @@ -420,7 +565,15 @@ public class CSVFormatTest { } @Test - public void testEqualsQuotePolicy() { + public void testEqualsQuoteChar() { + final CSVFormat right = CSVFormat.newFormat('\'').builder().setQuote('"').build(); + final CSVFormat left = right.builder().setQuote('!').build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsQuotePolicy0() { final CSVFormat right = CSVFormat.newFormat('\'') .withQuote('"') .withQuoteMode(QuoteMode.ALL); @@ -431,7 +584,20 @@ public class CSVFormatTest { } @Test - public void testEqualsRecordSeparator() { + public void testEqualsQuotePolicy() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setQuoteMode(QuoteMode.MINIMAL) + .build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsRecordSeparator0() { final CSVFormat right = CSVFormat.newFormat('\'') .withRecordSeparator(CR) .withCommentMarker('#') @@ -447,7 +613,25 @@ public class CSVFormatTest { } @Test - public void testEqualsSkipHeaderRecord() { + public void testEqualsRecordSeparator() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setRecordSeparator(CR) + .setCommentMarker('#') + .setEscape('+') + .setIgnoreEmptyLines(true) + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .build(); + final CSVFormat left = right.builder() + .setRecordSeparator(LF) + .build(); + + assertNotEquals(right, left); + } + + @Test + public void testEqualsSkipHeaderRecord0() { final CSVFormat right = CSVFormat.newFormat('\'') .withRecordSeparator(CR) .withCommentMarker('#') @@ -464,6 +648,25 @@ public class CSVFormatTest { assertNotEquals(right, left); } + public void testEqualsSkipHeaderRecord() { + final CSVFormat right = CSVFormat.newFormat('\'').builder() + .setRecordSeparator(CR) + .setCommentMarker('#') + .setEscape('+') + .setIgnoreEmptyLines(true) + .setIgnoreSurroundingSpaces(true) + .setQuote('"') + .setQuoteMode(QuoteMode.ALL) + .setNullString("null") + .setSkipHeaderRecord(true) + .build(); + final CSVFormat left = right.builder() + .setSkipHeaderRecord(false) + .build(); + + assertNotEquals(right, left); + } + @Test public void testEqualsWithNull() { @@ -528,12 +731,17 @@ public class CSVFormatTest { } @Test - public void testEscapeSameAsCommentStartThrowsException() { + public void testEscapeSameAsCommentStartThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withEscape('!').withCommentMarker('!')); } @Test - public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType() { + public void testEscapeSameAsCommentStartThrowsException() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setEscape('!').setCommentMarker('!').build()); + } + + @Test + public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType0() { // Cannot assume that callers won't use different Character objects assertThrows( IllegalArgumentException.class, @@ -541,6 +749,14 @@ public class CSVFormatTest { } @Test + public void testEscapeSameAsCommentStartThrowsExceptionForWrapperType() { + // Cannot assume that callers won't use different Character objects + assertThrows( + IllegalArgumentException.class, + () -> CSVFormat.DEFAULT.builder().setEscape(Character.valueOf('!')).setCommentMarker(Character.valueOf('!')).build()); + } + + @Test public void testFormat() { final CSVFormat format = CSVFormat.DEFAULT; @@ -590,11 +806,16 @@ public class CSVFormatTest { } @Test - public void testJiraCsv236() { + public void testJiraCsv236_0() { CSVFormat.DEFAULT.withAllowDuplicateHeaderNames().withHeader("CC","VV","VV"); } @Test + public void testJiraCsv236() { + CSVFormat.DEFAULT.builder().setAllowDuplicateHeaderNames(true).setHeader("CC","VV","VV").build(); + } + + @Test public void testNewFormat() { final CSVFormat csvFormat = CSVFormat.newFormat('X'); @@ -656,7 +877,7 @@ public class CSVFormatTest { } @Test - public void testNullRecordSeparatorCsv106() { + public void testNullRecordSeparatorCsv106_0() { final CSVFormat format = CSVFormat.newFormat(';').withSkipHeaderRecord().withHeader("H1", "H2"); final String formatStr = format.format("A", "B"); assertNotNull(formatStr); @@ -664,11 +885,32 @@ public class CSVFormatTest { } @Test - public void testQuoteCharSameAsCommentStartThrowsException() { + public void testNullRecordSeparatorCsv106() { + final CSVFormat format = CSVFormat.newFormat(';').builder().setSkipHeaderRecord(true).setHeader("H1", "H2").build(); + final String formatStr = format.format("A", "B"); + assertNotNull(formatStr); + assertFalse(formatStr.endsWith("null")); + } + + @Test + public void testQuoteCharSameAsCommentStartThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withCommentMarker('!')); } @Test + public void testQuoteCharSameAsCommentStartThrowsException() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setCommentMarker('!').build()); + } + + @Test + public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType0() { + // Cannot assume that callers won't use different Character objects + assertThrows( + IllegalArgumentException.class, + () -> CSVFormat.DEFAULT.builder().setQuote(Character.valueOf('!')).setCommentMarker('!').build()); + } + + @Test public void testQuoteCharSameAsCommentStartThrowsExceptionForWrapperType() { // Cannot assume that callers won't use different Character objects assertThrows( @@ -677,16 +919,26 @@ public class CSVFormatTest { } @Test - public void testQuoteCharSameAsDelimiterThrowsException() { + public void testQuoteCharSameAsDelimiterThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.withQuote('!').withDelimiter('!')); } @Test - public void testQuotePolicyNoneWithoutEscapeThrowsException() { + public void testQuoteCharSameAsDelimiterThrowsException() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.DEFAULT.builder().setQuote('!').setDelimiter('!').build()); + } + + @Test + public void testQuotePolicyNoneWithoutEscapeThrowsException0() { assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').withQuoteMode(QuoteMode.NONE)); } @Test + public void testQuotePolicyNoneWithoutEscapeThrowsException() { + assertThrows(IllegalArgumentException.class, () -> CSVFormat.newFormat('!').builder().setQuoteMode(QuoteMode.NONE).build()); + } + + @Test public void testRFC4180() { assertEquals(null, RFC4180.getCommentMarker()); assertEquals(',', RFC4180.getDelimiter());