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