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