Source code
Description: source: package: org.apache.commons.csv, class: CSVFormat, enum: Predefined

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one
    003 * or more contributor license agreements.  See the NOTICE file
    004 * distributed with this work for additional information
    005 * regarding copyright ownership.  The ASF licenses this file
    006 * to you under the Apache License, Version 2.0 (the
    007 * "License"); you may not use this file except in compliance
    008 * with the License.  You may obtain a copy of the License at
    009 *
    010 *   https://www.apache.org/licenses/LICENSE-2.0
    011 *
    012 * Unless required by applicable law or agreed to in writing,
    013 * software distributed under the License is distributed on an
    014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015 * KIND, either express or implied.  See the License for the
    016 * specific language governing permissions and limitations
    017 * under the License.
    018 */
    019
    020package org.apache.commons.csv;
    021
    022import static org.apache.commons.io.IOUtils.EOF;
    023
    024import java.io.File;
    025import java.io.IOException;
    026import java.io.InputStream;
    027import java.io.OutputStream;
    028import java.io.Reader;
    029import java.io.Serializable;
    030import java.io.StringWriter;
    031import java.io.Writer;
    032import java.nio.charset.Charset;
    033import java.nio.file.Files;
    034import java.nio.file.Path;
    035import java.sql.ResultSet;
    036import java.sql.ResultSetMetaData;
    037import java.sql.SQLException;
    038import java.util.Arrays;
    039import java.util.HashSet;
    040import java.util.Objects;
    041import java.util.Set;
    042import java.util.function.Supplier;
    043
    044import org.apache.commons.codec.binary.Base64OutputStream;
    045import org.apache.commons.io.IOUtils;
    046import org.apache.commons.io.function.IOStream;
    047import org.apache.commons.io.function.Uncheck;
    048import org.apache.commons.io.output.AppendableOutputStream;
    049
    050/**
    051 * Specifies the format of a CSV file for parsing and writing.
    052 *
    053 * <h2>Using predefined formats</h2>
    054 *
    055 * <p>
    056 * You can use one of the predefined formats:
    057 * </p>
    058 *
    059 * <ul>
    060 * <li>{@link #DEFAULT}</li>
    061 * <li>{@link #EXCEL}</li>
    062 * <li>{@link #INFORMIX_UNLOAD}</li>
    063 * <li>{@link #INFORMIX_UNLOAD_CSV}</li>
    064 * <li>{@link #MONGODB_CSV}</li>
    065 * <li>{@link #MONGODB_TSV}</li>
    066 * <li>{@link #MYSQL}</li>
    067 * <li>{@link #ORACLE}</li>
    068 * <li>{@link #POSTGRESQL_CSV}</li>
    069 * <li>{@link #POSTGRESQL_TEXT}</li>
    070 * <li>{@link #RFC4180}</li>
    071 * <li>{@link #TDF}</li>
    072 * </ul>
    073 *
    074 * <p>
    075 * For example:
    076 * </p>
    077 *
    078 * <pre>
    079 * CSVParser parser = CSVFormat.EXCEL.parse(reader);
    080 * </pre>
    081 *
    082 * <p>
    083 * The {@link CSVParser} provides static methods to parse other input types, for example:
    084 * </p>
    085 *
    086 * <pre>
    087 * CSVParser parser = CSVParser.parse(file, StandardCharsets.US_ASCII, CSVFormat.EXCEL);
    088 * </pre>
    089 *
    090 * <h2>Defining formats</h2>
    091 *
    092 * <p>
    093 * You can extend a format by calling the {@code set} methods. For example:
    094 * </p>
    095 *
    096 * <pre>{@code
    097 * CSVFormat.EXCEL.builder().setNullString("N/A").setIgnoreSurroundingSpaces(true).get();
    098 * }</pre>
    099 *
    100 * <h2>Defining column names</h2>
    101 *
    102 * <p>
    103 * To define the column names you want to use to access records, write:
    104 * </p>
    105 *
    106 * <pre>{@code
    107 * CSVFormat.EXCEL.builder().setHeader("Col1", "Col2", "Col3").get();
    108 * }</pre>
    109 *
    110 * <p>
    111 * 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
    112 * contain a first record that also defines column names.
    113 *
    114 * If it does, then you are overriding this metadata with your names and you should skip the first record by calling
    115 * {@link Builder#setSkipHeaderRecord(boolean)} with {@code true}.
    116 * </p>
    117 *
    118 * <h2>Parsing</h2>
    119 *
    120 * <p>
    121 * You can use a format directly to parse a reader. For example, to parse an Excel file with columns header, write:
    122 * </p>
    123 *
    124 * <pre>{@code
    125 * Reader in = ...;
    126 * CSVFormat.EXCEL.builder().setHeader("Col1", "Col2", "Col3").get().parse(in);
    127 * }</pre>
    128 *
    129 * <p>
    130 * For other input types, like resources, files, and URLs, use the static methods on {@link CSVParser}.
    131 * </p>
    132 *
    133 * <h2>Referencing columns safely</h2>
    134 *
    135 * <p>
    136 * If your source contains a header record, you can simplify your code and safely reference columns, by using {@link Builder#setHeader(String...)} with no
    137 * arguments:
    138 * </p>
    139 *
    140 * <pre>
    141 * CSVFormat.EXCEL.builder().setHeader().get();
    142 * </pre>
    143 *
    144 * <p>
    145 * This causes the parser to read the first record and use its values as column names.
    146 *
    147 * Then, call one of the {@link CSVRecord} get method that takes a String column name argument:
    148 * </p>
    149 *
    150 * <pre>{@code
    151 * String value = record.get("Col1");
    152 * }</pre>
    153 *
    154 * <p>
    155 * This makes your code impervious to changes in column order in the CSV file.
    156 * </p>
    157 *
    158 * <h2>Serialization</h2>
    159 * <p>
    160 * This class implements the {@link Serializable} interface with the following caveats:
    161 * </p>
    162 * <ul>
    163 * <li>This class will no longer implement Serializable in 2.0.</li>
    164 * <li>Serialization is not supported from one version to the next.</li>
    165 * </ul>
    166 * <p>
    167 * The {@code serialVersionUID} values are:
    168 * </p>
    169 * <ul>
    170 * <li>Version 1.10.0: {@code 2L}</li>
    171 * <li>Version 1.9.0 through 1.0: {@code 1L}</li>
    172 * </ul>
    173 *
    174 * <h2>Notes</h2>
    175 * <p>
    176 * This class is immutable.
    177 * </p>
    178 * <p>
    179 * Not all settings are used for both parsing and writing.
    180 * </p>
    181 */
    182public final class CSVFormat implements Serializable {
    183
    184    /**
    185     * Builds CSVFormat instances.
    186     *
    187     * @since 1.9.0
    188     */
    189    public static class Builder implements Supplier<CSVFormat> {
    190
    191        /**
    192         * Creates a new default builder, as for {@link #RFC4180} but allowing empty lines.
    193         *
    194         * <p>
    195         * The {@link Builder} settings are:
    196         * </p>
    197         * <ul>
    198         * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    199         * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    200         * <li>{@link Builder#setRecordSeparator(String) setRecordSeparator}{@code ("\r\n")}</li>
    201         * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (true)}</li>
    202         * <li>{@link Builder#setDuplicateHeaderMode(DuplicateHeaderMode) setDuplicateHeaderMode}{@code (DuplicateHeaderMode.ALLOW_ALL)}</li>
    203         * <li>All other values take their Java defaults, {@code false} for booleans, {@code null} for object references.</li>
    204         * </ul>
    205         *
    206         * @see Predefined#Default
    207         * @see DuplicateHeaderMode#ALLOW_ALL
    208         *
    209         * @return a copy of the builder
    210         */
    211        public static Builder create() {
    212            // @formatter:off
    213            return new Builder()
    214                    .setDelimiter(Constants.COMMA)
    215                    .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    216                    .setRecordSeparator(Constants.CRLF)
    217                    .setIgnoreEmptyLines(true)
    218                    .setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL);
    219            // @formatter:on
    220        }
    221
    222        /**
    223         * Creates a new builder from the given format.
    224         *
    225         * @param csvFormat the source format.
    226         * @return a new builder.
    227         */
    228        public static Builder create(final CSVFormat csvFormat) {
    229            return new Builder(csvFormat);
    230        }
    231
    232        private boolean allowMissingColumnNames;
    233
    234        private boolean autoFlush;
    235
    236        private Character commentMarker;
    237
    238        private String delimiter;
    239
    240        private DuplicateHeaderMode duplicateHeaderMode;
    241
    242        private Character escapeCharacter;
    243
    244        private String[] headerComments;
    245
    246        private String[] headers;
    247
    248        private boolean ignoreEmptyLines;
    249
    250        private boolean ignoreHeaderCase;
    251
    252        private boolean ignoreSurroundingSpaces;
    253
    254        private String nullString;
    255
    256        private Character quoteCharacter;
    257
    258        private String quotedNullString;
    259
    260        private QuoteMode quoteMode;
    261
    262        private String recordSeparator;
    263
    264        private boolean skipHeaderRecord;
    265
    266        private boolean lenientEof;
    267
    268        private boolean trailingData;
    269
    270        private boolean trailingDelimiter;
    271
    272        private boolean trim;
    273
    274        /** The maximum number of rows to process, excluding the header row. */
    275        private long maxRows;
    276
    277        private Builder() {
    278            // empty
    279        }
    280
    281        private Builder(final CSVFormat csvFormat) {
    282            this.allowMissingColumnNames = csvFormat.allowMissingColumnNames;
    283            this.autoFlush = csvFormat.autoFlush;
    284            this.commentMarker = csvFormat.commentMarker;
    285            this.delimiter = csvFormat.delimiter;
    286            this.duplicateHeaderMode = csvFormat.duplicateHeaderMode;
    287            this.escapeCharacter = csvFormat.escapeCharacter;
    288            this.headerComments = csvFormat.headerComments;
    289            this.headers = csvFormat.headers;
    290            this.ignoreEmptyLines = csvFormat.ignoreEmptyLines;
    291            this.ignoreHeaderCase = csvFormat.ignoreHeaderCase;
    292            this.ignoreSurroundingSpaces = csvFormat.ignoreSurroundingSpaces;
    293            this.lenientEof = csvFormat.lenientEof;
    294            this.maxRows = csvFormat.maxRows;
    295            this.nullString = csvFormat.nullString;
    296            this.quoteCharacter = csvFormat.quoteCharacter;
    297            this.quoteMode = csvFormat.quoteMode;
    298            this.quotedNullString = csvFormat.quotedNullString;
    299            this.recordSeparator = csvFormat.recordSeparator;
    300            this.skipHeaderRecord = csvFormat.skipHeaderRecord;
    301            this.trailingData = csvFormat.trailingData;
    302            this.trailingDelimiter = csvFormat.trailingDelimiter;
    303            this.trim = csvFormat.trim;
    304        }
    305
    306        /**
    307         * Builds a new CSVFormat instance.
    308         *
    309         * @return a new CSVFormat instance.
    310         * @deprecated Use {@link #get()}.
    311         */
    312        @Deprecated
    313        public CSVFormat build() {
    314            return get();
    315        }
    316
    317        /**
    318         * Builds a new CSVFormat instance.
    319         *
    320         * @return a new CSVFormat instance.
    321         * @since 1.13.0
    322         */
    323        @Override
    324        public CSVFormat get() {
    325            return new CSVFormat(this);
    326        }
    327
    328        /**
    329         * Sets the duplicate header names behavior, true to allow, false to disallow.
    330         *
    331         * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow.
    332         * @return This instance.
    333         * @deprecated Use {@link #setDuplicateHeaderMode(DuplicateHeaderMode)}.
    334         */
    335        @Deprecated
    336        public Builder setAllowDuplicateHeaderNames(final boolean allowDuplicateHeaderNames) {
    337            setDuplicateHeaderMode(allowDuplicateHeaderNames ? DuplicateHeaderMode.ALLOW_ALL : DuplicateHeaderMode.ALLOW_EMPTY);
    338            return this;
    339        }
    340
    341        /**
    342         * Sets the parser missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an
    343         * {@link IllegalArgumentException} to be thrown.
    344         *
    345         * @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to
    346         *                                cause an {@link IllegalArgumentException} to be thrown.
    347         * @return This instance.
    348         */
    349        public Builder setAllowMissingColumnNames(final boolean allowMissingColumnNames) {
    350            this.allowMissingColumnNames = allowMissingColumnNames;
    351            return this;
    352        }
    353
    354        /**
    355         * Sets whether to flush on close.
    356         *
    357         * @param autoFlush whether to flush on close.
    358         * @return This instance.
    359         */
    360        public Builder setAutoFlush(final boolean autoFlush) {
    361            this.autoFlush = autoFlush;
    362            return this;
    363        }
    364
    365        /**
    366         * Sets the comment marker character, use {@code null} to disable comments.
    367         * <p>
    368         * The comment start character is only recognized at the start of a line.
    369         * </p>
    370         * <p>
    371         * Comments are printed first, before headers.
    372         * </p>
    373         * <p>
    374         * Use {@link #setCommentMarker(char)} or {@link #setCommentMarker(Character)} to set the comment marker written at the start of each comment line.
    375         * </p>
    376         * <p>
    377         * If the comment marker is not set, then the header comments are ignored.
    378         * </p>
    379         * <p>
    380         * For example:
    381         * </p>
    382         *
    383         * <pre>
    384         * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0));
    385         * </pre>
    386         * <p>
    387         * writes:
    388         * </p>
    389         *
    390         * <pre>
    391         * # Generated by Apache Commons CSV.
    392         * # 1970-01-01T00:00:00Z
    393         * </pre>
    394         *
    395         * @param commentMarker the comment start marker, use {@code null} to disable.
    396         * @return This instance.
    397         * @throws IllegalArgumentException thrown if the specified character is a line break
    398         */
    399        public Builder setCommentMarker(final char commentMarker) {
    400            setCommentMarker(Character.valueOf(commentMarker));
    401            return this;
    402        }
    403
    404        /**
    405         * Sets the comment marker character, use {@code null} to disable comments.
    406         * <p>
    407         * The comment start character is only recognized at the start of a line.
    408         * </p>
    409         * <p>
    410         * Comments are printed first, before headers.
    411         * </p>
    412         * <p>
    413         * Use {@link #setCommentMarker(char)} or {@link #setCommentMarker(Character)} to set the comment marker written at the start of each comment line.
    414         * </p>
    415         * <p>
    416         * If the comment marker is not set, then the header comments are ignored.
    417         * </p>
    418         * <p>
    419         * For example:
    420         * </p>
    421         *
    422         * <pre>
    423         * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0));
    424         * </pre>
    425         * <p>
    426         * writes:
    427         * </p>
    428         *
    429         * <pre>
    430         * # Generated by Apache Commons CSV.
    431         * # 1970-01-01T00:00:00Z
    432         * </pre>
    433         *
    434         * @param commentMarker the comment start marker, use {@code null} to disable.
    435         * @return This instance.
    436         * @throws IllegalArgumentException thrown if the specified character is a line break
    437         */
    438        public Builder setCommentMarker(final Character commentMarker) {
    439            if (isLineBreak(commentMarker)) {
    440                throw new IllegalArgumentException("The comment start marker character cannot be a line break");
    441            }
    442            this.commentMarker = commentMarker;
    443            return this;
    444        }
    445
    446        /**
    447         * Sets the delimiter character.
    448         *
    449         * @param delimiter the delimiter character.
    450         * @return This instance.
    451         */
    452        public Builder setDelimiter(final char delimiter) {
    453            return setDelimiter(String.valueOf(delimiter));
    454        }
    455
    456        /**
    457         * Sets the delimiter character.
    458         *
    459         * @param delimiter the delimiter character.
    460         * @return This instance.
    461         */
    462        public Builder setDelimiter(final String delimiter) {
    463            if (containsLineBreak(delimiter)) {
    464                throw new IllegalArgumentException("The delimiter cannot be a line break");
    465            }
    466            if (delimiter.isEmpty()) {
    467                throw new IllegalArgumentException("The delimiter cannot be empty");
    468            }
    469            this.delimiter = delimiter;
    470            return this;
    471        }
    472
    473        /**
    474         * Sets the duplicate header names behavior.
    475         *
    476         * @param duplicateHeaderMode the duplicate header names behavior
    477         * @return This instance.
    478         * @since 1.10.0
    479         */
    480        public Builder setDuplicateHeaderMode(final DuplicateHeaderMode duplicateHeaderMode) {
    481            this.duplicateHeaderMode = Objects.requireNonNull(duplicateHeaderMode, "duplicateHeaderMode");
    482            return this;
    483        }
    484
    485        /**
    486         * Sets the escape character.
    487         *
    488         * @param escapeCharacter the escape character.
    489         * @return This instance.
    490         * @throws IllegalArgumentException thrown if the specified character is a line break
    491         */
    492        public Builder setEscape(final char escapeCharacter) {
    493            setEscape(Character.valueOf(escapeCharacter));
    494            return this;
    495        }
    496
    497        /**
    498         * Sets the escape character.
    499         *
    500         * @param escapeCharacter the escape character.
    501         * @return This instance.
    502         * @throws IllegalArgumentException thrown if the specified character is a line break
    503         */
    504        public Builder setEscape(final Character escapeCharacter) {
    505            if (isLineBreak(escapeCharacter)) {
    506                throw new IllegalArgumentException("The escape character cannot be a line break");
    507            }
    508            this.escapeCharacter = escapeCharacter;
    509            return this;
    510        }
    511
    512        /**
    513         * Sets the header defined by the given {@link Enum} class.
    514         *
    515         * <p>
    516         * Example:
    517         * </p>
    518         *
    519         * <pre>
    520         * public enum HeaderEnum {
    521         *     Name, Email, Phone
    522         * }
    523         *
    524         * Builder builder = builder.setHeader(HeaderEnum.class);
    525         * </pre>
    526         * <p>
    527         * The header is also used by the {@link CSVPrinter}.
    528         * </p>
    529         *
    530         * @param headerEnum the enum defining the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    531         * @return This instance.
    532         */
    533        public Builder setHeader(final Class<? extends Enum<?>> headerEnum) {
    534            String[] header = null;
    535            if (headerEnum != null) {
    536                final Enum<?>[] enumValues = headerEnum.getEnumConstants();
    537                header = new String[enumValues.length];
    538                Arrays.setAll(header, i -> enumValues[i].name());
    539            }
    540            return setHeader(header);
    541        }
    542
    543        /**
    544         * Sets the header from the result set metadata. The header can be parsed automatically from the input file with:
    545         *
    546         * <pre>
    547         * builder.setHeader();
    548         * </pre>
    549         *
    550         * or specified manually with:
    551         *
    552         * <pre>
    553         * builder.setHeader(resultSet);
    554         * </pre>
    555         * <p>
    556         * The header is also used by the {@link CSVPrinter}.
    557         * </p>
    558         *
    559         * @param resultSet the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    560         * @return This instance.
    561         * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set.
    562         */
    563        public Builder setHeader(final ResultSet resultSet) throws SQLException {
    564            return setHeader(resultSet != null ? resultSet.getMetaData() : null);
    565        }
    566
    567        /**
    568         * Sets the header from the result set metadata. The header can be parsed automatically from the input file with:
    569         *
    570         * <pre>
    571         * builder.setHeader();
    572         * </pre>
    573         *
    574         * or specified manually with:
    575         *
    576         * <pre>
    577         * builder.setHeader(resultSetMetaData);
    578         * </pre>
    579         * <p>
    580         * The header is also used by the {@link CSVPrinter}.
    581         * </p>
    582         *
    583         * @param resultSetMetaData the metaData for the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    584         * @return This instance.
    585         * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set.
    586         */
    587        public Builder setHeader(final ResultSetMetaData resultSetMetaData) throws SQLException {
    588            String[] labels = null;
    589            if (resultSetMetaData != null) {
    590                final int columnCount = resultSetMetaData.getColumnCount();
    591                labels = new String[columnCount];
    592                for (int i = 0; i < columnCount; i++) {
    593                    labels[i] = resultSetMetaData.getColumnLabel(i + 1);
    594                }
    595            }
    596            return setHeader(labels);
    597        }
    598
    599        /**
    600         * Sets the header to the given values. The header can be parsed automatically from the input file with:
    601         *
    602         * <pre>
    603         * builder.setHeader();
    604         * </pre>
    605         *
    606         * or specified manually with:
    607         *
    608         * <pre>{@code
    609         * builder.setHeader("name", "email", "phone");
    610         * }</pre>
    611         * <p>
    612         * The header is also used by the {@link CSVPrinter}.
    613         * </p>
    614         * <p>
    615         * This method keeps a copy of the input array.
    616         * </p>
    617         * @param header the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    618         * @return This instance.
    619         */
    620        public Builder setHeader(final String... header) {
    621            this.headers = CSVFormat.clone(header);
    622            return this;
    623        }
    624
    625        /**
    626         * Sets the header comments to write before the CSV data.
    627         * <p>
    628         * This setting is ignored by the parser.
    629         * </p>
    630         * <p>
    631         * Comments are printed first, before headers.
    632         * </p>
    633         * <p>
    634         * Use {@link #setCommentMarker(char)} or {@link #setCommentMarker(Character)} to set the comment marker written at the start of each comment line.
    635         * </p>
    636         * <p>
    637         * If the comment marker is not set, then the header comments are ignored.
    638         * </p>
    639         * <p>
    640         * For example:
    641         * </p>
    642         *
    643         * <pre>
    644         * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0));
    645         * </pre>
    646         * <p>
    647         * writes:
    648         * </p>
    649         *
    650         * <pre>
    651         * # Generated by Apache Commons CSV.
    652         * # 1970-01-01T00:00:00Z
    653         * </pre>
    654         * <p>
    655         * This method keeps a copy of the input array.
    656         * </p>
    657         *
    658         * @param headerComments the headerComments which will be printed by the Printer before the CSV data.
    659         * @return This instance.
    660         */
    661        public Builder setHeaderComments(final Object... headerComments) {
    662            this.headerComments = CSVFormat.clone(toStringArray(headerComments));
    663            return this;
    664        }
    665
    666        /**
    667         * Sets the header comments to write before the CSV data.
    668         * <p>
    669         * This setting is ignored by the parser.
    670         * </p>
    671         * <p>
    672         * Comments are printed first, before headers.
    673         * </p>
    674         * <p>
    675         * Use {@link #setCommentMarker(char)} or {@link #setCommentMarker(Character)} to set the comment marker written at the start of each comment line.
    676         * </p>
    677         * <p>
    678         * If the comment marker is not set, then the header comments are ignored.
    679         * </p>
    680         * <p>
    681         * For example:
    682         * </p>
    683         *
    684         * <pre>
    685         * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0).toString());
    686         * </pre>
    687         * <p>
    688         * writes:
    689         * </p>
    690         *
    691         * <pre>
    692         * # Generated by Apache Commons CSV.
    693         * # 1970-01-01T00:00:00Z
    694         * </pre>
    695         * <p>
    696         * This method keeps a copy of the input array.
    697         * </p>
    698         * @param headerComments the headerComments which will be printed by the Printer before the CSV data.
    699         * @return This instance.
    700         */
    701        public Builder setHeaderComments(final String... headerComments) {
    702            this.headerComments = CSVFormat.clone(headerComments);
    703            return this;
    704        }
    705
    706        /**
    707         * Sets the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate empty lines to empty
    708         * records.
    709         *
    710         * @param ignoreEmptyLines the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate
    711         *                         empty lines to empty records.
    712         * @return This instance.
    713         */
    714        public Builder setIgnoreEmptyLines(final boolean ignoreEmptyLines) {
    715            this.ignoreEmptyLines = ignoreEmptyLines;
    716            return this;
    717        }
    718
    719        /**
    720         * Sets the parser case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
    721         *
    722         * @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
    723         * @return This instance.
    724         */
    725        public Builder setIgnoreHeaderCase(final boolean ignoreHeaderCase) {
    726            this.ignoreHeaderCase = ignoreHeaderCase;
    727            return this;
    728        }
    729
    730        /**
    731         * Sets the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is.
    732         *
    733         * @param ignoreSurroundingSpaces the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is.
    734         * @return This instance.
    735         */
    736        public Builder setIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
    737            this.ignoreSurroundingSpaces = ignoreSurroundingSpaces;
    738            return this;
    739        }
    740
    741        /**
    742         * Sets whether reading end-of-file is allowed even when input is malformed, helps Excel compatibility.
    743         *
    744         * @param lenientEof whether reading end-of-file is allowed even when input is malformed, helps Excel compatibility.
    745         * @return This instance.
    746         * @since 1.11.0
    747         */
    748        public Builder setLenientEof(final boolean lenientEof) {
    749            this.lenientEof = lenientEof;
    750            return this;
    751        }
    752
    753        /**
    754         * Sets the maximum number of rows to process, excluding the header row.
    755         * <p>
    756         * Values less than or equal to 0 mean no limit.
    757         * </p>
    758         *
    759         * @param maxRows the maximum number of rows to process, excluding the header row.
    760         * @return This instance.
    761         * @since 1.14.0
    762         */
    763        public Builder setMaxRows(final long maxRows) {
    764            this.maxRows = maxRows;
    765            return this;
    766        }
    767
    768        /**
    769         * Sets the String to convert to and from {@code null}. No substitution occurs if {@code null}.
    770         *
    771         * <ul>
    772         * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li>
    773         * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
    774         * </ul>
    775         *
    776         * @param nullString the String to convert to and from {@code null}. No substitution occurs if {@code null}.
    777         * @return This instance.
    778         */
    779        public Builder setNullString(final String nullString) {
    780            this.nullString = nullString;
    781            this.quotedNullString = quoteCharacter + nullString + quoteCharacter;
    782            return this;
    783        }
    784
    785        /**
    786         * Sets the quote character.
    787         *
    788         * @param quoteCharacter the quote character.
    789         * @return This instance.
    790         */
    791        public Builder setQuote(final char quoteCharacter) {
    792            setQuote(Character.valueOf(quoteCharacter));
    793            return this;
    794        }
    795
    796        /**
    797         * Sets the quote character, use {@code null} to disable.
    798         *
    799         * @param quoteCharacter the quote character, use {@code null} to disable.
    800         * @return This instance.
    801         */
    802        public Builder setQuote(final Character quoteCharacter) {
    803            if (isLineBreak(quoteCharacter)) {
    804                throw new IllegalArgumentException("The quoteCharacter cannot be a line break");
    805            }
    806            this.quoteCharacter = quoteCharacter;
    807            return this;
    808        }
    809
    810        /**
    811         * Sets the quote policy to use for output.
    812         *
    813         * @param quoteMode the quote policy to use for output.
    814         * @return This instance.
    815         */
    816        public Builder setQuoteMode(final QuoteMode quoteMode) {
    817            this.quoteMode = quoteMode;
    818            return this;
    819        }
    820
    821        /**
    822         * Sets the record separator to use for output.
    823         *
    824         * <p>
    825         * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r'
    826         * and "\r\n"
    827         * </p>
    828         *
    829         * @param recordSeparator the record separator to use for output.
    830         * @return This instance.
    831         */
    832        public Builder setRecordSeparator(final char recordSeparator) {
    833            this.recordSeparator = String.valueOf(recordSeparator);
    834            return this;
    835        }
    836
    837        /**
    838         * Sets the record separator to use for output.
    839         *
    840         * <p>
    841         * <strong>Note:</strong> This setting is only used during printing and does not affect parsing. Parsing currently only works for inputs with '\n', '\r'
    842         * and "\r\n"
    843         * </p>
    844         *
    845         * @param recordSeparator the record separator to use for output.
    846         * @return This instance.
    847         */
    848        public Builder setRecordSeparator(final String recordSeparator) {
    849            this.recordSeparator = recordSeparator;
    850            return this;
    851        }
    852
    853        /**
    854         * Sets whether to skip the header record.
    855         *
    856         * @param skipHeaderRecord whether to skip the header record.
    857         * @return This instance.
    858         */
    859        public Builder setSkipHeaderRecord(final boolean skipHeaderRecord) {
    860            this.skipHeaderRecord = skipHeaderRecord;
    861            return this;
    862        }
    863
    864        /**
    865         * Sets whether reading trailing data is allowed in records, helps Excel compatibility.
    866         *
    867         * @param trailingData whether reading trailing data is allowed in records, helps Excel compatibility.
    868         * @return This instance.
    869         * @since 1.11.0
    870         */
    871        public Builder setTrailingData(final boolean trailingData) {
    872            this.trailingData = trailingData;
    873            return this;
    874        }
    875
    876        /**
    877         * Sets whether to add a trailing delimiter.
    878         *
    879         * @param trailingDelimiter whether to add a trailing delimiter.
    880         * @return This instance.
    881         */
    882        public Builder setTrailingDelimiter(final boolean trailingDelimiter) {
    883            this.trailingDelimiter = trailingDelimiter;
    884            return this;
    885        }
    886
    887
    888        /**
    889         * Sets whether to trim leading and trailing blanks.
    890         *
    891         * @param trim whether to trim leading and trailing blanks.
    892         * @return This instance.
    893         */
    894        public Builder setTrim(final boolean trim) {
    895            this.trim = trim;
    896            return this;
    897        }
    898    }
    899
    900    /**
    901     * Predefines formats.
    902     *
    903     * @since 1.2
    904     */
    905    public enum Predefined {
    906
    907        /**
    908         * The DEFAULT predefined format.
    909         *
    910         * @see CSVFormat#DEFAULT
    911         */
    912        Default(DEFAULT),
    913
    914        /**
    915         * The EXCEL predefined format.
    916         *
    917         * @see CSVFormat#EXCEL
    918         */
    919        Excel(EXCEL),
    920
    921        /**
    922         * The INFORMIX_UNLOAD predefined format.
    923         *
    924         * @see CSVFormat#INFORMIX_UNLOAD
    925         * @since 1.3
    926         */
    927        InformixUnload(INFORMIX_UNLOAD),
    928
    929        /**
    930         * The INFORMIX_UNLOAD_CSV predefined format.
    931         *
    932         * @see CSVFormat#INFORMIX_UNLOAD_CSV
    933         * @since 1.3
    934         */
    935        InformixUnloadCsv(INFORMIX_UNLOAD_CSV),
    936
    937        /**
    938         * The MONGODB_CSV predefined format.
    939         *
    940         * @see CSVFormat#MONGODB_CSV
    941         * @since 1.7
    942         */
    943        MongoDBCsv(MONGODB_CSV),
    944
    945        /**
    946         * The MONGODB_TSV predefined format.
    947         *
    948         * @see CSVFormat#MONGODB_TSV
    949         * @since 1.7
    950         */
    951        MongoDBTsv(MONGODB_TSV),
    952
    953        /**
    954         * The MYSQL predefined format.
    955         *
    956         * @see CSVFormat#MYSQL
    957         */
    958        MySQL(MYSQL),
    959
    960        /**
    961         * The ORACLE predefined format.
    962         *
    963         * @see CSVFormat#ORACLE
    964         */
    965        Oracle(ORACLE),
    966
    967        /**
    968         * The POSTGRESQL_CSV predefined format.
    969         *
    970         * @see CSVFormat#POSTGRESQL_CSV
    971         * @since 1.5
    972         */
    973        PostgreSQLCsv(POSTGRESQL_CSV),
    974
    975        /**
    976         * The POSTGRESQL_TEXT predefined format.
    977         *
    978         * @see CSVFormat#POSTGRESQL_TEXT
    979         */
    980        PostgreSQLText(POSTGRESQL_TEXT),
    981
    982        /**
    983         * The RFC4180 predefined format.
    984         *
    985         * @see CSVFormat#RFC4180
    986         */
    987        RFC4180(CSVFormat.RFC4180),
    988
    989        /**
    990         * The TDF predefined format.
    991         *
    992         * @see CSVFormat#TDF
    993         */
    994        TDF(CSVFormat.TDF);
    995
    996        private final CSVFormat format;
    997
    998        Predefined(final CSVFormat format) {
    999            this.format = format;
    1000        }
    1001
    1002        /**
    1003         * Gets the format.
    1004         *
    1005         * @return the format.
    1006         */
    1007        public CSVFormat getFormat() {
    1008            return format;
    1009        }
    1010    }
    1011
    1012    /**
    1013     * Standard Comma Separated Value format, as for {@link #RFC4180} but allowing empty lines.
    1014     *
    1015     * <p>
    1016     * The {@link Builder} settings are:
    1017     * </p>
    1018     * <ul>
    1019     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1020     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1021     * <li>{@link Builder#setRecordSeparator(String) setRecordSeparator}{@code ("\r\n")}</li>
    1022     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (true)}</li>
    1023     * <li>{@link Builder#setDuplicateHeaderMode(DuplicateHeaderMode) setDuplicateHeaderMode}{@code (DuplicateHeaderMode.ALLOW_ALL)}</li>
    1024     * </ul>
    1025     *
    1026     * @see Predefined#Default
    1027     * @see DuplicateHeaderMode#ALLOW_ALL
    1028     */
    1029    public static final CSVFormat DEFAULT = new CSVFormat(Builder.create());
    1030
    1031    /**
    1032     * <a href="https://support.microsoft.com/en-us/office/import-or-export-text-txt-or-csv-files-5250ac4c-663c-47ce-937b-339e391393ba">Microsoft Excel</a> file
    1033     * 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
    1034     * this format to accommodate your regional settings.
    1035     *
    1036     * <p>
    1037     * For example for parsing or generating a CSV file on a French system the following format will be used:
    1038     * </p>
    1039     *
    1040     * <pre>
    1041     * CSVFormat format = CSVFormat.EXCEL.builder().setDelimiter(';').get();
    1042     * </pre>
    1043     *
    1044     * <p>
    1045     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1046     * </p>
    1047     * <ul>
    1048     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1049     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1050     * <li>{@link Builder#setRecordSeparator(String) setRecordSeparator}{@code ("\r\n")}</li>
    1051     * <li>{@link Builder#setDuplicateHeaderMode(DuplicateHeaderMode) setDuplicateHeaderMode}{@code (DuplicateHeaderMode.ALLOW_ALL)}</li>
    1052     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1053     * <li>{@link Builder#setAllowMissingColumnNames(boolean) setAllowMissingColumnNames}{@code (true)}</li>
    1054     * <li>{@link Builder#setTrailingData(boolean) setTrailingData}{@code (true)}</li>
    1055     * <li>{@link Builder#setLenientEof(boolean) setLenientEof}{@code (true)}</li>
    1056     * </ul>
    1057     * <p>
    1058     * Note: This is currently like {@link #RFC4180} plus {@link Builder#setAllowMissingColumnNames(boolean) Builder#setAllowMissingColumnNames(true)} and
    1059     * {@link Builder#setIgnoreEmptyLines(boolean) Builder#setIgnoreEmptyLines(false)}.
    1060     * </p>
    1061     *
    1062     * @see Predefined#Excel
    1063     * @see DuplicateHeaderMode#ALLOW_ALL
    1064     * @see <a href="https://support.microsoft.com/en-us/office/import-or-export-text-txt-or-csv-files-5250ac4c-663c-47ce-937b-339e391393ba">Microsoft Excel
    1065     *      </a>
    1066     */
    1067    // @formatter:off
    1068    public static final CSVFormat EXCEL = DEFAULT.builder()
    1069            .setIgnoreEmptyLines(false)
    1070            .setAllowMissingColumnNames(true)
    1071            .setTrailingData(true)
    1072            .setLenientEof(true)
    1073            .get();
    1074    // @formatter:on
    1075
    1076    /**
    1077     * Default <a href="https://www.ibm.com/docs/en/informix-servers/14.10?topic=statements-unload-statement">Informix CSV UNLOAD</a>
    1078     * format used by the {@code UNLOAD TO file_name} operation.
    1079     *
    1080     * <p>
    1081     * This is a comma-delimited format with an LF character as the line separator. Values are not quoted and special characters are escaped with {@code '\'}.
    1082     * The default NULL string is {@code "\\N"}.
    1083     * </p>
    1084     *
    1085     * <p>
    1086     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1087     * </p>
    1088     * <ul>
    1089     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1090     * <li>{@link Builder#setEscape(char) setEscape}{@code ('\\')}</li>
    1091     * <li>{@link Builder#setQuote(char) setQuote}{@code ('\"')}</li>
    1092     * <li>{@link Builder#setRecordSeparator(char) setRecordSeparator}{@code ('\n')}</li>
    1093     * </ul>
    1094     *
    1095     * @see Predefined#MySQL
    1096     * @see <a href="https://www.ibm.com/docs/en/informix-servers/14.10?topic=statements-unload-statement">Informix CSV UNLOAD</a>
    1097     * @since 1.3
    1098     */
    1099    // @formatter:off
    1100    public static final CSVFormat INFORMIX_UNLOAD = DEFAULT.builder()
    1101            .setDelimiter(Constants.PIPE)
    1102            .setEscape(Constants.BACKSLASH)
    1103            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1104            .setRecordSeparator(Constants.LF)
    1105            .get();
    1106    // @formatter:on
    1107
    1108    /**
    1109     * Default <a href="https://www.ibm.com/docs/en/informix-servers/14.10?topic=statements-unload-statement">Informix CSV UNLOAD</a>
    1110     * format used by the {@code UNLOAD TO file_name} operation (escaping is disabled.)
    1111     *
    1112     * <p>
    1113     * This is a comma-delimited format with an LF character as the line separator. Values are not quoted and special characters are escaped with {@code '\'}.
    1114     * The default NULL string is {@code "\\N"}.
    1115     * </p>
    1116     *
    1117     * <p>
    1118     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1119     * </p>
    1120     * <ul>
    1121     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1122     * <li>{@link Builder#setQuote(char) setQuote}{@code ('\"')}</li>
    1123     * <li>{@link Builder#setRecordSeparator(char) setRecordSeparator}{@code ('\n')}</li>
    1124     * </ul>
    1125     *
    1126     * @see Predefined#MySQL
    1127     * @see <a href= "http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm">
    1128     *      http://www.ibm.com/support/knowledgecenter/SSBJG3_2.5.0/com.ibm.gen_busug.doc/c_fgl_InOutSql_UNLOAD.htm</a>
    1129     * @since 1.3
    1130     */
    1131    // @formatter:off
    1132    public static final CSVFormat INFORMIX_UNLOAD_CSV = DEFAULT.builder()
    1133            .setDelimiter(Constants.COMMA)
    1134            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1135            .setRecordSeparator(Constants.LF)
    1136            .get();
    1137    // @formatter:on
    1138
    1139    /**
    1140     * Default MongoDB CSV format used by the {@code mongoexport} operation.
    1141     * <p>
    1142     * <strong>Parsing is not supported yet.</strong>
    1143     * </p>
    1144     *
    1145     * <p>
    1146     * 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
    1147     * names is expected.
    1148     * </p>
    1149     * <p>
    1150     * As of 2024-04-05, the MongoDB documentation for {@code mongoimport} states:
    1151     * </p>
    1152     * <blockquote>The csv parser accepts that data that complies with RFC <a href="https://tools.ietf.org/html/4180">RFC-4180</a>. As a result, backslashes are
    1153     * not a valid escape character. If you use double-quotes to enclose fields in the CSV data, you must escape internal double-quote marks by prepending
    1154     * another double-quote. </blockquote>
    1155     * <p>
    1156     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1157     * </p>
    1158     * <ul>
    1159     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1160     * <li>{@link Builder#setEscape(char) setEscape}{@code ('"')}</li>
    1161     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1162     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.MINIMAL)}</li>
    1163     * </ul>
    1164     *
    1165     * @see Predefined#MongoDBCsv
    1166     * @see QuoteMode#ALL_NON_NULL
    1167     * @see <a href="https://docs.mongodb.com/manual/reference/program/mongoexport/">MongoDB mongoexport command documentation</a>
    1168     * @since 1.7
    1169     */
    1170    // @formatter:off
    1171    public static final CSVFormat MONGODB_CSV = DEFAULT.builder()
    1172            .setDelimiter(Constants.COMMA)
    1173            .setEscape(Constants.DOUBLE_QUOTE_CHAR)
    1174            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1175            .setQuoteMode(QuoteMode.MINIMAL)
    1176            .get();
    1177    // @formatter:off
    1178
    1179    /**
    1180     * Default MongoDB TSV format used by the {@code mongoexport} operation.
    1181     * <p>
    1182     * <strong>Parsing is not supported yet.</strong>
    1183     * </p>
    1184     *
    1185     * <p>
    1186     * This is a tab-delimited format. Values are double quoted only if needed and special
    1187     * characters are escaped with {@code '"'}. A header line with field names is expected.
    1188     * </p>
    1189     *
    1190     * <p>
    1191     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1192     * </p>
    1193     * <ul>
    1194     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code ('\t')}</li>
    1195     * <li>{@link Builder#setEscape(char) setEscape}{@code ('"')}</li>
    1196     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1197     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.MINIMAL)}</li>
    1198     * <li>{@link Builder#setSkipHeaderRecord(boolean) setSkipHeaderRecord}{@code (false)}</li>
    1199     * </ul>
    1200     *
    1201     * @see Predefined#MongoDBCsv
    1202     * @see QuoteMode#ALL_NON_NULL
    1203     * @see <a href="https://docs.mongodb.com/manual/reference/program/mongoexport/">MongoDB mongoexport command
    1204     *          documentation</a>
    1205     * @since 1.7
    1206     */
    1207    // @formatter:off
    1208    public static final CSVFormat MONGODB_TSV = DEFAULT.builder()
    1209            .setDelimiter(Constants.TAB)
    1210            .setEscape(Constants.DOUBLE_QUOTE_CHAR)
    1211            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1212            .setQuoteMode(QuoteMode.MINIMAL)
    1213            .setSkipHeaderRecord(false)
    1214            .get();
    1215    // @formatter:off
    1216
    1217    /**
    1218     * Default <a href="https://dev.mysql.com/doc/refman/8.0/en/mysqldump-delimited-text.html">MySQL</a>
    1219     * format used by the {@code SELECT INTO OUTFILE} and {@code LOAD DATA INFILE} operations.
    1220     *
    1221     * <p>
    1222     * This is a tab-delimited format with an LF character as the line separator. Values are not quoted and special
    1223     * characters are escaped with {@code '\'}. The default NULL string is {@code "\\N"}.
    1224     * </p>
    1225     *
    1226     * <p>
    1227     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1228     * </p>
    1229     * <ul>
    1230     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code ('\t')}</li>
    1231     * <li>{@link Builder#setEscape(char) setEscape}{@code ('\\')}</li>
    1232     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1233     * <li>{@link Builder#setQuote(Character) setQuote}{@code (null)}</li>
    1234     * <li>{@link Builder#setRecordSeparator(char) setRecordSeparator}{@code ('\n')}</li>
    1235     * <li>{@link Builder#setNullString(String) setNullString}{@code ("\\N")}</li>
    1236     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.ALL_NON_NULL)}</li>
    1237     * </ul>
    1238     *
    1239     * @see Predefined#MySQL
    1240     * @see QuoteMode#ALL_NON_NULL
    1241     * @see <a href="https://dev.mysql.com/doc/refman/8.0/en/mysqldump-delimited-text.html">MySQL</a>
    1242     */
    1243    // @formatter:off
    1244    public static final CSVFormat MYSQL = DEFAULT.builder()
    1245            .setDelimiter(Constants.TAB)
    1246            .setEscape(Constants.BACKSLASH)
    1247            .setIgnoreEmptyLines(false)
    1248            .setQuote(null)
    1249            .setRecordSeparator(Constants.LF)
    1250            .setNullString(Constants.SQL_NULL_STRING)
    1251            .setQuoteMode(QuoteMode.ALL_NON_NULL)
    1252            .get();
    1253    // @formatter:off
    1254
    1255    /**
    1256     * Default
    1257     * <a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/sutil/oracle-sql-loader-control-file-contents.html#GUID-D1762699-8154-40F6-90DE-EFB8EB6A9AB0">Oracle</a>
    1258     * format used by the SQL*Loader utility.
    1259     *
    1260     * <p>
    1261     * This is a comma-delimited format with the system line separator character as the record separator. Values are
    1262     * double quoted when needed and special characters are escaped with {@code '"'}. The default NULL string is
    1263     * {@code ""}. Values are trimmed.
    1264     * </p>
    1265     *
    1266     * <p>
    1267     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1268     * </p>
    1269     * <ul>
    1270     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')} // default is {@code FIELDS TERMINATED BY ','}}</li>
    1271     * <li>{@link Builder#setEscape(char) setEscape}{@code ('\\')}</li>
    1272     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1273     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')} // default is {@code OPTIONALLY ENCLOSED BY '"'}}</li>
    1274     * <li>{@link Builder#setNullString(String) setNullString}{@code ("\\N")}</li>
    1275     * <li>{@link Builder#setTrim(boolean) setTrim}{@code (true)}</li>
    1276     * <li>{@link Builder#setRecordSeparator(String) setRecordSeparator}{@code (System.lineSeparator())}</li>
    1277     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.MINIMAL)}</li>
    1278     * </ul>
    1279     *
    1280     * @see Predefined#Oracle
    1281     * @see QuoteMode#MINIMAL
    1282     * @see <a href="https://docs.oracle.com/en/database/oracle/oracle-database/23/sutil/oracle-sql-loader-control-file-contents.html#GUID-D1762699-8154-40F6-90DE-EFB8EB6A9AB0">Oracle CSV Format Specification</a>
    1283     * @since 1.6
    1284     */
    1285    // @formatter:off
    1286    public static final CSVFormat ORACLE = DEFAULT.builder()
    1287            .setDelimiter(Constants.COMMA)
    1288            .setEscape(Constants.BACKSLASH)
    1289            .setIgnoreEmptyLines(false)
    1290            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1291            .setNullString(Constants.SQL_NULL_STRING)
    1292            .setTrim(true)
    1293            .setRecordSeparator(System.lineSeparator())
    1294            .setQuoteMode(QuoteMode.MINIMAL)
    1295            .get();
    1296    // @formatter:off
    1297
    1298    /**
    1299     * Default <a href="https://www.postgresql.org/docs/current/static/sql-copy.html">PostgreSQL CSV</a> format used by the {@code COPY} operation.
    1300     *
    1301     * <p>
    1302     * This is a comma-delimited format with an LF character as the line separator. Values are double quoted and special
    1303     * characters are not escaped. The default NULL string is {@code ""}.
    1304     * </p>
    1305     *
    1306     * <p>
    1307     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1308     * </p>
    1309     * <ul>
    1310     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1311     * <li>{@link Builder#setEscape(Character) setEscape}{@code (null)}</li>
    1312     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1313     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1314     * <li>{@link Builder#setRecordSeparator(char) setRecordSeparator}{@code ('\n')}</li>
    1315     * <li>{@link Builder#setNullString(String) setNullString}{@code ("")}</li>
    1316     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.ALL_NON_NULL)}</li>
    1317     * </ul>
    1318     *
    1319     * @see Predefined#MySQL
    1320     * @see QuoteMode#ALL_NON_NULL
    1321     * @see <a href="https://www.postgresql.org/docs/current/static/sql-copy.html">PostgreSQL CSV</a>
    1322     * @since 1.5
    1323     */
    1324    // @formatter:off
    1325    public static final CSVFormat POSTGRESQL_CSV = DEFAULT.builder()
    1326            .setDelimiter(Constants.COMMA)
    1327            .setEscape(null)
    1328            .setIgnoreEmptyLines(false)
    1329            .setQuote(Constants.DOUBLE_QUOTE_CHAR)
    1330            .setRecordSeparator(Constants.LF)
    1331            .setNullString(Constants.EMPTY)
    1332            .setQuoteMode(QuoteMode.ALL_NON_NULL)
    1333            .get();
    1334    // @formatter:off
    1335
    1336    /**
    1337     * Default <a href="https://www.postgresql.org/docs/current/static/sql-copy.html">PostgreSQL Text</a> format used by the {@code COPY} operation.
    1338     *
    1339     * <p>
    1340     * This is a tab-delimited format with an LF character as the line separator. Values are not quoted and special
    1341     * characters are escaped with {@code '\\'}. The default NULL string is {@code "\\N"}.
    1342     * </p>
    1343     *
    1344     * <p>
    1345     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1346     * </p>
    1347     * <ul>
    1348     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code ('\t')}</li>
    1349     * <li>{@link Builder#setEscape(char) setEscape}{@code ('\\')}</li>
    1350     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1351     * <li>{@link Builder#setQuote(Character) setQuote}{@code (null)}</li>
    1352     * <li>{@link Builder#setRecordSeparator(char) setRecordSeparator}{@code ('\n')}</li>
    1353     * <li>{@link Builder#setNullString(String) setNullString}{@code ("\\N")}</li>
    1354     * <li>{@link Builder#setQuoteMode(QuoteMode) setQuoteMode}{@code (QuoteMode.ALL_NON_NULL)}</li>
    1355     * </ul>
    1356     *
    1357     * @see Predefined#MySQL
    1358     * @see QuoteMode#ALL_NON_NULL
    1359     * @see <a href="https://www.postgresql.org/docs/current/static/sql-copy.html">PostgreSQL Text</a>
    1360     * @since 1.5
    1361     */
    1362    // @formatter:off
    1363    public static final CSVFormat POSTGRESQL_TEXT = DEFAULT.builder()
    1364            .setDelimiter(Constants.TAB)
    1365            .setEscape(Constants.BACKSLASH)
    1366            .setIgnoreEmptyLines(false)
    1367            .setQuote(null)
    1368            .setRecordSeparator(Constants.LF)
    1369            .setNullString(Constants.SQL_NULL_STRING)
    1370            .setQuoteMode(QuoteMode.ALL_NON_NULL)
    1371            .get();
    1372    // @formatter:off
    1373
    1374    /**
    1375     * Comma separated format as defined by <a href="https://tools.ietf.org/html/rfc4180">RFC 4180</a>.
    1376     *
    1377     * <p>
    1378     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1379     * </p>
    1380     * <ul>
    1381     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code (',')}</li>
    1382     * <li>{@link Builder#setQuote(char) setQuote}{@code ('"')}</li>
    1383     * <li>{@link Builder#setRecordSeparator(String) setRecordSeparator}{@code ("\r\n")}</li>
    1384     * <li>{@link Builder#setIgnoreEmptyLines(boolean) setIgnoreEmptyLines}{@code (false)}</li>
    1385     * </ul>
    1386     *
    1387     * @see Predefined#RFC4180
    1388     * @see <a href="https://tools.ietf.org/html/rfc4180">RFC 4180</a>
    1389     */
    1390    public static final CSVFormat RFC4180 = DEFAULT.builder().setIgnoreEmptyLines(false).get();
    1391
    1392    private static final long serialVersionUID = 2L;
    1393
    1394    /**
    1395     * Tab-delimited format (<a href="https://en.wikipedia.org/wiki/Tab-separated_values">TDF</a>).
    1396     *
    1397     * <p>
    1398     * The {@link Builder} settings are the {@link #DEFAULT} <em>with</em>:
    1399     * </p>
    1400     * <ul>
    1401     * <li>{@link Builder#setDelimiter(char) setDelimiter}{@code ('\t')}</li>
    1402     * <li>{@link Builder#setIgnoreSurroundingSpaces(boolean) setIgnoreSurroundingSpaces}{@code (true)}</li>
    1403     * </ul>
    1404     *
    1405     * @see Predefined#TDF
    1406     * @see <a href="https://en.wikipedia.org/wiki/Tab-separated_values">TDF</a>
    1407     */
    1408    // @formatter:off
    1409    public static final CSVFormat TDF = DEFAULT.builder()
    1410            .setDelimiter(Constants.TAB)
    1411            .setIgnoreSurroundingSpaces(true)
    1412            .get();
    1413    // @formatter:on
    1414
    1415    /**
    1416     * Null-safe clone of an array.
    1417     *
    1418     * @param <T>    The array element type.
    1419     * @param values the source array
    1420     * @return the cloned array.
    1421     */
    1422    @SafeVarargs
    1423    static <T> T[] clone(final T... values) {
    1424        return values == null ? null : values.clone();
    1425    }
    1426
    1427    /**
    1428     * Returns true if the given string contains the search char.
    1429     *
    1430     * @param source   the string to check.
    1431     * @param searchCh the character to search.
    1432     * @return true if {@code c} contains a line break character
    1433     */
    1434    private static boolean contains(final String source, final char searchCh) {
    1435        return Objects.requireNonNull(source, "source").indexOf(searchCh) >= 0;
    1436    }
    1437
    1438    /**
    1439     * Returns true if the given string contains a line break character.
    1440     *
    1441     * @param source the string to check.
    1442     * @return true if {@code c} contains a line break character.
    1443     */
    1444    private static boolean containsLineBreak(final String source) {
    1445        return contains(source, Constants.CR) || contains(source, Constants.LF);
    1446    }
    1447
    1448    /**
    1449     * Creates a null-safe copy of the given instance.
    1450     *
    1451     * @return a copy of the given instance or null if the input is null.
    1452     */
    1453    static CSVFormat copy(final CSVFormat format) {
    1454        return format != null ? format.copy() : null;
    1455    }
    1456
    1457    static boolean isBlank(final String value) {
    1458        return value == null || value.trim().isEmpty();
    1459    }
    1460
    1461    /**
    1462     * Returns true if the given character is a line break character.
    1463     *
    1464     * @param c the character to check.
    1465     * @return true if {@code c} is a line break character.
    1466     */
    1467    private static boolean isLineBreak(final char c) {
    1468        return c == Constants.LF || c == Constants.CR;
    1469    }
    1470
    1471    /**
    1472     * Returns true if the given character is a line break character.
    1473     *
    1474     * @param c the character to check, may be null.
    1475     * @return true if {@code c} is a line break character (and not null).
    1476     */
    1477    private static boolean isLineBreak(final Character c) {
    1478        return c != null && isLineBreak(c.charValue()); // Explicit (un)boxing is intentional
    1479    }
    1480
    1481    /** Same test as in as {@link String#trim()}. */
    1482    private static boolean isTrimChar(final char ch) {
    1483        return ch <= Constants.SP;
    1484    }
    1485
    1486    /** Same test as in as {@link String#trim()}. */
    1487    private static boolean isTrimChar(final CharSequence charSequence, final int pos) {
    1488        return isTrimChar(charSequence.charAt(pos));
    1489    }
    1490
    1491    /**
    1492     * Creates a new CSV format with the specified delimiter.
    1493     *
    1494     * <p>
    1495     * Use this method if you want to create a CSVFormat from scratch. All fields but the delimiter will be initialized with null/false.
    1496     * </p>
    1497     *
    1498     * @param delimiter the char used for value separation, must not be a line break character
    1499     * @return a new CSV format.
    1500     * @throws IllegalArgumentException if the delimiter is a line break character
    1501     * @see #DEFAULT
    1502     * @see #RFC4180
    1503     * @see #MYSQL
    1504     * @see #EXCEL
    1505     * @see #TDF
    1506     */
    1507    public static CSVFormat newFormat(final char delimiter) {
    1508        return new CSVFormat(new Builder().setDelimiter(delimiter));
    1509    }
    1510
    1511    static String[] toStringArray(final Object[] values) {
    1512        if (values == null) {
    1513            return null;
    1514        }
    1515        final String[] strings = new String[values.length];
    1516        Arrays.setAll(strings, i -> Objects.toString(values[i], null));
    1517        return strings;
    1518    }
    1519
    1520    static CharSequence trim(final CharSequence charSequence) {
    1521        if (charSequence instanceof String) {
    1522            return ((String) charSequence).trim();
    1523        }
    1524        final int count = charSequence.length();
    1525        int len = count;
    1526        int pos = 0;
    1527
    1528        while (pos < len && isTrimChar(charSequence, pos)) {
    1529            pos++;
    1530        }
    1531        while (pos < len && isTrimChar(charSequence, len - 1)) {
    1532            len--;
    1533        }
    1534        return pos > 0 || len < count ? charSequence.subSequence(pos, len) : charSequence;
    1535    }
    1536
    1537    /**
    1538     * Gets one of the predefined formats from {@link CSVFormat.Predefined}.
    1539     *
    1540     * @param format name
    1541     * @return one of the predefined formats
    1542     * @since 1.2
    1543     */
    1544    public static CSVFormat valueOf(final String format) {
    1545        return CSVFormat.Predefined.valueOf(format).getFormat();
    1546    }
    1547
    1548    /** How duplicate headers are handled. */
    1549    private final DuplicateHeaderMode duplicateHeaderMode;
    1550
    1551    /** Whether missing column names are allowed when parsing the header line. */
    1552    private final boolean allowMissingColumnNames;
    1553
    1554    /** Whether to flush on close. */
    1555    private final boolean autoFlush;
    1556
    1557    /** Set to null if commenting is disabled. */
    1558    private final Character commentMarker;
    1559
    1560    /** The character delimiting the values (typically ";", "," or "\t"). */
    1561    private final String delimiter;
    1562
    1563    /** Set to null if escaping is disabled. */
    1564    private final Character escapeCharacter;
    1565
    1566    /** Array of header column names. */
    1567    private final String[] headers;
    1568
    1569    /** Array of header comment lines. */
    1570    private final String[] headerComments;
    1571
    1572    /** Whether empty lines between records are ignored when parsing input. */
    1573    private final boolean ignoreEmptyLines;
    1574
    1575    /** Should ignore header names case. */
    1576    private final boolean ignoreHeaderCase;
    1577
    1578    /** Should leading/trailing spaces be ignored around values?. */
    1579    private final boolean ignoreSurroundingSpaces;
    1580
    1581    /** The string to be used for null values. */
    1582    private final String nullString;
    1583
    1584    /** Set to null if quoting is disabled. */
    1585    private final Character quoteCharacter;
    1586
    1587    /** Set to {@code quoteCharacter + nullString + quoteCharacter} */
    1588    private final String quotedNullString;
    1589
    1590    /** The quote policy output fields. */
    1591    private final QuoteMode quoteMode;
    1592
    1593    /** For output. */
    1594    private final String recordSeparator;
    1595
    1596    /** Whether to skip the header record. */
    1597    private final boolean skipHeaderRecord;
    1598
    1599    /** Whether reading end-of-file is allowed even when input is malformed, helps Excel compatibility. */
    1600    private final boolean lenientEof;
    1601
    1602    /** Whether reading trailing data is allowed in records, helps Excel compatibility. */
    1603    private final boolean trailingData;
    1604
    1605    /** Whether to add a trailing delimiter. */
    1606    private final boolean trailingDelimiter;
    1607
    1608    /** Whether to trim leading and trailing blanks. */
    1609    private final boolean trim;
    1610
    1611    /** The maximum number of rows to process, excluding the header row. */
    1612    private final long maxRows;
    1613
    1614    private CSVFormat(final Builder builder) {
    1615        this.allowMissingColumnNames = builder.allowMissingColumnNames;
    1616        this.autoFlush = builder.autoFlush;
    1617        this.commentMarker = builder.commentMarker;
    1618        this.delimiter = builder.delimiter;
    1619        this.duplicateHeaderMode = builder.duplicateHeaderMode;
    1620        this.escapeCharacter = builder.escapeCharacter;
    1621        this.headerComments = builder.headerComments;
    1622        this.headers = builder.headers;
    1623        this.ignoreEmptyLines = builder.ignoreEmptyLines;
    1624        this.ignoreHeaderCase = builder.ignoreHeaderCase;
    1625        this.ignoreSurroundingSpaces = builder.ignoreSurroundingSpaces;
    1626        this.lenientEof = builder.lenientEof;
    1627        this.maxRows = builder.maxRows;
    1628        this.nullString = builder.nullString;
    1629        this.quoteCharacter = builder.quoteCharacter;
    1630        this.quoteMode = builder.quoteMode;
    1631        this.quotedNullString = builder.quotedNullString;
    1632        this.recordSeparator = builder.recordSeparator;
    1633        this.skipHeaderRecord = builder.skipHeaderRecord;
    1634        this.trailingData = builder.trailingData;
    1635        this.trailingDelimiter = builder.trailingDelimiter;
    1636        this.trim = builder.trim;
    1637        validate();
    1638    }
    1639
    1640    private void append(final char c, final Appendable appendable) throws IOException {
    1641        // try {
    1642        appendable.append(c);
    1643        // } catch (final IOException e) {
    1644        // throw new UncheckedIOException(e);
    1645        // }
    1646    }
    1647
    1648    private void append(final CharSequence csq, final Appendable appendable) throws IOException {
    1649        // try {
    1650        appendable.append(csq);
    1651        // } catch (final IOException e) {
    1652        // throw new UncheckedIOException(e);
    1653        // }
    1654    }
    1655
    1656    /**
    1657     * Creates a new Builder for this instance.
    1658     *
    1659     * @return a new Builder.
    1660     */
    1661    public Builder builder() {
    1662        return Builder.create(this);
    1663    }
    1664
    1665    /**
    1666     * Creates a copy of this instance.
    1667     *
    1668     * @return a copy of this instance.
    1669     */
    1670    CSVFormat copy() {
    1671        return builder().get();
    1672    }
    1673
    1674    @Override
    1675    public boolean equals(final Object obj) {
    1676        if (this == obj) {
    1677            return true;
    1678        }
    1679        if (obj == null) {
    1680            return false;
    1681        }
    1682        if (getClass() != obj.getClass()) {
    1683            return false;
    1684        }
    1685        final CSVFormat other = (CSVFormat) obj;
    1686        return allowMissingColumnNames == other.allowMissingColumnNames && autoFlush == other.autoFlush &&
    1687                Objects.equals(commentMarker, other.commentMarker) && Objects.equals(delimiter, other.delimiter) &&
    1688                duplicateHeaderMode == other.duplicateHeaderMode && Objects.equals(escapeCharacter, other.escapeCharacter) &&
    1689                Arrays.equals(headerComments, other.headerComments) && Arrays.equals(headers, other.headers) &&
    1690                ignoreEmptyLines == other.ignoreEmptyLines && ignoreHeaderCase == other.ignoreHeaderCase &&
    1691                ignoreSurroundingSpaces == other.ignoreSurroundingSpaces && lenientEof == other.lenientEof &&
    1692                Objects.equals(nullString, other.nullString) && Objects.equals(quoteCharacter, other.quoteCharacter) &&
    1693                quoteMode == other.quoteMode && Objects.equals(quotedNullString, other.quotedNullString) &&
    1694                Objects.equals(recordSeparator, other.recordSeparator) && skipHeaderRecord == other.skipHeaderRecord &&
    1695                trailingData == other.trailingData && trailingDelimiter == other.trailingDelimiter && trim == other.trim;
    1696    }
    1697
    1698    private void escape(final char c, final Appendable appendable) throws IOException {
    1699        append(escapeCharacter.charValue(), appendable); // Explicit (un)boxing is intentional
    1700        append(c, appendable);
    1701    }
    1702
    1703    /**
    1704     * Formats the specified values as a CSV record string.
    1705     *
    1706     * @param values the values to format.
    1707     * @return the formatted values.
    1708     */
    1709    public String format(final Object... values) {
    1710        return Uncheck.get(() -> format_(values));
    1711    }
    1712
    1713    private String format_(final Object... values) throws IOException {
    1714        final StringWriter out = new StringWriter();
    1715        try (CSVPrinter csvPrinter = new CSVPrinter(out, this)) {
    1716            csvPrinter.printRecord(values);
    1717            final String res = out.toString();
    1718            final int len = recordSeparator != null ? res.length() - recordSeparator.length() : res.length();
    1719            return res.substring(0, len);
    1720        }
    1721    }
    1722
    1723    /**
    1724     * Gets whether duplicate names are allowed in the headers.
    1725     *
    1726     * @return whether duplicate header names are allowed
    1727     * @since 1.7
    1728     * @deprecated Use {@link #getDuplicateHeaderMode()}.
    1729     */
    1730    @Deprecated
    1731    public boolean getAllowDuplicateHeaderNames() {
    1732        return duplicateHeaderMode == DuplicateHeaderMode.ALLOW_ALL;
    1733    }
    1734
    1735    /**
    1736     * Gets whether missing column names are allowed when parsing the header line.
    1737     *
    1738     * @return {@code true} if missing column names are allowed when parsing the header line, {@code false} to throw an {@link IllegalArgumentException}.
    1739     */
    1740    public boolean getAllowMissingColumnNames() {
    1741        return allowMissingColumnNames;
    1742    }
    1743
    1744    /**
    1745     * Gets whether to flush on close.
    1746     *
    1747     * @return whether to flush on close.
    1748     * @since 1.6
    1749     */
    1750    public boolean getAutoFlush() {
    1751        return autoFlush;
    1752    }
    1753
    1754    /**
    1755     * Gets the comment marker character, {@code null} disables comments.
    1756     * <p>
    1757     * The comment start character is only recognized at the start of a line.
    1758     * </p>
    1759     * <p>
    1760     * Comments are printed first, before headers.
    1761     * </p>
    1762     * <p>
    1763     * Use {@link Builder#setCommentMarker(char)} or {@link Builder#setCommentMarker(Character)} to set the comment marker written at the start of each comment
    1764     * line.
    1765     * </p>
    1766     * <p>
    1767     * If the comment marker is not set, then the header comments are ignored.
    1768     * </p>
    1769     * <p>
    1770     * For example:
    1771     * </p>
    1772     *
    1773     * <pre>
    1774     * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0));
    1775     * </pre>
    1776     * <p>
    1777     * writes:
    1778     * </p>
    1779     *
    1780     * <pre>
    1781     * # Generated by Apache Commons CSV.
    1782     * # 1970-01-01T00:00:00Z
    1783     * </pre>
    1784     *
    1785     * @return the comment start marker, may be {@code null}
    1786     */
    1787    public Character getCommentMarker() {
    1788        return commentMarker;
    1789    }
    1790
    1791    /**
    1792     * Gets the first character delimiting the values (typically ';', ',' or '\t').
    1793     *
    1794     * @return the first delimiter character.
    1795     * @deprecated Use {@link #getDelimiterString()}.
    1796     */
    1797    @Deprecated
    1798    public char getDelimiter() {
    1799        return delimiter.charAt(0);
    1800    }
    1801
    1802    /**
    1803     * Gets the character delimiting the values (typically ";", "," or "\t").
    1804     *
    1805     * @return the delimiter.
    1806     */
    1807    char[] getDelimiterCharArray() {
    1808        return delimiter.toCharArray();
    1809    }
    1810
    1811    /**
    1812     * Gets the character delimiting the values (typically ";", "," or "\t").
    1813     *
    1814     * @return the delimiter.
    1815     * @since 1.9.0
    1816     */
    1817    public String getDelimiterString() {
    1818        return delimiter;
    1819    }
    1820
    1821    /**
    1822     * Gets how duplicate headers are handled.
    1823     *
    1824     * @return if duplicate header values are allowed, allowed conditionally, or disallowed.
    1825     * @since 1.10.0
    1826     */
    1827    public DuplicateHeaderMode getDuplicateHeaderMode() {
    1828        return duplicateHeaderMode;
    1829    }
    1830
    1831    /**
    1832     * Gets the escape character.
    1833     *
    1834     * @return the escape character, may be {@code 0}
    1835     */
    1836    char getEscapeChar() {
    1837        return escapeCharacter != null ? escapeCharacter.charValue() : 0; // Explicit (un)boxing is intentional
    1838    }
    1839
    1840    /**
    1841     * Gets the escape character.
    1842     *
    1843     * @return the escape character, may be {@code null}
    1844     */
    1845    public Character getEscapeCharacter() {
    1846        return escapeCharacter;
    1847    }
    1848
    1849    /**
    1850     * Gets a copy of the header array.
    1851     *
    1852     * @return a copy of the header array; {@code null} if disabled, the empty array if to be read from the file
    1853     */
    1854    public String[] getHeader() {
    1855        return headers != null ? headers.clone() : null;
    1856    }
    1857
    1858    /**
    1859     * Gets a copy of the header comment array to write before the CSV data.
    1860     * <p>
    1861     * This setting is ignored by the parser.
    1862     * </p>
    1863     * <p>
    1864     * Comments are printed first, before headers.
    1865     * </p>
    1866     * <p>
    1867     * Use {@link Builder#setCommentMarker(char)} or {@link Builder#setCommentMarker(Character)} to set the comment marker written at the start of each comment
    1868     * line.
    1869     * </p>
    1870     * <p>
    1871     * If the comment marker is not set, then the header comments are ignored.
    1872     * </p>
    1873     * <p>
    1874     * For example:
    1875     * </p>
    1876     *
    1877     * <pre>
    1878     * builder.setCommentMarker('#').setHeaderComments("Generated by Apache Commons CSV", Instant.ofEpochMilli(0));
    1879     * </pre>
    1880     * <p>
    1881     * writes:
    1882     * </p>
    1883     *
    1884     * <pre>
    1885     * # Generated by Apache Commons CSV.
    1886     * # 1970-01-01T00:00:00Z
    1887     * </pre>
    1888     *
    1889     * @return a copy of the header comment array; {@code null} if disabled.
    1890     */
    1891    public String[] getHeaderComments() {
    1892        return headerComments != null ? headerComments.clone() : null;
    1893    }
    1894
    1895    /**
    1896     * Gets whether empty lines between records are ignored when parsing input.
    1897     *
    1898     * @return {@code true} if empty lines between records are ignored, {@code false} if they are turned into empty records.
    1899     */
    1900    public boolean getIgnoreEmptyLines() {
    1901        return ignoreEmptyLines;
    1902    }
    1903
    1904    /**
    1905     * Gets whether header names will be accessed ignoring case when parsing input.
    1906     *
    1907     * @return {@code true} if header names cases are ignored, {@code false} if they are case-sensitive.
    1908     * @since 1.3
    1909     */
    1910    public boolean getIgnoreHeaderCase() {
    1911        return ignoreHeaderCase;
    1912    }
    1913
    1914    /**
    1915     * Gets whether spaces around values are ignored when parsing input.
    1916     *
    1917     * @return {@code true} if spaces around values are ignored, {@code false} if they are treated as part of the value.
    1918     */
    1919    public boolean getIgnoreSurroundingSpaces() {
    1920        return ignoreSurroundingSpaces;
    1921    }
    1922
    1923    /**
    1924     * Gets whether reading end-of-file is allowed even when input is malformed, helps Excel compatibility.
    1925     *
    1926     * @return whether reading end-of-file is allowed even when input is malformed, helps Excel compatibility.
    1927     * @since 1.11.0
    1928     */
    1929    public boolean getLenientEof() {
    1930        return lenientEof;
    1931    }
    1932
    1933    /**
    1934     * Gets the maximum number of rows to process, excluding the header row.
    1935     * <p>
    1936     * Values less than or equal to 0 mean no limit.
    1937     * </p>
    1938     *
    1939     * @return The maximum number of rows to process, excluding the header row.
    1940     * @since 1.14.0
    1941     */
    1942    public long getMaxRows() {
    1943        return maxRows;
    1944    }
    1945
    1946    /**
    1947     * Gets the String to convert to and from {@code null}.
    1948     * <ul>
    1949     * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li>
    1950     * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
    1951     * </ul>
    1952     *
    1953     * @return the String to convert to and from {@code null}. No substitution occurs if {@code null}
    1954     */
    1955    public String getNullString() {
    1956        return nullString;
    1957    }
    1958
    1959    /**
    1960     * Gets the character used to encapsulate values containing special characters.
    1961     *
    1962     * @return the quoteChar character, may be {@code null}
    1963     */
    1964    public Character getQuoteCharacter() {
    1965        return quoteCharacter;
    1966    }
    1967
    1968    /**
    1969     * Gets the quote policy output fields.
    1970     *
    1971     * @return the quote policy
    1972     */
    1973    public QuoteMode getQuoteMode() {
    1974        return quoteMode;
    1975    }
    1976
    1977    /**
    1978     * Gets the record separator delimiting output records.
    1979     *
    1980     * @return the record separator
    1981     */
    1982    public String getRecordSeparator() {
    1983        return recordSeparator;
    1984    }
    1985
    1986    /**
    1987     * Gets whether to skip the header record.
    1988     *
    1989     * @return whether to skip the header record.
    1990     */
    1991    public boolean getSkipHeaderRecord() {
    1992        return skipHeaderRecord;
    1993    }
    1994
    1995    /**
    1996     * Gets whether reading trailing data is allowed in records, helps Excel compatibility.
    1997     *
    1998     * @return whether reading trailing data is allowed in records, helps Excel compatibility.
    1999     * @since 1.11.0
    2000     */
    2001    public boolean getTrailingData() {
    2002        return trailingData;
    2003    }
    2004
    2005    /**
    2006     * Gets whether to add a trailing delimiter.
    2007     *
    2008     * @return whether to add a trailing delimiter.
    2009     * @since 1.3
    2010     */
    2011    public boolean getTrailingDelimiter() {
    2012        return trailingDelimiter;
    2013    }
    2014
    2015    /**
    2016     * Gets whether to trim leading and trailing blanks. This is used by {@link #print(Object, Appendable, boolean)} Also by {CSVParser#addRecordValue(boolean)}
    2017     *
    2018     * @return whether to trim leading and trailing blanks.
    2019     */
    2020    public boolean getTrim() {
    2021        return trim;
    2022    }
    2023
    2024    @Override
    2025    public int hashCode() {
    2026        final int prime = 31;
    2027        int result = 1;
    2028        result = prime * result + Arrays.hashCode(headerComments);
    2029        result = prime * result + Arrays.hashCode(headers);
    2030        return prime * result + Objects.hash(allowMissingColumnNames, autoFlush, commentMarker, delimiter, duplicateHeaderMode, escapeCharacter,
    2031                ignoreEmptyLines, ignoreHeaderCase, ignoreSurroundingSpaces, lenientEof, nullString, quoteCharacter, quoteMode, quotedNullString,
    2032                recordSeparator, skipHeaderRecord, trailingData, trailingDelimiter, trim);
    2033    }
    2034
    2035    /**
    2036     * Tests whether comments are supported by this format.
    2037     *
    2038     * Note that the comment introducer character is only recognized at the start of a line.
    2039     *
    2040     * @return {@code true} is comments are supported, {@code false} otherwise
    2041     */
    2042    public boolean isCommentMarkerSet() {
    2043        return commentMarker != null;
    2044    }
    2045
    2046    /**
    2047     * Tests whether the next characters constitute a delimiter
    2048     *
    2049     * @param ch0             the first char (index 0).
    2050     * @param charSeq         the match char sequence
    2051     * @param startIndex      where start to match
    2052     * @param delimiter       the delimiter
    2053     * @param delimiterLength the delimiter length
    2054     * @return true if the match is successful
    2055     */
    2056    private boolean isDelimiter(final char ch0, final CharSequence charSeq, final int startIndex, final char[] delimiter, final int delimiterLength) {
    2057        if (ch0 != delimiter[0]) {
    2058            return false;
    2059        }
    2060        final int len = charSeq.length();
    2061        if (startIndex + delimiterLength > len) {
    2062            return false;
    2063        }
    2064        for (int i = 1; i < delimiterLength; i++) {
    2065            if (charSeq.charAt(startIndex + i) != delimiter[i]) {
    2066                return false;
    2067            }
    2068        }
    2069        return true;
    2070    }
    2071
    2072    /**
    2073     * Tests whether escapes are being processed.
    2074     *
    2075     * @return {@code true} if escapes are processed
    2076     */
    2077    public boolean isEscapeCharacterSet() {
    2078        return escapeCharacter != null;
    2079    }
    2080
    2081    /**
    2082     * Tests whether a null string has been defined.
    2083     *
    2084     * @return {@code true} if a nullString is defined
    2085     */
    2086    public boolean isNullStringSet() {
    2087        return nullString != null;
    2088    }
    2089
    2090    /**
    2091     * Tests whether a quoteChar has been defined.
    2092     *
    2093     * @return {@code true} if a quoteChar is defined
    2094     */
    2095    public boolean isQuoteCharacterSet() {
    2096        return quoteCharacter != null;
    2097    }
    2098
    2099    <T> IOStream<T> limit(final IOStream<T> stream) {
    2100        return useMaxRows() ? stream.limit(getMaxRows()) : stream;
    2101    }
    2102
    2103    /**
    2104     * Parses the specified content.
    2105     *
    2106     * <p>
    2107     * See also the various static parse methods on {@link CSVParser}.
    2108     * </p>
    2109     *
    2110     * @param reader the input stream
    2111     * @return a parser over a stream of {@link CSVRecord}s.
    2112     * @throws IOException  If an I/O error occurs
    2113     * @throws CSVException Thrown on invalid input.
    2114     */
    2115    public CSVParser parse(final Reader reader) throws IOException {
    2116        return CSVParser.builder().setReader(reader).setFormat(this).get();
    2117    }
    2118
    2119    /**
    2120     * Prints to the specified output.
    2121     *
    2122     * <p>
    2123     * See also {@link CSVPrinter}.
    2124     * </p>
    2125     *
    2126     * @param out the output.
    2127     * @return a printer to an output.
    2128     * @throws IOException thrown if the optional header cannot be printed.
    2129     */
    2130    public CSVPrinter print(final Appendable out) throws IOException {
    2131        return new CSVPrinter(out, this);
    2132    }
    2133
    2134    /**
    2135     * Prints to the specified {@code File} with given {@code Charset}.
    2136     *
    2137     * <p>
    2138     * See also {@link CSVPrinter}.
    2139     * </p>
    2140     *
    2141     * @param out     the output.
    2142     * @param charset A charset.
    2143     * @return a printer to an output.
    2144     * @throws IOException thrown if the optional header cannot be printed.
    2145     * @since 1.5
    2146     */
    2147    public CSVPrinter print(final File out, final Charset charset) throws IOException {
    2148        return print(out.toPath(), charset);
    2149    }
    2150
    2151    private void print(final InputStream inputStream, final Appendable out, final boolean newRecord) throws IOException {
    2152        // InputStream is never null here
    2153        // There is nothing to escape when quoting is used which is the default.
    2154        if (!newRecord) {
    2155            append(getDelimiterString(), out);
    2156        }
    2157        final boolean quoteCharacterSet = isQuoteCharacterSet();
    2158        if (quoteCharacterSet) {
    2159            append(getQuoteCharacter().charValue(), out); // Explicit (un)boxing is intentional
    2160        }
    2161        // Stream the input to the output without reading or holding the whole value in memory.
    2162        // AppendableOutputStream cannot "close" an Appendable.
    2163        try (OutputStream outputStream = new Base64OutputStream(new AppendableOutputStream<>(out))) {
    2164            IOUtils.copy(inputStream, outputStream);
    2165        }
    2166        if (quoteCharacterSet) {
    2167            append(getQuoteCharacter().charValue(), out); // Explicit (un)boxing is intentional
    2168        }
    2169    }
    2170
    2171    /**
    2172     * 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
    2173     * avoid creating CSVPrinters. Trims the value if {@link #getTrim()} is true.
    2174     *
    2175     * @param value     value to output.
    2176     * @param out       where to print the value.
    2177     * @param newRecord if this a new record.
    2178     * @throws IOException If an I/O error occurs.
    2179     * @since 1.4
    2180     */
    2181    public synchronized void print(final Object value, final Appendable out, final boolean newRecord) throws IOException {
    2182        // null values are considered empty
    2183        // Only call CharSequence.toString() if you have to, helps GC-free use cases.
    2184        CharSequence charSequence;
    2185        if (value == null) {
    2186            // https://issues.apache.org/jira/browse/CSV-203
    2187            if (null == nullString) {
    2188                charSequence = Constants.EMPTY;
    2189            } else if (QuoteMode.ALL == quoteMode) {
    2190                charSequence = quotedNullString;
    2191            } else {
    2192                charSequence = nullString;
    2193            }
    2194        } else if (value instanceof CharSequence) {
    2195            charSequence = (CharSequence) value;
    2196        } else if (value instanceof Reader) {
    2197            print((Reader) value, out, newRecord);
    2198            return;
    2199        } else if (value instanceof InputStream) {
    2200            print((InputStream) value, out, newRecord);
    2201            return;
    2202        } else {
    2203            charSequence = value.toString();
    2204        }
    2205        charSequence = getTrim() ? trim(charSequence) : charSequence;
    2206        print(value, charSequence, out, newRecord);
    2207    }
    2208
    2209    private synchronized void print(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) throws IOException {
    2210        final int offset = 0;
    2211        final int len = value.length();
    2212        if (!newRecord) {
    2213            out.append(getDelimiterString());
    2214        }
    2215        if (object == null) {
    2216            out.append(value);
    2217        } else if (isQuoteCharacterSet()) {
    2218            // The original object is needed so can check for Number
    2219            printWithQuotes(object, value, out, newRecord);
    2220        } else if (isEscapeCharacterSet()) {
    2221            printWithEscapes(value, out);
    2222        } else {
    2223            out.append(value, offset, len);
    2224        }
    2225    }
    2226
    2227    /**
    2228     * Prints to the specified {@code Path} with given {@code Charset}, returns a {@code CSVPrinter} which the caller MUST close.
    2229     *
    2230     * <p>
    2231     * See also {@link CSVPrinter}.
    2232     * </p>
    2233     *
    2234     * @param out     the output.
    2235     * @param charset A charset.
    2236     * @return a printer to an output.
    2237     * @throws IOException thrown if the optional header cannot be printed.
    2238     * @since 1.5
    2239     */
    2240    @SuppressWarnings("resource")
    2241    public CSVPrinter print(final Path out, final Charset charset) throws IOException {
    2242        return print(Files.newBufferedWriter(out, charset));
    2243    }
    2244
    2245    private void print(final Reader reader, final Appendable out, final boolean newRecord) throws IOException {
    2246        // Reader is never null here
    2247        if (!newRecord) {
    2248            append(getDelimiterString(), out);
    2249        }
    2250        if (isQuoteCharacterSet()) {
    2251            printWithQuotes(reader, out);
    2252        } else if (isEscapeCharacterSet()) {
    2253            printWithEscapes(reader, out);
    2254        } else if (out instanceof Writer) {
    2255            IOUtils.copyLarge(reader, (Writer) out);
    2256        } else {
    2257            IOUtils.copy(reader, out);
    2258        }
    2259    }
    2260
    2261    /**
    2262     * Prints to the {@link System#out}.
    2263     *
    2264     * <p>
    2265     * See also {@link CSVPrinter}.
    2266     * </p>
    2267     *
    2268     * @return a printer to {@link System#out}.
    2269     * @throws IOException thrown if the optional header cannot be printed.
    2270     * @since 1.5
    2271     */
    2272    public CSVPrinter printer() throws IOException {
    2273        return new CSVPrinter(System.out, this);
    2274    }
    2275
    2276    /**
    2277     * Outputs the trailing delimiter (if set) followed by the record separator (if set).
    2278     *
    2279     * @param appendable where to write
    2280     * @throws IOException If an I/O error occurs.
    2281     * @since 1.4
    2282     */
    2283    public synchronized void println(final Appendable appendable) throws IOException {
    2284        if (getTrailingDelimiter()) {
    2285            append(getDelimiterString(), appendable);
    2286        }
    2287        if (recordSeparator != null) {
    2288            append(recordSeparator, appendable);
    2289        }
    2290    }
    2291
    2292    /**
    2293     * Prints the given {@code values} to {@code out} as a single record of delimiter-separated values followed by the record separator.
    2294     *
    2295     * <p>
    2296     * 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
    2297     * the record, so there is no need to call {@link #println(Appendable)}.
    2298     * </p>
    2299     *
    2300     * @param appendable where to write.
    2301     * @param values     values to output.
    2302     * @throws IOException If an I/O error occurs.
    2303     * @since 1.4
    2304     */
    2305    public synchronized void printRecord(final Appendable appendable, final Object... values) throws IOException {
    2306        for (int i = 0; i < values.length; i++) {
    2307            print(values[i], appendable, i == 0);
    2308        }
    2309        println(appendable);
    2310    }
    2311
    2312    /*
    2313     * This method must only be called if escaping is enabled, otherwise can throw exceptions.
    2314     */
    2315    private void printWithEscapes(final CharSequence charSeq, final Appendable appendable) throws IOException {
    2316        int start = 0;
    2317        int pos = 0;
    2318        final int end = charSeq.length();
    2319        final char[] delimArray = getDelimiterCharArray();
    2320        final int delimLength = delimArray.length;
    2321        final char escape = getEscapeChar();
    2322        while (pos < end) {
    2323            char c = charSeq.charAt(pos);
    2324            final boolean isDelimiterStart = isDelimiter(c, charSeq, pos, delimArray, delimLength);
    2325            final boolean isCr = c == Constants.CR;
    2326            final boolean isLf = c == Constants.LF;
    2327            if (isCr || isLf || c == escape || isDelimiterStart) {
    2328                // write out segment up until this char
    2329                if (pos > start) {
    2330                    appendable.append(charSeq, start, pos);
    2331                }
    2332                if (isLf) {
    2333                    c = 'n';
    2334                } else if (isCr) {
    2335                    c = 'r';
    2336                }
    2337                escape(c, appendable);
    2338                if (isDelimiterStart) {
    2339                    for (int i = 1; i < delimLength; i++) {
    2340                        pos++;
    2341                        escape(charSeq.charAt(pos), appendable);
    2342                    }
    2343                }
    2344                start = pos + 1; // start on the current char after this one
    2345            }
    2346            pos++;
    2347        }
    2348
    2349        // write last segment
    2350        if (pos > start) {
    2351            appendable.append(charSeq, start, pos);
    2352        }
    2353    }
    2354
    2355    /*
    2356     * This method must only be called if escaping is enabled, otherwise can throw exceptions.
    2357     */
    2358    private void printWithEscapes(final Reader reader, final Appendable appendable) throws IOException {
    2359        int start = 0;
    2360        int pos = 0;
    2361        @SuppressWarnings("resource") // Temp reader on input reader.
    2362        final ExtendedBufferedReader bufferedReader = new ExtendedBufferedReader(reader);
    2363        final char[] delimArray = getDelimiterCharArray();
    2364        final int delimLength = delimArray.length;
    2365        final char escape = getEscapeChar();
    2366        final StringBuilder builder = new StringBuilder(IOUtils.DEFAULT_BUFFER_SIZE);
    2367        int c;
    2368        final char[] lookAheadBuffer = new char[delimLength - 1];
    2369        while (EOF != (c = bufferedReader.read())) {
    2370            builder.append((char) c);
    2371            Arrays.fill(lookAheadBuffer, (char) 0);
    2372            bufferedReader.peek(lookAheadBuffer);
    2373            final String test = builder.toString() + new String(lookAheadBuffer);
    2374            final boolean isDelimiterStart = isDelimiter((char) c, test, pos, delimArray, delimLength);
    2375            final boolean isCr = c == Constants.CR;
    2376            final boolean isLf = c == Constants.LF;
    2377            if (isCr || isLf || c == escape || isDelimiterStart) {
    2378                // write out segment up until this char
    2379                if (pos > start) {
    2380                    append(builder.substring(start, pos), appendable);
    2381                    builder.setLength(0);
    2382                    pos = -1;
    2383                }
    2384                if (isLf) {
    2385                    c = 'n';
    2386                } else if (isCr) {
    2387                    c = 'r';
    2388                }
    2389                escape((char) c, appendable);
    2390                if (isDelimiterStart) {
    2391                    for (int i = 1; i < delimLength; i++) {
    2392                        escape((char) bufferedReader.read(), appendable);
    2393                    }
    2394                }
    2395                start = pos + 1; // start on the current char after this one
    2396            }
    2397            pos++;
    2398        }
    2399        // write last segment
    2400        if (pos > start) {
    2401            appendable.append(builder, start, pos);
    2402        }
    2403    }
    2404
    2405    /*
    2406     * This method must only be called if quoting is enabled, otherwise will generate NPE.
    2407     * The original object is needed so can check for Number
    2408     */
    2409    private void printWithQuotes(final Object object, final CharSequence charSeq, final Appendable out, final boolean newRecord) throws IOException {
    2410        boolean quote = false;
    2411        int start = 0;
    2412        int pos = 0;
    2413        final int len = charSeq.length();
    2414        final char[] delim = getDelimiterCharArray();
    2415        final int delimLength = delim.length;
    2416        final char quoteChar = getQuoteCharacter().charValue(); // Explicit (un)boxing is intentional
    2417        // If escape char not specified, default to the quote char
    2418        // This avoids having to keep checking whether there is an escape character
    2419        // at the cost of checking against quote twice
    2420        final char escapeChar = isEscapeCharacterSet() ? getEscapeChar() : quoteChar;
    2421        QuoteMode quoteModePolicy = getQuoteMode();
    2422        if (quoteModePolicy == null) {
    2423            quoteModePolicy = QuoteMode.MINIMAL;
    2424        }
    2425        switch (quoteModePolicy) {
    2426        case ALL:
    2427        case ALL_NON_NULL:
    2428            quote = true;
    2429            break;
    2430        case NON_NUMERIC:
    2431            quote = !(object instanceof Number);
    2432            break;
    2433        case NONE:
    2434            // Use the existing escaping code
    2435            printWithEscapes(charSeq, out);
    2436            return;
    2437        case MINIMAL:
    2438            if (len <= 0) {
    2439                // Always quote an empty token that is the first
    2440                // on the line, as it may be the only thing on the
    2441                // line. If it were not quoted in that case,
    2442                // an empty line has no tokens.
    2443                if (newRecord) {
    2444                    quote = true;
    2445                }
    2446            } else {
    2447                char c = charSeq.charAt(pos);
    2448                if (c <= Constants.COMMENT) {
    2449                    // Some other chars at the start of a value caused the parser to fail, so for now
    2450                    // encapsulate if we start in anything less than '#'. We are being conservative
    2451                    // by including the default comment char too.
    2452                    quote = true;
    2453                } else {
    2454                    while (pos < len) {
    2455                        c = charSeq.charAt(pos);
    2456                        if (c == Constants.LF || c == Constants.CR || c == quoteChar || c == escapeChar || isDelimiter(c, charSeq, pos, delim, delimLength)) {
    2457                            quote = true;
    2458                            break;
    2459                        }
    2460                        pos++;
    2461                    }
    2462
    2463                    if (!quote) {
    2464                        pos = len - 1;
    2465                        c = charSeq.charAt(pos);
    2466                        // Some other chars at the end caused the parser to fail, so for now
    2467                        // encapsulate if we end in anything less than ' '
    2468                        if (isTrimChar(c)) {
    2469                            quote = true;
    2470                        }
    2471                    }
    2472                }
    2473            }
    2474            if (!quote) {
    2475                // No encapsulation needed - write out the original value
    2476                out.append(charSeq, start, len);
    2477                return;
    2478            }
    2479            break;
    2480        default:
    2481            throw new IllegalStateException("Unexpected Quote value: " + quoteModePolicy);
    2482        }
    2483        if (!quote) {
    2484            // No encapsulation needed - write out the original value
    2485            out.append(charSeq, start, len);
    2486            return;
    2487        }
    2488        // We hit something that needed encapsulation
    2489        out.append(quoteChar);
    2490        // Pick up where we left off: pos should be positioned on the first character that caused
    2491        // the need for encapsulation.
    2492        while (pos < len) {
    2493            final char c = charSeq.charAt(pos);
    2494            if (c == quoteChar || c == escapeChar) {
    2495                // write out the chunk up until this point
    2496                out.append(charSeq, start, pos);
    2497                out.append(escapeChar); // now output the escape
    2498                start = pos; // and restart with the matched char
    2499            }
    2500            pos++;
    2501        }
    2502        // Write the last segment
    2503        out.append(charSeq, start, pos);
    2504        out.append(quoteChar);
    2505    }
    2506
    2507    /**
    2508     * Always use quotes unless QuoteMode is NONE, so we do not have to look ahead.
    2509     *
    2510     * @param reader     What to print
    2511     * @param appendable Where to print it
    2512     * @throws IOException If an I/O error occurs
    2513     */
    2514    private void printWithQuotes(final Reader reader, final Appendable appendable) throws IOException {
    2515        if (getQuoteMode() == QuoteMode.NONE) {
    2516            printWithEscapes(reader, appendable);
    2517            return;
    2518        }
    2519        final char quote = getQuoteCharacter().charValue(); // Explicit (un)boxing is intentional
    2520        // (1) Append opening quote
    2521        append(quote, appendable);
    2522        // (2) Append Reader contents, doubling quotes
    2523        int c;
    2524        while (EOF != (c = reader.read())) {
    2525            append((char) c, appendable);
    2526            if (c == quote) {
    2527                append(quote, appendable);
    2528            }
    2529        }
    2530        // (3) Append closing quote
    2531        append(quote, appendable);
    2532    }
    2533
    2534    @Override
    2535    public String toString() {
    2536        final StringBuilder sb = new StringBuilder();
    2537        sb.append("Delimiter=<").append(delimiter).append('>');
    2538        if (isEscapeCharacterSet()) {
    2539            sb.append(Constants.SP);
    2540            sb.append("Escape=<").append(escapeCharacter).append('>');
    2541        }
    2542        if (isQuoteCharacterSet()) {
    2543            sb.append(Constants.SP);
    2544            sb.append("QuoteChar=<").append(quoteCharacter).append('>');
    2545        }
    2546        if (quoteMode != null) {
    2547            sb.append(Constants.SP);
    2548            sb.append("QuoteMode=<").append(quoteMode).append('>');
    2549        }
    2550        if (isCommentMarkerSet()) {
    2551            sb.append(Constants.SP);
    2552            sb.append("CommentStart=<").append(commentMarker).append('>');
    2553        }
    2554        if (isNullStringSet()) {
    2555            sb.append(Constants.SP);
    2556            sb.append("NullString=<").append(nullString).append('>');
    2557        }
    2558        if (recordSeparator != null) {
    2559            sb.append(Constants.SP);
    2560            sb.append("RecordSeparator=<").append(recordSeparator).append('>');
    2561        }
    2562        if (getIgnoreEmptyLines()) {
    2563            sb.append(" EmptyLines:ignored");
    2564        }
    2565        if (getIgnoreSurroundingSpaces()) {
    2566            sb.append(" SurroundingSpaces:ignored");
    2567        }
    2568        if (getIgnoreHeaderCase()) {
    2569            sb.append(" IgnoreHeaderCase:ignored");
    2570        }
    2571        sb.append(" SkipHeaderRecord:").append(skipHeaderRecord);
    2572        if (headerComments != null) {
    2573            sb.append(Constants.SP);
    2574            sb.append("HeaderComments:").append(Arrays.toString(headerComments));
    2575        }
    2576        if (headers != null) {
    2577            sb.append(Constants.SP);
    2578            sb.append("Header:").append(Arrays.toString(headers));
    2579        }
    2580        return sb.toString();
    2581    }
    2582
    2583    String trim(final String value) {
    2584        return getTrim() ? value.trim() : value;
    2585    }
    2586
    2587    boolean useMaxRows() {
    2588        return getMaxRows() > 0;
    2589    }
    2590
    2591    boolean useRow(final long rowNum) {
    2592        return !useMaxRows() || rowNum <= getMaxRows();
    2593    }
    2594
    2595    /**
    2596     * Verifies the validity and consistency of the attributes, and throws an {@link IllegalArgumentException} if necessary.
    2597     * <p>
    2598     * Because an instance can be used for both writing and parsing, not all conditions can be tested here. For example, allowMissingColumnNames is only used
    2599     * for parsing, so it cannot be used here.
    2600     * </p>
    2601     *
    2602     * @throws IllegalArgumentException Throw when any attribute is invalid or inconsistent with other attributes.
    2603     */
    2604    private void validate() throws IllegalArgumentException {
    2605        if (quoteCharacter != null && contains(delimiter, quoteCharacter.charValue())) { // Explicit (un)boxing is intentional
    2606            throw new IllegalArgumentException("The quoteChar character and the delimiter cannot be the same ('" + quoteCharacter + "')");
    2607        }
    2608        if (escapeCharacter != null && contains(delimiter, escapeCharacter.charValue())) { // Explicit (un)boxing is intentional
    2609            throw new IllegalArgumentException("The escape character and the delimiter cannot be the same ('" + escapeCharacter + "')");
    2610        }
    2611        if (commentMarker != null && contains(delimiter, commentMarker.charValue())) { // Explicit (un)boxing is intentional
    2612            throw new IllegalArgumentException("The comment start character and the delimiter cannot be the same ('" + commentMarker + "')");
    2613        }
    2614        if (quoteCharacter != null && quoteCharacter.equals(commentMarker)) {
    2615            throw new IllegalArgumentException("The comment start character and the quoteChar cannot be the same ('" + commentMarker + "')");
    2616        }
    2617        if (escapeCharacter != null && escapeCharacter.equals(commentMarker)) {
    2618            throw new IllegalArgumentException("The comment start and the escape character cannot be the same ('" + commentMarker + "')");
    2619        }
    2620        if (escapeCharacter == null && quoteMode == QuoteMode.NONE) {
    2621            throw new IllegalArgumentException("Quote mode set to NONE but no escape character is set");
    2622        }
    2623        // Validate headers
    2624        if (headers != null && duplicateHeaderMode != DuplicateHeaderMode.ALLOW_ALL) {
    2625            final Set<String> dupCheckSet = new HashSet<>(headers.length);
    2626            final boolean emptyDuplicatesAllowed = duplicateHeaderMode == DuplicateHeaderMode.ALLOW_EMPTY;
    2627            for (final String header : headers) {
    2628                final boolean blank = isBlank(header);
    2629                // Sanitize all empty headers to the empty string "" when checking duplicates
    2630                final boolean containsHeader = !dupCheckSet.add(blank ? "" : header);
    2631                if (containsHeader && !(blank && emptyDuplicatesAllowed)) {
    2632                    throw new IllegalArgumentException(String.format(
    2633                            "The header contains a duplicate name: \"%s\" in %s. If this is valid then use CSVFormat.Builder.setDuplicateHeaderMode().", header,
    2634                            Arrays.toString(headers)));
    2635                }
    2636            }
    2637        }
    2638    }
    2639
    2640    /**
    2641     * Builds a new {@code CSVFormat} that allows duplicate header names.
    2642     *
    2643     * @return a new {@code CSVFormat} that allows duplicate header names
    2644     * @since 1.7
    2645     * @deprecated Use {@link Builder#setAllowDuplicateHeaderNames(boolean) Builder#setAllowDuplicateHeaderNames(true)}
    2646     */
    2647    @Deprecated
    2648    public CSVFormat withAllowDuplicateHeaderNames() {
    2649        return builder().setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL).get();
    2650    }
    2651
    2652    /**
    2653     * Builds a new {@code CSVFormat} with duplicate header names behavior set to the given value.
    2654     *
    2655     * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow.
    2656     * @return a new {@code CSVFormat} with duplicate header names behavior set to the given value.
    2657     * @since 1.7
    2658     * @deprecated Use {@link Builder#setAllowDuplicateHeaderNames(boolean)}
    2659     */
    2660    @Deprecated
    2661    public CSVFormat withAllowDuplicateHeaderNames(final boolean allowDuplicateHeaderNames) {
    2662        final DuplicateHeaderMode mode = allowDuplicateHeaderNames ? DuplicateHeaderMode.ALLOW_ALL : DuplicateHeaderMode.ALLOW_EMPTY;
    2663        return builder().setDuplicateHeaderMode(mode).get();
    2664    }
    2665
    2666    /**
    2667     * Builds a new {@code CSVFormat} with the missing column names behavior of the format set to {@code true}.
    2668     *
    2669     * @return A new CSVFormat that is equal to this but with the specified missing column names behavior.
    2670     * @see Builder#setAllowMissingColumnNames(boolean)
    2671     * @since 1.1
    2672     * @deprecated Use {@link Builder#setAllowMissingColumnNames(boolean) Builder#setAllowMissingColumnNames(true)}
    2673     */
    2674    @Deprecated
    2675    public CSVFormat withAllowMissingColumnNames() {
    2676        return builder().setAllowMissingColumnNames(true).get();
    2677    }
    2678
    2679    /**
    2680     * Builds a new {@code CSVFormat} with the missing column names behavior of the format set to the given value.
    2681     *
    2682     * @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause
    2683     *                                an {@link IllegalArgumentException} to be thrown.
    2684     * @return A new CSVFormat that is equal to this but with the specified missing column names behavior.
    2685     * @deprecated Use {@link Builder#setAllowMissingColumnNames(boolean)}
    2686     */
    2687    @Deprecated
    2688    public CSVFormat withAllowMissingColumnNames(final boolean allowMissingColumnNames) {
    2689        return builder().setAllowMissingColumnNames(allowMissingColumnNames).get();
    2690    }
    2691
    2692    /**
    2693     * Builds a new {@code CSVFormat} with whether to flush on close.
    2694     *
    2695     * @param autoFlush whether to flush on close.
    2696     * @return A new CSVFormat that is equal to this but with the specified autoFlush setting.
    2697     * @since 1.6
    2698     * @deprecated Use {@link Builder#setAutoFlush(boolean)}
    2699     */
    2700    @Deprecated
    2701    public CSVFormat withAutoFlush(final boolean autoFlush) {
    2702        return builder().setAutoFlush(autoFlush).get();
    2703    }
    2704
    2705    /**
    2706     * Builds a new {@code CSVFormat} with the comment start marker of the format set to the specified character.
    2707     *
    2708     * Note that the comment start character is only recognized at the start of a line.
    2709     *
    2710     * @param commentMarker the comment start marker
    2711     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
    2712     * @throws IllegalArgumentException thrown if the specified character is a line break
    2713     * @deprecated Use {@link Builder#setCommentMarker(char)}
    2714     */
    2715    @Deprecated
    2716    public CSVFormat withCommentMarker(final char commentMarker) {
    2717        return builder().setCommentMarker(commentMarker).get();
    2718    }
    2719
    2720    /**
    2721     * Builds a new {@code CSVFormat} with the comment start marker of the format set to the specified character.
    2722     *
    2723     * Note that the comment start character is only recognized at the start of a line.
    2724     *
    2725     * @param commentMarker the comment start marker, use {@code null} to disable
    2726     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
    2727     * @throws IllegalArgumentException thrown if the specified character is a line break
    2728     * @deprecated Use {@link Builder#setCommentMarker(Character)}
    2729     */
    2730    @Deprecated
    2731    public CSVFormat withCommentMarker(final Character commentMarker) {
    2732        return builder().setCommentMarker(commentMarker).get();
    2733    }
    2734
    2735    /**
    2736     * Builds a new {@code CSVFormat} with the delimiter of the format set to the specified character.
    2737     *
    2738     * @param delimiter the delimiter character
    2739     * @return A new CSVFormat that is equal to this with the specified character as a delimiter
    2740     * @throws IllegalArgumentException thrown if the specified character is a line break
    2741     * @deprecated Use {@link Builder#setDelimiter(char)}
    2742     */
    2743    @Deprecated
    2744    public CSVFormat withDelimiter(final char delimiter) {
    2745        return builder().setDelimiter(delimiter).get();
    2746    }
    2747
    2748    /**
    2749     * Builds a new {@code CSVFormat} with the escape character of the format set to the specified character.
    2750     *
    2751     * @param escape the escape character
    2752     * @return A new CSVFormat that is equal to this but with the specified character as the escape character
    2753     * @throws IllegalArgumentException thrown if the specified character is a line break
    2754     * @deprecated Use {@link Builder#setEscape(char)}
    2755     */
    2756    @Deprecated
    2757    public CSVFormat withEscape(final char escape) {
    2758        return builder().setEscape(escape).get();
    2759    }
    2760
    2761    /**
    2762     * Builds a new {@code CSVFormat} with the escape character of the format set to the specified character.
    2763     *
    2764     * @param escape the escape character, use {@code null} to disable
    2765     * @return A new CSVFormat that is equal to this but with the specified character as the escape character
    2766     * @throws IllegalArgumentException thrown if the specified character is a line break
    2767     * @deprecated Use {@link Builder#setEscape(Character)}
    2768     */
    2769    @Deprecated
    2770    public CSVFormat withEscape(final Character escape) {
    2771        return builder().setEscape(escape).get();
    2772    }
    2773
    2774    // @formatter:off
    2775    /**
    2776     * Builds a new {@code CSVFormat} using the first record as header.
    2777     *
    2778     * <p>
    2779     * Calling this method is equivalent to calling:
    2780     * </p>
    2781     *
    2782     * <pre>
    2783     * CSVFormat format = aFormat.builder()
    2784     *                           .setHeader()
    2785     *                           .setSkipHeaderRecord(true)
    2786     *                           .get();
    2787     * </pre>
    2788     *
    2789     * @return A new CSVFormat that is equal to this but using the first record as header.
    2790     * @see Builder#setSkipHeaderRecord(boolean)
    2791     * @see Builder#setHeader(String...)
    2792     * @since 1.3
    2793     * @deprecated Use {@link Builder#setHeader(String...) Builder#setHeader()}.{@link Builder#setSkipHeaderRecord(boolean) setSkipHeaderRecord(true)}.
    2794     */
    2795    // @formatter:on
    2796    @Deprecated
    2797    public CSVFormat withFirstRecordAsHeader() {
    2798        // @formatter:off
    2799        return builder()
    2800                .setHeader()
    2801                .setSkipHeaderRecord(true)
    2802                .get();
    2803        // @formatter:on
    2804    }
    2805
    2806    /**
    2807     * Builds a new {@code CSVFormat} with the header of the format defined by the enum class.
    2808     *
    2809     * <p>
    2810     * Example:
    2811     * </p>
    2812     *
    2813     * <pre>
    2814     * public enum MyHeader {
    2815     *     Name, Email, Phone
    2816     * }
    2817     * ...
    2818     * CSVFormat format = aFormat.builder().setHeader(MyHeader.class).get();
    2819     * </pre>
    2820     * <p>
    2821     * The header is also used by the {@link CSVPrinter}.
    2822     * </p>
    2823     *
    2824     * @param headerEnum the enum defining the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise.
    2825     * @return A new CSVFormat that is equal to this but with the specified header
    2826     * @see Builder#setHeader(String...)
    2827     * @see Builder#setSkipHeaderRecord(boolean)
    2828     * @since 1.3
    2829     * @deprecated Use {@link Builder#setHeader(Class)}
    2830     */
    2831    @Deprecated
    2832    public CSVFormat withHeader(final Class<? extends Enum<?>> headerEnum) {
    2833        return builder().setHeader(headerEnum).get();
    2834    }
    2835
    2836    /**
    2837     * Builds 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
    2838     * input file with:
    2839     *
    2840     * <pre>
    2841     * CSVFormat format = aFormat.builder().setHeader().get();
    2842     * </pre>
    2843     *
    2844     * or specified manually with:
    2845     *
    2846     * <pre>
    2847     * CSVFormat format = aFormat.builder().setHeader(resultSet).get();
    2848     * </pre>
    2849     * <p>
    2850     * The header is also used by the {@link CSVPrinter}.
    2851     * </p>
    2852     *
    2853     * @param resultSet the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    2854     * @return A new CSVFormat that is equal to this but with the specified header
    2855     * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set.
    2856     * @since 1.1
    2857     * @deprecated Use {@link Builder#setHeader(ResultSet)}
    2858     */
    2859    @Deprecated
    2860    public CSVFormat withHeader(final ResultSet resultSet) throws SQLException {
    2861        return builder().setHeader(resultSet).get();
    2862    }
    2863
    2864    /**
    2865     * Builds 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
    2866     * input file with:
    2867     *
    2868     * <pre>
    2869     * CSVFormat format = aFormat.builder().setHeader().get()
    2870     * </pre>
    2871     *
    2872     * or specified manually with:
    2873     *
    2874     * <pre>
    2875     * CSVFormat format = aFormat.builder().setHeader(resultSetMetaData).get()
    2876     * </pre>
    2877     * <p>
    2878     * The header is also used by the {@link CSVPrinter}.
    2879     * </p>
    2880     *
    2881     * @param resultSetMetaData the metaData for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise.
    2882     * @return A new CSVFormat that is equal to this but with the specified header
    2883     * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set.
    2884     * @since 1.1
    2885     * @deprecated Use {@link Builder#setHeader(ResultSetMetaData)}
    2886     */
    2887    @Deprecated
    2888    public CSVFormat withHeader(final ResultSetMetaData resultSetMetaData) throws SQLException {
    2889        return builder().setHeader(resultSetMetaData).get();
    2890    }
    2891
    2892    /**
    2893     * Builds 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
    2894     * with:
    2895     *
    2896     * <pre>
    2897     * CSVFormat format = aFormat.builder().setHeader().get();
    2898     * </pre>
    2899     *
    2900     * or specified manually with:
    2901     *
    2902     * <pre>{@code
    2903     * CSVFormat format = aFormat.builder().setHeader("name", "email", "phone").get();
    2904     * }</pre>
    2905     * <p>
    2906     * The header is also used by the {@link CSVPrinter}.
    2907     * </p>
    2908     *
    2909     * @param header the header, {@code null} if disabled, empty if parsed automatically, user-specified otherwise.
    2910     * @return A new CSVFormat that is equal to this but with the specified header
    2911     * @see Builder#setSkipHeaderRecord(boolean)
    2912     * @deprecated Use {@link Builder#setHeader(String...)}
    2913     */
    2914    @Deprecated
    2915    public CSVFormat withHeader(final String... header) {
    2916        return builder().setHeader(header).get();
    2917    }
    2918
    2919    /**
    2920     * Builds 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.
    2921     * This setting is ignored by the parser.
    2922     *
    2923     * <pre>{@code
    2924     * CSVFormat format = aFormat.builder().setHeaderComments("Generated by Apache Commons CSV.", Instant.now()).get();
    2925     * }</pre>
    2926     *
    2927     * @param headerComments the headerComments which will be printed by the Printer before the actual CSV data.
    2928     * @return A new CSVFormat that is equal to this but with the specified header
    2929     * @see Builder#setSkipHeaderRecord(boolean)
    2930     * @since 1.1
    2931     * @deprecated Use {@link Builder#setHeaderComments(Object...)}
    2932     */
    2933    @Deprecated
    2934    public CSVFormat withHeaderComments(final Object... headerComments) {
    2935        return builder().setHeaderComments(headerComments).get();
    2936    }
    2937
    2938    /**
    2939     * Builds a new {@code CSVFormat} with the empty line skipping behavior of the format set to {@code true}.
    2940     *
    2941     * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior.
    2942     * @see Builder#setIgnoreEmptyLines(boolean)
    2943     * @since 1.1
    2944     * @deprecated Use {@link Builder#setIgnoreEmptyLines(boolean) Builder#setIgnoreEmptyLines(true)}
    2945     */
    2946    @Deprecated
    2947    public CSVFormat withIgnoreEmptyLines() {
    2948        return builder().setIgnoreEmptyLines(true).get();
    2949    }
    2950
    2951    /**
    2952     * Builds a new {@code CSVFormat} with the empty line skipping behavior of the format set to the given value.
    2953     *
    2954     * @param ignoreEmptyLines the empty line skipping behavior, {@code true} to ignore the empty lines between the records, {@code false} to translate empty
    2955     *                         lines to empty records.
    2956     * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior.
    2957     * @deprecated Use {@link Builder#setIgnoreEmptyLines(boolean)}
    2958     */
    2959    @Deprecated
    2960    public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
    2961        return builder().setIgnoreEmptyLines(ignoreEmptyLines).get();
    2962    }
    2963
    2964    /**
    2965     * Builds a new {@code CSVFormat} with the header ignore case behavior set to {@code true}.
    2966     *
    2967     * @return A new CSVFormat that will ignore the new case header name behavior.
    2968     * @see Builder#setIgnoreHeaderCase(boolean)
    2969     * @since 1.3
    2970     * @deprecated Use {@link Builder#setIgnoreHeaderCase(boolean) Builder#setIgnoreHeaderCase(true)}
    2971     */
    2972    @Deprecated
    2973    public CSVFormat withIgnoreHeaderCase() {
    2974        return builder().setIgnoreHeaderCase(true).get();
    2975    }
    2976
    2977    /**
    2978     * Builds a new {@code CSVFormat} with whether header names should be accessed ignoring case.
    2979     *
    2980     * @param ignoreHeaderCase the case mapping behavior, {@code true} to access name/values, {@code false} to leave the mapping as is.
    2981     * @return A new CSVFormat that will ignore case header name if specified as {@code true}
    2982     * @since 1.3
    2983     * @deprecated Use {@link Builder#setIgnoreHeaderCase(boolean)}
    2984     */
    2985    @Deprecated
    2986    public CSVFormat withIgnoreHeaderCase(final boolean ignoreHeaderCase) {
    2987        return builder().setIgnoreHeaderCase(ignoreHeaderCase).get();
    2988    }
    2989
    2990    /**
    2991     * Builds a new {@code CSVFormat} with the parser trimming behavior of the format set to {@code true}.
    2992     *
    2993     * @return A new CSVFormat that is equal to this but with the specified parser trimming behavior.
    2994     * @see Builder#setIgnoreSurroundingSpaces(boolean)
    2995     * @since 1.1
    2996     * @deprecated Use {@link Builder#setIgnoreSurroundingSpaces(boolean) Builder#setIgnoreSurroundingSpaces(true)}
    2997     */
    2998    @Deprecated
    2999    public CSVFormat withIgnoreSurroundingSpaces() {
    3000        return builder().setIgnoreSurroundingSpaces(true).get();
    3001    }
    3002
    3003    /**
    3004     * Builds a new {@code CSVFormat} with the parser trimming behavior of the format set to the given value.
    3005     *
    3006     * @param ignoreSurroundingSpaces the parser trimming behavior, {@code true} to remove the surrounding spaces, {@code false} to leave the spaces as is.
    3007     * @return A new CSVFormat that is equal to this but with the specified trimming behavior.
    3008     * @deprecated Use {@link Builder#setIgnoreSurroundingSpaces(boolean)}
    3009     */
    3010    @Deprecated
    3011    public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
    3012        return builder().setIgnoreSurroundingSpaces(ignoreSurroundingSpaces).get();
    3013    }
    3014
    3015    /**
    3016     * Builds a new {@code CSVFormat} with conversions to and from null for strings on input and output.
    3017     * <ul>
    3018     * <li><strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading records.</li>
    3019     * <li><strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
    3020     * </ul>
    3021     *
    3022     * @param nullString the String to convert to and from {@code null}. No substitution occurs if {@code null}
    3023     * @return A new CSVFormat that is equal to this but with the specified null conversion string.
    3024     * @deprecated Use {@link Builder#setNullString(String)}
    3025     */
    3026    @Deprecated
    3027    public CSVFormat withNullString(final String nullString) {
    3028        return builder().setNullString(nullString).get();
    3029    }
    3030
    3031    /**
    3032     * Builds a new {@code CSVFormat} with the quoteChar of the format set to the specified character.
    3033     *
    3034     * @param quoteChar the quote character
    3035     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
    3036     * @throws IllegalArgumentException thrown if the specified character is a line break
    3037     * @deprecated Use {@link Builder#setQuote(char)}
    3038     */
    3039    @Deprecated
    3040    public CSVFormat withQuote(final char quoteChar) {
    3041        return builder().setQuote(quoteChar).get();
    3042    }
    3043
    3044    /**
    3045     * Builds a new {@code CSVFormat} with the quoteChar of the format set to the specified character.
    3046     *
    3047     * @param quoteChar the quote character, use {@code null} to disable.
    3048     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
    3049     * @throws IllegalArgumentException thrown if the specified character is a line break
    3050     * @deprecated Use {@link Builder#setQuote(Character)}
    3051     */
    3052    @Deprecated
    3053    public CSVFormat withQuote(final Character quoteChar) {
    3054        return builder().setQuote(quoteChar).get();
    3055    }
    3056
    3057    /**
    3058     * Builds a new {@code CSVFormat} with the output quote policy of the format set to the specified value.
    3059     *
    3060     * @param quoteMode the quote policy to use for output.
    3061     * @return A new CSVFormat that is equal to this but with the specified quote policy
    3062     * @deprecated Use {@link Builder#setQuoteMode(QuoteMode)}
    3063     */
    3064    @Deprecated
    3065    public CSVFormat withQuoteMode(final QuoteMode quoteMode) {
    3066        return builder().setQuoteMode(quoteMode).get();
    3067    }
    3068
    3069    /**
    3070     * Builds a new {@code CSVFormat} with the record separator of the format set to the specified character.
    3071     *
    3072     * <p>
    3073     * <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
    3074     * "\r\n"
    3075     * </p>
    3076     *
    3077     * @param recordSeparator the record separator to use for output.
    3078     * @return A new CSVFormat that is equal to this but with the specified output record separator
    3079     * @deprecated Use {@link Builder#setRecordSeparator(char)}
    3080     */
    3081    @Deprecated
    3082    public CSVFormat withRecordSeparator(final char recordSeparator) {
    3083        return builder().setRecordSeparator(recordSeparator).get();
    3084    }
    3085
    3086    /**
    3087     * Builds a new {@code CSVFormat} with the record separator of the format set to the specified String.
    3088     *
    3089     * <p>
    3090     * <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
    3091     * "\r\n"
    3092     * </p>
    3093     *
    3094     * @param recordSeparator the record separator to use for output.
    3095     * @return A new CSVFormat that is equal to this but with the specified output record separator
    3096     * @throws IllegalArgumentException if recordSeparator is none of CR, LF or CRLF
    3097     * @deprecated Use {@link Builder#setRecordSeparator(String)}
    3098     */
    3099    @Deprecated
    3100    public CSVFormat withRecordSeparator(final String recordSeparator) {
    3101        return builder().setRecordSeparator(recordSeparator).get();
    3102    }
    3103
    3104    /**
    3105     * Builds a new {@code CSVFormat} with skipping the header record set to {@code true}.
    3106     *
    3107     * @return A new CSVFormat that is equal to this but with the specified skipHeaderRecord setting.
    3108     * @see Builder#setSkipHeaderRecord(boolean)
    3109     * @see Builder#setHeader(String...)
    3110     * @since 1.1
    3111     * @deprecated Use {@link Builder#setSkipHeaderRecord(boolean) Builder#setSkipHeaderRecord(true)}
    3112     */
    3113    @Deprecated
    3114    public CSVFormat withSkipHeaderRecord() {
    3115        return builder().setSkipHeaderRecord(true).get();
    3116    }
    3117
    3118    /**
    3119     * Builds a new {@code CSVFormat} with whether to skip the header record.
    3120     *
    3121     * @param skipHeaderRecord whether to skip the header record.
    3122     * @return A new CSVFormat that is equal to this but with the specified skipHeaderRecord setting.
    3123     * @see Builder#setHeader(String...)
    3124     * @deprecated Use {@link Builder#setSkipHeaderRecord(boolean)}
    3125     */
    3126    @Deprecated
    3127    public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
    3128        return builder().setSkipHeaderRecord(skipHeaderRecord).get();
    3129    }
    3130
    3131    /**
    3132     * Builds 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
    3133     * and LF on Linux.
    3134     *
    3135     * <p>
    3136     * <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
    3137     * "\r\n"
    3138     * </p>
    3139     *
    3140     * @return A new CSVFormat that is equal to this but with the operating system's line separator string.
    3141     * @since 1.6
    3142     * @deprecated Use {@link Builder#setRecordSeparator(String) setRecordSeparator(System.lineSeparator())}
    3143     */
    3144    @Deprecated
    3145    public CSVFormat withSystemRecordSeparator() {
    3146        return builder().setRecordSeparator(System.lineSeparator()).get();
    3147    }
    3148
    3149    /**
    3150     * Builds a new {@code CSVFormat} to add a trailing delimiter.
    3151     *
    3152     * @return A new CSVFormat that is equal to this but with the trailing delimiter setting.
    3153     * @since 1.3
    3154     * @deprecated Use {@link Builder#setTrailingDelimiter(boolean) Builder#setTrailingDelimiter(true)}
    3155     */
    3156    @Deprecated
    3157    public CSVFormat withTrailingDelimiter() {
    3158        return builder().setTrailingDelimiter(true).get();
    3159    }
    3160
    3161    /**
    3162     * Builds a new {@code CSVFormat} with whether to add a trailing delimiter.
    3163     *
    3164     * @param trailingDelimiter whether to add a trailing delimiter.
    3165     * @return A new CSVFormat that is equal to this but with the specified trailing delimiter setting.
    3166     * @since 1.3
    3167     * @deprecated Use {@link Builder#setTrailingDelimiter(boolean)}
    3168     */
    3169    @Deprecated
    3170    public CSVFormat withTrailingDelimiter(final boolean trailingDelimiter) {
    3171        return builder().setTrailingDelimiter(trailingDelimiter).get();
    3172    }
    3173
    3174    /**
    3175     * Builds a new {@code CSVFormat} to trim leading and trailing blanks. See {@link #getTrim()} for details of where this is used.
    3176     *
    3177     * @return A new CSVFormat that is equal to this but with the trim setting on.
    3178     * @since 1.3
    3179     * @deprecated Use {@link Builder#setTrim(boolean) Builder#setTrim(true)}
    3180     */
    3181    @Deprecated
    3182    public CSVFormat withTrim() {
    3183        return builder().setTrim(true).get();
    3184    }
    3185
    3186    /**
    3187     * Builds a new {@code CSVFormat} with whether to trim leading and trailing blanks. See {@link #getTrim()} for details of where this is used.
    3188     *
    3189     * @param trim whether to trim leading and trailing blanks.
    3190     * @return A new CSVFormat that is equal to this but with the specified trim setting.
    3191     * @since 1.3
    3192     * @deprecated Use {@link Builder#setTrim(boolean)}
    3193     */
    3194    @Deprecated
    3195    public CSVFormat withTrim(final boolean trim) {
    3196        return builder().setTrim(trim).get();
    3197    }
    3198
    3199}