001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.csv;
019
020import static org.apache.commons.csv.Constants.BACKSLASH;
021import static org.apache.commons.csv.Constants.COMMA;
022import static org.apache.commons.csv.Constants.CR;
023import static org.apache.commons.csv.Constants.CRLF;
024import static org.apache.commons.csv.Constants.DOUBLE_QUOTE_CHAR;
025import static org.apache.commons.csv.Constants.LF;
026import static org.apache.commons.csv.Constants.TAB;
027
028import java.io.IOException;
029import java.io.Reader;
030import java.io.Serializable;
031import java.io.StringWriter;
032import java.util.Arrays;
033import java.util.HashSet;
034import java.util.Set;
035
036/**
037 * Specifies the format of a CSV file and parses input.
038 *
039 * <h2>Using predefined formats</h2>
040 *
041 * <p>
042 * You can use one of the predefined formats:
043 * </p>
044 *
045 * <ul>
046 *      <li>{@link #DEFAULT}</li>
047 *      <li>{@link #EXCEL}</li>
048 *      <li>{@link #MYSQL}</li>
049 *      <li>{@link #RFC4180}</li>
050 *      <li>{@link #TDF}</li>
051 * </ul>
052 *
053 * <p>
054 * For example:
055 * </p>
056 *
057 * <pre>
058 * CSVParser parser = CSVFormat.EXCEL.parse(reader);
059 * </pre>
060 *
061 * <p>
062 * The {@link CSVRecord} provides static methods to parse other input types, for example:
063 * </p>
064 *
065 * <pre>CSVParser parser = CSVFormat.parseFile(file, CSVFormat.EXCEL);</pre>
066 *
067 * <h2>Defining formats</h2>
068 *
069 * <p>
070 * You can extend a format by calling the {@code with} methods. For example:
071 * </p>
072 *
073 * <pre>
074 * CSVFormat.EXCEL
075 *   .withNullString(&quot;N/A&quot;)
076 *   .withIgnoreSurroundingSpaces(true);
077 * </pre>
078 *
079 * <h2>Defining column names</h2>
080 *
081 * <p>
082 * To define the column names you want to use to access records, write:
083 * </p>
084 *
085 * <pre>
086 * CSVFormat.EXCEL.withHeader(&quot;Col1&quot;, &quot;Col2&quot;, &quot;Col3&quot;);
087 * </pre>
088 *
089 * <p>
090 * Calling {@link #withHeader(String...)} let's you use the given names to address values in a {@link CSVRecord}, and
091 * assumes that your CSV source does not contain a first record that also defines column names.
092 *
093 * If it does, then you are overriding this metadata with your names and you should skip the first record by calling
094 * {@link #withSkipHeaderRecord(boolean)} with {@code true}.
095 * </p>
096 *
097 * <h2>Parsing</h2>
098 *
099 * <p>
100 * You can use a format directly to parse a reader. For example, to parse an Excel file with columns header, write:
101 * </p>
102 *
103 * <pre>
104 * Reader in = ...;
105 * CSVFormat.EXCEL.withHeader(&quot;Col1&quot;, &quot;Col2&quot;, &quot;Col3&quot;).parse(in);
106 * </pre>
107 *
108 * <p>
109 * For other input types, like resources, files, and URLs, use the static methods on {@link CSVParser}.
110 * </p>
111 *
112 * <h2>Referencing columns safely</h2>
113 *
114 * <p>
115 * If your source contains a header record, you can simplify your code and safely reference columns,
116 * by using {@link #withHeader(String...)} with no arguments:
117 * </p>
118 *
119 * <pre>
120 * CSVFormat.EXCEL.withHeader();
121 * </pre>
122 *
123 * <p>
124 * This causes the parser to read the first record and use its values as column names.
125 *
126 * Then, call one of the {@link CSVRecord} get method that takes a String column name argument:
127 * </p>
128 *
129 * <pre>
130 * String value = record.get(&quot;Col1&quot;);
131 * </pre>
132 *
133 * <p>
134 * This makes your code impervious to changes in column order in the CSV file.
135 * </p>
136 *
137 * <h2>Notes</h2>
138 *
139 * <p>
140 * This class is immutable.
141 * </p>
142 *
143 * @version $Id: CSVFormat.java 1592365 2014-05-04 16:08:56Z britter $
144 */
145public final class CSVFormat implements Serializable {
146
147    private static final long serialVersionUID = 1L;
148
149    private final char delimiter;
150    private final Character quoteChar; // null if quoting is disabled
151    private final Quote quotePolicy;
152    private final Character commentStart; // null if commenting is disabled
153    private final Character escape; // null if escaping is disabled
154    private final boolean ignoreSurroundingSpaces; // Should leading/trailing spaces be ignored around values?
155    private final boolean ignoreEmptyLines;
156    private final String recordSeparator; // for outputs
157    private final String nullString; // the string to be used for null values
158    private final String[] header;
159    private final boolean skipHeaderRecord;
160
161    /**
162     * Standard comma separated format, as for {@link #RFC4180} but allowing empty lines.
163     * <h3>RFC 4180:</h3>
164     * <ul>
165     * <li>withDelimiter(',')</li>
166     * <li>withQuoteChar('"')</li>
167     * <li>withRecordSeparator(CRLF)</li>
168     * </ul>
169     * <h3>Additional:</h3>
170     * <ul>
171     * <li>withIgnoreEmptyLines(true)</li>
172     * </ul>
173     */
174    public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null,
175                                                            false, true, CRLF, null, null, false);
176
177    /**
178     * Comma separated format as defined by <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
179     * <h3>RFC 4180:</h3>
180     * <ul>
181     * <li>withDelimiter(',')</li>
182     * <li>withQuoteChar('"')</li>
183     * <li>withRecordSeparator(CRLF)</li>
184     * </ul>
185     */
186    public static final CSVFormat RFC4180 = DEFAULT.withIgnoreEmptyLines(false);
187
188    /**
189     * Excel file format (using a comma as the value delimiter). Note that the actual value delimiter used by Excel is
190     * locale dependent, it might be necessary to customize this format to accommodate to your regional settings.
191     *
192     * <p>
193     * For example for parsing or generating a CSV file on a French system the following format will be used:
194     * </p>
195     *
196     * <pre>
197     * CSVFormat fmt = CSVFormat.newBuilder(EXCEL).withDelimiter(';');
198     * </pre>
199     *
200     * <p>
201     * Settings are:
202     * </p>
203     * <ul>
204     * <li>withDelimiter(',')</li>
205     * <li>withQuoteChar('"')</li>
206     * <li>withRecordSeparator(CRLF)</li>
207     * </ul>
208     * Note: this is currently the same as RFC4180
209     */
210    public static final CSVFormat EXCEL = DEFAULT.withIgnoreEmptyLines(false);
211
212    /** Tab-delimited format, with quote; leading and trailing spaces ignored. */
213    public static final CSVFormat TDF =
214            DEFAULT
215            .withDelimiter(TAB)
216            .withIgnoreSurroundingSpaces(true);
217
218    /**
219     * Default MySQL format used by the <tt>SELECT INTO OUTFILE</tt> and <tt>LOAD DATA INFILE</tt> operations. This is
220     * a tab-delimited format with a LF character as the line separator. Values are not quoted and special characters
221     * are escaped with '\'.
222     *
223     * @see <a href="http://dev.mysql.com/doc/refman/5.1/en/load-data.html">
224     *      http://dev.mysql.com/doc/refman/5.1/en/load-data.html</a>
225     */
226    public static final CSVFormat MYSQL =
227            DEFAULT
228            .withDelimiter(TAB)
229            .withEscape(BACKSLASH)
230            .withIgnoreEmptyLines(false)
231            .withQuoteChar(null)
232            .withRecordSeparator(LF);
233
234    /**
235     * Returns true if the given character is a line break character.
236     *
237     * @param c
238     *            the character to check
239     *
240     * @return true if <code>c</code> is a line break character
241     */
242    private static boolean isLineBreak(final char c) {
243        return c == LF || c == CR;
244    }
245
246    /**
247     * Returns true if the given character is a line break character.
248     *
249     * @param c
250     *            the character to check, may be null
251     *
252     * @return true if <code>c</code> is a line break character (and not null)
253     */
254    private static boolean isLineBreak(final Character c) {
255        return c != null && isLineBreak(c.charValue());
256    }
257
258    /**
259     * Creates a new CSV format with the specified delimiter.
260     *
261     * @param delimiter
262     *            the char used for value separation, must not be a line break character
263     * @return a new CSV format.
264     * @throws IllegalArgumentException if the delimiter is a line break character
265     */
266    public static CSVFormat newFormat(final char delimiter) {
267        return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, false);
268    }
269
270    /**
271     * Creates a customized CSV format.
272     *
273     * @param delimiter
274     *            the char used for value separation, must not be a line break character
275     * @param quoteChar
276     *            the Character used as value encapsulation marker, may be {@code null} to disable
277     * @param quotePolicy
278     *            the quote policy
279     * @param commentStart
280     *            the Character used for comment identification, may be {@code null} to disable
281     * @param escape
282     *            the Character used to escape special characters in values, may be {@code null} to disable
283     * @param ignoreSurroundingSpaces
284     *            <tt>true</tt> when whitespaces enclosing values should be ignored
285     * @param ignoreEmptyLines
286     *            <tt>true</tt> when the parser should skip empty lines
287     * @param recordSeparator
288     *            the line separator to use for output
289     * @param nullString
290     *            the line separator to use for output
291     * @param header
292     *            the header
293     * @param skipHeaderRecord TODO
294     * @throws IllegalArgumentException if the delimiter is a line break character
295     */
296    // package protected to give access without needing a synthetic accessor
297    CSVFormat(final char delimiter, final Character quoteChar,
298            final Quote quotePolicy, final Character commentStart,
299            final Character escape, final boolean ignoreSurroundingSpaces,
300            final boolean ignoreEmptyLines, final String recordSeparator,
301            final String nullString, final String[] header, final boolean skipHeaderRecord) {
302        if (isLineBreak(delimiter)) {
303            throw new IllegalArgumentException("The delimiter cannot be a line break");
304        }
305        this.delimiter = delimiter;
306        this.quoteChar = quoteChar;
307        this.quotePolicy = quotePolicy;
308        this.commentStart = commentStart;
309        this.escape = escape;
310        this.ignoreSurroundingSpaces = ignoreSurroundingSpaces;
311        this.ignoreEmptyLines = ignoreEmptyLines;
312        this.recordSeparator = recordSeparator;
313        this.nullString = nullString;
314        this.header = header == null ? null : header.clone();
315        this.skipHeaderRecord = skipHeaderRecord;
316    }
317
318    @Override
319    public boolean equals(final Object obj) {
320        if (this == obj) {
321            return true;
322        }
323        if (obj == null) {
324            return false;
325        }
326        if (getClass() != obj.getClass()) {
327            return false;
328        }
329
330        final CSVFormat other = (CSVFormat) obj;
331        if (delimiter != other.delimiter) {
332            return false;
333        }
334        if (quotePolicy != other.quotePolicy) {
335            return false;
336        }
337        if (quoteChar == null) {
338            if (other.quoteChar != null) {
339                return false;
340            }
341        } else if (!quoteChar.equals(other.quoteChar)) {
342            return false;
343        }
344        if (commentStart == null) {
345            if (other.commentStart != null) {
346                return false;
347            }
348        } else if (!commentStart.equals(other.commentStart)) {
349            return false;
350        }
351        if (escape == null) {
352            if (other.escape != null) {
353                return false;
354            }
355        } else if (!escape.equals(other.escape)) {
356            return false;
357        }
358        if (!Arrays.equals(header, other.header)) {
359            return false;
360        }
361        if (ignoreSurroundingSpaces != other.ignoreSurroundingSpaces) {
362            return false;
363        }
364        if (ignoreEmptyLines != other.ignoreEmptyLines) {
365            return false;
366        }
367        if (recordSeparator == null) {
368            if (other.recordSeparator != null) {
369                return false;
370            }
371        } else if (!recordSeparator.equals(other.recordSeparator)) {
372            return false;
373        }
374        return true;
375    }
376
377    /**
378     * Formats the specified values.
379     *
380     * @param values
381     *            the values to format
382     * @return the formatted values
383     */
384    public String format(final Object... values) {
385        final StringWriter out = new StringWriter();
386        try {
387            new CSVPrinter(out, this).printRecord(values);
388            return out.toString().trim();
389        } catch (final IOException e) {
390            // should not happen because a StringWriter does not do IO.
391            throw new IllegalStateException(e);
392        }
393    }
394
395    /**
396     * Returns the character marking the start of a line comment.
397     *
398     * @return the comment start marker, may be {@code null}
399     */
400    public Character getCommentStart() {
401        return commentStart;
402    }
403
404    /**
405     * Returns the character delimiting the values (typically ';', ',' or '\t').
406     *
407     * @return the delimiter character
408     */
409    public char getDelimiter() {
410        return delimiter;
411    }
412
413    /**
414     * Returns the escape character.
415     *
416     * @return the escape character, may be {@code null}
417     */
418    public Character getEscape() {
419        return escape;
420    }
421
422    /**
423     * Returns a copy of the header array.
424     *
425     * @return a copy of the header array
426     */
427    public String[] getHeader() {
428        return header != null ? header.clone() : null;
429    }
430
431    /**
432     * Specifies whether empty lines between records are ignored when parsing input.
433     *
434     * @return <tt>true</tt> if empty lines between records are ignored, <tt>false</tt> if they are turned into empty
435     *         records.
436     */
437    public boolean getIgnoreEmptyLines() {
438        return ignoreEmptyLines;
439    }
440
441    /**
442     * Specifies whether spaces around values are ignored when parsing input.
443     *
444     * @return <tt>true</tt> if spaces around values are ignored, <tt>false</tt> if they are treated as part of the
445     *         value.
446     */
447    public boolean getIgnoreSurroundingSpaces() {
448        return ignoreSurroundingSpaces;
449    }
450
451    /**
452     * Gets the String to convert to and from {@code null}.
453     * <ul>
454     * <li>
455     * <strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading
456     * records.
457     * </li>
458     * <li>
459     * <strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
460     * </ul>
461     *
462     * @return the String to convert to and from {@code null}. No substitution occurs if {@code null}
463     */
464    public String getNullString() {
465        return nullString;
466    }
467
468    /**
469     * Returns the character used to encapsulate values containing special characters.
470     *
471     * @return the quoteChar character, may be {@code null}
472     */
473    public Character getQuoteChar() {
474        return quoteChar;
475    }
476
477    /**
478     * Returns the quote policy output fields.
479     *
480     * @return the quote policy
481     */
482    public Quote getQuotePolicy() {
483        return quotePolicy;
484    }
485
486    /**
487     * Returns the line separator delimiting output records.
488     *
489     * @return the line separator
490     */
491    public String getRecordSeparator() {
492        return recordSeparator;
493    }
494
495    /**
496     * Returns whether to skip the header record.
497     *
498     * @return whether to skip the header record.
499     */
500    public boolean getSkipHeaderRecord() {
501        return skipHeaderRecord;
502    }
503
504    @Override
505    public int hashCode()
506    {
507        final int prime = 31;
508        int result = 1;
509
510        result = prime * result + delimiter;
511        result = prime * result + ((quotePolicy == null) ? 0 : quotePolicy.hashCode());
512        result = prime * result + ((quoteChar == null) ? 0 : quoteChar.hashCode());
513        result = prime * result + ((commentStart == null) ? 0 : commentStart.hashCode());
514        result = prime * result + ((escape == null) ? 0 : escape.hashCode());
515        result = prime * result + (ignoreSurroundingSpaces ? 1231 : 1237);
516        result = prime * result + (ignoreEmptyLines ? 1231 : 1237);
517        result = prime * result + ((recordSeparator == null) ? 0 : recordSeparator.hashCode());
518        result = prime * result + Arrays.hashCode(header);
519        return result;
520    }
521
522    /**
523     * Specifies whether comments are supported by this format.
524     *
525     * Note that the comment introducer character is only recognized at the start of a line.
526     *
527     * @return <tt>true</tt> is comments are supported, <tt>false</tt> otherwise
528     */
529    public boolean isCommentingEnabled() {
530        return commentStart != null;
531    }
532
533    /**
534     * Returns whether escape are being processed.
535     *
536     * @return {@code true} if escapes are processed
537     */
538    public boolean isEscaping() {
539        return escape != null;
540    }
541
542    /**
543     * Returns whether a nullString has been defined.
544     *
545     * @return {@code true} if a nullString is defined
546     */
547    public boolean isNullHandling() {
548        return nullString != null;
549    }
550
551    /**
552     * Returns whether a quoteChar has been defined.
553     *
554     * @return {@code true} if a quoteChar is defined
555     */
556    public boolean isQuoting() {
557        return quoteChar != null;
558    }
559
560    /**
561     * Parses the specified content.
562     *
563     * <p>
564     * See also the various static parse methods on {@link CSVParser}.
565     * </p>
566     *
567     * @param in
568     *            the input stream
569     * @return a parser over a stream of {@link CSVRecord}s.
570     * @throws IOException
571     *             If an I/O error occurs
572     */
573    public CSVParser parse(final Reader in) throws IOException {
574        return new CSVParser(in, this);
575    }
576
577    @Override
578    public String toString() {
579        final StringBuilder sb = new StringBuilder();
580        sb.append("Delimiter=<").append(delimiter).append('>');
581        if (isEscaping()) {
582            sb.append(' ');
583            sb.append("Escape=<").append(escape).append('>');
584        }
585        if (isQuoting()) {
586            sb.append(' ');
587            sb.append("QuoteChar=<").append(quoteChar).append('>');
588        }
589        if (isCommentingEnabled()) {
590            sb.append(' ');
591            sb.append("CommentStart=<").append(commentStart).append('>');
592        }
593        if (isNullHandling()) {
594            sb.append(' ');
595            sb.append("NullString=<").append(nullString).append('>');
596        }
597        if(recordSeparator != null) {
598            sb.append(' ');
599            sb.append("RecordSeparator=<").append(recordSeparator).append('>');
600        }
601        if (getIgnoreEmptyLines()) {
602            sb.append(" EmptyLines:ignored");
603        }
604        if (getIgnoreSurroundingSpaces()) {
605            sb.append(" SurroundingSpaces:ignored");
606        }
607        sb.append(" SkipHeaderRecord:").append(skipHeaderRecord);
608        if (header != null) {
609            sb.append(' ');
610            sb.append("Header:").append(Arrays.toString(header));
611        }
612        return sb.toString();
613    }
614
615    /**
616     * Verifies the consistency of the parameters and throws an IllegalStateException if necessary.
617     *
618     * @throws IllegalStateException
619     */
620    void validate() throws IllegalStateException {
621        if (quoteChar != null && delimiter == quoteChar.charValue()) {
622            throw new IllegalStateException(
623                    "The quoteChar character and the delimiter cannot be the same ('" + quoteChar + "')");
624        }
625
626        if (escape != null && delimiter == escape.charValue()) {
627            throw new IllegalStateException(
628                    "The escape character and the delimiter cannot be the same ('" + escape + "')");
629        }
630
631        if (commentStart != null && delimiter == commentStart.charValue()) {
632            throw new IllegalStateException(
633                    "The comment start character and the delimiter cannot be the same ('" + commentStart + "')");
634        }
635
636        if (quoteChar != null && quoteChar.equals(commentStart)) {
637            throw new IllegalStateException(
638                    "The comment start character and the quoteChar cannot be the same ('" + commentStart + "')");
639        }
640
641        if (escape != null && escape.equals(commentStart)) {
642            throw new IllegalStateException(
643                    "The comment start and the escape character cannot be the same ('" + commentStart + "')");
644        }
645
646        if (escape == null && quotePolicy == Quote.NONE) {
647            throw new IllegalStateException("No quotes mode set but no escape character is set");
648        }
649
650        if (header != null) {
651            final Set<String> set = new HashSet<String>(header.length);
652            set.addAll(Arrays.asList(header));
653            if (set.size() != header.length) {
654                throw new IllegalStateException("The header contains duplicate names: " + Arrays.toString(header));
655            }
656        }
657    }
658
659    /**
660     * Sets the comment start marker of the format to the specified character.
661     *
662     * Note that the comment start character is only recognized at the start of a line.
663     *
664     * @param commentStart
665     *            the comment start marker
666     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
667     * @throws IllegalArgumentException
668     *             thrown if the specified character is a line break
669     */
670    public CSVFormat withCommentStart(final char commentStart) {
671        return withCommentStart(Character.valueOf(commentStart));
672    }
673
674    /**
675     * Sets the comment start marker of the format to the specified character.
676     *
677     * Note that the comment start character is only recognized at the start of a line.
678     *
679     * @param commentStart
680     *            the comment start marker, use {@code null} to disable
681     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
682     * @throws IllegalArgumentException
683     *             thrown if the specified character is a line break
684     */
685    public CSVFormat withCommentStart(final Character commentStart) {
686        if (isLineBreak(commentStart)) {
687            throw new IllegalArgumentException("The comment start character cannot be a line break");
688        }
689        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
690                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
691    }
692
693    /**
694     * Sets the delimiter of the format to the specified character.
695     *
696     * @param delimiter
697     *            the delimiter character
698     * @return A new CSVFormat that is equal to this with the specified character as delimiter
699     * @throws IllegalArgumentException
700     *             thrown if the specified character is a line break
701     */
702    public CSVFormat withDelimiter(final char delimiter) {
703        if (isLineBreak(delimiter)) {
704            throw new IllegalArgumentException("The delimiter cannot be a line break");
705        }
706        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
707                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
708    }
709
710    /**
711     * Sets the escape character of the format to the specified character.
712     *
713     * @param escape
714     *            the escape character
715     * @return A new CSVFormat that is equal to his but with the specified character as the escape character
716     * @throws IllegalArgumentException
717     *             thrown if the specified character is a line break
718     */
719    public CSVFormat withEscape(final char escape) {
720        return withEscape(Character.valueOf(escape));
721    }
722
723    /**
724     * Sets the escape character of the format to the specified character.
725     *
726     * @param escape
727     *            the escape character, use {@code null} to disable
728     * @return A new CSVFormat that is equal to this but with the specified character as the escape character
729     * @throws IllegalArgumentException
730     *             thrown if the specified character is a line break
731     */
732    public CSVFormat withEscape(final Character escape) {
733        if (isLineBreak(escape)) {
734            throw new IllegalArgumentException("The escape character cannot be a line break");
735        }
736        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
737                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
738    }
739
740    /**
741     * Sets the header of the format. The header can either be parsed automatically from the input file with:
742     *
743     * <pre>
744     * CSVFormat format = aformat.withHeader();</pre>
745     *
746     * or specified manually with:
747     *
748     * <pre>
749     * CSVFormat format = aformat.withHeader(&quot;name&quot;, &quot;email&quot;, &quot;phone&quot;);</pre>
750     *
751     * @param header
752     *            the header, <tt>null</tt> if disabled, empty if parsed automatically, user specified otherwise.
753     *
754     * @return A new CSVFormat that is equal to this but with the specified header
755     * @see #withSkipHeaderRecord(boolean)
756     */
757    public CSVFormat withHeader(final String... header) {
758        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
759                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
760    }
761
762    /**
763     * Sets the empty line skipping behavior of the format.
764     *
765     * @param ignoreEmptyLines
766     *            the empty line skipping behavior, <tt>true</tt> to ignore the empty lines between the records,
767     *            <tt>false</tt> to translate empty lines to empty records.
768     * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior.
769     */
770    public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
771        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
772                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
773    }
774
775    /**
776     * Sets the trimming behavior of the format.
777     *
778     * @param ignoreSurroundingSpaces
779     *            the trimming behavior, <tt>true</tt> to remove the surrounding spaces, <tt>false</tt> to leave the
780     *            spaces as is.
781     * @return A new CSVFormat that is equal to this but with the specified trimming behavior.
782     */
783    public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
784        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
785                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
786    }
787
788    /**
789     * Performs conversions to and from null for strings on input and output.
790     * <ul>
791     * <li>
792     * <strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading
793     * records.</li>
794     * <li>
795     * <strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
796     * </ul>
797     *
798     * @param nullString
799     *            the String to convert to and from {@code null}. No substitution occurs if {@code null}
800     *
801     * @return A new CSVFormat that is equal to this but with the specified null conversion string.
802     */
803    public CSVFormat withNullString(final String nullString) {
804        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
805                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
806    }
807
808    /**
809     * Sets the quoteChar of the format to the specified character.
810     *
811     * @param quoteChar
812     *            the quoteChar character
813     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
814     * @throws IllegalArgumentException
815     *             thrown if the specified character is a line break
816     */
817    public CSVFormat withQuoteChar(final char quoteChar) {
818        return withQuoteChar(Character.valueOf(quoteChar));
819    }
820
821    /**
822     * Sets the quoteChar of the format to the specified character.
823     *
824     * @param quoteChar
825     *            the quoteChar character, use {@code null} to disable
826     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
827     * @throws IllegalArgumentException
828     *             thrown if the specified character is a line break
829     */
830    public CSVFormat withQuoteChar(final Character quoteChar) {
831        if (isLineBreak(quoteChar)) {
832            throw new IllegalArgumentException("The quoteChar cannot be a line break");
833        }
834        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
835                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
836    }
837
838    /**
839     * Sets the output quote policy of the format to the specified value.
840     *
841     * @param quotePolicy
842     *            the quote policy to use for output.
843     *
844     * @return A new CSVFormat that is equal to this but with the specified quote policy
845     */
846    public CSVFormat withQuotePolicy(final Quote quotePolicy) {
847        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
848                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
849    }
850
851    /**
852     * Sets the record separator of the format to the specified character.
853     *
854     * @param recordSeparator
855     *            the record separator to use for output.
856     *
857     * @return A new CSVFormat that is equal to this but with the the specified output record separator
858     */
859    public CSVFormat withRecordSeparator(final char recordSeparator) {
860        return withRecordSeparator(String.valueOf(recordSeparator));
861    }
862
863    /**
864     * Sets the record separator of the format to the specified String.
865     *
866     * @param recordSeparator
867     *            the record separator to use for output.
868     *
869     * @return A new CSVFormat that is equal to this but with the the specified output record separator
870     */
871    public CSVFormat withRecordSeparator(final String recordSeparator) {
872        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
873                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
874    }
875
876    /**
877     * Sets whether to skip the header record.
878     *
879     * @param skipHeaderRecord
880     *            whether to skip the header record.
881     *
882     * @return A new CSVFormat that is equal to this but with the the specified skipHeaderRecord setting.
883     * @see #withHeader(String...)
884     */
885    public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
886        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
887                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
888    }
889}