LineNumberInputStream.java revision b9cc455ed89df1a0cf4186c92b352c9649995d96
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import java.util.Arrays;
21import libcore.base.Streams;
22
23/**
24 * Wraps an existing {@link InputStream} and counts the line terminators
25 * encountered while reading the data. Line numbering starts at 0. Recognized
26 * line terminator sequences are {@code '\r'}, {@code '\n'} and {@code "\r\n"}.
27 * When using {@code read}, line terminator sequences are always translated into
28 * {@code '\n'}.
29 *
30 * @deprecated Use {@link LineNumberReader}
31 */
32@Deprecated
33public class LineNumberInputStream extends FilterInputStream {
34
35    private int lineNumber;
36
37    private int markedLineNumber = -1;
38
39    private int lastChar = -1;
40
41    private int markedLastChar;
42
43    /**
44     * Constructs a new {@code LineNumberInputStream} on the {@link InputStream}
45     * {@code in}. Line numbers are counted for all data read from this stream.
46     *
47     * <p><strong>Warning:</strong> passing a null source creates an invalid
48     * {@code LineNumberInputStream}. All operations on such a stream will fail.
49     *
50     * @param in
51     *            The non-null input stream to count line numbers.
52     */
53    public LineNumberInputStream(InputStream in) {
54        super(in);
55    }
56
57    /**
58     * {@inheritDoc}
59     *
60     * <p>Note that the source stream may just be a sequence of {@code "\r\n"} bytes
61     * which are converted into {@code '\n'} by this stream. Therefore,
62     * {@code available} returns only {@code in.available() / 2} bytes as
63     * result.
64     */
65    @Override
66    public int available() throws IOException {
67        return in.available() / 2 + (lastChar == -1 ? 0 : 1);
68    }
69
70    /**
71     * Returns the current line number for this stream. Numbering starts at 0.
72     *
73     * @return the current line number.
74     */
75    public int getLineNumber() {
76        return lineNumber;
77    }
78
79    /**
80     * Sets a mark position in this stream. The parameter {@code readlimit}
81     * indicates how many bytes can be read before the mark is invalidated.
82     * Sending {@code reset()} will reposition this stream back to the marked
83     * position, provided that {@code readlimit} has not been surpassed.
84     * The line number count will also be reset to the last marked
85     * line number count.
86     * <p>
87     * This implementation sets a mark in the filtered stream.
88     *
89     * @param readlimit
90     *            the number of bytes that can be read from this stream before
91     *            the mark is invalidated.
92     * @see #markSupported()
93     * @see #reset()
94     */
95    @Override
96    public void mark(int readlimit) {
97        in.mark(readlimit);
98        markedLineNumber = lineNumber;
99        markedLastChar = lastChar;
100    }
101
102    /**
103     * Reads a single byte from the filtered stream and returns it as an integer
104     * in the range from 0 to 255. Returns -1 if the end of this stream has been
105     * reached.
106     * <p>
107     * The line number count is incremented if a line terminator is encountered.
108     * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and
109     * {@code "\r\n"}. Line terminator sequences are always translated into
110     * {@code '\n'}.
111     *
112     * @return the byte read or -1 if the end of the filtered stream has been
113     *         reached.
114     * @throws IOException
115     *             if the stream is closed or another IOException occurs.
116     */
117    @SuppressWarnings("fallthrough")
118    @Override
119    public int read() throws IOException {
120        int currentChar = lastChar;
121        if (currentChar == -1) {
122            currentChar = in.read();
123        } else {
124            lastChar = -1;
125        }
126        switch (currentChar) {
127            case '\r':
128                currentChar = '\n';
129                lastChar = in.read();
130                if (lastChar == '\n') {
131                    lastChar = -1;
132                }
133                // fall through
134            case '\n':
135                lineNumber++;
136        }
137        return currentChar;
138    }
139
140    /**
141     * Reads at most {@code length} bytes from the filtered stream and stores
142     * them in the byte array {@code buffer} starting at {@code offset}.
143     * Returns the number of bytes actually read or -1 if no bytes have been
144     * read and the end of this stream has been reached.
145     * <p>
146     * The line number count is incremented if a line terminator is encountered.
147     * Recognized line terminator sequences are {@code '\r'}, {@code '\n'} and
148     * {@code "\r\n"}. Line terminator sequences are always translated into
149     * {@code '\n'}.
150     *
151     * @param buffer
152     *            the array in which to store the bytes read.
153     * @param offset
154     *            the initial position in {@code buffer} to store the bytes read
155     *            from this stream.
156     * @param length
157     *            the maximum number of bytes to store in {@code buffer}.
158     * @return the number of bytes actually read or -1 if the end of the
159     *         filtered stream has been reached while reading.
160     * @throws IndexOutOfBoundsException
161     *             if {@code offset < 0} or {@code length < 0}, or if
162     *             {@code offset + length} is greater than the length of
163     *             {@code buffer}.
164     * @throws IOException
165     *             if this stream is closed or another IOException occurs.
166     * @throws NullPointerException
167     *             if {@code buffer} is {@code null}.
168     */
169    @Override
170    public int read(byte[] buffer, int offset, int length) throws IOException {
171        Arrays.checkOffsetAndCount(buffer.length, offset, length);
172        for (int i = 0; i < length; i++) {
173            int currentChar;
174            try {
175                currentChar = read();
176            } catch (IOException e) {
177                if (i != 0) {
178                    return i;
179                }
180                throw e;
181            }
182            if (currentChar == -1) {
183                return i == 0 ? -1 : i;
184            }
185            buffer[offset + i] = (byte) currentChar;
186        }
187        return length;
188    }
189
190    /**
191     * Resets this stream to the last marked location. It also resets the line
192     * count to what is was when this stream was marked.
193     *
194     * @throws IOException
195     *             if this stream is already closed, no mark has been set or the
196     *             mark is no longer valid because more than {@code readlimit}
197     *             bytes have been read since setting the mark.
198     * @see #mark(int)
199     * @see #markSupported()
200     */
201    @Override
202    public void reset() throws IOException {
203        in.reset();
204        lineNumber = markedLineNumber;
205        lastChar = markedLastChar;
206    }
207
208    /**
209     * Sets the line number of this stream to the specified
210     * {@code lineNumber}. Note that this may have side effects on the
211     * line number associated with the last marked position.
212     *
213     * @param lineNumber
214     *            the new lineNumber value.
215     * @see #mark(int)
216     * @see #reset()
217     */
218    public void setLineNumber(int lineNumber) {
219        this.lineNumber = lineNumber;
220    }
221
222    /**
223     * Skips {@code count} number of bytes in this stream. Subsequent
224     * {@code read()}'s will not return these bytes unless {@code reset()} is
225     * used. This implementation skips {@code count} number of bytes in the
226     * filtered stream and increments the line number count whenever line
227     * terminator sequences are skipped.
228     *
229     * @param byteCount
230     *            the number of bytes to skip.
231     * @return the number of bytes actually skipped.
232     * @throws IOException
233     *             if this stream is closed or another IOException occurs.
234     * @see #mark(int)
235     * @see #read()
236     * @see #reset()
237     */
238    @Override
239    public long skip(long byteCount) throws IOException {
240        return Streams.skipByReading(this, byteCount);
241    }
242}
243