Changed CSVPrinter to print to any Appendable and optimized the internals to avoid string copies

git-svn-id: https://svn.apache.org/repos/asf/commons/sandbox/csv/trunk@1297309 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Emmanuel Bourg 2012-03-06 00:48:34 +00:00
parent e80b8112a7
commit e7434e5cb5

View File

@ -17,8 +17,8 @@
package org.apache.commons.csv; package org.apache.commons.csv;
import java.io.Flushable;
import java.io.IOException; import java.io.IOException;
import java.io.Writer;
/** /**
* Print values as a comma separated list. * Print values as a comma separated list.
@ -26,15 +26,12 @@ import java.io.Writer;
public class CSVPrinter { public class CSVPrinter {
/** The place that the values get written. */ /** The place that the values get written. */
private final Writer out; private final Appendable out;
private final CSVFormat format; private final CSVFormat format;
/** True if we just began a new line. */ /** True if we just began a new line. */
private boolean newLine = true; private boolean newLine = true;
/** Temporary buffer */
private char[] buf = new char[0];
/** /**
* Create a printer that will print values to the given stream following the CSVFormat. * Create a printer that will print values to the given stream following the CSVFormat.
* <p/> * <p/>
@ -44,7 +41,7 @@ public class CSVPrinter {
* @param out stream to which to print. * @param out stream to which to print.
* @param format describes the CSV variation. * @param format describes the CSV variation.
*/ */
public CSVPrinter(Writer out, CSVFormat format) { public CSVPrinter(Appendable out, CSVFormat format) {
this.out = out; this.out = out;
this.format = format == null ? CSVFormat.DEFAULT : format; this.format = format == null ? CSVFormat.DEFAULT : format;
} }
@ -57,7 +54,7 @@ public class CSVPrinter {
* Output a blank line * Output a blank line
*/ */
public void println() throws IOException { public void println() throws IOException {
out.write(format.getLineSeparator()); out.append(format.getLineSeparator());
newLine = true; newLine = true;
} }
@ -67,9 +64,10 @@ public class CSVPrinter {
* @throws IOException * @throws IOException
*/ */
public void flush() throws IOException { public void flush() throws IOException {
out.flush(); if (out instanceof Flushable) {
((Flushable) out).flush();
}
} }
/** /**
* Print a single line of comma separated values. * Print a single line of comma separated values.
@ -103,8 +101,8 @@ public class CSVPrinter {
if (!newLine) { if (!newLine) {
println(); println();
} }
out.write(format.getCommentStart()); out.append(format.getCommentStart());
out.write(' '); out.append(' ');
for (int i = 0; i < comment.length(); i++) { for (int i = 0; i < comment.length(); i++) {
char c = comment.charAt(i); char c = comment.charAt(i);
switch (c) { switch (c) {
@ -115,11 +113,11 @@ public class CSVPrinter {
// break intentionally excluded. // break intentionally excluded.
case '\n': case '\n':
println(); println();
out.write(format.getCommentStart()); out.append(format.getCommentStart());
out.write(' '); out.append(' ');
break; break;
default: default:
out.write(c); out.append(c);
break; break;
} }
} }
@ -127,14 +125,14 @@ public class CSVPrinter {
} }
private void print(char[] value, int offset, int len) throws IOException { private void print(CharSequence value, int offset, int len) throws IOException {
if (format.isEncapsulating()) { if (format.isEncapsulating()) {
printAndEncapsulate(value, offset, len); printAndEncapsulate(value, offset, len);
} else if (format.isEscaping()) { } else if (format.isEscaping()) {
printAndEscape(value, offset, len); printAndEscape(value, offset, len);
} else { } else {
printSep(); printSep();
out.write(value, offset, len); out.append(value, offset, offset + len);
} }
} }
@ -142,11 +140,11 @@ public class CSVPrinter {
if (newLine) { if (newLine) {
newLine = false; newLine = false;
} else { } else {
out.write(format.getDelimiter()); out.append(format.getDelimiter());
} }
} }
void printAndEscape(char[] value, int offset, int len) throws IOException { void printAndEscape(CharSequence value, int offset, int len) throws IOException {
int start = offset; int start = offset;
int pos = offset; int pos = offset;
int end = offset + len; int end = offset + len;
@ -157,12 +155,11 @@ public class CSVPrinter {
char escape = format.getEscape(); char escape = format.getEscape();
while (pos < end) { while (pos < end) {
char c = value[pos]; char c = value.charAt(pos);
if (c == '\r' || c == '\n' || c == delim || c == escape) { if (c == '\r' || c == '\n' || c == delim || c == escape) {
// write out segment up until this char // write out segment up until this char
int l = pos - start; if (pos > start) {
if (l > 0) { out.append(value, start, pos);
out.write(value, start, l);
} }
if (c == '\n') { if (c == '\n') {
c = 'n'; c = 'n';
@ -170,8 +167,8 @@ public class CSVPrinter {
c = 'r'; c = 'r';
} }
out.write(escape); out.append(escape);
out.write(c); out.append(c);
start = pos + 1; // start on the current char after this one start = pos + 1; // start on the current char after this one
} }
@ -180,13 +177,12 @@ public class CSVPrinter {
} }
// write last segment // write last segment
int l = pos - start; if (pos > start) {
if (l > 0) { out.append(value, start, pos);
out.write(value, start, l);
} }
} }
void printAndEncapsulate(char[] value, int offset, int len) throws IOException { void printAndEncapsulate(CharSequence value, int offset, int len) throws IOException {
boolean first = newLine; // is this the first value on this line? boolean first = newLine; // is this the first value on this line?
boolean quote = false; boolean quote = false;
int start = offset; int start = offset;
@ -207,7 +203,7 @@ public class CSVPrinter {
quote = true; quote = true;
} }
} else { } else {
char c = value[pos]; char c = value.charAt(pos);
// Hmmm, where did this rule come from? // Hmmm, where did this rule come from?
if (first if (first
@ -224,7 +220,7 @@ public class CSVPrinter {
quote = true; quote = true;
} else { } else {
while (pos < end) { while (pos < end) {
c = value[pos]; c = value.charAt(pos);
if (c == '\n' || c == '\r' || c == encapsulator || c == delim) { if (c == '\n' || c == '\r' || c == encapsulator || c == delim) {
quote = true; quote = true;
break; break;
@ -234,7 +230,7 @@ public class CSVPrinter {
if (!quote) { if (!quote) {
pos = end - 1; pos = end - 1;
c = value[pos]; c = value.charAt(pos);
// if (c == ' ' || c == '\f' || c == '\t') { // if (c == ' ' || c == '\f' || c == '\t') {
// Some other chars at the end caused the parser to fail, so for now // Some other chars at the end caused the parser to fail, so for now
// encapsulate if we end in anything less than ' ' // encapsulate if we end in anything less than ' '
@ -247,22 +243,22 @@ public class CSVPrinter {
if (!quote) { if (!quote) {
// no encapsulation needed - write out the original value // no encapsulation needed - write out the original value
out.write(value, offset, len); out.append(value, start, end);
return; return;
} }
// we hit something that needed encapsulation // we hit something that needed encapsulation
out.write(encapsulator); out.append(encapsulator);
// Pick up where we left off: pos should be positioned on the first character that caused // Pick up where we left off: pos should be positioned on the first character that caused
// the need for encapsulation. // the need for encapsulation.
while (pos < end) { while (pos < end) {
char c = value[pos]; char c = value.charAt(pos);
if (c == encapsulator) { if (c == encapsulator) {
// write out the chunk up until this point // write out the chunk up until this point
// add 1 to the length to write out the encapsulator also // add 1 to the length to write out the encapsulator also
out.write(value, start, pos - start + 1); out.append(value, start, pos + 1);
// put the next starting position on the encapsulator so we will // put the next starting position on the encapsulator so we will
// write it out again with the next string (effectively doubling it) // write it out again with the next string (effectively doubling it)
start = pos; start = pos;
@ -271,8 +267,8 @@ public class CSVPrinter {
} }
// write the last segment // write the last segment
out.write(value, start, pos - start); out.append(value, start, pos);
out.write(encapsulator); out.append(encapsulator);
} }
/** /**
@ -290,16 +286,10 @@ public class CSVPrinter {
if (!checkForEscape) { if (!checkForEscape) {
// write directly from string // write directly from string
printSep(); printSep();
out.write(value); out.append(value);
return; } else {
print(value, 0, value.length());
} }
if (buf.length < value.length()) {
buf = new char[value.length()];
}
value.getChars(0, value.length(), buf, 0);
print(buf, 0, value.length());
} }
/** /**