/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
/**
* @author Thomas Davis (sunsetbrew)
* @copyright Copyright (c) 2010, Thomas Davis
* @license http://opensource.org/licenses/mit-license.php MIT License
*/
package divconq.util;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import divconq.lang.op.OperationResult;
import divconq.struct.ListStruct;
import divconq.struct.RecordStruct;
import divconq.struct.Struct;
/**
* CSV4180Writer provides a simple way to export CSV values to a file or
* console. The writer extends BufferedWriter for improved performance.
*/
public class CSVWriter extends BufferedWriter {
static public OperationResult writeRecords(OutputStream out, ListStruct records, String... fields) {
OperationResult or = new OperationResult();
try {
CSVWriter wrt = new CSVWriter(new OutputStreamWriter(out));
for (String fld : fields) {
wrt.writeField(fld);
}
wrt.newLine();
for (Struct s : records.getItems()) {
RecordStruct rec = (RecordStruct) s;
for (String fld : fields)
wrt.writeField(Struct.objectToString(rec.getField(fld)));
wrt.newLine();
};
wrt.close();
}
catch (Exception x) {
or.error("Unable to write CSV: " + x);
}
return or;
}
/**
* Create a buffered character-output stream that uses a default-sized
* output buffer.
*
* @param out
* A writer
*/
public CSVWriter(Writer out) {
super(out);
}
/**
* Create a new buffered character-output stream that uses an output buffer
* of the given size.
*
* @param out
* A writer
* @param sz
* Output-buffer size, a positive integer
*/
public CSVWriter(Writer out, int sz) {
super(out, sz);
}
/**
* Writes a line of fields to the writer. A new line is automatically added
* if beyond the first call to this method. This is a convenience method.
*
* @param fields
* fields to output
* @throws IOException
* on general I/O error
*/
public void writeFields(ArrayList<String> fields) throws IOException {
if (newWriter) {
this.newWriter = false;
} else {
this.newLine();
}
Iterator<String> si = fields.iterator();
while (si.hasNext()) {
this.writeField(si.next());
}
}
/**
* Writes a line of fields to the writer. A new line is automatically added
* if beyond the first call to this method. This is a convenience method.
*
* @param fields
* fields to output
* @throws IOException
* on general I/O error
*/
public void writeFields(String[] fields) throws IOException {
if (newWriter) {
this.newWriter = false;
} else {
this.newLine();
}
for (int i = 0; i < fields.length; i++) {
this.writeField(fields[i]);
}
}
/**
* Write a line separator.
*
* @exception IOException
* If an I/O error occurs
* @see java.io.BufferedWriter#newLine()
*/
@Override
public void newLine() throws IOException {
this.newLine = true;
super.write("\r\n");
}
/**
* Write a field to the output quoting as necessary and adding comma
* separators between fields.
*
* @param field
* String to write
* @throws IOException
* If an I/O error occurs
*/
public void writeField(String field) throws IOException {
if (this.newLine) {
this.newLine = false;
} else {
this.write(',');
}
// case 0: empty string, simple :)
if ((field == null) || (field.length() == 0)) {
return;
}
// case 1: field has quotes in it, if so convert to, quote field and
// double all quotes
Matcher matcher = escapePattern.matcher(field);
if (matcher.find()) {
this.write('"');
this.tmpBuffer.setLength(0);
do {
matcher.appendReplacement(this.tmpBuffer, "\"\"");
} while (matcher.find());
matcher.appendTail(this.tmpBuffer);
this.write(this.tmpBuffer.toString());
this.write('"');
return;
}
// case 2: field has a comma, carriage return or new line in it, if so
// quote field and double all quotes
matcher = specialCharsPattern.matcher(field);
if (matcher.find()) {
this.write('"');
this.write(field);
this.write('"');
return;
}
// case 3: safe string to just add
this.append(field);
}
/** @ignore */
private boolean newLine = true;
/** @ignore */
private final StringBuffer tmpBuffer = new StringBuffer();
/** @ignore */
private static Pattern escapePattern = Pattern.compile("(\")");
/** @ignore */
private static Pattern specialCharsPattern = Pattern.compile("[,\r\n]");
/** @ignore */
private boolean newWriter = true;
}