18413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe/*
28413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * Copyright (C) 2015 The Android Open Source Project
38413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe *
48413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * Licensed under the Apache License, Version 2.0 (the "License");
58413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * you may not use this file except in compliance with the License.
68413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * You may obtain a copy of the License at
78413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe *
88413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe *      http://www.apache.org/licenses/LICENSE-2.0
98413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe *
108413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * Unless required by applicable law or agreed to in writing, software
118413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * distributed under the License is distributed on an "AS IS" BASIS,
128413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * See the License for the specific language governing permissions and
148413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * limitations under the License.
158413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe */
168413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
178413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampepackage com.android.internal.util;
188413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
198413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampeimport java.io.PrintWriter;
208413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampeimport java.io.Writer;
218413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampeimport java.util.Arrays;
228413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
238413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe/**
248413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * A writer that breaks up its output into chunks before writing to its out writer,
258413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * and which is linebreak aware, i.e., chunks will created along line breaks, if
268413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * possible.
278413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe *
288413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe * Note: this class is not thread-safe.
298413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe */
308413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampepublic class LineBreakBufferedWriter extends PrintWriter {
318413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
328413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
338413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * A buffer to collect data until the buffer size is reached.
348413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     *
358413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Note: we manage a char[] ourselves to avoid an allocation when printing to the
368413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     *       out writer. Otherwise a StringBuilder would have been simpler to use.
378413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
388413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private char[] buffer;
398413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
408413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
418413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * The index of the first free element in the buffer.
428413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
438413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private int bufferIndex;
448413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
458413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
468413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * The chunk size (=maximum buffer size) to use for this writer.
478413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
488413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private final int bufferSize;
498413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
508413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
518413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
528413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Index of the last newline character discovered in the buffer. The writer will try
538413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * to split there.
548413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
558413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private int lastNewline = -1;
568413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
578413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
588413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * The line separator for println().
598413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
608413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private final String lineSeparator;
618413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
628413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
638413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Create a new linebreak-aware buffered writer with the given output and buffer
648413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * size. The initial capacity will be a default value.
658413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param out The writer to write to.
668413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param bufferSize The maximum buffer size.
678413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
688413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public LineBreakBufferedWriter(Writer out, int bufferSize) {
698413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        this(out, bufferSize, 16);  // 16 is the default size of a StringBuilder buffer.
708413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
718413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
728413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
738413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Create a new linebreak-aware buffered writer with the given output, buffer
748413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * size and initial capacity.
758413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param out The writer to write to.
768413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param bufferSize The maximum buffer size.
778413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param initialCapacity The initial capacity of the internal buffer.
788413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
798413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public LineBreakBufferedWriter(Writer out, int bufferSize, int initialCapacity) {
808413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        super(out);
818413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        this.buffer = new char[Math.min(initialCapacity, bufferSize)];
828413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        this.bufferIndex = 0;
838413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        this.bufferSize = bufferSize;
848413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        this.lineSeparator = System.getProperty("line.separator");
858413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
868413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
878413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
888413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Flush the current buffer. This will ignore line breaks.
898413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
908413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    @Override
918413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public void flush() {
928413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        writeBuffer(bufferIndex);
938413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        bufferIndex = 0;
948413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        super.flush();
958413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
968413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
978413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    @Override
988413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public void write(int c) {
99f72cc9437b7592980003050c492526c660dc5b14Andreas Gampe        if (bufferIndex < buffer.length) {
1008413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            buffer[bufferIndex] = (char)c;
1018413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            bufferIndex++;
1028413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            if ((char)c == '\n') {
1038413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                lastNewline = bufferIndex;
1048413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
1058413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        } else {
1068413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // This should be an uncommon case, we mostly expect char[] and String. So
1078413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // let the chunking be handled by the char[] case.
1088413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            write(new char[] { (char)c }, 0 ,1);
1098413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
1108413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
1118413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1128413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    @Override
1138413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public void println() {
1148413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        write(lineSeparator);
1158413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
1168413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1178413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    @Override
1188413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public void write(char[] buf, int off, int len) {
1198413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        while (bufferIndex + len > bufferSize) {
1208413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Find the next newline in the buffer, see if that's below the limit.
1218413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Repeat.
1228413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            int nextNewLine = -1;
1238413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            int maxLength = bufferSize - bufferIndex;
1248413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            for (int i = 0; i < maxLength; i++) {
1258413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                if (buf[off + i] == '\n') {
1268413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    if (bufferIndex + i < bufferSize) {
1278413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                        nextNewLine = i;
1288413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    } else {
1298413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                        break;
1308413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    }
1318413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                }
1328413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
1338413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1348413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            if (nextNewLine != -1) {
1358413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // We can add some more data.
1368413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                appendToBuffer(buf, off, nextNewLine);
1378413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(bufferIndex);
1388413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                bufferIndex = 0;
1398413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                lastNewline = -1;
1408413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                off += nextNewLine + 1;
1418413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                len -= nextNewLine + 1;
1428413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            } else if (lastNewline != -1) {
1438413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // Use the last newline.
1448413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(lastNewline);
1458413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                removeFromBuffer(lastNewline + 1);
1468413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                lastNewline = -1;
1478413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            } else {
1488413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // OK, there was no newline, break at a full buffer.
1498413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                int rest = bufferSize - bufferIndex;
1508413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                appendToBuffer(buf, off, rest);
1518413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(bufferIndex);
1528413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                bufferIndex = 0;
1538413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                off += rest;
1548413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                len -= rest;
1558413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
1568413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
1578413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1588413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        // Add to the buffer, this will fit.
1598413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (len > 0) {
1608413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Add the chars, find the last newline.
1618413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            appendToBuffer(buf, off, len);
1628413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            for (int i = len - 1; i >= 0; i--) {
1638413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                if (buf[off + i] == '\n') {
1648413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    lastNewline = bufferIndex - len + i;
1658413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    break;
1668413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                }
1678413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
1688413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
1698413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
1708413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1718413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    @Override
1728413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    public void write(String s, int off, int len) {
1738413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        while (bufferIndex + len > bufferSize) {
1748413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Find the next newline in the buffer, see if that's below the limit.
1758413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Repeat.
1768413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            int nextNewLine = -1;
1778413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            int maxLength = bufferSize - bufferIndex;
1788413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            for (int i = 0; i < maxLength; i++) {
1798413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                if (s.charAt(off + i) == '\n') {
1808413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    if (bufferIndex + i < bufferSize) {
1818413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                        nextNewLine = i;
1828413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    } else {
1838413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                        break;
1848413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    }
1858413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                }
1868413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
1878413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
1888413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            if (nextNewLine != -1) {
1898413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // We can add some more data.
1908413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                appendToBuffer(s, off, nextNewLine);
1918413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(bufferIndex);
1928413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                bufferIndex = 0;
1938413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                lastNewline = -1;
1948413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                off += nextNewLine + 1;
1958413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                len -= nextNewLine + 1;
1968413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            } else if (lastNewline != -1) {
1978413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // Use the last newline.
1988413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(lastNewline);
1998413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                removeFromBuffer(lastNewline + 1);
2008413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                lastNewline = -1;
2018413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            } else {
2028413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                // OK, there was no newline, break at a full buffer.
2038413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                int rest = bufferSize - bufferIndex;
2048413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                appendToBuffer(s, off, rest);
2058413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                writeBuffer(bufferIndex);
2068413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                bufferIndex = 0;
2078413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                off += rest;
2088413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                len -= rest;
2098413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
2108413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2118413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2128413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        // Add to the buffer, this will fit.
2138413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (len > 0) {
2148413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            // Add the chars, find the last newline.
2158413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            appendToBuffer(s, off, len);
2168413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            for (int i = len - 1; i >= 0; i--) {
2178413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                if (s.charAt(off + i) == '\n') {
2188413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    lastNewline = bufferIndex - len + i;
2198413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                    break;
2208413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe                }
2218413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            }
2228413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2238413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2248413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2258413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
2268413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Append the characters to the buffer. This will potentially resize the buffer,
2278413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * and move the index along.
2288413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param buf The char[] containing the data.
2298413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param off The start index to copy from.
2308413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param len The number of characters to copy.
2318413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
2328413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private void appendToBuffer(char[] buf, int off, int len) {
2338413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (bufferIndex + len > buffer.length) {
2348413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            ensureCapacity(bufferIndex + len);
2358413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2368413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        System.arraycopy(buf, off, buffer, bufferIndex, len);
2378413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        bufferIndex += len;
2388413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2398413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2408413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
2418413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Append the characters from the given string to the buffer. This will potentially
2428413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * resize the buffer, and move the index along.
2438413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param s The string supplying the characters.
2448413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param off The start index to copy from.
2458413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param len The number of characters to copy.
2468413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
2478413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private void appendToBuffer(String s, int off, int len) {
2488413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (bufferIndex + len > buffer.length) {
2498413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            ensureCapacity(bufferIndex + len);
2508413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2518413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        s.getChars(off, off + len, buffer, bufferIndex);
2528413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        bufferIndex += len;
2538413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2548413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2558413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
2568413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Resize the buffer. We use the usual double-the-size plus constant scheme for
2578413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * amortized O(1) insert. Note: we expect small buffers, so this won't check for
2588413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * overflow.
2598413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param capacity The size to be ensured.
2608413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
2618413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private void ensureCapacity(int capacity) {
2628413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        int newSize = buffer.length * 2 + 2;
2638413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (newSize < capacity) {
2648413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            newSize = capacity;
2658413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2668413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        buffer = Arrays.copyOf(buffer, newSize);
2678413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2688413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2698413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
2708413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Remove the characters up to (and excluding) index i from the buffer. This will
2718413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * not resize the buffer, but will update bufferIndex.
2728413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param i The number of characters to remove from the front.
2738413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
2748413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private void removeFromBuffer(int i) {
2758413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        int rest = bufferIndex - i;
2768413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (rest > 0) {
2778413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            System.arraycopy(buffer, bufferIndex - rest, buffer, 0, rest);
2788413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            bufferIndex = rest;
2798413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        } else {
2808413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            bufferIndex = 0;
2818413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2828413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2838413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe
2848413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    /**
2858413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * Helper method, write the given part of the buffer, [start,length), to the output.
2868413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     * @param length The number of characters to flush.
2878413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe     */
2888413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    private void writeBuffer(int length) {
2898413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        if (length > 0) {
2908413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe            super.write(buffer, 0, length);
2918413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe        }
2928413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe    }
2938413db8c36f4ba5979cdc2b1c3e1429e6ba34d6aAndreas Gampe}
294