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