package org.agnitas.util; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import org.apache.commons.io.IOUtils; public class CsvWriter extends Writer { public static final String DEFAULT_ENCODING = "UTF-8"; public static final char DEFAULT_SEPARATOR = ','; public static final char DEFAULT_STRING_QUOTE = '"'; public static final String DEFAULT_LINEBREAK = "\n"; private char separator; private String separatorString; private char stringQuote; private String stringQuoteString; private String doubleStringQuoteString; private boolean useStringQuote; private String lineBreak; private OutputStream outputStream; private Charset encoding; private boolean alwaysQuote = false; private boolean quoteAllStrings = false; private int writtenLines = 0; private int numberOfColumns = -1; private BufferedWriter outputWriter = null; public CsvWriter(OutputStream outputStream) { this(outputStream, Charset.forName(DEFAULT_ENCODING), DEFAULT_SEPARATOR, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, String encoding) { this(outputStream, Charset.forName(encoding), DEFAULT_SEPARATOR, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, Charset encoding) { this(outputStream, encoding, DEFAULT_SEPARATOR, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, char separator) { this(outputStream, Charset.forName(DEFAULT_ENCODING), separator, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, String encoding, char separator) { this(outputStream, Charset.forName(encoding), separator, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, Charset encoding, char separator) { this(outputStream, encoding, separator, DEFAULT_STRING_QUOTE, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, char separator, Character stringQuote) { this(outputStream, Charset.forName(DEFAULT_ENCODING), separator, stringQuote, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, char separator, Character stringQuote, String lineBreak) { this(outputStream, Charset.forName(DEFAULT_ENCODING), separator, stringQuote, lineBreak); } public CsvWriter(OutputStream outputStream, String encoding, char separator, Character stringQuote) { this(outputStream, Charset.forName(encoding), separator, stringQuote, DEFAULT_LINEBREAK); } public CsvWriter(OutputStream outputStream, Charset encoding, char separator, Character stringQuote, String lineBreak) { this.outputStream = outputStream; this.encoding = encoding; this.separator = separator; separatorString = Character.toString(separator); this.lineBreak = lineBreak; if (stringQuote != null) { this.stringQuote = stringQuote; stringQuoteString = Character.toString(stringQuote); doubleStringQuoteString = stringQuoteString + stringQuoteString; this.useStringQuote = true; } else { this.useStringQuote = false; } if (this.encoding == null) { throw new IllegalArgumentException("Encoding is null"); } else if (this.outputStream == null) { throw new IllegalArgumentException("OutputStream is null"); } else if (AgnUtils.anyCharsAreEqual(this.separator, '\r', '\n')) { throw new IllegalArgumentException("Separator '" + this.separator + "' is invalid"); } else if (useStringQuote && AgnUtils.anyCharsAreEqual(this.separator, this.stringQuote, '\r', '\n')) { throw new IllegalArgumentException("Stringquote '" + this.stringQuote + "' is invalid"); } else if (!this.lineBreak.equals("\r") && !this.lineBreak.equals("\n") && !this.lineBreak.equals("\r\n")) { throw new IllegalArgumentException("Given linebreak is invalid"); } } public boolean isAlwaysQuote() { return alwaysQuote; } public void setAlwaysQuote(boolean alwaysQuote) { this.alwaysQuote = alwaysQuote; if (alwaysQuote) { quoteAllStrings = false; } } public boolean isQuoteAllStrings() { return quoteAllStrings; } public void setQuoteAllStrings(boolean quoteAllStrings) { this.quoteAllStrings = quoteAllStrings; if (quoteAllStrings) { alwaysQuote = false; } } public void writeValues(Object... values) throws Exception { writeValues(Arrays.asList(values)); } public void writeValues(List<? extends Object> values) throws CsvDataException, IOException { if (numberOfColumns != -1 && (values == null || numberOfColumns != values.size())) { throw new CsvDataException("Inconsistent number of values after " + writtenLines + " written lines (expected: " + numberOfColumns + " was: " + (values == null ? "null" : values.size()) + ")", writtenLines); } if (outputWriter == null) { if (outputStream == null) { throw new IllegalStateException("CsvWriter is already closed"); } outputWriter = new BufferedWriter(new OutputStreamWriter(outputStream, encoding)); } boolean isFirst = true; for (Object value : values) { if (!isFirst) { outputWriter.write(separator); } isFirst = false; outputWriter.write(escapeValue(value)); } outputWriter.write(lineBreak); writtenLines++; numberOfColumns = values.size(); } public void writeAll(List<List<? extends Object>> valueLines) throws Exception { for (List<? extends Object> valuesOfLine : valueLines) { writeValues(valuesOfLine); } } private String escapeValue(Object value) throws CsvDataException { String valueString = ""; if (value != null) { valueString = value.toString(); } if (alwaysQuote || (quoteAllStrings && value instanceof String) || valueString.contains(separatorString) || valueString.contains("\r") || valueString.contains("\n") || (useStringQuote && valueString.contains(stringQuoteString))) { if (!useStringQuote) { throw new CsvDataException("StringQuote was deactivated but is needed for csv-value after " + writtenLines + " written lines", writtenLines); } else { StringBuilder escapedValue = new StringBuilder(); escapedValue.append(stringQuote); escapedValue.append(valueString.replace(stringQuoteString, doubleStringQuoteString)); escapedValue.append(stringQuote); return escapedValue.toString(); } } else { return valueString; } } @Override public void close() { IOUtils.closeQuietly(outputWriter); outputWriter = null; IOUtils.closeQuietly(outputStream); outputStream = null; } public int getWrittenLines() { return writtenLines; } /** * Not supported method */ @Override public void write(char[] cbuf, int off, int len) throws IOException { throw new IOException("Write by offset and length is not supported by " + getClass().getSimpleName()); } @Override public void flush() throws IOException { if (outputWriter != null) { outputWriter.flush(); } } public static String getCsvLine(char separator, char stringQuote, List<? extends Object> values) { return getCsvLine(separator, stringQuote, values.toArray()); } public static String getCsvLine(char separator, char stringQuote, Object... values) { StringBuilder returnValue = new StringBuilder(); String separatorString = Character.toString(separator); String stringQuoteString = Character.toString(stringQuote); String doubleStringQuoteString = stringQuoteString + stringQuoteString; if (values != null) { for (Object value : values) { if (returnValue.length() > 0) { returnValue.append(separator); } if (value != null) { String valueString = value.toString(); if (valueString.contains(separatorString) || valueString.contains("\r") || valueString.contains("\n") || valueString.contains(stringQuoteString)) { returnValue.append(stringQuoteString); returnValue.append(valueString.replace(stringQuoteString, doubleStringQuoteString)); returnValue.append(stringQuoteString); } else { returnValue.append(valueString); } } } } return returnValue.toString(); } }