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