BufferedReader.java revision 4fefecee9d4a5d2a4510f516b4015607b19e8d09
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 private char[] buf; 48 49 private int marklimit = -1; 50 51 private int count; 52 53 private int markpos = -1; 54 55 private int pos; 56 57 /** 58 * Constructs a new BufferedReader on the Reader {@code in}. The 59 * buffer gets the default size (8 KB). 60 * 61 * @param in 62 * the Reader that is buffered. 63 */ 64 public BufferedReader(Reader in) { 65 super(in); 66 this.in = in; 67 buf = new char[8192]; 68 69 // BEGIN android-added 70 /* 71 * For Android, we want to discourage the use of this 72 * constructor (with its arguably too-large default), so we 73 * note its use in the log. We don't disable it, nor do we 74 * alter the default, however, because we still aim to behave 75 * compatibly, and the default value, though not documented, 76 * is established by convention. 77 */ 78 Logger.global.info( 79 "Default buffer size used in BufferedReader " + 80 "constructor. It would be " + 81 "better to be explicit if an 8k-char buffer is required."); 82 // END android-added 83 } 84 85 /** 86 * Constructs a new BufferedReader on the Reader {@code in}. The buffer 87 * size is specified by the parameter {@code size}. 88 * 89 * @param in 90 * the Reader that is buffered. 91 * @param size 92 * the size of the buffer to allocate. 93 * @throws IllegalArgumentException 94 * if {@code size <= 0}. 95 */ 96 public BufferedReader(Reader in, int size) { 97 super(in); 98 if (size <= 0) { 99 throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$ 100 } 101 this.in = in; 102 buf = new char[size]; 103 } 104 105 /** 106 * Closes this reader. This implementation closes the buffered source reader 107 * and releases the buffer. Nothing is done if this reader has already been 108 * closed. 109 * 110 * @throws IOException 111 * if an error occurs while closing this reader. 112 */ 113 @Override 114 public void close() throws IOException { 115 synchronized (lock) { 116 if (!isClosed()) { 117 in.close(); 118 buf = null; 119 } 120 } 121 } 122 123 private int fillbuf() throws IOException { 124 if (markpos == -1 || (pos - markpos >= marklimit)) { 125 /* Mark position not set or exceeded readlimit */ 126 int result = in.read(buf, 0, buf.length); 127 if (result > 0) { 128 markpos = -1; 129 pos = 0; 130 count = result == -1 ? 0 : result; 131 } 132 return result; 133 } 134 if (markpos == 0 && marklimit > buf.length) { 135 /* Increase buffer size to accommodate the readlimit */ 136 int newLength = buf.length * 2; 137 if (newLength > marklimit) { 138 newLength = marklimit; 139 } 140 char[] newbuf = new char[newLength]; 141 System.arraycopy(buf, 0, newbuf, 0, buf.length); 142 buf = newbuf; 143 } else if (markpos > 0) { 144 System.arraycopy(buf, markpos, buf, 0, buf.length - markpos); 145 } 146 147 /* Set the new position and mark position */ 148 pos -= markpos; 149 count = markpos = 0; 150 int charsread = in.read(buf, pos, buf.length - pos); 151 count = charsread == -1 ? pos : pos + charsread; 152 return charsread; 153 } 154 155 /** 156 * Indicates whether or not this reader is closed. 157 * 158 * @return {@code true} if this reader is closed, {@code false} 159 * otherwise. 160 */ 161 private boolean isClosed() { 162 return buf == null; 163 } 164 165 /** 166 * Sets a mark position in this reader. The parameter {@code readlimit} 167 * indicates how many characters can be read before the mark is invalidated. 168 * Calling {@code reset()} will reposition the reader back to the marked 169 * position if {@code readlimit} has not been surpassed. 170 * 171 * @param readlimit 172 * the number of characters that can be read before the mark is 173 * invalidated. 174 * @throws IllegalArgumentException 175 * if {@code readlimit < 0}. 176 * @throws IOException 177 * if an error occurs while setting a mark in this reader. 178 * @see #markSupported() 179 * @see #reset() 180 */ 181 @Override 182 public void mark(int readlimit) throws IOException { 183 if (readlimit < 0) { 184 throw new IllegalArgumentException(); 185 } 186 synchronized (lock) { 187 if (isClosed()) { 188 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 189 } 190 marklimit = readlimit; 191 markpos = pos; 192 } 193 } 194 195 /** 196 * Indicates whether this reader supports the {@code mark()} and 197 * {@code reset()} methods. This implementation returns {@code true}. 198 * 199 * @return {@code true} for {@code BufferedReader}. 200 * @see #mark(int) 201 * @see #reset() 202 */ 203 @Override 204 public boolean markSupported() { 205 return true; 206 } 207 208 /** 209 * Reads a single character from this reader and returns it with the two 210 * higher-order bytes set to 0. If possible, BufferedReader returns a 211 * character from the buffer. If there are no characters available in the 212 * buffer, it fills the buffer and then returns a character. It returns -1 213 * if there are no more characters in the source reader. 214 * 215 * @return the character read or -1 if the end of the source reader has been 216 * reached. 217 * @throws IOException 218 * if this reader is closed or some other I/O error occurs. 219 */ 220 @Override 221 public int read() throws IOException { 222 synchronized (lock) { 223 if (isClosed()) { 224 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 225 } 226 /* Are there buffered characters available? */ 227 if (pos < count || fillbuf() != -1) { 228 return buf[pos++]; 229 } 230 markpos = -1; 231 return -1; 232 } 233 } 234 235 /** 236 * Reads at most {@code length} characters from this reader and stores them 237 * at {@code offset} in the character array {@code buffer}. Returns the 238 * number of characters actually read or -1 if the end of the source reader 239 * has been reached. If all the buffered characters have been used, a mark 240 * has not been set and the requested number of characters is larger than 241 * this readers buffer size, BufferedReader bypasses the buffer and simply 242 * places the results directly into {@code buffer}. 243 * 244 * @param buffer 245 * the character array to store the characters read. 246 * @param offset 247 * the initial position in {@code buffer} to store the bytes read 248 * from this reader. 249 * @param length 250 * the maximum number of characters to read, must be 251 * non-negative. 252 * @return number of characters read or -1 if the end of the source reader 253 * has been reached. 254 * @throws IndexOutOfBoundsException 255 * if {@code offset < 0} or {@code length < 0}, or if 256 * {@code offset + length} is greater than the size of 257 * {@code buffer}. 258 * @throws IOException 259 * if this reader is closed or some other I/O error occurs. 260 */ 261 @Override 262 public int read(char[] buffer, int offset, int length) throws IOException { 263 synchronized (lock) { 264 if (isClosed()) { 265 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 266 } 267 // BEGIN android-changed 268 // Exception priorities (in case of multiple errors) differ from 269 // RI, but are spec-compliant. 270 // made implicit null check explicit, used (offset | length) < 0 271 // instead of (offset < 0) || (length < 0) to safe one operation 272 if (buffer == null) { 273 throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ 274 } 275 if ((offset | length) < 0 || offset > buffer.length - length) { 276 throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ 277 } 278 // END android-changed 279 if (length == 0) { 280 return 0; 281 } 282 int required; 283 if (pos < count) { 284 /* There are bytes available in the buffer. */ 285 int copylength = count - pos >= length ? length : count - pos; 286 System.arraycopy(buf, pos, buffer, offset, copylength); 287 pos += copylength; 288 if (copylength == length || !in.ready()) { 289 return copylength; 290 } 291 offset += copylength; 292 required = length - copylength; 293 } else { 294 required = length; 295 } 296 297 while (true) { 298 int read; 299 /* 300 * If we're not marked and the required size is greater than the 301 * buffer, simply read the bytes directly bypassing the buffer. 302 */ 303 if (markpos == -1 && required >= buf.length) { 304 read = in.read(buffer, offset, required); 305 if (read == -1) { 306 return required == length ? -1 : length - required; 307 } 308 } else { 309 if (fillbuf() == -1) { 310 return required == length ? -1 : length - required; 311 } 312 read = count - pos >= required ? required : count - pos; 313 System.arraycopy(buf, pos, buffer, offset, read); 314 pos += read; 315 } 316 required -= read; 317 if (required == 0) { 318 return length; 319 } 320 if (!in.ready()) { 321 return length - required; 322 } 323 offset += read; 324 } 325 } 326 } 327 328 /** 329 * Returns the next line of text available from this reader. A line is 330 * represented by zero or more characters followed by {@code '\n'}, 331 * {@code '\r'}, {@code "\r\n"} or the end of the reader. The string does 332 * not include the newline sequence. 333 * 334 * @return the contents of the line or {@code null} if no characters were 335 * read before the end of the reader has been reached. 336 * @throws IOException 337 * if this reader is closed or some other I/O error occurs. 338 */ 339 public String readLine() throws IOException { 340 synchronized (lock) { 341 if (isClosed()) { 342 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 343 } 344 /* Are there buffered characters available? */ 345 if ((pos >= count) && (fillbuf() == -1)) { 346 return null; 347 } 348 for (int charPos = pos; charPos < count; charPos++) { 349 char ch = buf[charPos]; 350 // BEGIN android-note 351 // a switch statement may be more efficient 352 // END android-note 353 if (ch > '\r') { 354 continue; 355 } 356 if (ch == '\n') { 357 String res = new String(buf, pos, charPos - pos); 358 pos = charPos + 1; 359 return res; 360 } else if (ch == '\r') { 361 String res = new String(buf, pos, charPos - pos); 362 pos = charPos + 1; 363 if (((pos < count) || (fillbuf() != -1)) 364 && (buf[pos] == '\n')) { 365 pos++; 366 } 367 return res; 368 } 369 } 370 371 char eol = '\0'; 372 StringBuilder result = new StringBuilder(80); 373 /* Typical Line Length */ 374 375 result.append(buf, pos, count - pos); 376 pos = count; 377 while (true) { 378 /* Are there buffered characters available? */ 379 if (pos >= count) { 380 if (eol == '\n') { 381 return result.toString(); 382 } 383 // attempt to fill buffer 384 if (fillbuf() == -1) { 385 // characters or null. 386 return result.length() > 0 || eol != '\0' ? result 387 .toString() : null; 388 } 389 } 390 for (int charPos = pos; charPos < count; charPos++) { 391 // BEGIN android-note 392 // use a local variable for buf[charPos] and a switch statement 393 // END android-note 394 if (eol == '\0') { 395 if ((buf[charPos] == '\n' || buf[charPos] == '\r')) { 396 eol = buf[charPos]; 397 } 398 } else if (eol == '\r' && (buf[charPos] == '\n')) { 399 if (charPos > pos) { 400 result.append(buf, pos, charPos - pos - 1); 401 } 402 pos = charPos + 1; 403 return result.toString(); 404 } else { 405 if (charPos > pos) { 406 result.append(buf, pos, charPos - pos - 1); 407 } 408 pos = charPos; 409 return result.toString(); 410 } 411 } 412 if (eol == '\0') { 413 result.append(buf, pos, count - pos); 414 } else { 415 result.append(buf, pos, count - pos - 1); 416 } 417 pos = count; 418 } 419 } 420 421 } 422 423 /** 424 * Indicates whether this reader is ready to be read without blocking. 425 * 426 * @return {@code true} if this reader will not block when {@code read} is 427 * called, {@code false} if unknown or blocking will occur. 428 * @throws IOException 429 * if this reader is closed or some other I/O error occurs. 430 * @see #read() 431 * @see #read(char[], int, int) 432 * @see #readLine() 433 */ 434 @Override 435 public boolean ready() throws IOException { 436 synchronized (lock) { 437 if (isClosed()) { 438 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 439 } 440 return ((count - pos) > 0) || in.ready(); 441 } 442 } 443 444 /** 445 * Resets this reader's position to the last {@code mark()} location. 446 * Invocations of {@code read()} and {@code skip()} will occur from this new 447 * location. 448 * 449 * @throws IOException 450 * if this reader is closed or no mark has been set. 451 * @see #mark(int) 452 * @see #markSupported() 453 */ 454 @Override 455 public void reset() throws IOException { 456 synchronized (lock) { 457 if (isClosed()) { 458 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 459 } 460 if (markpos == -1) { 461 throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$ 462 } 463 pos = markpos; 464 } 465 } 466 467 /** 468 * Skips {@code amount} characters in this reader. Subsequent 469 * {@code read()}s will not return these characters unless {@code reset()} 470 * is used. Skipping characters may invalidate a mark if {@code readlimit} 471 * is surpassed. 472 * 473 * @param amount 474 * the maximum number of characters to skip. 475 * @return the number of characters actually skipped. 476 * @throws IllegalArgumentException 477 * if {@code amount < 0}. 478 * @throws IOException 479 * if this reader is closed or some other I/O error occurs. 480 * @see #mark(int) 481 * @see #markSupported() 482 * @see #reset() 483 */ 484 @Override 485 public long skip(long amount) throws IOException { 486 if (amount < 0) { 487 throw new IllegalArgumentException(); 488 } 489 synchronized (lock) { 490 if (isClosed()) { 491 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 492 } 493 if (amount < 1) { 494 return 0; 495 } 496 if (count - pos >= amount) { 497 pos += amount; 498 return amount; 499 } 500 501 long read = count - pos; 502 pos = count; 503 while (read < amount) { 504 if (fillbuf() == -1) { 505 return read; 506 } 507 if (count - pos >= amount - read) { 508 pos += amount - read; 509 return amount; 510 } 511 // Couldn't get all the characters, skip what we read 512 read += (count - pos); 513 pos = count; 514 } 515 return amount; 516 } 517 } 518} 519