BufferedReader.java revision fb4616d0efbba1903b9237c0a428b59868365a69
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.io; 19 20import org.apache.harmony.luni.util.Msg; 21 22// BEGIN android-added 23import java.util.logging.Logger; 24// END android-added 25 26/** 27 * Wraps an existing {@link Reader} and <em>buffers</em> the input. Expensive 28 * interaction with the underlying reader is minimized, since most (smaller) 29 * requests can be satisfied by accessing the buffer alone. The drawback is that 30 * some extra space is required to hold the buffer and that copying takes place 31 * when filling that buffer, but this is usually outweighed by the performance 32 * benefits. 33 * 34 * <p/>A typical application pattern for the class looks like this:<p/> 35 * 36 * <pre> 37 * BufferedReader buf = new BufferedReader(new FileReader("file.java")); 38 * </pre> 39 * 40 * @see BufferedWriter 41 * @since 1.1 42 */ 43public class BufferedReader extends Reader { 44 45 private Reader in; 46 47 /** 48 * The characters that can be read and refilled in bulk. We maintain three 49 * indices into this buffer:<pre> 50 * { X X X X X X X X X X X X - - } 51 * ^ ^ ^ 52 * | | | 53 * mark pos end</pre> 54 * Pos points to the next readable character. End is one greater than the 55 * last readable character. When {@code pos == end}, the buffer is empty and 56 * must be {@link #fillBuf() filled} before characters can be read. 57 * 58 * <p>Mark is the value pos will be set to on calls to {@link #reset}. Its 59 * value is in the range {@code [0...pos]}. If the mark is {@code -1}, the 60 * buffer cannot be reset. 61 * 62 * <p>MarkLimit limits the distance between the mark and the pos. When this 63 * limit is exceeded, {@link #reset} is permitted (but not required) to 64 * throw an exception. For shorter distances, {@link #reset} shall not throw 65 * (unless the reader is closed). 66 */ 67 private char[] buf; 68 69 private int pos; 70 71 private int end; 72 73 private int mark = -1; 74 75 private int markLimit = -1; 76 77 /** 78 * Constructs a new {@code BufferedReader}, providing {@code in} with a buffer 79 * of 8192 characters. 80 * 81 * @param in the {@code Reader} the buffer reads from. 82 */ 83 public BufferedReader(Reader in) { 84 this(in, 8192); 85 } 86 87 /** 88 * Constructs a new {@code BufferedReader}, providing {@code in} with {@code size} characters 89 * of buffer. 90 * 91 * @param in the {@code InputStream} the buffer reads from. 92 * @param size the size of buffer in characters. 93 * @throws IllegalArgumentException if {@code size <= 0}. 94 */ 95 public BufferedReader(Reader in, int size) { 96 super(in); 97 if (size <= 0) { 98 throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$ 99 } 100 this.in = in; 101 buf = new char[size]; 102 } 103 104 /** 105 * Closes this reader. This implementation closes the buffered source reader 106 * and releases the buffer. Nothing is done if this reader has already been 107 * closed. 108 * 109 * @throws IOException 110 * if an error occurs while closing this reader. 111 */ 112 @Override 113 public void close() throws IOException { 114 synchronized (lock) { 115 if (!isClosed()) { 116 in.close(); 117 buf = null; 118 } 119 } 120 } 121 122 /** 123 * Populates the buffer with data. It is an error to call this method when 124 * the buffer still contains data; ie. if {@code pos < end}. 125 * 126 * @return the number of bytes read into the buffer, or -1 if the end of the 127 * source stream has been reached. 128 */ 129 private int fillBuf() throws IOException { 130 // assert(pos == end); 131 132 if (mark == -1 || (pos - mark >= markLimit)) { 133 /* mark isn't set or has exceeded its limit. use the whole buffer */ 134 int result = in.read(buf, 0, buf.length); 135 if (result > 0) { 136 mark = -1; 137 pos = 0; 138 end = result; 139 } 140 return result; 141 } 142 143 if (mark == 0 && markLimit > buf.length) { 144 /* the only way to make room when mark=0 is by growing the buffer */ 145 int newLength = buf.length * 2; 146 if (newLength > markLimit) { 147 newLength = markLimit; 148 } 149 char[] newbuf = new char[newLength]; 150 System.arraycopy(buf, 0, newbuf, 0, buf.length); 151 buf = newbuf; 152 } else if (mark > 0) { 153 /* make room by shifting the buffered data to left mark positions */ 154 System.arraycopy(buf, mark, buf, 0, buf.length - mark); 155 pos -= mark; 156 end -= mark; 157 mark = 0; 158 } 159 160 /* Set the new position and mark position */ 161 int count = in.read(buf, pos, buf.length - pos); 162 if (count != -1) { 163 end += count; 164 } 165 return count; 166 } 167 168 /** 169 * Indicates whether or not this reader is closed. 170 * 171 * @return {@code true} if this reader is closed, {@code false} 172 * otherwise. 173 */ 174 private boolean isClosed() { 175 return buf == null; 176 } 177 178 /** 179 * Sets a mark position in this reader. The parameter {@code markLimit} 180 * indicates how many characters can be read before the mark is invalidated. 181 * Calling {@code reset()} will reposition the reader back to the marked 182 * position if {@code markLimit} has not been surpassed. 183 * 184 * @param markLimit 185 * the number of characters that can be read before the mark is 186 * invalidated. 187 * @throws IllegalArgumentException 188 * if {@code markLimit < 0}. 189 * @throws IOException 190 * if an error occurs while setting a mark in this reader. 191 * @see #markSupported() 192 * @see #reset() 193 */ 194 @Override 195 public void mark(int markLimit) throws IOException { 196 if (markLimit < 0) { 197 throw new IllegalArgumentException(); 198 } 199 synchronized (lock) { 200 if (isClosed()) { 201 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 202 } 203 this.markLimit = markLimit; 204 mark = pos; 205 } 206 } 207 208 /** 209 * Indicates whether this reader supports the {@code mark()} and 210 * {@code reset()} methods. This implementation returns {@code true}. 211 * 212 * @return {@code true} for {@code BufferedReader}. 213 * @see #mark(int) 214 * @see #reset() 215 */ 216 @Override 217 public boolean markSupported() { 218 return true; 219 } 220 221 /** 222 * Reads a single character from this reader and returns it with the two 223 * higher-order bytes set to 0. If possible, BufferedReader returns a 224 * character from the buffer. If there are no characters available in the 225 * buffer, it fills the buffer and then returns a character. It returns -1 226 * if there are no more characters in the source reader. 227 * 228 * @return the character read or -1 if the end of the source reader has been 229 * reached. 230 * @throws IOException 231 * if this reader is closed or some other I/O error occurs. 232 */ 233 @Override 234 public int read() throws IOException { 235 synchronized (lock) { 236 if (isClosed()) { 237 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 238 } 239 /* Are there buffered characters available? */ 240 if (pos < end || fillBuf() != -1) { 241 return buf[pos++]; 242 } 243 return -1; 244 } 245 } 246 247 /** 248 * Reads at most {@code length} characters from this reader and stores them 249 * at {@code offset} in the character array {@code buffer}. Returns the 250 * number of characters actually read or -1 if the end of the source reader 251 * has been reached. If all the buffered characters have been used, a mark 252 * has not been set and the requested number of characters is larger than 253 * this readers buffer size, BufferedReader bypasses the buffer and simply 254 * places the results directly into {@code buffer}. 255 * 256 * @param buffer 257 * the character array to store the characters read. 258 * @param offset 259 * the initial position in {@code buffer} to store the bytes read 260 * from this reader. 261 * @param length 262 * the maximum number of characters to read, must be 263 * non-negative. 264 * @return number of characters read or -1 if the end of the source reader 265 * has been reached. 266 * @throws IndexOutOfBoundsException 267 * if {@code offset < 0} or {@code length < 0}, or if 268 * {@code offset + length} is greater than the size of 269 * {@code buffer}. 270 * @throws IOException 271 * if this reader is closed or some other I/O error occurs. 272 */ 273 @Override 274 public int read(char[] buffer, int offset, int length) throws IOException { 275 synchronized (lock) { 276 if (isClosed()) { 277 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 278 } 279 if (offset < 0 || offset > buffer.length - length || length < 0) { 280 throw new IndexOutOfBoundsException(); 281 } 282 int outstanding = length; 283 while (outstanding > 0) { 284 285 /* 286 * If there are bytes in the buffer, grab those first. 287 */ 288 int available = end - pos; 289 if (available > 0) { 290 int count = available >= outstanding ? outstanding : available; 291 System.arraycopy(buf, pos, buffer, offset, count); 292 pos += count; 293 offset += count; 294 outstanding -= count; 295 } 296 297 /* 298 * Before attempting to read from the underlying stream, make 299 * sure we really, really want to. We won't bother if we're 300 * done, or if we've already got some bytes and reading from the 301 * underlying stream would block. 302 */ 303 if (outstanding == 0 || (outstanding < length && !in.ready())) { 304 break; 305 } 306 307 // assert(pos == end); 308 309 /* 310 * If we're unmarked and the requested size is greater than our 311 * buffer, read the bytes directly into the caller's buffer. We 312 * don't read into smaller buffers because that could result in 313 * a many reads. 314 */ 315 if ((mark == -1 || (pos - mark >= markLimit)) 316 && outstanding >= buf.length) { 317 int count = in.read(buffer, offset, outstanding); 318 if (count > 0) { 319 offset += count; 320 outstanding -= count; 321 mark = -1; 322 } 323 324 break; // assume the source stream gave us all that it could 325 } 326 327 if (fillBuf() == -1) { 328 break; // source is exhausted 329 } 330 } 331 332 int count = length - outstanding; 333 return (count > 0 || count == length) ? count : -1; 334 } 335 } 336 337 /** 338 * Peeks at the next input character, refilling the buffer if necessary. If 339 * this character is a newline character ("\n"), it is discarded. 340 */ 341 final void chompNewline() throws IOException { 342 if ((pos != end || fillBuf() != -1) 343 && buf[pos] == '\n') { 344 pos++; 345 } 346 } 347 348 /** 349 * Returns the next line of text available from this reader. A line is 350 * represented by zero or more characters followed by {@code '\n'}, 351 * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does 352 * not include the newline sequence. 353 * 354 * @return the contents of the line or {@code null} if no characters were 355 * read before the end of the reader has been reached. 356 * @throws IOException 357 * if this reader is closed or some other I/O error occurs. 358 */ 359 public String readLine() throws IOException { 360 synchronized (lock) { 361 if (isClosed()) { 362 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 363 } 364 /* has the underlying stream been exhausted? */ 365 if (pos == end && fillBuf() == -1) { 366 return null; 367 } 368 for (int charPos = pos; charPos < end; charPos++) { 369 char ch = buf[charPos]; 370 // BEGIN android-note 371 // a switch statement may be more efficient 372 // END android-note 373 if (ch > '\r') { 374 continue; 375 } 376 if (ch == '\n') { 377 String res = new String(buf, pos, charPos - pos); 378 pos = charPos + 1; 379 return res; 380 } else if (ch == '\r') { 381 String res = new String(buf, pos, charPos - pos); 382 pos = charPos + 1; 383 if (((pos < end) || (fillBuf() != -1)) 384 && (buf[pos] == '\n')) { 385 pos++; 386 } 387 return res; 388 } 389 } 390 391 char eol = '\0'; 392 StringBuilder result = new StringBuilder(80); 393 /* Typical Line Length */ 394 395 result.append(buf, pos, end - pos); 396 while (true) { 397 pos = end; 398 399 /* Are there buffered characters available? */ 400 if (eol == '\n') { 401 return result.toString(); 402 } 403 // attempt to fill buffer 404 if (fillBuf() == -1) { 405 // characters or null. 406 return result.length() > 0 || eol != '\0' 407 ? result.toString() 408 : null; 409 } 410 for (int charPos = pos; charPos < end; charPos++) { 411 char c = buf[charPos]; 412 if (eol == '\0') { 413 if ((c == '\n' || c == '\r')) { 414 eol = c; 415 } 416 } else if (eol == '\r' && c == '\n') { 417 if (charPos > pos) { 418 result.append(buf, pos, charPos - pos - 1); 419 } 420 pos = charPos + 1; 421 return result.toString(); 422 } else { 423 if (charPos > pos) { 424 result.append(buf, pos, charPos - pos - 1); 425 } 426 pos = charPos; 427 return result.toString(); 428 } 429 } 430 if (eol == '\0') { 431 result.append(buf, pos, end - pos); 432 } else { 433 result.append(buf, pos, end - pos - 1); 434 } 435 } 436 } 437 438 } 439 440 /** 441 * Indicates whether this reader is ready to be read without blocking. 442 * 443 * @return {@code true} if this reader will not block when {@code read} is 444 * called, {@code false} if unknown or blocking will occur. 445 * @throws IOException 446 * if this reader is closed or some other I/O error occurs. 447 * @see #read() 448 * @see #read(char[], int, int) 449 * @see #readLine() 450 */ 451 @Override 452 public boolean ready() throws IOException { 453 synchronized (lock) { 454 if (isClosed()) { 455 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 456 } 457 return ((end - pos) > 0) || in.ready(); 458 } 459 } 460 461 /** 462 * Resets this reader's position to the last {@code mark()} location. 463 * Invocations of {@code read()} and {@code skip()} will occur from this new 464 * location. 465 * 466 * @throws IOException 467 * if this reader is closed or no mark has been set. 468 * @see #mark(int) 469 * @see #markSupported() 470 */ 471 @Override 472 public void reset() throws IOException { 473 synchronized (lock) { 474 if (isClosed()) { 475 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 476 } 477 if (mark == -1) { 478 throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$ 479 } 480 pos = mark; 481 } 482 } 483 484 /** 485 * Skips {@code amount} characters in this reader. Subsequent 486 * {@code read()}s will not return these characters unless {@code reset()} 487 * is used. Skipping characters may invalidate a mark if {@code markLimit} 488 * is surpassed. 489 * 490 * @param amount 491 * the maximum number of characters to skip. 492 * @return the number of characters actually skipped. 493 * @throws IllegalArgumentException 494 * if {@code amount < 0}. 495 * @throws IOException 496 * if this reader is closed or some other I/O error occurs. 497 * @see #mark(int) 498 * @see #markSupported() 499 * @see #reset() 500 */ 501 @Override 502 public long skip(long amount) throws IOException { 503 if (amount < 0) { 504 throw new IllegalArgumentException(); 505 } 506 synchronized (lock) { 507 if (isClosed()) { 508 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 509 } 510 if (amount < 1) { 511 return 0; 512 } 513 if (end - pos >= amount) { 514 pos += amount; 515 return amount; 516 } 517 518 long read = end - pos; 519 pos = end; 520 while (read < amount) { 521 if (fillBuf() == -1) { 522 return read; 523 } 524 if (end - pos >= amount - read) { 525 pos += amount - read; 526 return amount; 527 } 528 // Couldn't get all the characters, skip what we read 529 read += (end - pos); 530 pos = end; 531 } 532 return amount; 533 } 534 } 535} 536