1. /*
  2. * @(#)BufferedReader.java 1.27 01/02/09
  3. *
  4. * Copyright 1996-2001 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. * Read text from a character-input stream, buffering characters so as to
  13. * provide for the efficient reading of characters, arrays, and lines.
  14. *
  15. * <p> The buffer size may be specified, or the default size may be used. The
  16. * default is large enough for most purposes.
  17. *
  18. * <p> In general, each read request made of a Reader causes a corresponding
  19. * read request to be made of the underlying character or byte stream. It is
  20. * therefore advisable to wrap a BufferedReader around any Reader whose read()
  21. * operations may be costly, such as FileReaders and InputStreamReaders. For
  22. * example,
  23. *
  24. * <pre>
  25. * BufferedReader in
  26. * = new BufferedReader(new FileReader("foo.in"));
  27. * </pre>
  28. *
  29. * will buffer the input from the specified file. Without buffering, each
  30. * invocation of read() or readLine() could cause bytes to be read from the
  31. * file, converted into characters, and then returned, which can be very
  32. * inefficient.
  33. *
  34. * <p> Programs that use DataInputStreams for textual input can be localized by
  35. * replacing each DataInputStream with an appropriate BufferedReader.
  36. *
  37. * @see FileReader
  38. * @see InputStreamReader
  39. *
  40. * @version 1.27, 01/02/09
  41. * @author Mark Reinhold
  42. * @since JDK1.1
  43. */
  44. public class BufferedReader extends Reader {
  45. private Reader in;
  46. private char cb[];
  47. private int nChars, nextChar;
  48. private static final int INVALIDATED = -2;
  49. private static final int UNMARKED = -1;
  50. private int markedChar = UNMARKED;
  51. private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
  52. /** If the next character is a line feed, skip it */
  53. private boolean skipLF = false;
  54. /** The skipLF flag when the mark was set */
  55. private boolean markedSkipLF = false;
  56. private static int defaultCharBufferSize = 8192;
  57. private static int defaultExpectedLineLength = 80;
  58. /**
  59. * Create a buffering character-input stream that uses an input buffer of
  60. * the specified size.
  61. *
  62. * @param in A Reader
  63. * @param sz Input-buffer size
  64. *
  65. * @exception IllegalArgumentException If sz is <= 0
  66. */
  67. public BufferedReader(Reader in, int sz) {
  68. super(in);
  69. if (sz <= 0)
  70. throw new IllegalArgumentException("Buffer size <= 0");
  71. this.in = in;
  72. cb = new char[sz];
  73. nextChar = nChars = 0;
  74. }
  75. /**
  76. * Create a buffering character-input stream that uses a default-sized
  77. * input buffer.
  78. *
  79. * @param in A Reader
  80. */
  81. public BufferedReader(Reader in) {
  82. this(in, defaultCharBufferSize);
  83. }
  84. /** Check to make sure that the stream has not been closed */
  85. private void ensureOpen() throws IOException {
  86. if (in == null)
  87. throw new IOException("Stream closed");
  88. }
  89. /**
  90. * Fill the input buffer, taking the mark into account if it is valid.
  91. */
  92. private void fill() throws IOException {
  93. int dst;
  94. if (markedChar <= UNMARKED) {
  95. /* No mark */
  96. dst = 0;
  97. } else {
  98. /* Marked */
  99. int delta = nextChar - markedChar;
  100. if (delta >= readAheadLimit) {
  101. /* Gone past read-ahead limit: Invalidate mark */
  102. markedChar = INVALIDATED;
  103. readAheadLimit = 0;
  104. dst = 0;
  105. } else {
  106. if (readAheadLimit <= cb.length) {
  107. /* Shuffle in the current buffer */
  108. System.arraycopy(cb, markedChar, cb, 0, delta);
  109. markedChar = 0;
  110. dst = delta;
  111. } else {
  112. /* Reallocate buffer to accomodate read-ahead limit */
  113. char ncb[] = new char[readAheadLimit];
  114. System.arraycopy(cb, markedChar, ncb, 0, delta);
  115. cb = ncb;
  116. markedChar = 0;
  117. dst = delta;
  118. }
  119. nextChar = nChars = delta;
  120. }
  121. }
  122. int n;
  123. do {
  124. n = in.read(cb, dst, cb.length - dst);
  125. } while (n == 0);
  126. if (n > 0) {
  127. nChars = dst + n;
  128. nextChar = dst;
  129. }
  130. }
  131. /**
  132. * Read a single character.
  133. *
  134. * @exception IOException If an I/O error occurs
  135. */
  136. public int read() throws IOException {
  137. synchronized (lock) {
  138. ensureOpen();
  139. for (;;) {
  140. if (nextChar >= nChars) {
  141. fill();
  142. if (nextChar >= nChars)
  143. return -1;
  144. }
  145. if (skipLF) {
  146. skipLF = false;
  147. if (cb[nextChar] == '\n') {
  148. nextChar++;
  149. continue;
  150. }
  151. }
  152. return cb[nextChar++];
  153. }
  154. }
  155. }
  156. /**
  157. * Read characters into a portion of an array, reading from the underlying
  158. * stream if necessary.
  159. */
  160. private int read1(char[] cbuf, int off, int len) throws IOException {
  161. if (nextChar >= nChars) {
  162. /* If the requested length is at least as large as the buffer, and
  163. if there is no mark/reset activity, and if line feeds are not
  164. being skipped, do not bother to copy the characters into the
  165. local buffer. In this way buffered streams will cascade
  166. harmlessly. */
  167. if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
  168. return in.read(cbuf, off, len);
  169. }
  170. fill();
  171. }
  172. if (nextChar >= nChars) return -1;
  173. if (skipLF) {
  174. skipLF = false;
  175. if (cb[nextChar] == '\n') {
  176. nextChar++;
  177. if (nextChar >= nChars)
  178. fill();
  179. if (nextChar >= nChars)
  180. return -1;
  181. }
  182. }
  183. int n = Math.min(len, nChars - nextChar);
  184. System.arraycopy(cb, nextChar, cbuf, off, n);
  185. nextChar += n;
  186. return n;
  187. }
  188. /**
  189. * Read characters into a portion of an array.
  190. *
  191. * <p> This method implements the general contract of the corresponding
  192. * <code>{@link Reader#read(char[], int, int) read}</code> method of the
  193. * <code>{@link Reader}</code> class. As an additional convenience, it
  194. * attempts to read as many characters as possible by repeatedly invoking
  195. * the <code>read</code> method of the underlying stream. This iterated
  196. * <code>read</code> continues until one of the following conditions becomes
  197. * true: <ul>
  198. *
  199. * <li> The specified number of characters have been read,
  200. *
  201. * <li> The <code>read</code> method of the underlying stream returns
  202. * <code>-1</code>, indicating end-of-file, or
  203. *
  204. * <li> The <code>ready</code> method of the underlying stream
  205. * returns <code>false</code>, indicating that further input requests
  206. * would block.
  207. *
  208. * </ul> If the first <code>read</code> on the underlying stream returns
  209. * <code>-1</code> to indicate end-of-file then this method returns
  210. * <code>-1</code>. Otherwise this method returns the number of characters
  211. * actually read.
  212. *
  213. * <p> Subclasses of this class are encouraged, but not required, to
  214. * attempt to read as many characters as possible in the same fashion.
  215. *
  216. * <p> Ordinarily this method takes characters from this stream's character
  217. * buffer, filling it from the underlying stream as necessary. If,
  218. * however, the buffer is empty, the mark is not valid, and the requested
  219. * length is at least as large as the buffer, then this method will read
  220. * characters directly from the underlying stream into the given array.
  221. * Thus redundant <code>BufferedReader</code>s will not copy data
  222. * unnecessarily.
  223. *
  224. * @param cbuf Destination buffer
  225. * @param off Offset at which to start storing characters
  226. * @param len Maximum number of characters to read
  227. *
  228. * @return The number of characters read, or -1 if the end of the
  229. * stream has been reached
  230. *
  231. * @exception IOException If an I/O error occurs
  232. */
  233. public int read(char cbuf[], int off, int len) throws IOException {
  234. synchronized (lock) {
  235. ensureOpen();
  236. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  237. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  238. throw new IndexOutOfBoundsException();
  239. } else if (len == 0) {
  240. return 0;
  241. }
  242. int n = read1(cbuf, off, len);
  243. if (n <= 0) return n;
  244. while ((n < len) && in.ready()) {
  245. int n1 = read1(cbuf, off + n, len - n);
  246. if (n1 <= 0) break;
  247. n += n1;
  248. }
  249. return n;
  250. }
  251. }
  252. /**
  253. * Read a line of text. A line is considered to be terminated by any one
  254. * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
  255. * followed immediately by a linefeed.
  256. *
  257. * @param ignoreLF If true, the next '\n' will be skipped
  258. *
  259. * @return A String containing the contents of the line, not including
  260. * any line-termination characters, or null if the end of the
  261. * stream has been reached
  262. *
  263. * @see java.io.LineNumberReader#readLine()
  264. *
  265. * @exception IOException If an I/O error occurs
  266. */
  267. String readLine(boolean ignoreLF) throws IOException {
  268. StringBuffer s = null;
  269. int startChar;
  270. boolean omitLF = ignoreLF || skipLF;
  271. synchronized (lock) {
  272. ensureOpen();
  273. bufferLoop:
  274. for (;;) {
  275. if (nextChar >= nChars)
  276. fill();
  277. if (nextChar >= nChars) { /* EOF */
  278. if (s != null && s.length() > 0)
  279. return s.toString();
  280. else
  281. return null;
  282. }
  283. boolean eol = false;
  284. char c = 0;
  285. int i;
  286. /* Skip a leftover '\n', if necessary */
  287. if (omitLF && (cb[nextChar] == '\n'))
  288. nextChar++;
  289. skipLF = false;
  290. omitLF = false;
  291. charLoop:
  292. for (i = nextChar; i < nChars; i++) {
  293. c = cb[i];
  294. if ((c == '\n') || (c == '\r')) {
  295. eol = true;
  296. break charLoop;
  297. }
  298. }
  299. startChar = nextChar;
  300. nextChar = i;
  301. if (eol) {
  302. String str;
  303. if (s == null) {
  304. str = new String(cb, startChar, i - startChar);
  305. } else {
  306. s.append(cb, startChar, i - startChar);
  307. str = s.toString();
  308. }
  309. nextChar++;
  310. if (c == '\r') {
  311. skipLF = true;
  312. }
  313. return str;
  314. }
  315. if (s == null)
  316. s = new StringBuffer(defaultExpectedLineLength);
  317. s.append(cb, startChar, i - startChar);
  318. }
  319. }
  320. }
  321. /**
  322. * Read a line of text. A line is considered to be terminated by any one
  323. * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
  324. * followed immediately by a linefeed.
  325. *
  326. * @return A String containing the contents of the line, not including
  327. * any line-termination characters, or null if the end of the
  328. * stream has been reached
  329. *
  330. * @exception IOException If an I/O error occurs
  331. */
  332. public String readLine() throws IOException {
  333. return readLine(false);
  334. }
  335. /**
  336. * Skip characters.
  337. *
  338. * @param n The number of characters to skip
  339. *
  340. * @return The number of characters actually skipped
  341. *
  342. * @exception IllegalArgumentException If <code>n</code> is negative.
  343. * @exception IOException If an I/O error occurs
  344. */
  345. public long skip(long n) throws IOException {
  346. if (n < 0L) {
  347. throw new IllegalArgumentException("skip value is negative");
  348. }
  349. synchronized (lock) {
  350. ensureOpen();
  351. long r = n;
  352. while (r > 0) {
  353. if (nextChar >= nChars)
  354. fill();
  355. if (nextChar >= nChars) /* EOF */
  356. break;
  357. if (skipLF) {
  358. skipLF = false;
  359. if (cb[nextChar] == '\n') {
  360. nextChar++;
  361. }
  362. }
  363. long d = nChars - nextChar;
  364. if (r <= d) {
  365. nextChar += r;
  366. r = 0;
  367. break;
  368. }
  369. else {
  370. r -= d;
  371. nextChar = nChars;
  372. }
  373. }
  374. return n - r;
  375. }
  376. }
  377. /**
  378. * Tell whether this stream is ready to be read. A buffered character
  379. * stream is ready if the buffer is not empty, or if the underlying
  380. * character stream is ready.
  381. *
  382. * @exception IOException If an I/O error occurs
  383. */
  384. public boolean ready() throws IOException {
  385. synchronized (lock) {
  386. ensureOpen();
  387. /*
  388. * If newline needs to be skipped and the next char to be read
  389. * is a newline character, then just skip it right away.
  390. */
  391. if (skipLF) {
  392. /* Note that in.ready() will return true if and only if the next
  393. * read on the stream will not block.
  394. */
  395. if (nextChar >= nChars && in.ready()) {
  396. fill();
  397. }
  398. if (nextChar < nChars) {
  399. if (cb[nextChar] == '\n')
  400. nextChar++;
  401. skipLF = false;
  402. }
  403. }
  404. return (nextChar < nChars) || in.ready();
  405. }
  406. }
  407. /**
  408. * Tell whether this stream supports the mark() operation, which it does.
  409. */
  410. public boolean markSupported() {
  411. return true;
  412. }
  413. /**
  414. * Mark the present position in the stream. Subsequent calls to reset()
  415. * will attempt to reposition the stream to this point.
  416. *
  417. * @param readAheadLimit Limit on the number of characters that may be
  418. * read while still preserving the mark. After
  419. * reading this many characters, attempting to
  420. * reset the stream may fail. A limit value larger
  421. * than the size of the input buffer will cause a
  422. * new buffer to be allocated whose size is no
  423. * smaller than limit. Therefore large values
  424. * should be used with care.
  425. *
  426. * @exception IllegalArgumentException If readAheadLimit is < 0
  427. * @exception IOException If an I/O error occurs
  428. */
  429. public void mark(int readAheadLimit) throws IOException {
  430. if (readAheadLimit < 0) {
  431. throw new IllegalArgumentException("Read-ahead limit < 0");
  432. }
  433. synchronized (lock) {
  434. ensureOpen();
  435. this.readAheadLimit = readAheadLimit;
  436. markedChar = nextChar;
  437. markedSkipLF = skipLF;
  438. }
  439. }
  440. /**
  441. * Reset the stream to the most recent mark.
  442. *
  443. * @exception IOException If the stream has never been marked,
  444. * or if the mark has been invalidated
  445. */
  446. public void reset() throws IOException {
  447. synchronized (lock) {
  448. ensureOpen();
  449. if (markedChar < 0)
  450. throw new IOException((markedChar == INVALIDATED)
  451. ? "Mark invalid"
  452. : "Stream not marked");
  453. nextChar = markedChar;
  454. skipLF = markedSkipLF;
  455. }
  456. }
  457. /**
  458. * Close the stream.
  459. *
  460. * @exception IOException If an I/O error occurs
  461. */
  462. public void close() throws IOException {
  463. synchronized (lock) {
  464. if (in == null)
  465. return;
  466. in.close();
  467. in = null;
  468. cb = null;
  469. }
  470. }
  471. }