Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CSV-216: Add mutator withValue() to CSVRecord #25

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
80 changes: 55 additions & 25 deletions src/main/java/org/apache/commons/csv/CSVFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ public CSVFormat getFormat() {
* @see Predefined#Default
*/
public static final CSVFormat DEFAULT = new CSVFormat(COMMA, DOUBLE_QUOTE_CHAR, null, null, null, false, true, CRLF,
null, null, null, false, false, false, false, false, false);
null, null, null, false, false, false, false, false, false, false);

/**
* Excel file format (using a comma as the value delimiter). Note that the actual value delimiter used by Excel is
Expand Down Expand Up @@ -537,7 +537,7 @@ private static boolean isLineBreak(final Character c) {
*/
public static CSVFormat newFormat(final char delimiter) {
return new CSVFormat(delimiter, null, null, null, null, false, false, null, null, null, null, false, false,
false, false, false, false);
false, false, false, false, false);
}

/**
Expand Down Expand Up @@ -570,6 +570,8 @@ public static CSVFormat valueOf(final String format) {

private final boolean ignoreSurroundingSpaces; // Should leading/trailing spaces be ignored around values?

private final boolean mutableRecords;

private final String nullString; // the string to be used for null values

private final Character quoteCharacter; // null if quoting is disabled
Expand Down Expand Up @@ -622,15 +624,19 @@ public static CSVFormat valueOf(final String format) {
* @param trailingDelimiter
* TODO
* @param autoFlush
* TODO
* @param mutableRecords
* if {@code true}, {@link CSVRecord}s are {@link CSVRecord#mutable()} by default, otherwise immutable
*
* @throws IllegalArgumentException
* if the delimiter is a line break character
*/
private CSVFormat(final char delimiter, final Character quoteChar, final QuoteMode quoteMode,
final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces,
final boolean ignoreEmptyLines, final String recordSeparator, final String nullString,
final Object[] headerComments, final String[] header, final boolean skipHeaderRecord,
final boolean allowMissingColumnNames, final boolean ignoreHeaderCase, final boolean trim,
final boolean trailingDelimiter, final boolean autoFlush) {
final Character commentStart, final Character escape, final boolean ignoreSurroundingSpaces,
final boolean ignoreEmptyLines, final String recordSeparator, final String nullString,
final Object[] headerComments, final String[] header, final boolean skipHeaderRecord,
final boolean allowMissingColumnNames, final boolean ignoreHeaderCase, final boolean trim,
final boolean trailingDelimiter, final boolean autoFlush, final boolean mutableRecords) {
this.delimiter = delimiter;
this.quoteCharacter = quoteChar;
this.quoteMode = quoteMode;
Expand All @@ -647,6 +653,7 @@ private CSVFormat(final char delimiter, final Character quoteChar, final QuoteMo
this.ignoreHeaderCase = ignoreHeaderCase;
this.trailingDelimiter = trailingDelimiter;
this.trim = trim;
this.mutableRecords = mutableRecords;
this.autoFlush = autoFlush;
validate();
}
Expand Down Expand Up @@ -941,6 +948,10 @@ public boolean isEscapeCharacterSet() {
return escapeCharacter != null;
}

public boolean isMutableRecords() {
return mutableRecords;
}

/**
* Returns whether a nullString has been defined.
*
Expand Down Expand Up @@ -1442,7 +1453,7 @@ public CSVFormat withAllowMissingColumnNames() {
public CSVFormat withAllowMissingColumnNames(final boolean allowMissingColumnNames) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1457,7 +1468,7 @@ public CSVFormat withAllowMissingColumnNames(final boolean allowMissingColumnNam
public CSVFormat withAutoFlush(final boolean autoFlush) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand Down Expand Up @@ -1492,7 +1503,7 @@ public CSVFormat withCommentMarker(final Character commentMarker) {
}
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1510,7 +1521,7 @@ public CSVFormat withDelimiter(final char delimiter) {
}
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand Down Expand Up @@ -1541,7 +1552,7 @@ public CSVFormat withEscape(final Character escape) {
}
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escape, ignoreSurroundingSpaces,
ignoreEmptyLines, recordSeparator, nullString, headerComments, header, skipHeaderRecord,
allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand Down Expand Up @@ -1696,7 +1707,7 @@ public CSVFormat withHeader(final ResultSetMetaData metaData) throws SQLExceptio
public CSVFormat withHeader(final String... header) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1717,7 +1728,7 @@ public CSVFormat withHeader(final String... header) {
public CSVFormat withHeaderComments(final Object... headerComments) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1742,7 +1753,7 @@ public CSVFormat withIgnoreEmptyLines() {
public CSVFormat withIgnoreEmptyLines(final boolean ignoreEmptyLines) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1768,7 +1779,7 @@ public CSVFormat withIgnoreHeaderCase() {
public CSVFormat withIgnoreHeaderCase(final boolean ignoreHeaderCase) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1793,7 +1804,25 @@ public CSVFormat withIgnoreSurroundingSpaces() {
public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpaces) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
* Returns a new {@code CSVFormat} with mutable or immutable records by default.
* <ul>
* <li><strong>Reading:</strong> Mutable by default.</li>
* <li><strong>Writing:</strong> No effect.</li>
* </ul>
*
* @param mutableRecords
* If true, parsed @link CSVRecord}s are {@link CSVRecord#mutable()}, otherwise {@link CSVRecord#immutable()}.
*
* @return A new CSVFormat that is equal to this but with setting to generate mutable/immutable records.
*/
public CSVFormat withMutableRecords(final boolean mutableRecords) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1812,7 +1841,7 @@ public CSVFormat withIgnoreSurroundingSpaces(final boolean ignoreSurroundingSpac
public CSVFormat withNullString(final String nullString) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand Down Expand Up @@ -1843,7 +1872,7 @@ public CSVFormat withQuote(final Character quoteChar) {
}
return new CSVFormat(delimiter, quoteChar, quoteMode, commentMarker, escapeCharacter, ignoreSurroundingSpaces,
ignoreEmptyLines, recordSeparator, nullString, headerComments, header, skipHeaderRecord,
allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1857,7 +1886,7 @@ public CSVFormat withQuote(final Character quoteChar) {
public CSVFormat withQuoteMode(final QuoteMode quoteModePolicy) {
return new CSVFormat(delimiter, quoteCharacter, quoteModePolicy, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand Down Expand Up @@ -1895,7 +1924,7 @@ public CSVFormat withRecordSeparator(final char recordSeparator) {
public CSVFormat withRecordSeparator(final String recordSeparator) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1922,7 +1951,7 @@ public CSVFormat withSkipHeaderRecord() {
public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1934,7 +1963,7 @@ public CSVFormat withSkipHeaderRecord(final boolean skipHeaderRecord) {
* only works for inputs with '\n', '\r' and "\r\n"
* </p>
*
* @return A new CSVFormat that is equal to this but with the operating system's line separator stringr
* @return A new CSVFormat that is equal to this but with the operating system's line separator string
* @since 1.6
*/
public CSVFormat withSystemRecordSeparator() {
Expand Down Expand Up @@ -1963,7 +1992,7 @@ public CSVFormat withTrailingDelimiter() {
public CSVFormat withTrailingDelimiter(final boolean trailingDelimiter) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

/**
Expand All @@ -1988,6 +2017,7 @@ public CSVFormat withTrim() {
public CSVFormat withTrim(final boolean trim) {
return new CSVFormat(delimiter, quoteCharacter, quoteMode, commentMarker, escapeCharacter,
ignoreSurroundingSpaces, ignoreEmptyLines, recordSeparator, nullString, headerComments, header,
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush);
skipHeaderRecord, allowMissingColumnNames, ignoreHeaderCase, trim, trailingDelimiter, autoFlush, mutableRecords);
}

}
65 changes: 65 additions & 0 deletions src/main/java/org/apache/commons/csv/CSVMutableRecord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.commons.csv;

import java.util.Map;

/**
* A mutable version of CSVRecord
* <p>
* As mutation is generally done within the parent {@link CSVRecord}, this
* package-private class just includes more efficient versions of
* mutator functions, bypassing the need to make copies
* (except in {@link #immutable()}).
* <p>
* To enable generating CSVMutableRecord by default, set
* {@link CSVFormat#withMutableRecords(boolean)} to <code>true</code>.
*
*/
final class CSVMutableRecord extends CSVRecord {

private static final long serialVersionUID = 1L;

CSVMutableRecord(String[] values, Map<String, Integer> mapping, String comment, long recordNumber,
Copy link
Member Author

@stain stain Feb 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am considering if there's not much point in the CSVMutableRecord class anymore, except in any hope of the compiler being clever due to the final methods and the inline isMutable() constant.

An alternative is to add a private final boolean mutable to CSVRecord and make it final again - a variant of #21 from @nmahendru.

long characterPosition) {
super(values, mapping, comment, recordNumber, characterPosition);
}

@Override
public CSVMutableRecord withValue(int index, String value) {
super.put(index, value);
return this;
}

@Override
public CSVMutableRecord withValue(String name, String value) {
super.put(name, value);
return this;
}

@Override
public CSVRecord withComment(String comment) {
this.comment = comment;
return this;
}

@Override
boolean isMutable() {
return true;
}
}
8 changes: 5 additions & 3 deletions src/main/java/org/apache/commons/csv/CSVParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ public static CSVParser parse(final URL url, final Charset charset, final CSVFor
private final long characterOffset;

private final Token reusableToken = new Token();

/**
* Customized CSV parser using the given {@link CSVFormat}
*
Expand Down Expand Up @@ -615,8 +615,10 @@ CSVRecord nextRecord() throws IOException {
if (!this.recordList.isEmpty()) {
this.recordNumber++;
final String comment = sb == null ? null : sb.toString();
result = new CSVRecord(this.recordList.toArray(new String[this.recordList.size()]), this.headerMap, comment,
this.recordNumber, startCharPosition);
String[] array = this.recordList.toArray(new String[this.recordList.size()]);
result = format.isMutableRecords()
? new CSVMutableRecord(array, this.headerMap, comment, this.recordNumber, startCharPosition)
: new CSVRecord(array, this.headerMap, comment, this.recordNumber, startCharPosition);
}
return result;
}
Expand Down
Loading