1 |
/* LineNumberReader.java -- A character input stream which counts line numbers |
/* LineNumberReader.java -- A character input stream which counts line numbers |
2 |
Copyright (C) 1998,2003 Free Software Foundation, Inc. |
Copyright (C) 1998, 2003 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
7 |
it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
8 |
the Free Software Foundation; either version 2, or (at your option) |
the Free Software Foundation; either version 2, or (at your option) |
9 |
any later version. |
any later version. |
10 |
|
|
11 |
GNU Classpath is distributed in the hope that it will be useful, but |
GNU Classpath is distributed in the hope that it will be useful, but |
12 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
WITHOUT ANY WARRANTY; without even the implied warranty of |
13 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
|
|
|
38 |
package java.io; |
package java.io; |
39 |
|
|
40 |
/** |
/** |
41 |
* This class functions like a standard <code>Reader</code> except that it |
* This class functions like a standard <code>Reader</code> except that it |
42 |
* counts line numbers, and canonicalizes newline characters. As data |
* counts line numbers, and canonicalizes newline characters. As data |
43 |
* is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered, |
* is read, whenever the char sequences "\r", "\n", or "\r\n" are encountered, |
44 |
* the running line count is incremeted by one. Additionally, the whatever |
* the running line count is incremeted by one. Additionally, the whatever |
45 |
* line termination sequence was encountered will be converted to a "\n" |
* line termination sequence was encountered will be converted to a "\n" |
46 |
* char. Note that this class numbers lines from 0. When the first |
* char. Note that this class numbers lines from 0. When the first |
47 |
* line terminator is encountered, the line number is incremented to 1, and |
* line terminator is encountered, the line number is incremented to 1, and |
48 |
* so on. Also note that actual "\r" and "\n" characters are looked for. |
* so on. Also note that actual "\r" and "\n" characters are looked for. |
49 |
* The system dependent line separator sequence is ignored. |
* The system dependent line separator sequence is ignored. |
50 |
* <p> |
* <p> |
51 |
* This class counts only line termination characters. If the last line |
* This class counts only line termination characters. If the last line |
52 |
* read from the stream does not end in a line termination sequence, it |
* read from the stream does not end in a line termination sequence, it |
53 |
* will not be counted as a line. |
* will not be counted as a line. |
54 |
* |
* |
55 |
* @author Aaron M. Renn (arenn@urbanophile.com) |
* @author Per Bothner <bothner@cygnus.com> |
56 |
*/ |
* @author Aaron M. Renn (arenn@urbanophile.com) |
57 |
|
* @date April 22, 1998. |
58 |
|
*/ |
59 |
|
/* Written using "Java Class Libraries", 2nd edition, plus online |
60 |
|
* API docs for JDK 1.2 beta from http://www.javasoft.com. |
61 |
|
* Status: Believed complete and correct. |
62 |
|
* |
63 |
|
* This implementation has the feature that if '\r' is read, it |
64 |
|
* does not look for a '\n', but immediately returns '\n'. |
65 |
|
* On the next read(), if a '\n' is read, it is skipped. |
66 |
|
* This has the advantage that we do not read (and hang) unnecessarily. |
67 |
|
* |
68 |
|
* This implementation is also minimal in the number of fields it uses. |
69 |
|
*/ |
70 |
public class LineNumberReader extends BufferedReader |
public class LineNumberReader extends BufferedReader |
71 |
{ |
{ |
72 |
|
/** The current line number. */ |
73 |
/*************************************************************************/ |
private int lineNumber; |
|
|
|
|
/* |
|
|
* Instance Variables |
|
|
*/ |
|
|
|
|
|
/** |
|
|
* This variable is used to keep track of the current line number |
|
|
*/ |
|
|
private int line_number; |
|
74 |
|
|
75 |
/** |
/** |
76 |
* This variable is used to keep track of the line number that was |
* Create a new <code>LineNumberReader</code> that reads from the |
77 |
* current when the <code>mark()</code> method was called. |
* specified subordinate <code>Reader</code>. A default 8K char sized |
|
*/ |
|
|
private int marked_line_number; |
|
|
|
|
|
/** |
|
|
* Determines whether or not a '\n' as the first character of a read |
|
|
* is the continuation of a '\r' as the last character of the previous |
|
|
* read or not |
|
|
*/ |
|
|
private boolean continued_newline; |
|
|
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/* |
|
|
* Constructors |
|
|
*/ |
|
|
|
|
|
/** |
|
|
* Create a new <code>LineNumberReader</code> that reads from the |
|
|
* specified subordinate <code>Reader</code>. A default 4096 char sized |
|
78 |
* buffer will be used for reads. |
* buffer will be used for reads. |
79 |
* |
* |
80 |
* @param in The subordinate <code>Reader</code> to read from |
* @param in The subordinate <code>Reader</code> to read from |
81 |
*/ |
*/ |
82 |
public LineNumberReader(Reader in) |
public LineNumberReader(Reader in) |
83 |
{ |
{ |
84 |
this(in, DEFAULT_BUFFER_SIZE); |
super(in, DEFAULT_BUFFER_SIZE); |
85 |
} |
} |
86 |
|
|
|
/*************************************************************************/ |
|
|
|
|
87 |
/** |
/** |
88 |
* This method initializes a new <code>LineNumberReader</code> to read |
* This method initializes a new <code>LineNumberReader</code> to read |
89 |
* from the specified subordinate <code>Reader</code> using the specified |
* from the specified subordinate <code>Reader</code> using the specified |
97 |
super(in, size); |
super(in, size); |
98 |
} |
} |
99 |
|
|
|
/*************************************************************************/ |
|
|
|
|
|
/* |
|
|
* Instance Methods |
|
|
*/ |
|
|
|
|
100 |
/** |
/** |
101 |
* This method returns the current line number |
* This method returns the current line number |
102 |
* |
* |
104 |
*/ |
*/ |
105 |
public int getLineNumber() |
public int getLineNumber() |
106 |
{ |
{ |
107 |
return(line_number); |
return lineNumber; |
108 |
} |
} |
109 |
|
|
|
/*************************************************************************/ |
|
|
|
|
110 |
/** |
/** |
111 |
* This method sets the current line number to the specified value. |
* This method sets the current line number to the specified value. |
112 |
* |
* |
113 |
* @param line_number The new line number |
* @param line_number The new line number |
114 |
*/ |
*/ |
115 |
public void setLineNumber(int line_number) |
public void setLineNumber(int lineNumber) |
116 |
{ |
{ |
117 |
this.line_number = line_number; |
this.lineNumber = lineNumber; |
118 |
} |
} |
119 |
|
|
120 |
/*************************************************************************/ |
private static int countLines (char[] buffer, int off, int len) |
121 |
|
{ |
122 |
|
int count = 0; |
123 |
|
char prev = '\0'; |
124 |
|
for (int i = 0; i < len; i++) |
125 |
|
{ |
126 |
|
char ch = buffer[i+off]; |
127 |
|
if ((ch == '\n' && prev != '\r') || ch == '\r') |
128 |
|
count++; |
129 |
|
prev = ch; |
130 |
|
} |
131 |
|
return count; |
132 |
|
} |
133 |
|
|
134 |
/** |
/** |
135 |
* This method marks a position in the input to which the stream can be |
* This method marks a position in the input to which the stream can be |
136 |
* "reset" char calling the <code>reset()</code> method. The parameter |
* "reset" char calling the <code>reset()</code> method. The parameter |
137 |
* <code>readlimit</code> is the number of chars that can be read from the |
* <code>readlimit</code> is the number of chars that can be read from the |
138 |
* stream after setting the mark before the mark becomes invalid. For |
* stream after setting the mark before the mark becomes invalid. For |
139 |
* example, if <code>mark()</code> is called with a read limit of 10, |
* example, if <code>mark()</code> is called with a read limit of 10, |
140 |
* then when |
* then when |
141 |
* 11 chars of data are read from the stream before the <code>reset()</code> |
* 11 chars of data are read from the stream before the <code>reset()</code> |
142 |
* method is called, then the mark is invalid and the stream object |
* method is called, then the mark is invalid and the stream object |
143 |
* instance is not required to remember the mark. |
* instance is not required to remember the mark. |
144 |
* <p> |
* <p> |
145 |
* In this class, this method will remember the current line number as well |
* In this class, this method will remember the current line number as well |
146 |
* as the current position in the stream. When the <code>reset()</code> |
* as the current position in the stream. When the <code>reset()</code> |
147 |
* method |
* method |
148 |
* is called, the line number will be restored to the saved line number in |
* is called, the line number will be restored to the saved line number in |
149 |
* addition to the stream position. |
* addition to the stream position. |
150 |
* |
* |
151 |
* @param readlimit The number of chars that can be read before the |
* @param readlimit The number of chars that can be read before the |
152 |
* mark becomes invalid |
* mark becomes invalid |
153 |
* |
* |
154 |
* @exception IOException If an error occurs |
* @exception IOException If an error occurs |
155 |
*/ |
*/ |
156 |
public void mark(int readlimit) throws IOException |
public void mark(int readLimit) throws IOException |
157 |
{ |
{ |
158 |
synchronized (lock) { |
synchronized (lock) |
159 |
|
{ |
160 |
super.mark(readlimit); |
// This is basically the same as BufferedReader.mark. |
161 |
|
// However, if the previous character was a '\r', we need to |
162 |
marked_line_number = line_number; |
// save that 'r', in case the next character is a '\n'. |
163 |
|
if (pos + readLimit > limit) |
164 |
} // synchronized |
{ |
165 |
|
int saveCR = (pos > 0 && buffer[pos-1] == '\r') ? 1 : 0; |
166 |
|
char[] old_buffer = buffer; |
167 |
|
if (readLimit > limit) |
168 |
|
buffer = new char[saveCR + readLimit]; |
169 |
|
int copy_start = pos - saveCR; |
170 |
|
limit -= copy_start; |
171 |
|
System.arraycopy(old_buffer, copy_start, buffer, 0, limit); |
172 |
|
pos = saveCR; |
173 |
|
} |
174 |
|
markPos = pos; |
175 |
|
} |
176 |
} |
} |
177 |
|
|
|
/*************************************************************************/ |
|
|
|
|
178 |
/** |
/** |
179 |
* This method resets a stream to the point where the <code>mark()</code> |
* This method resets a stream to the point where the <code>mark()</code> |
180 |
* method |
* method |
181 |
* was called. Any chars that were read after the mark point was set will |
* was called. Any chars that were read after the mark point was set will |
182 |
* be re-read during subsequent reads. |
* be re-read during subsequent reads. |
183 |
* <p> |
* <p> |
184 |
* In this class, this method will also restore the line number that was |
* In this class, this method will also restore the line number that was |
185 |
* current when the <code>mark()</code> method was called. |
* current when the <code>mark()</code> method was called. |
186 |
* |
* |
187 |
* @exception IOException If an error occurs |
* @exception IOException If an error occurs |
188 |
*/ |
*/ |
189 |
public void reset() throws IOException |
public void reset() throws IOException |
190 |
{ |
{ |
191 |
synchronized (lock) { |
synchronized (lock) |
192 |
|
{ |
193 |
super.reset(); |
if (markPos < 0) |
194 |
|
throw new IOException("mark never set or invalidated"); |
195 |
line_number = marked_line_number; |
if (markPos > 0 && pos > markPos && buffer[markPos-1] == '\r' |
196 |
|
&& buffer[markPos] == '\n') |
197 |
} // synchronized |
lineNumber--; |
198 |
|
lineNumber -= countLines(buffer, markPos, pos - markPos); |
199 |
|
pos = markPos; |
200 |
|
} |
201 |
} |
} |
202 |
|
|
|
/*************************************************************************/ |
|
|
|
|
203 |
/** |
/** |
204 |
* This method reads an unsigned char from the input stream and returns it |
* This method reads an unsigned char from the input stream and returns it |
205 |
* as an int in the range of 0-255. This method will return -1 if the |
* as an int in the range of 0-65535. This method will return -1 if the |
206 |
* end of the stream has been reached. |
* end of the stream has been reached. |
207 |
* <p> |
* <p> |
208 |
* Note that if a line termination sequence is encountered (ie, "\r", |
* Note that if a line termination sequence is encountered (ie, "\r", |
215 |
* to be read. |
* to be read. |
216 |
* |
* |
217 |
* @return The char read or -1 if end of stream |
* @return The char read or -1 if end of stream |
218 |
* |
* |
219 |
* @exception IOException If an error occurs |
* @exception IOException If an error occurs |
220 |
*/ |
*/ |
221 |
public int read() throws IOException |
public int read() throws IOException |
222 |
{ |
{ |
223 |
synchronized (lock) |
synchronized (lock) |
224 |
{ |
{ |
225 |
int char_read = super.read(); |
skipRedundantLF(); |
226 |
|
if (pos >= limit) |
227 |
if (char_read == '\n') |
{ |
228 |
++line_number; |
if (markPos >= 0 && limit == buffer.length) |
229 |
|
markPos = -1; |
230 |
if (char_read == '\r') |
if (markPos <= 0) |
231 |
{ |
pos = limit = 0; |
232 |
int extra_char_read = super.read(); |
int count = in.read(buffer, limit, buffer.length - limit); |
233 |
|
if (count <= 0) |
234 |
if ((extra_char_read != '\n') && (extra_char_read != -1)) |
return -1; |
235 |
pos--; |
limit += count; |
236 |
|
} |
237 |
char_read = '\n'; |
char ch = buffer[pos++]; |
238 |
++line_number; |
if (ch == '\r' || ch == '\n') |
239 |
} |
{ |
240 |
|
lineNumber++; |
241 |
return(char_read); |
return '\n'; |
242 |
} // synchronized |
} |
243 |
|
return (int) ch; |
244 |
|
} |
245 |
} |
} |
246 |
|
|
|
/*************************************************************************/ |
|
|
|
|
247 |
/** |
/** |
248 |
* This method reads chars from a stream and stores them into a caller |
* This method reads chars from a stream and stores them into a caller |
249 |
* supplied buffer. It starts storing data at index <code>offset</code> into |
* supplied buffer. It starts storing data at index <code>offset</code> into * the buffer and attemps to read <code>len</code> chars. This method can |
|
* the buffer and attemps to read <code>len</code> chars. This method can |
|
250 |
* return before reading the number of chars requested. The actual number |
* return before reading the number of chars requested. The actual number |
251 |
* of chars read is returned as an int. A -1 is returned to indicated the |
* of chars read is returned as an int. A -1 is returned to indicated the |
252 |
* end of the stream. |
* end of the stream. |
266 |
* |
* |
267 |
* @exception IOException If an error occurs. |
* @exception IOException If an error occurs. |
268 |
*/ |
*/ |
269 |
public int read(char[] buf, int offset, int len) throws IOException |
public int read(char[] buf, int offset, int count) throws IOException |
270 |
{ |
{ |
271 |
if (len == 0) |
if (count <= 0) |
|
return(0); |
|
|
|
|
|
synchronized (lock) |
|
272 |
{ |
{ |
273 |
|
if (count < 0) |
274 |
// Read the first char here in order to allow IOException's to |
throw new IndexOutOfBoundsException(); |
275 |
// propagate up |
return 0; |
276 |
int char_read = read(); |
} |
277 |
if (char_read == -1) |
synchronized (lock) |
278 |
return(-1); |
{ |
279 |
buf[offset] = (char)char_read; |
int first = read(); |
280 |
|
if (first < 0) |
281 |
int total_read = 1; |
return -1; |
282 |
|
int start_offset = offset; |
283 |
// Read the rest of the chars. We do this in a single char read() loop, |
buf[offset++] = (char) first; |
284 |
// which is bad and should be fixed in the future. *****RECODE THIS**** |
if (buffer[pos-1] == '\r' && pos < limit && buffer[pos] == '\n') |
285 |
try |
pos++; |
286 |
{ |
count--; |
287 |
for (int i = 1; i < len; i++) |
while (count-- > 0 && pos < limit) |
288 |
{ |
{ |
289 |
char_read = read(); |
char ch = buffer[pos++]; |
290 |
if (char_read == -1) |
if (ch == '\r') |
291 |
return(total_read); |
{ |
292 |
|
lineNumber++; |
293 |
buf[offset + i] = (char)char_read; |
ch = '\n'; |
294 |
|
if (pos < limit && buffer[pos] == '\n') |
295 |
++total_read; |
pos++; |
296 |
} |
} |
297 |
} |
else if (ch == '\n') |
298 |
catch (IOException e) |
lineNumber++; |
299 |
{ |
buf[offset++] = ch; |
300 |
return(total_read); |
} |
301 |
} |
return offset - start_offset; |
302 |
|
} |
|
return(total_read); |
|
|
} // synchronized |
|
303 |
} |
} |
304 |
|
|
305 |
/*************************************************************************/ |
private void skipRedundantLF() throws IOException |
306 |
|
{ |
307 |
|
if (pos > 0 && buffer[pos-1] == '\r') |
308 |
|
{ |
309 |
|
if (pos < limit) |
310 |
|
{ // fast case |
311 |
|
if (buffer[pos] == '\n') |
312 |
|
pos++; |
313 |
|
} |
314 |
|
else |
315 |
|
{ // use read() to deal with the general case. |
316 |
|
// Set pos and limit to zero to avoid infinite recursion in read. |
317 |
|
// May need to invalidate markPos if we've exceeded the buffer. |
318 |
|
if (pos >= buffer.length) |
319 |
|
markPos = -1; |
320 |
|
pos = limit = 0; |
321 |
|
int ch = read(); |
322 |
|
if (ch >= 0 && ch != '\n') |
323 |
|
pos--; |
324 |
|
} |
325 |
|
} |
326 |
|
} |
327 |
|
|
328 |
/** |
/** |
329 |
* This method reads a line of text from the input stream and returns |
* This method reads a line of text from the input stream and returns |
331 |
* by a "\r", "\n", or "\r\n" sequence, not by the system dependent line |
* by a "\r", "\n", or "\r\n" sequence, not by the system dependent line |
332 |
* separator. |
* separator. |
333 |
* |
* |
334 |
* @return The line read as a <code>String</code> or <code>null</code> |
* @return The line read as a <code>String</code> or <code>null</code> |
335 |
* if end of stream. |
* if end of stream. |
336 |
* |
* |
337 |
* @exception IOException If an error occurs |
* @exception IOException If an error occurs |
338 |
*/ |
*/ |
339 |
public String readLine() throws IOException |
public String readLine() throws IOException |
340 |
{ |
{ |
341 |
synchronized (lock) |
// BufferedReader.readLine already does this. Shouldn't need to keep |
342 |
{ |
// track of newlines (since the read method deals with this for us). |
343 |
String line = super.readLine(); |
// But if the buffer is large, we may not call the read method at all |
344 |
if (line != null) |
// and super.readLine can't increment lineNumber itself. |
345 |
++line_number; |
// Though it may seem kludgy, the safest thing to do is to save off |
346 |
|
// lineNumber and increment it explicitly when we're done (iff we |
347 |
|
// ended with a '\n' or '\r' as opposed to EOF). |
348 |
|
// |
349 |
|
// Also, we need to undo the special casing done by BufferedReader.readLine |
350 |
|
// when a '\r' is the last char in the buffer. That situation is marked |
351 |
|
// by 'pos > limit'. |
352 |
|
int tmpLineNumber = lineNumber; |
353 |
|
skipRedundantLF(); |
354 |
|
String str = super.readLine(); |
355 |
|
if (pos > limit) |
356 |
|
--pos; |
357 |
|
|
358 |
|
int ch; |
359 |
|
if (pos > 0 && ((ch = buffer[pos - 1]) == '\n' || ch == '\r')) |
360 |
|
lineNumber = tmpLineNumber + 1; |
361 |
|
|
362 |
return(line); |
return str; |
|
} // synchronized |
|
363 |
} |
} |
364 |
|
|
|
/*************************************************************************/ |
|
|
|
|
365 |
/** |
/** |
366 |
* This method skips over characters in the stream. This method will |
* This method skips over characters in the stream. This method will |
367 |
* skip the specified number of characters if possible, but is not required |
* skip the specified number of characters if possible, but is not required |
368 |
* to skip them all. The actual number of characters skipped is returned. |
* to skip them all. The actual number of characters skipped is returned. |
369 |
* This method returns 0 if the specified number of chars is less than 1. |
* This method returns 0 if the specified number of chars is less than 1. |
370 |
* |
* |
371 |
* @param num_chars The specified number of chars to skip. |
* @param count The specified number of chars to skip. |
372 |
* |
* |
373 |
* @return The actual number of chars skipped. |
* @return The actual number of chars skipped. |
374 |
* |
* |
375 |
* @exception IOException If an error occurs |
* @exception IOException If an error occurs |
376 |
*/ |
*/ |
377 |
public long skip(long num_chars) throws IOException |
public long skip(long count) throws IOException |
378 |
{ |
{ |
379 |
// The spec says to override this method so we do even though we |
return(super.skip(count)); |
380 |
// don't need to. Assume we aren't counting lines in skipped chars. |
// FIXME: This method not merged from gcj. The code below is the original |
381 |
return(super.skip(num_chars)); |
// gcj code that doesn't work. I'm not sure if the method above (from |
382 |
|
// Classpath) is broken or not, but it seems to work correctly in all |
383 |
|
// methods I've tested. If somebody wants to investigate why the code |
384 |
|
// below fails, feel free. |
385 |
|
/* |
386 |
|
if (count <= 0) |
387 |
|
return 0; |
388 |
|
long to_do = count; |
389 |
|
do |
390 |
|
{ |
391 |
|
int ch = read(); |
392 |
|
if (ch < 0) |
393 |
|
break; |
394 |
|
to_do--; |
395 |
|
if (ch == '\n' || ch == '\r') |
396 |
|
lineNumber++; |
397 |
|
else |
398 |
|
{ |
399 |
|
long fence = pos + to_do; |
400 |
|
if (limit < fence) |
401 |
|
fence = limit; |
402 |
|
int end = pos; |
403 |
|
for (; end < fence; end++) |
404 |
|
{ |
405 |
|
char endch = buffer[end]; |
406 |
|
if (endch == '\n' || endch == '\r') |
407 |
|
break; |
408 |
|
} |
409 |
|
to_do -= end - pos; |
410 |
|
pos = end; |
411 |
|
} |
412 |
|
} |
413 |
|
while (to_do > 0); |
414 |
|
return count - to_do; |
415 |
|
*/ |
416 |
} |
} |
417 |
|
} |
|
} // class LineNumberReader |
|
418 |
|
|