1717
1818package org .apache .commons .csv ;
1919
20- import static org .apache .commons .csv .Constants .BACKSLASH ;
21- import static org .apache .commons .csv .Constants .COMMA ;
22- import static org .apache .commons .csv .Constants .COMMENT ;
23- import static org .apache .commons .csv .Constants .CR ;
24- import static org .apache .commons .csv .Constants .CRLF ;
25- import static org .apache .commons .csv .Constants .DOUBLE_QUOTE_CHAR ;
26- import static org .apache .commons .csv .Constants .EMPTY ;
27- import static org .apache .commons .csv .Constants .LF ;
28- import static org .apache .commons .csv .Constants .PIPE ;
29- import static org .apache .commons .csv .Constants .SP ;
30- import static org .apache .commons .csv .Constants .TAB ;
31-
3220import java .io .File ;
3321import java .io .FileOutputStream ;
3422import java .io .IOException ;
4836import java .util .Objects ;
4937import java .util .Set ;
5038
39+ import static org .apache .commons .csv .Constants .BACKSLASH ;
40+ import static org .apache .commons .csv .Constants .COMMA ;
41+ import static org .apache .commons .csv .Constants .COMMENT ;
42+ import static org .apache .commons .csv .Constants .CR ;
43+ import static org .apache .commons .csv .Constants .CRLF ;
44+ import static org .apache .commons .csv .Constants .DOUBLE_QUOTE_CHAR ;
45+ import static org .apache .commons .csv .Constants .EMPTY ;
46+ import static org .apache .commons .csv .Constants .LF ;
47+ import static org .apache .commons .csv .Constants .PIPE ;
48+ import static org .apache .commons .csv .Constants .SP ;
49+ import static org .apache .commons .csv .Constants .TAB ;
50+
5151/**
5252 * Specifies the format of a CSV file and parses input.
5353 *
@@ -188,8 +188,6 @@ public static Builder create(final CSVFormat csvFormat) {
188188 return new Builder (csvFormat );
189189 }
190190
191- private boolean allowDuplicateHeaderNames ;
192-
193191 private boolean allowMissingColumnNames ;
194192
195193 private boolean autoFlush ;
@@ -198,6 +196,8 @@ public static Builder create(final CSVFormat csvFormat) {
198196
199197 private String delimiter ;
200198
199+ private DuplicateHeaderMode duplicateHeaderMode ;
200+
201201 private Character escapeCharacter ;
202202
203203 private String [] headerComments ;
@@ -245,7 +245,7 @@ private Builder(final CSVFormat csvFormat) {
245245 this .trim = csvFormat .trim ;
246246 this .autoFlush = csvFormat .autoFlush ;
247247 this .quotedNullString = csvFormat .quotedNullString ;
248- this .allowDuplicateHeaderNames = csvFormat .allowDuplicateHeaderNames ;
248+ this .duplicateHeaderMode = csvFormat .duplicateHeaderMode ;
249249 }
250250
251251 /**
@@ -262,12 +262,26 @@ public CSVFormat build() {
262262 *
263263 * @param allowDuplicateHeaderNames the duplicate header names behavior, true to allow, false to disallow.
264264 * @return This instance.
265+ * @deprecated Use {@link #setDuplicateHeaderMode(DuplicateHeaderMode)}.
265266 */
267+ @ Deprecated
266268 public Builder setAllowDuplicateHeaderNames (final boolean allowDuplicateHeaderNames ) {
267- this .allowDuplicateHeaderNames = allowDuplicateHeaderNames ;
269+ final DuplicateHeaderMode mode = allowDuplicateHeaderNames ? DuplicateHeaderMode .ALLOW_ALL : DuplicateHeaderMode .ALLOW_EMPTY ;
270+ setDuplicateHeaderMode (mode );
268271 return this ;
269272 }
270273
274+ /**
275+ * Sets the duplicate header names behavior.
276+ *
277+ * @param duplicateHeaderMode the duplicate header names behavior
278+ * @return This instance.
279+ */
280+ public Builder setDuplicateHeaderMode (final DuplicateHeaderMode duplicateHeaderMode ) {
281+ this .duplicateHeaderMode = duplicateHeaderMode ;
282+ return this ;
283+ }
284+
271285 /**
272286 * Sets the missing column names behavior, {@code true} to allow missing column names in the header line, {@code false} to cause an
273287 * {@link IllegalArgumentException} to be thrown.
@@ -760,7 +774,8 @@ public CSVFormat getFormat() {
760774 }
761775
762776 /**
763- * Standard Comma Separated Value format, as for {@link #RFC4180} but allowing empty lines.
777+ * Standard Comma Separated Value format, as for {@link #RFC4180} but allowing
778+ * empty lines.
764779 *
765780 * <p>
766781 * The {@link Builder} settings are:
@@ -770,13 +785,13 @@ public CSVFormat getFormat() {
770785 * <li>{@code setQuote('"')}</li>
771786 * <li>{@code setRecordSeparator("\r\n")}</li>
772787 * <li>{@code setIgnoreEmptyLines(true)}</li>
773- * <li>{@code setAllowDuplicateHeaderNames(true )}</li>
788+ * <li>{@code setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL )}</li>
774789 * </ul>
775790 *
776791 * @see Predefined#Default
777792 */
778793 public static final CSVFormat DEFAULT = new CSVFormat (COMMA , DOUBLE_QUOTE_CHAR , null , null , null , false , true , CRLF , null , null , null , false , false , false ,
779- false , false , false , true );
794+ false , false , false , DuplicateHeaderMode . ALLOW_ALL );
780795
781796 /**
782797 * 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
@@ -799,7 +814,7 @@ public CSVFormat getFormat() {
799814 * <li>{@code setRecordSeparator("\r\n")}</li>
800815 * <li>{@code setIgnoreEmptyLines(false)}</li>
801816 * <li>{@code setAllowMissingColumnNames(true)}</li>
802- * <li>{@code setAllowDuplicateHeaderNames(true )}</li>
817+ * <li>{@code setDuplicateHeaderMode(DuplicateHeaderMode.ALLOW_ALL )}</li>
803818 * </ul>
804819 * <p>
805820 * Note: This is currently like {@link #RFC4180} plus {@link Builder#setAllowMissingColumnNames(boolean) Builder#setAllowMissingColumnNames(true)} and
@@ -1219,7 +1234,7 @@ private static boolean isLineBreak(final Character c) {
12191234 */
12201235 public static CSVFormat newFormat (final char delimiter ) {
12211236 return new CSVFormat (String .valueOf (delimiter ), null , null , null , null , false , false , null , null , null , null , false , false , false , false , false , false ,
1222- true );
1237+ DuplicateHeaderMode . ALLOW_ALL );
12231238 }
12241239
12251240 static String [] toStringArray (final Object [] values ) {
@@ -1261,7 +1276,7 @@ public static CSVFormat valueOf(final String format) {
12611276 return CSVFormat .Predefined .valueOf (format ).getFormat ();
12621277 }
12631278
1264- private final boolean allowDuplicateHeaderNames ;
1279+ private final DuplicateHeaderMode duplicateHeaderMode ;
12651280
12661281 private final boolean allowMissingColumnNames ;
12671282
@@ -1318,7 +1333,7 @@ private CSVFormat(final Builder builder) {
13181333 this .trim = builder .trim ;
13191334 this .autoFlush = builder .autoFlush ;
13201335 this .quotedNullString = builder .quotedNullString ;
1321- this .allowDuplicateHeaderNames = builder .allowDuplicateHeaderNames ;
1336+ this .duplicateHeaderMode = builder .duplicateHeaderMode ;
13221337 validate ();
13231338 }
13241339
@@ -1342,13 +1357,14 @@ private CSVFormat(final Builder builder) {
13421357 * @param trim TODO Doc me.
13431358 * @param trailingDelimiter TODO Doc me.
13441359 * @param autoFlush TODO Doc me.
1360+ * @param duplicateHeaderMode the behavior when handling duplicate headers
13451361 * @throws IllegalArgumentException if the delimiter is a line break character.
13461362 */
13471363 private CSVFormat (final String delimiter , final Character quoteChar , final QuoteMode quoteMode , final Character commentStart , final Character escape ,
13481364 final boolean ignoreSurroundingSpaces , final boolean ignoreEmptyLines , final String recordSeparator , final String nullString ,
13491365 final Object [] headerComments , final String [] header , final boolean skipHeaderRecord , final boolean allowMissingColumnNames ,
13501366 final boolean ignoreHeaderCase , final boolean trim , final boolean trailingDelimiter , final boolean autoFlush ,
1351- final boolean allowDuplicateHeaderNames ) {
1367+ final DuplicateHeaderMode duplicateHeaderMode ) {
13521368 this .delimiter = delimiter ;
13531369 this .quoteCharacter = quoteChar ;
13541370 this .quoteMode = quoteMode ;
@@ -1367,7 +1383,7 @@ private CSVFormat(final String delimiter, final Character quoteChar, final Quote
13671383 this .trim = trim ;
13681384 this .autoFlush = autoFlush ;
13691385 this .quotedNullString = quoteCharacter + nullString + quoteCharacter ;
1370- this .allowDuplicateHeaderNames = allowDuplicateHeaderNames ;
1386+ this .duplicateHeaderMode = duplicateHeaderMode ;
13711387 validate ();
13721388 }
13731389
@@ -1405,7 +1421,7 @@ public boolean equals(final Object obj) {
14051421 return false ;
14061422 }
14071423 final CSVFormat other = (CSVFormat ) obj ;
1408- return allowDuplicateHeaderNames == other .allowDuplicateHeaderNames && allowMissingColumnNames == other .allowMissingColumnNames &&
1424+ return duplicateHeaderMode == other .duplicateHeaderMode && allowMissingColumnNames == other .allowMissingColumnNames &&
14091425 autoFlush == other .autoFlush && Objects .equals (commentMarker , other .commentMarker ) && Objects .equals (delimiter , other .delimiter ) &&
14101426 Objects .equals (escapeCharacter , other .escapeCharacter ) && Arrays .equals (header , other .header ) &&
14111427 Arrays .equals (headerComments , other .headerComments ) && ignoreEmptyLines == other .ignoreEmptyLines &&
@@ -1439,9 +1455,21 @@ public String format(final Object... values) {
14391455 *
14401456 * @return whether duplicate header names are allowed
14411457 * @since 1.7
1458+ * @deprecated Use {@link #getDuplicateHeaderMode()}.
14421459 */
1460+ @ Deprecated
14431461 public boolean getAllowDuplicateHeaderNames () {
1444- return allowDuplicateHeaderNames ;
1462+ return duplicateHeaderMode == DuplicateHeaderMode .ALLOW_ALL ;
1463+ }
1464+
1465+ /**
1466+ * Returns how duplicate headers are handled.
1467+ *
1468+ * @return if duplicate header values are allowed, allowed conditionally, or disallowed.
1469+ * @since 1.9
1470+ */
1471+ public DuplicateHeaderMode getDuplicateHeaderMode () {
1472+ return duplicateHeaderMode ;
14451473 }
14461474
14471475 /**
@@ -1622,7 +1650,7 @@ public int hashCode() {
16221650 int result = 1 ;
16231651 result = prime * result + Arrays .hashCode (header );
16241652 result = prime * result + Arrays .hashCode (headerComments );
1625- return prime * result + Objects .hash (allowDuplicateHeaderNames , allowMissingColumnNames , autoFlush , commentMarker , delimiter , escapeCharacter ,
1653+ return prime * result + Objects .hash (duplicateHeaderMode , allowMissingColumnNames , autoFlush , commentMarker , delimiter , escapeCharacter ,
16261654 ignoreEmptyLines , ignoreHeaderCase , ignoreSurroundingSpaces , nullString , quoteCharacter , quoteMode , quotedNullString , recordSeparator ,
16271655 skipHeaderRecord , trailingDelimiter , trim );
16281656 }
@@ -2222,7 +2250,7 @@ private void validate() throws IllegalArgumentException {
22222250 }
22232251
22242252 // validate header
2225- if (header != null && ! allowDuplicateHeaderNames ) {
2253+ if (header != null && duplicateHeaderMode != DuplicateHeaderMode . ALLOW_ALL ) {
22262254 final Set <String > dupCheck = new HashSet <>();
22272255 for (final String hdr : header ) {
22282256 if (!dupCheck .add (hdr )) {
@@ -2241,7 +2269,7 @@ private void validate() throws IllegalArgumentException {
22412269 */
22422270 @ Deprecated
22432271 public CSVFormat withAllowDuplicateHeaderNames () {
2244- return builder ().setAllowDuplicateHeaderNames ( true ).build ();
2272+ return builder ().setDuplicateHeaderMode ( DuplicateHeaderMode . ALLOW_ALL ).build ();
22452273 }
22462274
22472275 /**
@@ -2254,7 +2282,8 @@ public CSVFormat withAllowDuplicateHeaderNames() {
22542282 */
22552283 @ Deprecated
22562284 public CSVFormat withAllowDuplicateHeaderNames (final boolean allowDuplicateHeaderNames ) {
2257- return builder ().setAllowDuplicateHeaderNames (allowDuplicateHeaderNames ).build ();
2285+ final DuplicateHeaderMode mode = allowDuplicateHeaderNames ? DuplicateHeaderMode .ALLOW_ALL : DuplicateHeaderMode .ALLOW_EMPTY ;
2286+ return builder ().setDuplicateHeaderMode (mode ).build ();
22582287 }
22592288
22602289 /**
0 commit comments