All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sirius.kernel.commons.CSVWriter Maven / Gradle / Ivy

Go to download

Provides common core classes and the microkernel powering all Sirius applications

There is a newer version: 12.9.1
Show newest version
/*
 * Made with all the love in the world
 * by scireum in Remshalden, Germany
 *
 * Copyright by scireum GmbH
 * http://www.scireum.de - [email protected]
 */

package sirius.kernel.commons;

import sirius.kernel.nls.NLS;

import java.io.Closeable;
import java.io.IOException;
import java.io.Writer;
import java.util.List;

/**
 * Writes rows of data as CSV (comma separated values) files.
 * 

* By default ; is used to separate columns and line breaks are used to separate rows. If a column value * contains the separator character or a line break, it is quoted using ". *

* If the quotation character occurs withing an already quoted string, it is escaped using \. If no * quotation charater is specified (set to \0), the escape character is used if possible. If quoting or * escaping * is required but disabled (using \0 for their respective value), an exception will be thrown as no * valid output can be generated. */ public class CSVWriter implements Closeable { private String lineSeparator = "\n"; private Writer writer; private boolean firstLine = true; private char separator = ';'; private String separatorString = String.valueOf(';'); private char quotation = '"'; private boolean isQuotationEmpty = false; private char escape = '\\'; private boolean isEscapeEmpty = false; private boolean trim = true; /** * Creates a new writer sending data to the given writer. *

* The writer is closed by calling {@link #close()}. * * @param writer the target to write data to */ public CSVWriter(Writer writer) { this.writer = writer; } /** * Specifies the separator character to use. *

* By default this is ;. * * @param separator the separator to use * @return the writer itself for fluent method calls */ public CSVWriter withSeparator(char separator) { this.separator = separator; this.separatorString = String.valueOf(separator); return this; } /** * Specifies the quotation character to use. *

* By default this is ". Use \0 to disable quotation entirely. Note that quotation is required * if columns contain the separator character or a line break. * * @param quotation the quotation character to use * @return the writer itself for fluent method calls */ public CSVWriter withQuotation(char quotation) { this.quotation = quotation; this.isQuotationEmpty = quotation == '\0'; return this; } /** * Specifies the escape character to use. *

* By default this is \. Use \0 to disable escaping entirely. Note that escaping is required if * columns contain a quotation character inside an already quoted column. Or if values contain the separator * character and no quotation is possible (quotation character is \0). * * @param escape the escape character to use * @return the writer itself for fluent method calls */ public CSVWriter withEscape(char escape) { this.escape = escape; this.isEscapeEmpty = escape == '\0'; return this; } /** * Controls if each added cell value of the type String should be trimmed or not *

* By default this is true. Use false if strings should not be trimmed. * * @param trim the value controlling if strings should be trimmed * @return the writer itself for fluent method calls */ public CSVWriter withInputTrimming(boolean trim) { this.trim = trim; return this; } /** * Writes the given list of values as row. * * @param row the data to write. null values will be completely skipped. * @return the writer itself for fluent method calls * @throws IOException in case of an IO error when writing to the underlying writer */ public CSVWriter writeList(List row) throws IOException { if (row != null) { writeLineSeparator(); for (int i = 0; i < row.size(); i++) { if (i > 0) { writer.write(separator); } writeColumn(row.get(i)); } } return this; } /** * Writes the given array of values as row. * * @param row the data to write * @return the writer itself for fluent method calls * @throws IOException in case of an IO error when writing to the underlying writer */ public CSVWriter writeArray(Object... row) throws IOException { writeLineSeparator(); for (int i = 0; i < row.length; i++) { if (i > 0) { writer.write(separator); } writeColumn(row[i]); } return this; } private void writeLineSeparator() throws IOException { if (!firstLine) { writer.write(lineSeparator); } else { firstLine = false; } } private void writeColumn(Object o) throws IOException { String stringValue; if (!(o instanceof String) || trim) { stringValue = NLS.toMachineString(o); } else { stringValue = Strings.toString(o); } if (stringValue == null) { stringValue = ""; } StringBuilder effectiveValue = new StringBuilder(); boolean shouldQuote = shouldQuote(stringValue); for (int i = 0; i < stringValue.length(); i++) { char currentChar = stringValue.charAt(i); processCharacter(currentChar, effectiveValue, shouldQuote); } if (shouldQuote) { writer.append(quotation); writer.append(effectiveValue); writer.append(quotation); } else { writer.append(effectiveValue); } } private boolean shouldQuote(String stringValue) { if (isQuotationEmpty) { return false; } return stringValue.contains(separatorString) || stringValue.contains("\n") || stringValue.contains("\r"); } private void processCharacter(char currentChar, StringBuilder effectiveValue, boolean shouldQuote) { if (currentChar == escape) { effectiveValue.append(escape).append(currentChar); return; } if (isQuotationEmpty) { processCharacterWithoutQuotation(currentChar, effectiveValue); return; } if (shouldQuote && currentChar == quotation) { if (isEscapeEmpty) { throw new IllegalArgumentException( "Cannot output a quotation character within a quoted string without an escape character."); } else { effectiveValue.append(escape); } } effectiveValue.append(currentChar); } private void processCharacterWithoutQuotation(char currentChar, StringBuilder effectiveValue) { if (currentChar == separator) { if (isEscapeEmpty) { throw new IllegalArgumentException(Strings.apply( "Cannot output a column which contains the separator character '%s' " + "without an escape or quotation character.", separator)); } else { effectiveValue.append(escape).append(currentChar); } } else if (currentChar == '\r' || currentChar == '\n') { throw new IllegalArgumentException( "Cannot output a column which contains a line break without an quotation character."); } else { effectiveValue.append(currentChar); } } @Override public void close() throws IOException { writer.close(); } }