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