001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.csv; 019 020import static org.apache.commons.csv.Constants.BACKSLASH; 021import static org.apache.commons.csv.Constants.COMMA; 022import static org.apache.commons.csv.Constants.COMMENT; 023import static org.apache.commons.csv.Constants.CR; 024import static org.apache.commons.csv.Constants.CRLF; 025import static org.apache.commons.csv.Constants.DOUBLE_QUOTE_CHAR; 026import static org.apache.commons.csv.Constants.EMPTY; 027import static org.apache.commons.csv.Constants.LF; 028import static org.apache.commons.csv.Constants.PIPE; 029import static org.apache.commons.csv.Constants.SP; 030import static org.apache.commons.csv.Constants.TAB; 031 032import java.io.File; 033import java.io.FileOutputStream; 034import java.io.IOException; 035import java.io.OutputStreamWriter; 036import java.io.Reader; 037import java.io.Serializable; 038import java.io.StringWriter; 039import java.io.Writer; 040import java.nio.charset.Charset; 041import java.nio.file.Files; 042import java.nio.file.Path; 043import java.sql.ResultSet; 044import java.sql.ResultSetMetaData; 045import java.sql.SQLException; 046import java.util.Arrays; 047import java.util.HashSet; 048import java.util.Objects; 049import java.util.Set; 050 051/** 052 * Specifies the format of a CSV file for parsing and writing. 053 * 054 * <h2>Using predefined formats</h2> 055 * 056 * <p> 057 * You can use one of the predefined formats: 058 * </p> 059 * 060 * <ul> 061 * <li>{@link #DEFAULT}</li> 062 * <li>{@link #EXCEL}</li> 063 * <li>{@link #INFORMIX_UNLOAD}</li> 064 * <li>{@link #INFORMIX_UNLOAD_CSV}</li> 065 * <li>{@link #MYSQL}</li> 066 * <li>{@link #RFC4180}</li> 067 * <li>{@link #ORACLE}</li> 068 * <li>{@link #POSTGRESQL_CSV}</li> 069 * <li>{@link #POSTGRESQL_TEXT}</li> 070 * <li>{@link #TDF}</li> 071 * </ul> 072 * 073 * <p> 074 * For example: 075 * </p> 076 * 077 * <pre> 078 * CSVParser parser = CSVFormat.EXCEL.parse(reader); 079 * </pre> 080 * 081 * <p> 082 * The {@link CSVParser} provides static methods to parse other input types, for example: 083 * </p> 084 * 085 * <pre> 086 * CSVParser parser = CSVParser.parse(file, StandardCharsets.US_ASCII, CSVFormat.EXCEL); 087 * </pre> 088 * 089 * <h2>Defining formats</h2> 090 * 091 * <p> 092 * You can extend a format by calling the {@code set} methods. For example: 093 * </p> 094 * 095 * <pre> 096 * CSVFormat.EXCEL.withNullString("N/A").withIgnoreSurroundingSpaces(true); 097 * </pre> 098 * 099 * <h2>Defining column names</h2> 100 * 101 * <p> 102 * To define the column names you want to use to access records, write: 103 * </p> 104 * 105 * <pre> 106 * CSVFormat.EXCEL.withHeader("Col1", "Col2", "Col3"); 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("Col1", "Col2", "Col3").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("Col1"); 151 * </pre> 152 * 153 * <p> 154 * This makes your code impervious to changes in column order in the CSV file. 155 * </p> 156 * 157 * <h2>Serialization</h2> 158 * <p> 159 * This class implements the {@link Serializable} interface with the following caveats: 160 * </p> 161 * <ul> 162 * <li>This class will no longer implement Serializable in 2.0.</li> 163 * <li>Serialization is not supported from one version to the next.</li> 164 * </ul> 165 * <p> 166 * The {@code serialVersionUID} values are: 167 * </p> 168 * <ul> 169 * <li>Version 1.10.0: {@code 2L}</li> 170 * <li>Version 1.9.0 through 1.0: {@code 1L}</li> 171 * </ul> 172 * 173 * <h2>Notes</h2> 174 * <p> 175 * This class is immutable. 176 * </p> 177 * <p> 178 * Not all settings are used for both parsing and writing. 179 * </p> 180 */ 181public final class CSVFormat implements Serializable { 182 183 /** 184 * Builds CSVFormat instances. 185 * 186 * @since 1.9.0 187 */ 188 public static class Builder { 189 190 /** 191 * Creates a new default builder. 192 * 193 * @return a copy of the builder 194 */ 195 public static Builder create() { 196 return new Builder(CSVFormat.DEFAULT); 197 } 198 199 /** 200 * Creates a new builder for the given format. 201 * 202 * @param csvFormat the source format. 203 * @return a copy of the builder 204 */ 205 public static Builder create(final CSVFormat csvFormat) { 206 return new Builder(csvFormat); 207 } 208 209 private boolean allowMissingColumnNames; 210 211 private boolean autoFlush; 212 213 private Character commentMarker; 214 215 private String delimiter; 216 217 private DuplicateHeaderMode duplicateHeaderMode; 218 219 private Character escapeCharacter; 220 221 private String[] headerComments; 222 223 private String[] headers; 224 225 private boolean ignoreEmptyLines; 226 227 private boolean ignoreHeaderCase; 228 229 private boolean ignoreSurroundingSpaces; 230 231 private String nullString; 232 233 private Character quoteCharacter; 234 235 private String quotedNullString; 236 237 private QuoteMode quoteMode; 238 239 private String recordSeparator; 240 241 private boolean skipHeaderRecord; 242 243 private boolean trailingDelimiter; 244 245 private boolean trim; 246 247 private Builder(final CSVFormat csvFormat) { 248 this.delimiter = csvFormat.delimiter; 249 this.quoteCharacter = csvFormat.quoteCharacter; 250 this.quoteMode = csvFormat.quoteMode; 251 this.commentMarker = csvFormat.commentMarker; 252 this.escapeCharacter = csvFormat.escapeCharacter; 253 this.ignoreSurroundingSpaces = csvFormat.ignoreSurroundingSpaces; 254 this.allowMissingColumnNames = csvFormat.allowMissingColumnNames; 255 this.ignoreEmptyLines = csvFormat.ignoreEmptyLines; 256 this.recordSeparator = csvFormat.recordSeparator; 257 this.nullString = csvFormat.nullString; 258 this.headerComments = csvFormat.headerComments; 259 this.headers = csvFormat.headers; 260 this.skipHeaderRecord = csvFormat.skipHeaderRecord; 261 this.ignoreHeaderCase = csvFormat.ignoreHeaderCase; 262 this.trailingDelimiter = csvFormat.trailingDelimiter; 263 this.trim = csvFormat.trim; 264 this.autoFlush = csvFormat.autoFlush; 265 this.quotedNullString = csvFormat.quotedNullString; 266 this.duplicateHeaderMode = csvFormat.duplicateHeaderMode; 267 } 268 269 /** 270 * Builds a new CSVFormat instance. 271 * 272 * @return a new CSVFormat instance. 273 */ 274 public CSVFormat build() { 275 return new CSVFormat(this); 276 } 277 278 /** 279 * Sets the duplicate header names behavior, true to allow, false to disallow. 280 * 281 * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow. 282 * @return This instance. 283 * @deprecated Use {@link #setDuplicateHeaderMode(DuplicateHeaderMode)}. 284 */ 285 @Deprecated 286 public Builder setAllowDuplicateHeaderNames(final boolean allowDuplicateHeaderNames) { 287 setDuplicateHeaderMode(allowDuplicateHeaderNames ? DuplicateHeaderMode.ALLOW_ALL : DuplicateHeaderMode.ALLOW_EMPTY); 288 return this; 289 } 290 291 /** 292 * Sets the parser missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an 293 * {@link IllegalArgumentException} to be thrown. 294 * 295 * @param allowMissingColumnNames the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to 296 * cause an {@link IllegalArgumentException} to be thrown. 297 * @return This instance. 298 */ 299 public Builder setAllowMissingColumnNames(final boolean allowMissingColumnNames) { 300 this.allowMissingColumnNames = allowMissingColumnNames; 301 return this; 302 } 303 304 /** 305 * Sets whether to flush on close. 306 * 307 * @param autoFlush whether to flush on close. 308 * @return This instance. 309 */ 310 public Builder setAutoFlush(final boolean autoFlush) { 311 this.autoFlush = autoFlush; 312 return this; 313 } 314 315 /** 316 * Sets the comment start marker, use {@code null} to disable. 317 * 318 * Note that the comment start character is only recognized at the start of a line. 319 * 320 * @param commentMarker the comment start marker, use {@code null} to disable. 321 * @return This instance. 322 * @throws IllegalArgumentException thrown if the specified character is a line break 323 */ 324 public Builder setCommentMarker(final char commentMarker) { 325 setCommentMarker(Character.valueOf(commentMarker)); 326 return this; 327 } 328 329 /** 330 * Sets the comment start marker, use {@code null} to disable. 331 * 332 * Note that the comment start character is only recognized at the start of a line. 333 * 334 * @param commentMarker the comment start marker, use {@code null} to disable. 335 * @return This instance. 336 * @throws IllegalArgumentException thrown if the specified character is a line break 337 */ 338 public Builder setCommentMarker(final Character commentMarker) { 339 if (isLineBreak(commentMarker)) { 340 throw new IllegalArgumentException("The comment start marker character cannot be a line break"); 341 } 342 this.commentMarker = commentMarker; 343 return this; 344 } 345 346 /** 347 * Sets the delimiter character. 348 * 349 * @param delimiter the delimiter character. 350 * @return This instance. 351 */ 352 public Builder setDelimiter(final char delimiter) { 353 return setDelimiter(String.valueOf(delimiter)); 354 } 355 356 /** 357 * Sets the delimiter character. 358 * 359 * @param delimiter the delimiter character. 360 * @return This instance. 361 */ 362 public Builder setDelimiter(final String delimiter) { 363 if (containsLineBreak(delimiter)) { 364 throw new IllegalArgumentException("The delimiter cannot be a line break"); 365 } 366 if (delimiter.isEmpty()) { 367 throw new IllegalArgumentException("The delimiter cannot be empty"); 368 } 369 this.delimiter = delimiter; 370 return this; 371 } 372 373 /** 374 * Sets the duplicate header names behavior. 375 * 376 * @param duplicateHeaderMode the duplicate header names behavior 377 * @return This instance. 378 * @since 1.10.0 379 */ 380 public Builder setDuplicateHeaderMode(final DuplicateHeaderMode duplicateHeaderMode) { 381 this.duplicateHeaderMode = Objects.requireNonNull(duplicateHeaderMode, "duplicateHeaderMode"); 382 return this; 383 } 384 385 /** 386 * Sets the escape character. 387 * 388 * @param escapeCharacter the escape character. 389 * @return This instance. 390 * @throws IllegalArgumentException thrown if the specified character is a line break 391 */ 392 public Builder setEscape(final char escapeCharacter) { 393 setEscape(Character.valueOf(escapeCharacter)); 394 return this; 395 } 396 397 /** 398 * Sets the escape character. 399 * 400 * @param escapeCharacter the escape character. 401 * @return This instance. 402 * @throws IllegalArgumentException thrown if the specified character is a line break 403 */ 404 public Builder setEscape(final Character escapeCharacter) { 405 if (isLineBreak(escapeCharacter)) { 406 throw new IllegalArgumentException("The escape character cannot be a line break"); 407 } 408 this.escapeCharacter = escapeCharacter; 409 return this; 410 } 411 412 /** 413 * Sets the header defined by the given {@link Enum} class. 414 * 415 * <p> 416 * Example: 417 * </p> 418 * 419 * <pre> 420 * public enum HeaderEnum { 421 * Name, Email, Phone 422 * } 423 * 424 * Builder builder = builder.setHeader(HeaderEnum.class); 425 * </pre> 426 * <p> 427 * The header is also used by the {@link CSVPrinter}. 428 * </p> 429 * 430 * @param headerEnum the enum defining the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. 431 * @return This instance. 432 */ 433 public Builder setHeader(final Class<? extends Enum<?>> headerEnum) { 434 String[] header = null; 435 if (headerEnum != null) { 436 final Enum<?>[] enumValues = headerEnum.getEnumConstants(); 437 header = new String[enumValues.length]; 438 Arrays.setAll(header, i -> enumValues[i].name()); 439 } 440 return setHeader(header); 441 } 442 443 /** 444 * Sets the header from the result set metadata. The header can either be parsed automatically from the input file with: 445 * 446 * <pre> 447 * builder.setHeader(); 448 * </pre> 449 * 450 * or specified manually with: 451 * 452 * <pre> 453 * builder.setHeader(resultSet); 454 * </pre> 455 * <p> 456 * The header is also used by the {@link CSVPrinter}. 457 * </p> 458 * 459 * @param resultSet the resultSet for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. 460 * @return This instance. 461 * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. 462 */ 463 public Builder setHeader(final ResultSet resultSet) throws SQLException { 464 return setHeader(resultSet != null ? resultSet.getMetaData() : null); 465 } 466 467 /** 468 * Sets the header from the result set metadata. The header can either be parsed automatically from the input file with: 469 * 470 * <pre> 471 * builder.setHeader(); 472 * </pre> 473 * 474 * or specified manually with: 475 * 476 * <pre> 477 * builder.setHeader(resultSetMetaData); 478 * </pre> 479 * <p> 480 * The header is also used by the {@link CSVPrinter}. 481 * </p> 482 * 483 * @param resultSetMetaData the metaData for the header, {@code null} if disabled, empty if parsed automatically, user specified otherwise. 484 * @return This instance. 485 * @throws SQLException SQLException if a database access error occurs or this method is called on a closed result set. 486 */ 487 public Builder setHeader(final ResultSetMetaData resultSetMetaData) throws SQLException { 488 String[] labels = null; 489 if (resultSetMetaData != null) { 490 final int columnCount = resultSetMetaData.getColumnCount(); 491 labels = new String[columnCount]; 492 for (int i = 0; i < columnCount; i++) { 493 labels[i] = resultSetMetaData.getColumnLabel(i + 1); 494 } 495 } 496 return setHeader(labels); 497 } 498 499 /** 500 * Sets the header to the given values. The header can either be parsed automatically from the input file with: 501 * 502 * <pre> 503 * builder.setHeader(); 504 * </pre> 505 * 506 * or specified manually with: 507 * 508 * <pre> 509 * builder.setHeader("name", "email", "phone"); 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("Generated by Apache Commons CSV.", 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("Generated by Apache Commons CSV.", 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("name", "email", "phone"); 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("Generated by Apache Commons CSV.", 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}