src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/utils/WriterOutputStream.java Tue Dec 11 11:29:28 2018 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2002-2017, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * http://www.opensource.org/licenses/bsd-license.php
+ */
+package jdk.internal.org.jline.utils;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+
+/**
+ * Redirects an {@link OutputStream} to a {@link Writer} by decoding the data
+ * using the specified {@link Charset}.
+ *
+ * <p><b>Note:</b> This class should only be used if it is necessary to
+ * redirect an {@link OutputStream} to a {@link Writer} for compatibility
+ * purposes. It is much more efficient to write to the {@link Writer}
+ * directly.</p>
+ */
+public class WriterOutputStream extends OutputStream {
+
+ private final Writer out;
+ private final CharsetDecoder decoder;
+ private final ByteBuffer decoderIn = ByteBuffer.allocate(256);
+ private final CharBuffer decoderOut = CharBuffer.allocate(128);
+
+ public WriterOutputStream(Writer out, Charset charset) {
+ this(out, charset.newDecoder()
+ .onMalformedInput(CodingErrorAction.REPLACE)
+ .onUnmappableCharacter(CodingErrorAction.REPLACE));
+ }
+
+ public WriterOutputStream(Writer out, CharsetDecoder decoder) {
+ this.out = out;
+ this.decoder = decoder;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ write(new byte[] { (byte)b }, 0, 1);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ while (len > 0) {
+ final int c = Math.min(len, decoderIn.remaining());
+ decoderIn.put(b, off, c);
+ processInput(false);
+ len -= c;
+ off += c;
+ }
+ flush();
+ }
+
+ @Override
+ public void flush() throws IOException {
+ flushOutput();
+ out.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ processInput(true);
+ flush();
+ out.close();
+ }
+
+ /**
+ * Decode the contents of the input ByteBuffer into a CharBuffer.
+ *
+ * @param endOfInput indicates end of input
+ * @throws IOException if an I/O error occurs
+ */
+ private void processInput(final boolean endOfInput) throws IOException {
+ // Prepare decoderIn for reading
+ decoderIn.flip();
+ CoderResult coderResult;
+ while (true) {
+ coderResult = decoder.decode(decoderIn, decoderOut, endOfInput);
+ if (coderResult.isOverflow()) {
+ flushOutput();
+ } else if (coderResult.isUnderflow()) {
+ break;
+ } else {
+ // The decoder is configured to replace malformed input and unmappable characters,
+ // so we should not get here.
+ throw new IOException("Unexpected coder result");
+ }
+ }
+ // Discard the bytes that have been read
+ decoderIn.compact();
+ }
+
+ /**
+ * Flush the output.
+ *
+ * @throws IOException if an I/O error occurs
+ */
+ private void flushOutput() throws IOException {
+ if (decoderOut.position() > 0) {
+ out.write(decoderOut.array(), 0, decoderOut.position());
+ decoderOut.rewind();
+ }
+ }
+}