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 * <h4>Using predefined formats</h4>
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 * <h4>Defining formats</h4>
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 * <h4>Defining column names</h4>
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 * <h4>Parsing</h4>
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 * <h4>Referencing columns safely</h4>
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 * <h4>Notes</h4>
138 *
139 * <p>
140 * This class is immutable.
141 * </p>
142 *
143 * @version $Id: CSVFormat.java 1559908 2014-01-21 02:44:30Z ggregory $
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     * <p/>
192     * For example for parsing or generating a CSV file on a French system the following format will be used:
193     *
194     * <pre>
195     * CSVFormat fmt = CSVFormat.newBuilder(EXCEL).withDelimiter(';');
196     * </pre>
197     * Settings are:
198     * <ul>
199     * <li>withDelimiter(',')</li>
200     * <li>withQuoteChar('"')</li>
201     * <li>withRecordSeparator(CRLF)</li>
202     * </ul>
203     * Note: this is currently the same as RFC4180
204     */
205    public static final CSVFormat EXCEL = DEFAULT.withIgnoreEmptyLines(false);
206
207    /** Tab-delimited format, with quote; leading and trailing spaces ignored. */
208    public static final CSVFormat TDF =
209            DEFAULT
210            .withDelimiter(TAB)
211            .withIgnoreSurroundingSpaces(true);
212
213    /**
214     * Default MySQL format used by the <tt>SELECT INTO OUTFILE</tt> and <tt>LOAD DATA INFILE</tt> operations. This is
215     * a tab-delimited format with a LF character as the line separator. Values are not quoted and special characters
216     * are escaped with '\'.
217     *
218     * @see <a href="http://dev.mysql.com/doc/refman/5.1/en/load-data.html">
219     *      http://dev.mysql.com/doc/refman/5.1/en/load-data.html</a>
220     */
221    public static final CSVFormat MYSQL =
222            DEFAULT
223            .withDelimiter(TAB)
224            .withEscape(BACKSLASH)
225            .withIgnoreEmptyLines(false)
226            .withQuoteChar(null)
227            .withRecordSeparator(LF);
228
229    /**
230     * Returns true if the given character is a line break character.
231     *
232     * @param c
233     *            the character to check
234     *
235     * @return true if <code>c</code> is a line break character
236     */
237    private static boolean isLineBreak(final char c) {
238        return c == LF || c == CR;
239    }
240
241    /**
242     * Returns true if the given character is a line break character.
243     *
244     * @param c
245     *            the character to check, may be null
246     *
247     * @return true if <code>c</code> is a line break character (and not null)
248     */
249    private static boolean isLineBreak(final Character c) {
250        return c != null && isLineBreak(c.charValue());
251    }
252
253    /**
254     * Creates a new CSV format with the specified delimiter.
255     *
256     * @param delimiter
257     *            the char used for value separation, must not be a line break character
258     * @return a new CSV format.
259     * @throws IllegalArgumentException if the delimiter is a line break character
260     */
261    public static CSVFormat newFormat(final char delimiter) {
262        return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, false);
263    }
264
265    /**
266     * Creates a customized CSV format.
267     *
268     * @param delimiter
269     *            the char used for value separation, must not be a line break character
270     * @param quoteChar
271     *            the Character used as value encapsulation marker, may be {@code null} to disable
272     * @param quotePolicy
273     *            the quote policy
274     * @param commentStart
275     *            the Character used for comment identification, may be {@code null} to disable
276     * @param escape
277     *            the Character used to escape special characters in values, may be {@code null} to disable
278     * @param ignoreSurroundingSpaces
279     *            <tt>true</tt> when whitespaces enclosing values should be ignored
280     * @param ignoreEmptyLines
281     *            <tt>true</tt> when the parser should skip empty lines
282     * @param recordSeparator
283     *            the line separator to use for output
284     * @param nullString
285     *            the line separator to use for output
286     * @param header
287     *            the header
288     * @param skipHeaderRecord TODO
289     * @throws IllegalArgumentException if the delimiter is a line break character
290     */
291    // package protected to give access without needing a synthetic accessor
292    CSVFormat(final char delimiter, final Character quoteChar,
293            final Quote quotePolicy, final Character commentStart,
294            final Character escape, final boolean ignoreSurroundingSpaces,
295            final boolean ignoreEmptyLines, final String recordSeparator,
296            final String nullString, final String[] header, final boolean skipHeaderRecord) {
297        if (isLineBreak(delimiter)) {
298            throw new IllegalArgumentException("The delimiter cannot be a line break");
299        }
300        this.delimiter = delimiter;
301        this.quoteChar = quoteChar;
302        this.quotePolicy = quotePolicy;
303        this.commentStart = commentStart;
304        this.escape = escape;
305        this.ignoreSurroundingSpaces = ignoreSurroundingSpaces;
306        this.ignoreEmptyLines = ignoreEmptyLines;
307        this.recordSeparator = recordSeparator;
308        this.nullString = nullString;
309        this.header = header == null ? null : header.clone();
310        this.skipHeaderRecord = skipHeaderRecord;
311    }
312
313    @Override
314    public boolean equals(final Object obj) {
315        if (this == obj) {
316            return true;
317        }
318        if (obj == null) {
319            return false;
320        }
321        if (getClass() != obj.getClass()) {
322            return false;
323        }
324
325        final CSVFormat other = (CSVFormat) obj;
326        if (delimiter != other.delimiter) {
327            return false;
328        }
329        if (quotePolicy != other.quotePolicy) {
330            return false;
331        }
332        if (quoteChar == null) {
333            if (other.quoteChar != null) {
334                return false;
335            }
336        } else if (!quoteChar.equals(other.quoteChar)) {
337            return false;
338        }
339        if (commentStart == null) {
340            if (other.commentStart != null) {
341                return false;
342            }
343        } else if (!commentStart.equals(other.commentStart)) {
344            return false;
345        }
346        if (escape == null) {
347            if (other.escape != null) {
348                return false;
349            }
350        } else if (!escape.equals(other.escape)) {
351            return false;
352        }
353        if (!Arrays.equals(header, other.header)) {
354            return false;
355        }
356        if (ignoreSurroundingSpaces != other.ignoreSurroundingSpaces) {
357            return false;
358        }
359        if (ignoreEmptyLines != other.ignoreEmptyLines) {
360            return false;
361        }
362        if (recordSeparator == null) {
363            if (other.recordSeparator != null) {
364                return false;
365            }
366        } else if (!recordSeparator.equals(other.recordSeparator)) {
367            return false;
368        }
369        return true;
370    }
371
372    /**
373     * Formats the specified values.
374     *
375     * @param values
376     *            the values to format
377     * @return the formatted values
378     */
379    public String format(final Object... values) {
380        final StringWriter out = new StringWriter();
381        try {
382            new CSVPrinter(out, this).printRecord(values);
383            return out.toString().trim();
384        } catch (final IOException e) {
385            // should not happen because a StringWriter does not do IO.
386            throw new IllegalStateException(e);
387        }
388    }
389
390    /**
391     * Returns the character marking the start of a line comment.
392     *
393     * @return the comment start marker, may be {@code null}
394     */
395    public Character getCommentStart() {
396        return commentStart;
397    }
398
399    /**
400     * Returns the character delimiting the values (typically ';', ',' or '\t').
401     *
402     * @return the delimiter character
403     */
404    public char getDelimiter() {
405        return delimiter;
406    }
407
408    /**
409     * Returns the escape character.
410     *
411     * @return the escape character, may be {@code null}
412     */
413    public Character getEscape() {
414        return escape;
415    }
416
417    /**
418     * Returns a copy of the header array.
419     *
420     * @return a copy of the header array
421     */
422    public String[] getHeader() {
423        return header != null ? header.clone() : null;
424    }
425
426    /**
427     * Specifies whether empty lines between records are ignored when parsing input.
428     *
429     * @return <tt>true</tt> if empty lines between records are ignored, <tt>false</tt> if they are turned into empty
430     *         records.
431     */
432    public boolean getIgnoreEmptyLines() {
433        return ignoreEmptyLines;
434    }
435
436    /**
437     * Specifies whether spaces around values are ignored when parsing input.
438     *
439     * @return <tt>true</tt> if spaces around values are ignored, <tt>false</tt> if they are treated as part of the
440     *         value.
441     */
442    public boolean getIgnoreSurroundingSpaces() {
443        return ignoreSurroundingSpaces;
444    }
445
446    /**
447     * Gets the String to convert to and from {@code null}.
448     * <ul>
449     * <li>
450     * <strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading
451     * records.
452     * </li>
453     * <li>
454     * <strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
455     * </ul>
456     *
457     * @return the String to convert to and from {@code null}. No substitution occurs if {@code null}
458     */
459    public String getNullString() {
460        return nullString;
461    }
462
463    /**
464     * Returns the character used to encapsulate values containing special characters.
465     *
466     * @return the quoteChar character, may be {@code null}
467     */
468    public Character getQuoteChar() {
469        return quoteChar;
470    }
471
472    /**
473     * Returns the quote policy output fields.
474     *
475     * @return the quote policy
476     */
477    public Quote getQuotePolicy() {
478        return quotePolicy;
479    }
480
481    /**
482     * Returns the line separator delimiting output records.
483     *
484     * @return the line separator
485     */
486    public String getRecordSeparator() {
487        return recordSeparator;
488    }
489
490    /**
491     * Returns whether to skip the header record.
492     *
493     * @return whether to skip the header record.
494     */
495    public boolean getSkipHeaderRecord() {
496        return skipHeaderRecord;
497    }
498
499    @Override
500    public int hashCode()
501    {
502        final int prime = 31;
503        int result = 1;
504
505        result = prime * result + delimiter;
506        result = prime * result + ((quotePolicy == null) ? 0 : quotePolicy.hashCode());
507        result = prime * result + ((quoteChar == null) ? 0 : quoteChar.hashCode());
508        result = prime * result + ((commentStart == null) ? 0 : commentStart.hashCode());
509        result = prime * result + ((escape == null) ? 0 : escape.hashCode());
510        result = prime * result + (ignoreSurroundingSpaces ? 1231 : 1237);
511        result = prime * result + (ignoreEmptyLines ? 1231 : 1237);
512        result = prime * result + ((recordSeparator == null) ? 0 : recordSeparator.hashCode());
513        result = prime * result + Arrays.hashCode(header);
514        return result;
515    }
516
517    /**
518     * Specifies whether comments are supported by this format.
519     *
520     * Note that the comment introducer character is only recognized at the start of a line.
521     *
522     * @return <tt>true</tt> is comments are supported, <tt>false</tt> otherwise
523     */
524    public boolean isCommentingEnabled() {
525        return commentStart != null;
526    }
527
528    /**
529     * Returns whether escape are being processed.
530     *
531     * @return {@code true} if escapes are processed
532     */
533    public boolean isEscaping() {
534        return escape != null;
535    }
536
537    /**
538     * Returns whether a nullString has been defined.
539     *
540     * @return {@code true} if a nullString is defined
541     */
542    public boolean isNullHandling() {
543        return nullString != null;
544    }
545
546    /**
547     * Returns whether a quoteChar has been defined.
548     *
549     * @return {@code true} if a quoteChar is defined
550     */
551    public boolean isQuoting() {
552        return quoteChar != null;
553    }
554
555    /**
556     * Parses the specified content.
557     *
558     * <p>
559     * See also the various static parse methods on {@link CSVParser}.
560     * </p>
561     *
562     * @param in
563     *            the input stream
564     * @return a parser over a stream of {@link CSVRecord}s.
565     * @throws IOException
566     *             If an I/O error occurs
567     */
568    public CSVParser parse(final Reader in) throws IOException {
569        return new CSVParser(in, this);
570    }
571
572    @Override
573    public String toString() {
574        final StringBuilder sb = new StringBuilder();
575        sb.append("Delimiter=<").append(delimiter).append('>');
576        if (isEscaping()) {
577            sb.append(' ');
578            sb.append("Escape=<").append(escape).append('>');
579        }
580        if (isQuoting()) {
581            sb.append(' ');
582            sb.append("QuoteChar=<").append(quoteChar).append('>');
583        }
584        if (isCommentingEnabled()) {
585            sb.append(' ');
586            sb.append("CommentStart=<").append(commentStart).append('>');
587        }
588        if (isNullHandling()) {
589            sb.append(' ');
590            sb.append("NullString=<").append(nullString).append('>');
591        }
592        if(recordSeparator != null) {
593            sb.append(' ');
594            sb.append("RecordSeparator=<").append(recordSeparator).append('>');
595        }
596        if (getIgnoreEmptyLines()) {
597            sb.append(" EmptyLines:ignored");
598        }
599        if (getIgnoreSurroundingSpaces()) {
600            sb.append(" SurroundingSpaces:ignored");
601        }
602        sb.append(" SkipHeaderRecord:").append(skipHeaderRecord);
603        if (header != null) {
604            sb.append(' ');
605            sb.append("Header:").append(Arrays.toString(header));
606        }
607        return sb.toString();
608    }
609
610    /**
611     * Verifies the consistency of the parameters and throws an IllegalStateException if necessary.
612     *
613     * @throws IllegalStateException
614     */
615    void validate() throws IllegalStateException {
616        if (quoteChar != null && delimiter == quoteChar.charValue()) {
617            throw new IllegalStateException(
618                    "The quoteChar character and the delimiter cannot be the same ('" + quoteChar + "')");
619        }
620
621        if (escape != null && delimiter == escape.charValue()) {
622            throw new IllegalStateException(
623                    "The escape character and the delimiter cannot be the same ('" + escape + "')");
624        }
625
626        if (commentStart != null && delimiter == commentStart.charValue()) {
627            throw new IllegalStateException(
628                    "The comment start character and the delimiter cannot be the same ('" + commentStart + "')");
629        }
630
631        if (quoteChar != null && quoteChar.equals(commentStart)) {
632            throw new IllegalStateException(
633                    "The comment start character and the quoteChar cannot be the same ('" + commentStart + "')");
634        }
635
636        if (escape != null && escape.equals(commentStart)) {
637            throw new IllegalStateException(
638                    "The comment start and the escape character cannot be the same ('" + commentStart + "')");
639        }
640
641        if (escape == null && quotePolicy == Quote.NONE) {
642            throw new IllegalStateException("No quotes mode set but no escape character is set");
643        }
644
645        if (header != null) {
646            final Set<String> set = new HashSet<String>(header.length);
647            set.addAll(Arrays.asList(header));
648            if (set.size() != header.length) {
649                throw new IllegalStateException("The header contains duplicate names: " + Arrays.toString(header));
650            }
651        }
652    }
653
654    /**
655     * Sets the comment start marker of the format to the specified character.
656     *
657     * Note that the comment start character is only recognized at the start of a line.
658     *
659     * @param commentStart
660     *            the comment start marker
661     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
662     * @throws IllegalArgumentException
663     *             thrown if the specified character is a line break
664     */
665    public CSVFormat withCommentStart(final char commentStart) {
666        return withCommentStart(Character.valueOf(commentStart));
667    }
668
669    /**
670     * Sets the comment start marker of the format to the specified character.
671     *
672     * Note that the comment start character is only recognized at the start of a line.
673     *
674     * @param commentStart
675     *            the comment start marker, use {@code null} to disable
676     * @return A new CSVFormat that is equal to this one but with the specified character as the comment start marker
677     * @throws IllegalArgumentException
678     *             thrown if the specified character is a line break
679     */
680    public CSVFormat withCommentStart(final Character commentStart) {
681        if (isLineBreak(commentStart)) {
682            throw new IllegalArgumentException("The comment start character cannot be a line break");
683        }
684        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
685                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
686    }
687
688    /**
689     * Sets the delimiter of the format to the specified character.
690     *
691     * @param delimiter
692     *            the delimiter character
693     * @return A new CSVFormat that is equal to this with the specified character as delimiter
694     * @throws IllegalArgumentException
695     *             thrown if the specified character is a line break
696     */
697    public CSVFormat withDelimiter(final char delimiter) {
698        if (isLineBreak(delimiter)) {
699            throw new IllegalArgumentException("The delimiter cannot be a line break");
700        }
701        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
702                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
703    }
704
705    /**
706     * Sets the escape character of the format to the specified character.
707     *
708     * @param escape
709     *            the escape character
710     * @return A new CSVFormat that is equal to his but with the specified character as the escape character
711     * @throws IllegalArgumentException
712     *             thrown if the specified character is a line break
713     */
714    public CSVFormat withEscape(final char escape) {
715        return withEscape(Character.valueOf(escape));
716    }
717
718    /**
719     * Sets the escape character of the format to the specified character.
720     *
721     * @param escape
722     *            the escape character, use {@code null} to disable
723     * @return A new CSVFormat that is equal to this but with the specified character as the escape character
724     * @throws IllegalArgumentException
725     *             thrown if the specified character is a line break
726     */
727    public CSVFormat withEscape(final Character escape) {
728        if (isLineBreak(escape)) {
729            throw new IllegalArgumentException("The escape character cannot be a line break");
730        }
731        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
732                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
733    }
734
735    /**
736     * Sets the header of the format. The header can either be parsed automatically from the input file with:
737     *
738     * <pre>
739     * CSVFormat format = aformat.withHeader();</pre>
740     *
741     * or specified manually with:
742     *
743     * <pre>
744     * CSVFormat format = aformat.withHeader(&quot;name&quot;, &quot;email&quot;, &quot;phone&quot;);</pre>
745     *
746     * @param header
747     *            the header, <tt>null</tt> if disabled, empty if parsed automatically, user specified otherwise.
748     *
749     * @return A new CSVFormat that is equal to this but with the specified header
750     * @see #withSkipHeaderRecord(boolean)
751     */
752    public CSVFormat withHeader(final String... header) {
753        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
754                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
755    }
756
757    /**
758     * Sets the empty line skipping behavior of the format.
759     *
760     * @param ignoreEmptyLines
761     *            the empty line skipping behavior, <tt>true</tt> to ignore the empty lines between the records,
762     *            <tt>false</tt> to translate empty lines to empty records.
763     * @return A new CSVFormat that is equal to this but with the specified empty line skipping behavior.
764     */
765    public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
766        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
767                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
768    }
769
770    /**
771     * Sets the trimming behavior of the format.
772     *
773     * @param ignoreSurroundingSpaces
774     *            the trimming behavior, <tt>true</tt> to remove the surrounding spaces, <tt>false</tt> to leave the
775     *            spaces as is.
776     * @return A new CSVFormat that is equal to this but with the specified trimming behavior.
777     */
778    public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
779        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
780                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
781    }
782
783    /**
784     * Performs conversions to and from null for strings on input and output.
785     * <ul>
786     * <li>
787     * <strong>Reading:</strong> Converts strings equal to the given {@code nullString} to {@code null} when reading
788     * records.</li>
789     * <li>
790     * <strong>Writing:</strong> Writes {@code null} as the given {@code nullString} when writing records.</li>
791     * </ul>
792     *
793     * @param nullString
794     *            the String to convert to and from {@code null}. No substitution occurs if {@code null}
795     *
796     * @return A new CSVFormat that is equal to this but with the specified null conversion string.
797     */
798    public CSVFormat withNullString(final String nullString) {
799        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
800                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
801    }
802
803    /**
804     * Sets the quoteChar of the format to the specified character.
805     *
806     * @param quoteChar
807     *            the quoteChar character
808     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
809     * @throws IllegalArgumentException
810     *             thrown if the specified character is a line break
811     */
812    public CSVFormat withQuoteChar(final char quoteChar) {
813        return withQuoteChar(Character.valueOf(quoteChar));
814    }
815
816    /**
817     * Sets the quoteChar of the format to the specified character.
818     *
819     * @param quoteChar
820     *            the quoteChar character, use {@code null} to disable
821     * @return A new CSVFormat that is equal to this but with the specified character as quoteChar
822     * @throws IllegalArgumentException
823     *             thrown if the specified character is a line break
824     */
825    public CSVFormat withQuoteChar(final Character quoteChar) {
826        if (isLineBreak(quoteChar)) {
827            throw new IllegalArgumentException("The quoteChar cannot be a line break");
828        }
829        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
830                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
831    }
832
833    /**
834     * Sets the output quote policy of the format to the specified value.
835     *
836     * @param quotePolicy
837     *            the quote policy to use for output.
838     *
839     * @return A new CSVFormat that is equal to this but with the specified quote policy
840     */
841    public CSVFormat withQuotePolicy(final Quote quotePolicy) {
842        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
843                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
844    }
845
846    /**
847     * Sets the record separator of the format to the specified character.
848     *
849     * @param recordSeparator
850     *            the record separator to use for output.
851     *
852     * @return A new CSVFormat that is equal to this but with the the specified output record separator
853     */
854    public CSVFormat withRecordSeparator(final char recordSeparator) {
855        return withRecordSeparator(String.valueOf(recordSeparator));
856    }
857
858    /**
859     * Sets the record separator of the format to the specified String.
860     *
861     * @param recordSeparator
862     *            the record separator to use for output.
863     *
864     * @return A new CSVFormat that is equal to this but with the the specified output record separator
865     */
866    public CSVFormat withRecordSeparator(final String recordSeparator) {
867        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
868                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
869    }
870
871    /**
872     * Sets whether to skip the header record.
873     *
874     * @param skipHeaderRecord
875     *            whether to skip the header record.
876     *
877     * @return A new CSVFormat that is equal to this but with the the specified skipHeaderRecord setting.
878     * @see #withHeader(String...)
879     */
880    public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
881        return new CSVFormat(delimiter, quoteChar, quotePolicy, commentStart, escape,
882                ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, header, skipHeaderRecord);
883    }
884}