1. /*
  2. * @(#)LineNumberReader.java 1.10 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.io;
  8. /**
  9. * A buffered character-input stream that keeps track of line numbers. A line
  10. * is considered to be terminated by any one of a line feed ('\n'), a carriage
  11. * return ('\r'), or a carriage return followed immediately by a linefeed.
  12. *
  13. * @version 1.10, 01/11/29
  14. * @author Mark Reinhold
  15. * @since JDK1.1
  16. */
  17. public class LineNumberReader extends BufferedReader {
  18. /** The current line number */
  19. private int lineNumber = 0;
  20. /** The line number of the mark, if any */
  21. private int markedLineNumber;
  22. /** If the next character is a line feed, skip it */
  23. private boolean skipLF;
  24. /** The skipLF flag when the mark was set */
  25. private boolean markedSkipLF;
  26. /**
  27. * Create a new line-numbering reader, using the default input-buffer
  28. * size.
  29. */
  30. public LineNumberReader(Reader in) {
  31. super(in);
  32. }
  33. /**
  34. * Create a new line-numbering reader, reading characters into a buffer of
  35. * the given size.
  36. */
  37. public LineNumberReader(Reader in, int sz) {
  38. super(in, sz);
  39. }
  40. /**
  41. * Set the current line number.
  42. */
  43. public void setLineNumber(int lineNumber) {
  44. this.lineNumber = lineNumber;
  45. }
  46. /**
  47. * Get the current line number.
  48. */
  49. public int getLineNumber() {
  50. return lineNumber;
  51. }
  52. /**
  53. * Read a single character. Line terminators are compressed into single
  54. * newline ('\n') characters.
  55. *
  56. * @return The character read, or -1 if the end of the stream has been
  57. * reached
  58. *
  59. * @exception IOException If an I/O error occurs
  60. */
  61. public int read() throws IOException {
  62. synchronized (lock) {
  63. int c = super.read();
  64. if (skipLF) {
  65. if (c == '\n')
  66. c = super.read();
  67. skipLF = false;
  68. }
  69. switch (c) {
  70. case '\r':
  71. skipLF = true;
  72. case '\n': /* Fall through */
  73. lineNumber++;
  74. return '\n';
  75. }
  76. return c;
  77. }
  78. }
  79. /**
  80. * Read characters into a portion of an array.
  81. *
  82. * @param cbuf Destination buffer
  83. * @param off Offset at which to start storing characters
  84. * @param len Maximum number of characters to read
  85. *
  86. * @return The number of bytes read, or -1 if the end of the stream has
  87. * already been reached
  88. *
  89. * @exception IOException If an I/O error occurs
  90. */
  91. public int read(char cbuf[], int off, int len) throws IOException {
  92. synchronized (lock) {
  93. int n = super.read(cbuf, off, len);
  94. for (int i = off; i < off + n; i++) {
  95. int c = cbuf[i];
  96. if (skipLF) {
  97. skipLF = false;
  98. if (c == '\n')
  99. continue;
  100. }
  101. switch (c) {
  102. case '\r':
  103. skipLF = true;
  104. case '\n': /* Fall through */
  105. lineNumber++;
  106. break;
  107. }
  108. }
  109. return n;
  110. }
  111. }
  112. /**
  113. * Read a line of text. A line is considered to be terminated by any one
  114. * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
  115. * followed immediately by a linefeed.
  116. *
  117. * @return A String containing the contents of the line, not including
  118. * any line-termination characters, or null if the end of the
  119. * stream has been reached
  120. *
  121. * @exception IOException If an I/O error occurs
  122. */
  123. public String readLine() throws IOException {
  124. synchronized (lock) {
  125. String l = super.readLine(skipLF);
  126. skipLF = false;
  127. if (l != null)
  128. lineNumber++;
  129. return l;
  130. }
  131. }
  132. /** Maximum skip-buffer size */
  133. private static final int maxSkipBufferSize = 8192;
  134. /** Skip buffer, null until allocated */
  135. private char skipBuffer[] = null;
  136. /**
  137. * Skip characters.
  138. *
  139. * @param n The number of characters to skip
  140. *
  141. * @return The number of characters actually skipped
  142. *
  143. * @exception IOException If an I/O error occurs
  144. */
  145. public long skip(long n) throws IOException {
  146. int nn = (int) Math.min(n, maxSkipBufferSize);
  147. synchronized (lock) {
  148. if ((skipBuffer == null) || (skipBuffer.length < nn))
  149. skipBuffer = new char[nn];
  150. long r = n;
  151. while (r > 0) {
  152. int nc = read(skipBuffer, 0, (int) Math.min(r, nn));
  153. if (nc == -1)
  154. break;
  155. r -= nc;
  156. }
  157. return n - r;
  158. }
  159. }
  160. /**
  161. * Mark the present position in the stream. Subsequent calls to reset()
  162. * will attempt to reposition the stream to this point, and will also reset
  163. * the line number appropriately.
  164. *
  165. * @param readAheadLimit Limit on the number of characters that may be
  166. * read while still preserving the mark. After
  167. * reading this many characters, attempting to
  168. * reset the stream may fail.
  169. *
  170. * @exception IOException If an I/O error occurs
  171. */
  172. public void mark(int readAheadLimit) throws IOException {
  173. synchronized (lock) {
  174. super.mark(readAheadLimit);
  175. markedLineNumber = lineNumber;
  176. markedSkipLF = skipLF;
  177. }
  178. }
  179. /**
  180. * Reset the stream to the most recent mark.
  181. *
  182. * @exception IOException If the stream has not been marked,
  183. * or if the mark has been invalidated
  184. */
  185. public void reset() throws IOException {
  186. synchronized (lock) {
  187. super.reset();
  188. lineNumber = markedLineNumber;
  189. skipLF = markedSkipLF;
  190. }
  191. }
  192. }