• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.io;
27 
28 
29 /**
30  * A buffered character-input stream that keeps track of line numbers.  This
31  * class defines methods {@link #setLineNumber(int)} and {@link
32  * #getLineNumber()} for setting and getting the current line number
33  * respectively.
34  *
35  * <p> By default, line numbering begins at 0. This number increments at every
36  * <a href="#lt">line terminator</a> as the data is read, and can be changed
37  * with a call to <tt>setLineNumber(int)</tt>.  Note however, that
38  * <tt>setLineNumber(int)</tt> does not actually change the current position in
39  * the stream; it only changes the value that will be returned by
40  * <tt>getLineNumber()</tt>.
41  *
42  * <p> A line is considered to be <a name="lt">terminated</a> by any one of a
43  * line feed ('\n'), a carriage return ('\r'), or a carriage return followed
44  * immediately by a linefeed.
45  *
46  * @author      Mark Reinhold
47  * @since       JDK1.1
48  */
49 
50 public class LineNumberReader extends BufferedReader {
51 
52     /** The current line number */
53     private int lineNumber = 0;
54 
55     /** The line number of the mark, if any */
56     private int markedLineNumber; // Defaults to 0
57 
58     /** If the next character is a line feed, skip it */
59     private boolean skipLF;
60 
61     /** The skipLF flag when the mark was set */
62     private boolean markedSkipLF;
63 
64     /**
65      * Create a new line-numbering reader, using the default input-buffer
66      * size.
67      *
68      * @param  in
69      *         A Reader object to provide the underlying stream
70      */
LineNumberReader(Reader in)71     public LineNumberReader(Reader in) {
72         super(in);
73     }
74 
75     /**
76      * Create a new line-numbering reader, reading characters into a buffer of
77      * the given size.
78      *
79      * @param  in
80      *         A Reader object to provide the underlying stream
81      *
82      * @param  sz
83      *         An int specifying the size of the buffer
84      */
LineNumberReader(Reader in, int sz)85     public LineNumberReader(Reader in, int sz) {
86         super(in, sz);
87     }
88 
89     /**
90      * Set the current line number.
91      *
92      * @param  lineNumber
93      *         An int specifying the line number
94      *
95      * @see #getLineNumber
96      */
setLineNumber(int lineNumber)97     public void setLineNumber(int lineNumber) {
98         this.lineNumber = lineNumber;
99     }
100 
101     /**
102      * Get the current line number.
103      *
104      * @return  The current line number
105      *
106      * @see #setLineNumber
107      */
getLineNumber()108     public int getLineNumber() {
109         return lineNumber;
110     }
111 
112     /**
113      * Read a single character.  <a href="#lt">Line terminators</a> are
114      * compressed into single newline ('\n') characters.  Whenever a line
115      * terminator is read the current line number is incremented.
116      *
117      * @return  The character read, or -1 if the end of the stream has been
118      *          reached
119      *
120      * @throws  IOException
121      *          If an I/O error occurs
122      */
read()123     public int read() throws IOException {
124         synchronized (lock) {
125             int c = super.read();
126             if (skipLF) {
127                 if (c == '\n')
128                     c = super.read();
129                 skipLF = false;
130             }
131             switch (c) {
132             case '\r':
133                 skipLF = true;
134             case '\n':          /* Fall through */
135                 lineNumber++;
136                 return '\n';
137             }
138             return c;
139         }
140     }
141 
142     /**
143      * Read characters into a portion of an array.  Whenever a <a
144      * href="#lt">line terminator</a> is read the current line number is
145      * incremented.
146      *
147      * @param  cbuf
148      *         Destination buffer
149      *
150      * @param  off
151      *         Offset at which to start storing characters
152      *
153      * @param  len
154      *         Maximum number of characters to read
155      *
156      * @return  The number of bytes read, or -1 if the end of the stream has
157      *          already been reached
158      *
159      * @throws  IOException
160      *          If an I/O error occurs
161      */
read(char cbuf[], int off, int len)162     public int read(char cbuf[], int off, int len) throws IOException {
163         synchronized (lock) {
164             int n = super.read(cbuf, off, len);
165 
166             for (int i = off; i < off + n; i++) {
167                 int c = cbuf[i];
168                 if (skipLF) {
169                     skipLF = false;
170                     if (c == '\n')
171                         continue;
172                 }
173                 switch (c) {
174                 case '\r':
175                     skipLF = true;
176                 case '\n':      /* Fall through */
177                     lineNumber++;
178                     break;
179                 }
180             }
181 
182             return n;
183         }
184     }
185 
186     /**
187      * Read a line of text.  Whenever a <a href="#lt">line terminator</a> is
188      * read the current line number is incremented.
189      *
190      * @return  A String containing the contents of the line, not including
191      *          any <a href="#lt">line termination characters</a>, or
192      *          <tt>null</tt> if the end of the stream has been reached
193      *
194      * @throws  IOException
195      *          If an I/O error occurs
196      */
readLine()197     public String readLine() throws IOException {
198         synchronized (lock) {
199             String l = super.readLine(skipLF);
200             skipLF = false;
201             if (l != null)
202                 lineNumber++;
203             return l;
204         }
205     }
206 
207     /** Maximum skip-buffer size */
208     private static final int maxSkipBufferSize = 8192;
209 
210     /** Skip buffer, null until allocated */
211     private char skipBuffer[] = null;
212 
213     /**
214      * Skip characters.
215      *
216      * @param  n
217      *         The number of characters to skip
218      *
219      * @return  The number of characters actually skipped
220      *
221      * @throws  IOException
222      *          If an I/O error occurs
223      *
224      * @throws  IllegalArgumentException
225      *          If <tt>n</tt> is negative
226      */
skip(long n)227     public long skip(long n) throws IOException {
228         if (n < 0)
229             throw new IllegalArgumentException("skip() value is negative");
230         int nn = (int) Math.min(n, maxSkipBufferSize);
231         synchronized (lock) {
232             if ((skipBuffer == null) || (skipBuffer.length < nn))
233                 skipBuffer = new char[nn];
234             long r = n;
235             while (r > 0) {
236                 int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
237                 if (nc == -1)
238                     break;
239                 r -= nc;
240             }
241             return n - r;
242         }
243     }
244 
245     /**
246      * Mark the present position in the stream.  Subsequent calls to reset()
247      * will attempt to reposition the stream to this point, and will also reset
248      * the line number appropriately.
249      *
250      * @param  readAheadLimit
251      *         Limit on the number of characters that may be read while still
252      *         preserving the mark.  After reading this many characters,
253      *         attempting to reset the stream may fail.
254      *
255      * @throws  IOException
256      *          If an I/O error occurs
257      */
mark(int readAheadLimit)258     public void mark(int readAheadLimit) throws IOException {
259         synchronized (lock) {
260             super.mark(readAheadLimit);
261             markedLineNumber = lineNumber;
262             markedSkipLF     = skipLF;
263         }
264     }
265 
266     /**
267      * Reset the stream to the most recent mark.
268      *
269      * @throws  IOException
270      *          If the stream has not been marked, or if the mark has been
271      *          invalidated
272      */
reset()273     public void reset() throws IOException {
274         synchronized (lock) {
275             super.reset();
276             lineNumber = markedLineNumber;
277             skipLF     = markedSkipLF;
278         }
279     }
280 
281 }
282