osiegmar / FastCSV

CSV library for Java that is fast, RFC-compliant and dependency-free.
https://fastcsv.org/
MIT License
542 stars 93 forks source link

Use client passed Writer without wrapping it #50

Closed qtxo closed 3 years ago

qtxo commented 3 years ago

Is your feature request related to a problem? Please describe.

Basically CsvWriter fails the Single Responsibility Principle by tackling both high level csv formatting and low level IO buffering, creating problems for non trivial uses.
Some use cases rely on a Writer obtained previously which is used to write more data than just a single CSV file. For example the same stream can contain multiple CSVs or some other data at the end.
The user can not do a writer.flush() because the writer is internally wrapped with CachingWriter so the last bytes may never get written until a csvWriter.close() is issued which closes the writer as well.

Once a Writer is passed its state is unknown until a call to CavWriter.close() is made!

Describe the solution you'd like

Let construct a CsvWriter without messing in any way with the writer passed. Let CsvWriter deal with building the csv data structure, and let the passed Writer deal with the low level stuff.

Describe alternatives you've considered

No alternative possible except a source code modification.

RFC 4180 compliance Would this feature comply to RFC 4180?

Yes, but the important part is that the code will be more correct, because it won't mess with external code passed to it.

osiegmar commented 3 years ago

Your assumption about the usage of an user provided Writer by CsvWriter is incorrect.

If the user creates a CsvWriter using the CsvWriter.CsvWriterBuilder#build(Writer) method, the writeRow method of CsvWriter flushes its internal buffer to the provided writer on every call. This is documented in the JavaDoc of the build method.

Because of that, this works totally fine (exception handling omitted):

PrintWriter printWriter = new PrintWriter(System.out);
CsvWriter csvWriter = CsvWriter.builder().build(printWriter);

csvWriter.writeRow("foo", "bar");
printWriter.println("# my comment");
csvWriter.writeRow("1", "2");

printWriter.close();

And prints:

foo,bar
# my comment
1,2
qtxo commented 3 years ago

CsvWriter flushes its internal buffer to the provided writer on every call

Correct, but how to opt out of that? The only way is to pass a wrapped Writer that ignores flush() to leave the responsibility to the "real" writer ( OutputStreamWriter(GZIPOutputStream(CipherOutputStream)) in my use case)

My point is that this library should allow a "transparent" writer functionality where the Writer is used to only to write data, and leave the buffering and flushing to the application code that owns the Writer.

osiegmar commented 3 years ago

The only way is to pass a wrapped Writer that ignores flush() to leave the responsibility to the "real" writer

Nope. CsvWriter does not call flush in the provided Writer. As I said, CsvWriter flushes its internal buffer. In other words, if enough internal capacity exists, CsvWriter writes to the provided Writer only once (at the end of writeRow).

See: https://github.com/osiegmar/FastCSV/blob/master/src/main/java/de/siegmar/fastcsv/writer/CsvWriter.java#L354

qtxo commented 3 years ago

Yes, you are right, I overlooked that method...