StrictLineReader.java revision 7899c5ab935cf542069835ec7d3e457db596dbf7
17899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/*
27899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Copyright (C) 2012 The Android Open Source Project
37899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
47899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
57899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * you may not use this file except in compliance with the License.
67899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * You may obtain a copy of the License at
77899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
87899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
97899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Unless required by applicable law or agreed to in writing, software
117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * See the License for the specific language governing permissions and
147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * limitations under the License.
157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
177899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathpackage libcore.io;
187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.ByteArrayOutputStream;
207899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.Closeable;
217899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.EOFException;
227899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.IOException;
237899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.io.InputStream;
247899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport java.nio.charset.Charset;
257899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathimport libcore.util.Charsets;
267899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
277899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath/**
287899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * Buffers input from an {@link InputStream} for reading lines.
297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class is used for buffered reading of lines. For purposes of this class, a line ends with
317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated line at
327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}
337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * to detect it after catching the {@code EOFException}.
347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class is intended for reading input that strictly consists of lines, such as line-based
367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with
377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * {@link InputStreamReader} provides similar functionality, this class uses different
387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * end-of-input reporting and a more restrictive definition of a line.
397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath *
407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * and 10, respectively, and the representation of no other character contains these values.
427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath * The default charset is US_ASCII.
447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath */
457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamathpublic class StrictLineReader implements Closeable {
467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private static final byte CR = (byte) '\r';
477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private static final byte LF = (byte) '\n';
487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
497899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private final InputStream in;
507899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private final Charset charset;
517899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
527899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /*
537899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
557899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
567899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
587899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private byte[] buf;
597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private int pos;
607899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private int end;
617899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
627899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
637899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Constructs a new {@code StrictLineReader} with the default capacity and charset.
647899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param in the {@code InputStream} to read data from.
667899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws NullPointerException if {@code in} is null.
677899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
687899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public StrictLineReader(InputStream in) {
697899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        this(in, 8192);
707899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
717899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
727899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
737899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Constructs a new {@code LineReader} with the specified capacity and the default charset.
747899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
757899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param in the {@code InputStream} to read data from.
767899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param capacity the capacity of the buffer.
777899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws NullPointerException if {@code in} is null.
787899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IllegalArgumentException for negative or zero {@code capacity}.
797899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
807899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public StrictLineReader(InputStream in, int capacity) {
817899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        this(in, capacity, Charsets.US_ASCII);
827899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
837899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
847899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
857899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Constructs a new {@code LineReader} with the specified charset and the default capacity.
867899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
877899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param in the {@code InputStream} to read data from.
887899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param charset the charset used to decode data.
897899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
907899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws NullPointerException if {@code in} or {@code charset} is null.
917899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IllegalArgumentException if the specified charset is not supported.
927899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
937899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public StrictLineReader(InputStream in, Charset charset) {
947899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        this(in, 8192, charset);
957899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
967899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
977899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
987899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Constructs a new {@code LineReader} with the specified capacity and charset.
997899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
1007899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param in the {@code InputStream} to read data from.
1017899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param capacity the capacity of the buffer.
1027899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @param charset the charset used to decode data.
1037899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
1047899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws NullPointerException if {@code in} or {@code charset} is null.
1057899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IllegalArgumentException if {@code capacity} is negative or zero
1067899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *         or the specified charset is not supported.
1077899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
1087899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public StrictLineReader(InputStream in, int capacity, Charset charset) {
1097899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        if (in == null || charset == null) {
1107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            throw new NullPointerException();
1117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
1127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        if (capacity < 0) {
1137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            throw new IllegalArgumentException("capacity <= 0");
1147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
1157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        if (!(charset.equals(Charsets.US_ASCII) || charset.equals(Charsets.UTF_8)
1167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                || charset.equals(Charsets.ISO_8859_1))) {
1177899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            throw new IllegalArgumentException("Unsupported encoding");
1187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
1197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1207899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        this.in = in;
1217899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        this.charset = charset;
1227899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        buf = new byte[capacity];
1237899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
1247899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1257899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
1267899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Closes the reader by closing the underlying {@code InputStream} and
1277899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * marking this reader as closed.
1287899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
1297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IOException for errors when closing the underlying {@code InputStream}.
1307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
1317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    @Override
1327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public void close() throws IOException {
1337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        synchronized (in) {
1347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            if (buf != null) {
1357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                buf = null;
1367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                in.close();
1377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
1387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
1397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
1407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
1427899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
1437899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * this end of line marker is not included in the result.
1447899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
1457899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @return the next line from the input.
1467899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IOException for underlying {@code InputStream} errors.
1477899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws EOFException for the end of source stream.
1487899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
1497899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public String readLine() throws IOException {
1507899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        synchronized (in) {
1517899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            if (buf == null) {
1527899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                throw new IOException("LineReader is closed");
1537899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
1547899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1557899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            // Read more data if we are at the end of the buffered data.
1567899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            // Though it's an error to read after an exception, we will let {@code fillBuf()}
1577899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
1587899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            if (pos >= end) {
1597899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                fillBuf();
1607899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
1617899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            // Try to find LF in the buffered data and return the line if successful.
1627899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            for (int i = pos; i != end; ++i) {
1637899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                if (buf[i] == LF) {
1647899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
1657899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    String res = new String(buf, pos, lineEnd - pos, charset);
1667899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    pos = i + 1;
1677899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    return res;
1687899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                }
1697899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
1707899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1717899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            // Let's anticipate up to 80 characters on top of those already read.
1727899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
1737899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                @Override
1747899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                public String toString() {
1757899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
1767899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    return new String(buf, 0, length, charset);
1777899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                }
1787899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            };
1797899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1807899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            while (true) {
1817899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                out.write(buf, pos, end - pos);
1827899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                // Mark unterminated line in case fillBuf throws EOFException or IOException.
1837899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                end = -1;
1847899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                fillBuf();
1857899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                // Try to find LF in the buffered data and return the line if successful.
1867899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                for (int i = pos; i != end; ++i) {
1877899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    if (buf[i] == LF) {
1887899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                        if (i != pos) {
1897899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                            out.write(buf, pos, i - pos);
1907899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                        }
1917899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                        pos = i + 1;
1927899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                        return out.toString();
1937899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                    }
1947899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath                }
1957899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            }
1967899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
1977899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
1987899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
1997899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
2007899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Read an {@code int} from a line containing its decimal representation.
2017899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
2027899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @return the value of the {@code int} from the next line.
2037899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IOException for underlying {@code InputStream} errors or conversion error.
2047899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws EOFException for the end of source stream.
2057899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
2067899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public int readInt() throws IOException {
2077899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        String intString = readLine();
2087899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        try {
2097899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            return Integer.parseInt(intString);
2107899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        } catch (NumberFormatException e) {
2117899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            throw new IOException("expected an int but was \"" + intString + "\"");
2127899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
2137899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
2147899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
2157899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
2167899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Check whether there was an unterminated line at end of input after the line reader reported
2177899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * end-of-input with EOFException. The value is meaningless in any other situation.
2187899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
2197899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @return true if there was an unterminated line at end of input.
2207899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
2217899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    public boolean hasUnterminatedLine() {
2227899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        return end == -1;
2237899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
2247899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
2257899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    /**
2267899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * Reads new input data into the buffer. Call only with pos == end or end == -1,
2277899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * depending on the desired outcome if the function throws.
2287899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     *
2297899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws IOException for underlying {@code InputStream} errors.
2307899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     * @throws EOFException for the end of source stream.
2317899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath     */
2327899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    private void fillBuf() throws IOException {
2337899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        int result = in.read(buf, 0, buf.length);
2347899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        if (result == -1) {
2357899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath            throw new EOFException();
2367899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        }
2377899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        pos = 0;
2387899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath        end = result;
2397899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath    }
2407899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath}
2417899c5ab935cf542069835ec7d3e457db596dbf7Narayan Kamath
242