001: /*
002: * @(#)LineNumberReader.java 1.20 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.io;
029:
030: /**
031: * A buffered character-input stream that keeps track of line numbers.
032: * This class defines methods <CODE>void setLineNumber(int)</CODE> and
033: * <CODE>int getLineNumber()</CODE> for setting and getting the current
034: * line number respectively.
035: * <P>
036: * By default, line numbering begins at 0. This number increments as data is
037: * read, and can be changed with a call to <CODE>setLineNumber(int)</CODE>.
038: * Note however, that <CODE>setLineNumber(int)</CODE> does not actually change the current
039: * position in the stream; it only changes the value that will be returned
040: * by <CODE>getLineNumber()</CODE>.
041: * <P>
042: * A line is considered to be terminated by any one of a line feed ('\n'), a carriage
043: * return ('\r'), or a carriage return followed immediately by a linefeed.
044: *
045: * @version 1.13, 00/02/02
046: * @author Mark Reinhold
047: * @since JDK1.1
048: */
049:
050: public class LineNumberReader extends BufferedReader {
051:
052: /** The current line number */
053: private int lineNumber = 0;
054:
055: /** The line number of the mark, if any */
056: private int markedLineNumber;
057:
058: /** If the next character is a line feed, skip it */
059: private boolean skipLF;
060:
061: /** The skipLF flag when the mark was set */
062: private boolean markedSkipLF;
063:
064: /**
065: * Create a new line-numbering reader, using the default input-buffer
066: * size.
067: *
068: * @param in a Reader object to provide the underlying stream.
069: */
070: public LineNumberReader(Reader in) {
071: super (in);
072: }
073:
074: /**
075: * Create a new line-numbering reader, reading characters into a buffer of
076: * the given size.
077: *
078: * @param in a Reader object to provide the underlying stream.
079: * @param sz an int specifying the size of the buffer.
080: */
081: public LineNumberReader(Reader in, int sz) {
082: super (in, sz);
083: }
084:
085: /**
086: * Set the current line number.
087: *
088: * @param lineNumber an int specifying the line number.
089: * @see #getLineNumber
090: */
091: public void setLineNumber(int lineNumber) {
092: this .lineNumber = lineNumber;
093: }
094:
095: /**
096: * Get the current line number.
097: *
098: * @return The current line number.
099: * @see #setLineNumber
100: */
101: public int getLineNumber() {
102: return lineNumber;
103: }
104:
105: /**
106: * Read a single character. Line terminators are compressed into single
107: * newline ('\n') characters.
108: *
109: * @return The character read, or -1 if the end of the stream has been
110: * reached
111: *
112: * @exception IOException If an I/O error occurs
113: */
114: public int read() throws IOException {
115: synchronized (lock) {
116: int c = super .read();
117: if (skipLF) {
118: if (c == '\n')
119: c = super .read();
120: skipLF = false;
121: }
122: switch (c) {
123: case '\r':
124: skipLF = true;
125: case '\n': /* Fall through */
126: lineNumber++;
127: return '\n';
128: }
129: return c;
130: }
131: }
132:
133: /**
134: * Read characters into a portion of an array.
135: *
136: * @param cbuf Destination buffer
137: * @param off Offset at which to start storing characters
138: * @param len Maximum number of characters to read
139: *
140: * @return The number of bytes read, or -1 if the end of the stream has
141: * already been reached
142: *
143: * @exception IOException If an I/O error occurs
144: */
145: public int read(char cbuf[], int off, int len) throws IOException {
146: synchronized (lock) {
147: int n = super .read(cbuf, off, len);
148:
149: for (int i = off; i < off + n; i++) {
150: int c = cbuf[i];
151: if (skipLF) {
152: skipLF = false;
153: if (c == '\n')
154: continue;
155: }
156: switch (c) {
157: case '\r':
158: skipLF = true;
159: case '\n': /* Fall through */
160: lineNumber++;
161: break;
162: }
163: }
164:
165: return n;
166: }
167: }
168:
169: /**
170: * Read a line of text. A line is considered to be terminated by any one
171: * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
172: * followed immediately by a linefeed.
173: *
174: * @return A String containing the contents of the line, not including
175: * any line-termination characters, or null if the end of the
176: * stream has been reached
177: *
178: * @exception IOException If an I/O error occurs
179: */
180: public String readLine() throws IOException {
181: synchronized (lock) {
182: String l = super .readLine(skipLF);
183: skipLF = false;
184: if (l != null)
185: lineNumber++;
186: return l;
187: }
188: }
189:
190: /** Maximum skip-buffer size */
191: private static final int maxSkipBufferSize = 8192;
192:
193: /** Skip buffer, null until allocated */
194: private char skipBuffer[] = null;
195:
196: /**
197: * Skip characters.
198: *
199: * @param n The number of characters to skip
200: *
201: * @return The number of characters actually skipped
202: *
203: * @exception IOException If an I/O error occurs
204: */
205: public long skip(long n) throws IOException {
206: if (n < 0)
207: throw new IllegalArgumentException(
208: "skip() value is negative");
209: int nn = (int) Math.min(n, maxSkipBufferSize);
210: synchronized (lock) {
211: if ((skipBuffer == null) || (skipBuffer.length < nn))
212: skipBuffer = new char[nn];
213: long r = n;
214: while (r > 0) {
215: int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
216: if (nc == -1)
217: break;
218: r -= nc;
219: }
220: return n - r;
221: }
222: }
223:
224: /**
225: * Mark the present position in the stream. Subsequent calls to reset()
226: * will attempt to reposition the stream to this point, and will also reset
227: * the line number appropriately.
228: *
229: * @param readAheadLimit Limit on the number of characters that may be
230: * read while still preserving the mark. After
231: * reading this many characters, attempting to
232: * reset the stream may fail.
233: *
234: * @exception IOException If an I/O error occurs
235: */
236: public void mark(int readAheadLimit) throws IOException {
237: synchronized (lock) {
238: super .mark(readAheadLimit);
239: markedLineNumber = lineNumber;
240: markedSkipLF = skipLF;
241: }
242: }
243:
244: /**
245: * Reset the stream to the most recent mark.
246: *
247: * @exception IOException If the stream has not been marked,
248: * or if the mark has been invalidated
249: */
250: public void reset() throws IOException {
251: synchronized (lock) {
252: super.reset();
253: lineNumber = markedLineNumber;
254: skipLF = markedSkipLF;
255: }
256: }
257:
258: }
|