package org.embulk.spi.util; import java.io.Writer; import java.io.BufferedWriter; import java.io.OutputStreamWriter; import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CodingErrorAction; import org.embulk.config.Task; import org.embulk.config.Config; import org.embulk.config.ConfigInject; import org.embulk.config.ConfigDefault; import org.embulk.spi.FileOutput; import org.embulk.spi.BufferAllocator; public class LineEncoder implements AutoCloseable { // TODO optimize public interface EncoderTask extends Task { @Config("charset") @ConfigDefault("\"utf-8\"") public Charset getCharset(); @Config("newline") @ConfigDefault("\"CRLF\"") public Newline getNewline(); @ConfigInject public BufferAllocator getBufferAllocator(); } private final String newline; private final FileOutput underlyingFileOutput; private final FileOutputOutputStream outputStream; private Writer writer; public LineEncoder(FileOutput out, EncoderTask task) { CharsetEncoder encoder = task.getCharset() .newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) // TODO configurable? .onUnmappableCharacter(CodingErrorAction.REPLACE); // TODO configurable? this.newline = task.getNewline().getString(); this.underlyingFileOutput = out; this.outputStream = new FileOutputOutputStream(underlyingFileOutput, task.getBufferAllocator(), FileOutputOutputStream.CloseMode.FLUSH_FINISH); this.writer = new BufferedWriter(new OutputStreamWriter(outputStream, encoder), 32*1024); } public void addNewLine() { try { writer.append(newline); } catch (IOException ex) { // unexpected throw new RuntimeException(ex); } } public void addLine(String line) { try { writer.append(line); } catch (IOException ex) { // unexpected throw new RuntimeException(ex); } addNewLine(); } public void addText(String text) { try { writer.append(text); } catch (IOException ex) { // unexpected throw new RuntimeException(ex); } } public void nextFile() { try { writer.flush(); } catch (IOException ex) { // unexpected throw new RuntimeException(ex); } outputStream.nextFile(); } public void finish() { try { if (writer != null) { writer.close(); // FLUSH_FINISH writer = null; // underlyingFileOutput.finish() is already called by close() because CloseMode is FLUSH_FINISH } } catch (IOException ex) { throw new RuntimeException(ex); } } @Override public void close() { try { if (writer != null) { writer.close(); // FLUSH_FINISH writer = null; } underlyingFileOutput.close(); // this is necessary because CloseMode is not FLUSH_FINISH_CLOSE } catch (IOException ex) { // unexpected throw new RuntimeException(ex); } } }