Frames | No Frames |
1: /* LineNumberReader.java -- A character input stream which counts line numbers 2: Copyright (C) 1998, 1999, 2001, 2003, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: 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) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.io; 39: 40: /** 41: * This class functions like a standard <code>Reader</code> except that it 42: * counts line numbers, and canonicalizes newline characters. As data 43: * 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 45: * line termination sequence was encountered will be converted to a "\n" 46: * 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 48: * so on. Also note that actual "\r" and "\n" characters are looked for. 49: * The system dependent line separator sequence is ignored. 50: * <p> 51: * 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 53: * will not be counted as a line. 54: * 55: * @author Per Bothner (bothner@cygnus.com) 56: * @author Aaron M. Renn (arenn@urbanophile.com) 57: * @author Guilhem Lavaux (guilhem@kaffe.org) 58: * @date December 28, 2003. 59: */ 60: /* Written using "Java Class Libraries", 2nd edition, plus online 61: * API docs for JDK 1.2 beta from http://www.javasoft.com. 62: * Status: Believed complete and correct. 63: * 64: * This implementation has the feature that if '\r' is read, it 65: * does not look for a '\n', but immediately returns '\n'. 66: * On the next read(), if a '\n' is read, it is skipped. 67: * This has the advantage that we do not read (and hang) unnecessarily. 68: * 69: * This implementation is also minimal in the number of fields it uses. 70: */ 71: public class LineNumberReader extends BufferedReader 72: { 73: /** The current line number. */ 74: private int lineNumber; 75: /** Whether we already found a new line in the former call. */ 76: private boolean matchedNewLine; 77: /** The saved line number when calling mark() */ 78: private int savedLineNumber; 79: 80: /** 81: * Create a new <code>LineNumberReader</code> that reads from the 82: * specified subordinate <code>Reader</code>. A default 8K char sized 83: * buffer will be used for reads. 84: * 85: * @param in The subordinate <code>Reader</code> to read from 86: */ 87: public LineNumberReader(Reader in) 88: { 89: super(in, DEFAULT_BUFFER_SIZE); 90: } 91: 92: /** 93: * This method initializes a new <code>LineNumberReader</code> to read 94: * from the specified subordinate <code>Reader</code> using the specified 95: * read buffer size. 96: * 97: * @param in The subordinate <code>Reader</code> to read from 98: * @param size The buffer size to use for reading 99: */ 100: public LineNumberReader(Reader in, int size) 101: { 102: super(in, size); 103: } 104: 105: /** 106: * This method returns the current line number 107: * 108: * @return The current line number 109: */ 110: public int getLineNumber() 111: { 112: return lineNumber; 113: } 114: 115: /** 116: * This method sets the current line number to the specified value. 117: * 118: * @param lineNumber The new line number 119: */ 120: public void setLineNumber(int lineNumber) 121: { 122: this.lineNumber = lineNumber; 123: } 124: 125: /** 126: * This method marks a position in the input to which the stream can be 127: * "reset" char calling the <code>reset()</code> method. The parameter 128: * <code>readlimit</code> is the number of chars that can be read from the 129: * stream after setting the mark before the mark becomes invalid. For 130: * example, if <code>mark()</code> is called with a read limit of 10, 131: * then when 132: * 11 chars of data are read from the stream before the <code>reset()</code> 133: * method is called, then the mark is invalid and the stream object 134: * instance is not required to remember the mark. 135: * <p> 136: * In this class, this method will remember the current line number as well 137: * as the current position in the stream. When the <code>reset()</code> 138: * method 139: * is called, the line number will be restored to the saved line number in 140: * addition to the stream position. 141: * 142: * @param readLimit The number of chars that can be read before the 143: * mark becomes invalid 144: * 145: * @exception IOException If an error occurs 146: */ 147: public void mark(int readLimit) throws IOException 148: { 149: if (readLimit < 0) 150: throw new IllegalArgumentException("Read-ahead limit is negative"); 151: 152: synchronized (lock) 153: { 154: // This is basically the same as BufferedReader.mark. 155: // However, if the previous character was a '\r', we need to 156: // save that 'r', in case the next character is a '\n'. 157: if (pos + readLimit > limit) 158: { 159: int saveCR = matchedNewLine ? 1 : 0; 160: char[] old_buffer = buffer; 161: if (readLimit > limit) 162: buffer = new char[saveCR + readLimit]; 163: int copy_start = pos - saveCR; 164: savedLineNumber = lineNumber; 165: limit -= copy_start; 166: System.arraycopy(old_buffer, copy_start, buffer, 0, limit); 167: pos = saveCR; 168: } 169: markPos = pos; 170: } 171: } 172: 173: /** 174: * This method resets a stream to the point where the <code>mark()</code> 175: * method 176: * was called. Any chars that were read after the mark point was set will 177: * be re-read during subsequent reads. 178: * <p> 179: * In this class, this method will also restore the line number that was 180: * current when the <code>mark()</code> method was called. 181: * 182: * @exception IOException If an error occurs 183: */ 184: public void reset() throws IOException 185: { 186: synchronized (lock) 187: { 188: if (markPos < 0) 189: throw new IOException("mark never set or invalidated"); 190: lineNumber = savedLineNumber; 191: pos = markPos; 192: matchedNewLine = (markPos > 0 && buffer[markPos-1] == '\r'); 193: } 194: } 195: 196: /** 197: * This private method fills the input buffer whatever pos is. 198: * Consequently pos should be checked before calling this method. 199: * 200: * @return the number of bytes actually read from the input stream or 201: * -1 if end of stream. 202: * @exception IOException If an error occurs. 203: */ 204: private int fill() throws IOException 205: { 206: if (markPos >= 0 && limit == buffer.length) 207: markPos = -1; 208: if (markPos < 0) 209: pos = limit = 0; 210: int count = in.read(buffer, limit, buffer.length - limit); 211: if (count <= 0) 212: return -1; 213: limit += count; 214: 215: return count; 216: } 217: 218: /** 219: * This method reads an unsigned char from the input stream and returns it 220: * as an int in the range of 0-65535. This method will return -1 if the 221: * end of the stream has been reached. 222: * <p> 223: * Note that if a line termination sequence is encountered (ie, "\r", 224: * "\n", or "\r\n") then that line termination sequence is converted to 225: * a single "\n" value which is returned from this method. This means 226: * that it is possible this method reads two chars from the subordinate 227: * stream instead of just one. 228: * <p> 229: * Note that this method will block until a char of data is available 230: * to be read. 231: * 232: * @return The char read or -1 if end of stream 233: * 234: * @exception IOException If an error occurs 235: */ 236: public int read() throws IOException 237: { 238: synchronized (lock) 239: { 240: skipRedundantLF(); 241: if (pos >= limit && fill() < 0) 242: return -1; 243: char ch = buffer[pos++]; 244: 245: if ((matchedNewLine = (ch == '\r')) || ch == '\n') 246: { 247: lineNumber++; 248: return '\n'; 249: } 250: matchedNewLine = false; 251: return (int) ch; 252: } 253: } 254: 255: /** 256: * This method reads chars from a stream and stores them into a caller 257: * supplied buffer. It starts storing data at index <code>offset</code> into 258: * the buffer and attemps to read <code>len</code> chars. This method can 259: * return before reading the number of chars requested. The actual number 260: * of chars read is returned as an int. A -1 is returned to indicated the 261: * end of the stream. 262: * <p> 263: * This method will block until some data can be read. 264: * <p> 265: * Note that if a line termination sequence is encountered (ie, "\r", 266: * "\n", or "\r\n") then that line termination sequence is converted to 267: * a single "\n" value which is stored in the buffer. Only a single 268: * char is counted towards the number of chars read in this case. 269: * 270: * @param buf The array into which the chars read should be stored 271: * @param offset The offset into the array to start storing chars 272: * @param count The requested number of chars to read 273: * 274: * @return The actual number of chars read, or -1 if end of stream 275: * 276: * @exception IOException If an error occurs. 277: * @exception NullPointerException If buf is null (in any case). 278: * @exception IndexOutOfBoundsException If buffer parameters (offset and 279: * count) lies outside of the buffer capacity. 280: */ 281: public int read(char[] buf, int offset, int count) throws IOException 282: { 283: if (buf == null) 284: throw new NullPointerException(); 285: 286: if (offset + count > buf.length || offset < 0) 287: throw new IndexOutOfBoundsException(); 288: 289: if (count <= 0) 290: { 291: if (count < 0) 292: throw new IndexOutOfBoundsException(); 293: return 0; 294: } 295: 296: synchronized (lock) 297: { 298: if (pos >= limit && fill() < 0) 299: return -1; 300: 301: int start_offset = offset; 302: boolean matched = matchedNewLine; 303: 304: while (count-- > 0 && pos < limit) 305: { 306: char ch = buffer[pos++]; 307: if (ch == '\r') 308: { 309: lineNumber++; 310: matched = true; 311: } 312: else if (ch == '\n' && !matched) 313: lineNumber++; 314: else 315: matched = false; 316: 317: buf[offset++] = ch; 318: } 319: 320: matchedNewLine = matched; 321: return offset - start_offset; 322: } 323: } 324: 325: private void skipRedundantLF() throws IOException 326: { 327: if (pos > 0 && matchedNewLine) 328: { 329: if (pos < limit) 330: { // fast case 331: if (buffer[pos] == '\n') 332: pos++; 333: } 334: else 335: { // check whether the next buffer begins with '\n'. 336: // in that case kill the '\n'. 337: if (fill() <= 0) 338: return; 339: if (buffer[pos] == '\n') 340: pos++; 341: } 342: matchedNewLine = true; 343: } 344: } 345: 346: /** 347: * This method reads a line of text from the input stream and returns 348: * it as a <code>String</code>. A line is considered to be terminated 349: * by a "\r", "\n", or "\r\n" sequence, not by the system dependent line 350: * separator. 351: * 352: * @return The line read as a <code>String</code> or <code>null</code> 353: * if end of stream. 354: * 355: * @exception IOException If an error occurs 356: */ 357: public String readLine() throws IOException 358: { 359: // BufferedReader.readLine already does this. Shouldn't need to keep 360: // track of newlines (since the read method deals with this for us). 361: // But if the buffer is large, we may not call the read method at all 362: // and super.readLine can't increment lineNumber itself. 363: // Though it may seem kludgy, the safest thing to do is to save off 364: // lineNumber and increment it explicitly when we're done (iff we 365: // ended with a '\n' or '\r' as opposed to EOF). 366: // 367: // Also, we need to undo the special casing done by BufferedReader.readLine 368: // when a '\r' is the last char in the buffer. That situation is marked 369: // by 'pos > limit'. 370: int tmpLineNumber = lineNumber; 371: skipRedundantLF(); 372: String str = super.readLine(); 373: if (pos > limit) 374: --pos; 375: 376: // The only case where you mustn't increment the line number is you are 377: // at the EOS. 378: if (str != null) 379: lineNumber = tmpLineNumber + 1; 380: 381: return str; 382: } 383: 384: /** 385: * This method skips over characters in the stream. This method will 386: * skip the specified number of characters if possible, but is not required 387: * to skip them all. The actual number of characters skipped is returned. 388: * This method returns 0 if the specified number of chars is less than 1. 389: * 390: * @param count The specified number of chars to skip. 391: * 392: * @return The actual number of chars skipped. 393: * 394: * @exception IOException If an error occurs 395: */ 396: public long skip (long count) throws IOException 397: { 398: if (count < 0) 399: throw new IllegalArgumentException("skip() value is negative"); 400: if (count == 0) 401: return 0; 402: 403: int skipped; 404: char[] buf = new char[1]; 405: 406: for (skipped = 0; skipped < count; skipped++) 407: { 408: int ch = read(buf, 0, 1); 409: 410: if (ch < 0) 411: break; 412: } 413: 414: return skipped; 415: } 416: }