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