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