1ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko/*
2ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * Copyright (C) 2012 The Android Open Source Project
3ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
4ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * Licensed under the Apache License, Version 2.0 (the "License");
5ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * you may not use this file except in compliance with the License.
6ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * You may obtain a copy of the License at
7ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
8ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *      http://www.apache.org/licenses/LICENSE-2.0
9ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
10ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * Unless required by applicable law or agreed to in writing, software
11ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * distributed under the License is distributed on an "AS IS" BASIS,
12ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * See the License for the specific language governing permissions and
14ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * limitations under the License.
15ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko */
16ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
17ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markopackage libcore.io;
18ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
19ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.io.ByteArrayOutputStream;
20ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.io.Closeable;
21ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.io.EOFException;
22ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.io.InputStream;
23ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.io.IOException;
24ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markoimport java.nio.charset.Charset;
252a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughesimport java.nio.charset.StandardCharsets;
26ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
27ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko/**
28ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * Buffers input from an {@link InputStream} for reading lines.
29ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
30ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * This class is used for buffered reading of lines. For purposes of this class, a line ends with
31ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated line at
32ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * end of input is invalid and will be ignored, the caller may use {@code hasUnterminatedLine()}
33ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * to detect it after catching the {@code EOFException}.
34ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
35ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * This class is intended for reading input that strictly consists of lines, such as line-based
36ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * cache entries or cache journal. Unlike the {@link BufferedReader} which in conjunction with
37ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * {@link InputStreamReader} provides similar functionality, this class uses different
38ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * end-of-input reporting and a more restrictive definition of a line.
39ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko *
40ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
41ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * and 10, respectively, and the representation of no other character contains these values.
42ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
43ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko * The default charset is US_ASCII.
44ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko */
45ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Markopublic class StrictLineReader implements Closeable {
46ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private static final byte CR = (byte)'\r';
47ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private static final byte LF = (byte)'\n';
48ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
49ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private final InputStream in;
50ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private final Charset charset;
51ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
52ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /*
53ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
54ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
55ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
56ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
57ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
58ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private byte[] buf;
59ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private int pos;
60ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private int end;
61ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
62ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
63ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Constructs a new {@code StrictLineReader} with the default capacity and charset.
64ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
65ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param in the {@code InputStream} to read data from.
66ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws NullPointerException if {@code in} is null.
67ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
68ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public StrictLineReader(InputStream in) {
69ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        this(in, 8192);
70ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
71ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
72ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
73ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Constructs a new {@code LineReader} with the specified capacity and the default charset.
74ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
75ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param in the {@code InputStream} to read data from.
76ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param capacity the capacity of the buffer.
77ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws NullPointerException if {@code in} is null.
78ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IllegalArgumentException for negative or zero {@code capacity}.
79ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
80ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public StrictLineReader(InputStream in, int capacity) {
812a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes        this(in, capacity, StandardCharsets.US_ASCII);
82ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
83ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
84ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
85ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Constructs a new {@code LineReader} with the specified charset and the default capacity.
86ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
87ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param in the {@code InputStream} to read data from.
88ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param charset the charset used to decode data.
89ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
90ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws NullPointerException if {@code in} or {@code charset} is null.
91ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IllegalArgumentException if the specified charset is not supported.
92ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
93ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public StrictLineReader(InputStream in, Charset charset) {
94ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        this(in, 8192, charset);
95ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
96ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
97ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
98ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Constructs a new {@code LineReader} with the specified capacity and charset.
99ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
100ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param in the {@code InputStream} to read data from.
101ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param capacity the capacity of the buffer.
102ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @param charset the charset used to decode data.
103ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *         Only US-ASCII, UTF-8 and ISO-8859-1 is supported.
104ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws NullPointerException if {@code in} or {@code charset} is null.
105ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IllegalArgumentException if {@code capacity} is negative or zero
106ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *         or the specified charset is not supported.
107ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
108ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public StrictLineReader(InputStream in, int capacity, Charset charset) {
10986acc043d3334651ee26c65467d78d6cefedd397Kenny Root        if (in == null) {
11086acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("in == null");
11186acc043d3334651ee26c65467d78d6cefedd397Kenny Root        } else if (charset == null) {
11286acc043d3334651ee26c65467d78d6cefedd397Kenny Root            throw new NullPointerException("charset == null");
113ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
114ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        if (capacity < 0) {
115ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            throw new IllegalArgumentException("capacity <= 0");
116ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
1172a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes        if (!(charset.equals(StandardCharsets.US_ASCII) || charset.equals(StandardCharsets.UTF_8) ||
1182a6f23ff8690ac2f025588a360547ce96cde0943Elliott Hughes                charset.equals(StandardCharsets.ISO_8859_1))) {
119ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            throw new IllegalArgumentException("Unsupported encoding");
120ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
121ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
122ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        this.in = in;
123ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        this.charset = charset;
124ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        buf = new byte[capacity];
125ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
126ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
127ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
128ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Closes the reader by closing the underlying {@code InputStream} and
129ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * marking this reader as closed.
130ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
131ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IOException for errors when closing the underlying {@code InputStream}.
132ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
133ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    @Override
134ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public void close() throws IOException {
135ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        synchronized (in) {
136ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            if (buf != null) {
137ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                buf = null;
138ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                in.close();
139ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            }
140ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
141ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
142ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
143ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
144ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
145ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * this end of line marker is not included in the result.
146ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
147ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @return the next line from the input.
148ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IOException for underlying {@code InputStream} errors.
149ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws EOFException for the end of source stream.
150ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
151ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public String readLine() throws IOException {
152ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        synchronized (in) {
153ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            if (buf == null) {
154ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                throw new IOException("LineReader is closed");
155ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            }
156ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
157ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            // Read more data if we are at the end of the buffered data.
158ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            // Though it's an error to read after an exception, we will let {@code fillBuf()}
159ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
160ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            if (pos >= end) {
161ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                fillBuf();
162ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            }
163ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            // Try to find LF in the buffered data and return the line if successful.
164ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            for (int i = pos; i != end; ++i) {
165ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                if (buf[i] == LF) {
166ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
167ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    String res = new String(buf, pos, lineEnd - pos, charset);
168ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    pos = i + 1;
169ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    return res;
170ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                }
171ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            }
172ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
173ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            // Let's anticipate up to 80 characters on top of those already read.
174ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
175ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                @Override
176ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                public String toString() {
177ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
178ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    return new String(buf, 0, length, charset);
179ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                }
180ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            };
181ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
182ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            while (true) {
183ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                out.write(buf, pos, end - pos);
184ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                // Mark unterminated line in case fillBuf throws EOFException or IOException.
185ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                end = -1;
186ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                fillBuf();
187ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                // Try to find LF in the buffered data and return the line if successful.
188ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                for (int i = pos; i != end; ++i) {
189ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    if (buf[i] == LF) {
190ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                        if (i != pos) {
191ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                            out.write(buf, pos, i - pos);
192ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                        }
193ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                        pos = i + 1;
194ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                        return out.toString();
195ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                    }
196ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko                }
197ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            }
198ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
199ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
200ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
201ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
202ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Read an {@code int} from a line containing its decimal representation.
203ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
204ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @return the value of the {@code int} from the next line.
205ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IOException for underlying {@code InputStream} errors or conversion error.
206ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws EOFException for the end of source stream.
207ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
208ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public int readInt() throws IOException {
209ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        String intString = readLine();
210ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        try {
211ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            return Integer.parseInt(intString);
212ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        } catch (NumberFormatException e) {
213ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            throw new IOException("expected an int but was \"" + intString + "\"");
214ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
215ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
216ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
217ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
218ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Check whether there was an unterminated line at end of input after the line reader reported
219ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * end-of-input with EOFException. The value is meaningless in any other situation.
220ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
221ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @return true if there was an unterminated line at end of input.
222ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
223ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    public boolean hasUnterminatedLine() {
224ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        return end == -1;
225ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
226ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko
227ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    /**
228ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * Reads new input data into the buffer. Call only with pos == end or end == -1,
229ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * depending on the desired outcome if the function throws.
230ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     *
231ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws IOException for underlying {@code InputStream} errors.
232ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     * @throws EOFException for the end of source stream.
233ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko     */
234ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    private void fillBuf() throws IOException {
235ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        int result = in.read(buf, 0, buf.length);
236ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        if (result == -1) {
237ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko            throw new EOFException();
238ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        }
239ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        pos = 0;
240ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko        end = result;
241ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko    }
242ac665275e161fab8ecd4225e73e159a9edf10501Vladimir Marko}
243