/*
* CSVWriter.java
*
*/
package org.smartly.commons.csv;
import org.smartly.commons.csv.formatter.CSVDataFormatter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.text.DateFormat;
import java.util.*;
/**
*
*/
public class CSVWriter
implements ICSVConstants {
private Writer _writer;
private char _separator = DEFAULT_SEPARATOR;
private char _quotechar = DEFAULT_QUOTE_CHARACTER;
private String _lineEnd = DEFAULT_LINE_END;
private Locale _locale = DEFAULT_LOCALE;
//-- late initialized --//
private PrintWriter __pwriter;
private CSVDataFormatter __formatter;
// ------------------------------------------------------------------------
// c o n s t r u c t o r
// ------------------------------------------------------------------------
public CSVWriter() {
}
/**
* Constructs CSVWriter using a comma for the _separator.
*
* @param writer the writer to an underlying CSV source.
*/
public CSVWriter(final Writer writer) {
this(writer, DEFAULT_SEPARATOR);
}
/**
* Constructs CSVWriter with supplied _separator.
*
* @param writer the writer to an underlying CSV source.
* @param separator the delimiter to use for separating entries.
*/
public CSVWriter(final Writer writer, char separator) {
this(writer, separator, DEFAULT_QUOTE_CHARACTER);
}
/**
* Constructs CSVWriter with supplied _separator and quote char.
*
* @param writer the writer to an underlying CSV source.
* @param separator the delimiter to use for separating entries
* @param quotechar the character to use for quoted elements
*/
public CSVWriter(final Writer writer, char separator, char quotechar) {
this(writer, separator, quotechar, "\n", DEFAULT_LOCALE);
}
/**
* Constructs CSVWriter with supplied _separator and quote char.
*
* @param writer the writer to an underlying CSV source.
* @param separator the delimiter to use for separating entries
* @param quotechar the character to use for quoted elements
* @param lineEnd the line feed terminator to use
*/
public CSVWriter(final Writer writer, char separator,
char quotechar, String lineEnd, Locale locale) {
_writer = writer;
_separator = separator;
_quotechar = quotechar;
_lineEnd = lineEnd;
_locale = locale;
}
@Override
protected void finalize() throws Throwable {
this.close();
super.finalize();
}
// ------------------------------------------------------------------------
// properties
// ------------------------------------------------------------------------
public void setWriter(final Writer writer) {
_writer = writer;
}
public Writer getWriter() {
return _writer;
}
public char getSeparator() {
return _separator;
}
public void setSeparator(char separator) {
this._separator = separator;
}
public char getQuotechar() {
return _quotechar;
}
public void setQuotechar(char quotechar) {
this._quotechar = quotechar;
}
public String getLineEnd() {
return _lineEnd;
}
public void setLineEnd(String lineEnd) {
this._lineEnd = lineEnd;
}
public Locale getLocale() {
return _locale;
}
public void setLocale(Locale locale) {
this._locale = locale;
}
// ------------------------------------------------------------------------
// p u b l i c
// ------------------------------------------------------------------------
/**
* Writes the entire list to a CSV file. The list is assumed to be a
* String[]
*
* @param allLines a List of String[], with each String[] representing a line of
* the file.
*/
public int writeAll(List<String[]> allLines) {
int result = 0;
for (Iterator iter = allLines.iterator(); iter.hasNext(); ) {
String[] nextLine = (String[]) iter.next();
this.writeNext(nextLine);
result++;
}
return result;
}
/**
* Writes the entire list to a CSV file. The list is assumed to be a Map
*
* @param allLines a List of Map, with each row representing a line of the file.
*/
public int writeAll(final List<Map> allLines,
boolean includeColumnNames) {
int result = 0;
int i = 0;
String[] header = null;
for (final Map item : allLines) {
if (i == 0 && includeColumnNames) {
header = this.getNames(item);
this.writeNext(header);
}
String[] nextLine = this.getValues(header, item);
this.writeNext(nextLine);
result++;
i++;
}
return result;
}
/**
* Writes the entire ResultSet to a CSV file.
* <p/>
* The caller is responsible for closing the ResultSet.
*
* @param rs the recordset to write
* @param includeColumnNames true if you want column names in the output, false otherwise
*/
public int writeAll(ResultSet rs, boolean includeColumnNames) throws SQLException, IOException {
try {
rs.beforeFirst();
} catch (Throwable t) {
}
int result = 0;
ResultSetMetaData metadata = rs.getMetaData();
if (includeColumnNames) {
this.writeColumnNames(metadata);
}
int columnCount = metadata.getColumnCount();
while (rs.next()) {
String[] nextLine = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
nextLine[i] = this.getColumnValue(rs, metadata.getColumnType(i + 1), i + 1);
}
this.writeNext(nextLine);
result++;
}
return result;
}
/**
* Writes the next line to the file.
*
* @param nextLine a string array with each comma-separated element as a separate
* entry.
*/
public void writeNext(final String[] nextLine) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < nextLine.length; i++) {
if (i != 0) {
sb.append(_separator);
}
String nextElement = nextLine[i];
if (nextElement == null) {
continue;
}
if (_quotechar != NO_QUOTE_CHARACTER) {
sb.append(_quotechar);
}
for (int j = 0; j < nextElement.length(); j++) {
char nextChar = nextElement.charAt(j);
if (nextChar == _quotechar) {
sb.append(ESCAPE_CHARACTER).append(nextChar);
} else if (nextChar == ESCAPE_CHARACTER) {
sb.append(ESCAPE_CHARACTER).append(nextChar);
} else {
sb.append(nextChar);
}
}
if (_quotechar != NO_QUOTE_CHARACTER) {
sb.append(_quotechar);
}
}
sb.append(_lineEnd);
this.getPWriter().write(sb.toString());
}
/**
* Close the underlying stream writer flushing any buffered content.
*
* @throws java.io.IOException if bad things happen
*/
public void close() throws IOException {
if (null != __pwriter) {
__pwriter.flush();
__pwriter.close();
}
if (null != _writer) {
_writer.close();
}
}
// ------------------------------------------------------------------------
// p r o t e c t e d
// ------------------------------------------------------------------------
protected void writeColumnNames(ResultSetMetaData metadata)
throws SQLException {
int columnCount = metadata.getColumnCount();
String[] nextLine = new String[columnCount];
for (int i = 0; i < columnCount; i++) {
String name = metadata.getColumnName(i + 1);
if (null == name || name.length() == 0) {
name = metadata.getColumnLabel(i + 1);
}
nextLine[i] = name;
}
writeNext(nextLine);
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private CSVDataFormatter getFormatter() {
if (null == __formatter) {
__formatter = new CSVDataFormatter(DateFormat.SHORT, "", _locale);
}
return __formatter;
}
private PrintWriter getPWriter() {
if (null == __pwriter) {
if (null != _writer) {
__pwriter = new PrintWriter(_writer);
}
}
return __pwriter;
}
private String[] getNames(final Map map) {
final String[] result = new String[map.size()];
int i = 0;
final Set keys = map.keySet();
for (final Object key : keys) {
result[i] = key.toString();
i++;
}
return result;
}
private String[] getValues(final String[] keys,
final Map map) {
final String[] result = new String[map.size()];
int i = 0;
if (null != keys && keys.length > 0) {
// respect keys order
for (final String key : keys) {
result[i] = this.getFormatter().serialize(map.get(key));
i++;
}
} else {
final Collection values = map.values();
for (final Object value : values) {
result[i] = this.getFormatter().serialize(value);
i++;
}
}
return result;
}
private String getColumnValue(ResultSet rs, int colType, int colIndex)
throws SQLException, IOException {
return this.getFormatter().serialize(rs, colType, colIndex);
}
}