BufferedReader.java revision fdb2704414a9ed92394ada0d1395e4db86889465
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 * BufferedReader is a buffered character input reader. Buffering allows reading 28 * from character streams more efficiently. If the default size of the buffer is 29 * not practical, another size may be specified. Reading a character from a 30 * Reader class usually involves reading a character from its Stream or 31 * subsequent Reader. It is advisable to wrap a BufferedReader around those 32 * Readers whose read operations may have high latency. For example, the 33 * following code 34 * 35 * <pre> 36 * BufferedReader inReader = new BufferedReader(new FileReader("file.java")); 37 * </pre> 38 * 39 * will buffer input for the file <code>file.java</code>. 40 * 41 * @see BufferedWriter 42 * @since 1.1 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</code>. The 60 * default buffer size (8K) is allocated and all reads can now be filtered 61 * through this BufferedReader. 62 * 63 * @param in 64 * the Reader to buffer reads on. 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</code>. The 89 * buffer size is specified by the parameter <code>size</code> and all 90 * reads can now be filtered through this BufferedReader. 91 * 92 * @param in 93 * the Reader to buffer reads on. 94 * @param size 95 * the size of buffer to allocate. 96 * @throws IllegalArgumentException 97 * if the size is <= 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 * Close the Reader. This implementation closes the Reader being filtered 110 * and releases the buffer used by this reader. If this BufferedReader has 111 * already been closed, nothing is done. 112 * 113 * @throws IOException 114 * If an error occurs attempting to close this BufferedReader. 115 */ 116 @Override 117 public void close() throws IOException { 118 synchronized (lock) { 119 if (!isClosed()) { 120 in.close(); 121 buf = null; 122 } 123 } 124 } 125 126 private int fillbuf() throws IOException { 127 if (markpos == -1 || (pos - markpos >= marklimit)) { 128 /* Mark position not set or exceeded readlimit */ 129 int result = in.read(buf, 0, buf.length); 130 if (result > 0) { 131 markpos = -1; 132 pos = 0; 133 count = result == -1 ? 0 : result; 134 } 135 return result; 136 } 137 if (markpos == 0 && marklimit > buf.length) { 138 /* Increase buffer size to accommodate the readlimit */ 139 int newLength = buf.length * 2; 140 if (newLength > marklimit) { 141 newLength = marklimit; 142 } 143 char[] newbuf = new char[newLength]; 144 System.arraycopy(buf, 0, newbuf, 0, buf.length); 145 buf = newbuf; 146 } else if (markpos > 0) { 147 System.arraycopy(buf, markpos, buf, 0, buf.length - markpos); 148 } 149 150 /* Set the new position and mark position */ 151 pos -= markpos; 152 count = markpos = 0; 153 int charsread = in.read(buf, pos, buf.length - pos); 154 count = charsread == -1 ? pos : pos + charsread; 155 return charsread; 156 } 157 158 /** 159 * Answer a boolean indicating whether or not this BufferedReader is closed. 160 * 161 * @return <code>true</code> if this reader is closed, <code>false</code> 162 * otherwise 163 */ 164 private boolean isClosed() { 165 return buf == null; 166 } 167 168 /** 169 * Set a Mark position in this BufferedReader. The parameter 170 * <code>readLimit</code> indicates how many characters can be read before 171 * a mark is invalidated. Sending reset() will reposition the reader back to 172 * the marked position provided <code>readLimit</code> has not been 173 * surpassed. 174 * 175 * @param readlimit 176 * an int representing how many characters must be read before 177 * invalidating the mark. 178 * 179 * @throws IOException 180 * If an error occurs attempting mark this BufferedReader. 181 * @throws IllegalArgumentException 182 * If readlimit is < 0 183 */ 184 @Override 185 public void mark(int readlimit) throws IOException { 186 if (readlimit < 0) { 187 throw new IllegalArgumentException(); 188 } 189 synchronized (lock) { 190 if (isClosed()) { 191 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 192 } 193 marklimit = readlimit; 194 markpos = pos; 195 } 196 } 197 198 /** 199 * Returns a boolean indicating whether or not this Reader supports mark() 200 * and reset(). This implementation returns <code>true</code>. 201 * 202 * @return <code>true</code> if mark() and reset() are supported, 203 * <code>false</code> otherwise 204 */ 205 @Override 206 public boolean markSupported() { 207 return true; 208 } 209 210 /** 211 * Reads a single character from this reader and returns the result as an 212 * int. The 2 higher-order characters are set to 0. If the end of reader was 213 * encountered then return -1. This implementation either returns a 214 * character from the buffer or if there are no characters available, fill 215 * the buffer then return a character or -1. 216 * 217 * @return the character read or -1 if end of reader. 218 * 219 * @throws IOException 220 * If the BufferedReader is already closed or some other IO 221 * error occurs. 222 */ 223 @Override 224 public int read() throws IOException { 225 synchronized (lock) { 226 if (isClosed()) { 227 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 228 } 229 /* Are there buffered characters available? */ 230 if (pos < count || fillbuf() != -1) { 231 return buf[pos++]; 232 } 233 return -1; 234 } 235 } 236 237 /** 238 * Reads at most <code>length</code> characters from this BufferedReader 239 * and stores them at <code>offset</code> in the character array 240 * <code>buffer</code>. Returns the number of characters actually read or 241 * -1 if the end of reader was encountered. If all the buffered characters 242 * have been used, a mark has not been set, and the requested number of 243 * characters is larger than this Readers buffer size, this implementation 244 * bypasses the buffer and simply places the results directly into 245 * <code>buffer</code>. 246 * 247 * @param buffer 248 * character array to store the read characters 249 * @param offset 250 * offset in buf to store the read characters 251 * @param length 252 * maximum number of characters to read 253 * @return number of characters read or -1 if end of reader. 254 * 255 * @throws IOException 256 * If the BufferedReader is already closed or some other IO 257 * error occurs. 258 */ 259 @Override 260 public int read(char[] buffer, int offset, int length) throws IOException { 261 synchronized (lock) { 262 if (isClosed()) { 263 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 264 } 265 if (offset < 0 || offset > buffer.length - length || length < 0) { 266 throw new IndexOutOfBoundsException(); 267 } 268 if (length == 0) { 269 return 0; 270 } 271 int required; 272 if (pos < count) { 273 /* There are bytes available in the buffer. */ 274 int copylength = count - pos >= length ? length : count - pos; 275 System.arraycopy(buf, pos, buffer, offset, copylength); 276 pos += copylength; 277 if (copylength == length || !in.ready()) { 278 return copylength; 279 } 280 offset += copylength; 281 required = length - copylength; 282 } else { 283 required = length; 284 } 285 286 while (true) { 287 int read; 288 /* 289 * If we're not marked and the required size is greater than the 290 * buffer, simply read the bytes directly bypassing the buffer. 291 */ 292 if (markpos == -1 && required >= buf.length) { 293 read = in.read(buffer, offset, required); 294 if (read == -1) { 295 return required == length ? -1 : length - required; 296 } 297 } else { 298 if (fillbuf() == -1) { 299 return required == length ? -1 : length - required; 300 } 301 read = count - pos >= required ? required : count - pos; 302 System.arraycopy(buf, pos, buffer, offset, read); 303 pos += read; 304 } 305 required -= read; 306 if (required == 0) { 307 return length; 308 } 309 if (!in.ready()) { 310 return length - required; 311 } 312 offset += read; 313 } 314 } 315 } 316 317 /** 318 * Returns a <code>String</code> representing the next line of text 319 * available in this BufferedReader. A line is represented by 0 or more 320 * characters followed by <code>'\n'</code>, <code>'\r'</code>, 321 * <code>'\r\n'</code> or end of stream. The <code>String</code> does 322 * not include the newline sequence. 323 * 324 * @return the contents of the line or null if no characters were read 325 * before end of stream. 326 * 327 * @throws IOException 328 * If the BufferedReader is already closed or some other IO 329 * error occurs. 330 */ 331 public String readLine() throws IOException { 332 synchronized (lock) { 333 if (isClosed()) { 334 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 335 } 336 /* Are there buffered characters available? */ 337 if ((pos >= count) && (fillbuf() == -1)) { 338 return null; 339 } 340 for (int charPos = pos; charPos < count; charPos++) { 341 char ch = buf[charPos]; 342 if (ch > '\r') { 343 continue; 344 } 345 if (ch == '\n') { 346 String res = new String(buf, pos, charPos - pos); 347 pos = charPos + 1; 348 return res; 349 } else if (ch == '\r') { 350 String res = new String(buf, pos, charPos - pos); 351 pos = charPos + 1; 352 if (((pos < count) || (fillbuf() != -1)) 353 && (buf[pos] == '\n')) { 354 pos++; 355 } 356 return res; 357 } 358 } 359 360 char eol = '\0'; 361 StringBuilder result = new StringBuilder(80); 362 /* Typical Line Length */ 363 364 result.append(buf, pos, count - pos); 365 pos = count; 366 while (true) { 367 /* Are there buffered characters available? */ 368 if (pos >= count) { 369 if (eol == '\n') { 370 return result.toString(); 371 } 372 // attempt to fill buffer 373 if (fillbuf() == -1) { 374 // characters or null. 375 return result.length() > 0 || eol != '\0' ? result 376 .toString() : null; 377 } 378 } 379 for (int charPos = pos; charPos < count; charPos++) { 380 if (eol == '\0') { 381 if ((buf[charPos] == '\n' || buf[charPos] == '\r')) { 382 eol = buf[charPos]; 383 } 384 } else if (eol == '\r' && (buf[charPos] == '\n')) { 385 if (charPos > pos) { 386 result.append(buf, pos, charPos - pos - 1); 387 } 388 pos = charPos + 1; 389 return result.toString(); 390 } else if (eol != '\0') { 391 if (charPos > pos) { 392 result.append(buf, pos, charPos - pos - 1); 393 } 394 pos = charPos; 395 return result.toString(); 396 } 397 } 398 if (eol == '\0') { 399 result.append(buf, pos, count - pos); 400 } else { 401 result.append(buf, pos, count - pos - 1); 402 } 403 pos = count; 404 } 405 } 406 407 } 408 409 /** 410 * Returns a <code>boolean</code> indicating whether or not this Reader is 411 * ready to be read without blocking. If the result is <code>true</code>, 412 * the next <code>read()</code> will not block. If the result is 413 * <code>false</code> this Reader may or may not block when 414 * <code>read()</code> is sent. 415 * 416 * @return <code>true</code> if the receiver will not block when 417 * <code>read()</code> is called, <code>false</code> if unknown 418 * or blocking will occur. 419 * 420 * @throws IOException 421 * If the BufferedReader is already closed or some other IO 422 * error occurs. 423 */ 424 @Override 425 public boolean ready() throws IOException { 426 synchronized (lock) { 427 if (isClosed()) { 428 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 429 } 430 return ((count - pos) > 0) || in.ready(); 431 } 432 } 433 434 /** 435 * Reset this BufferedReader's position to the last <code>mark()</code> 436 * location. Invocations of <code>read()/skip()</code> will occur from 437 * this new location. If this Reader was not marked, throw IOException. 438 * 439 * @throws IOException 440 * If a problem occurred, the receiver does not support 441 * <code>mark()/reset()</code>, or no mark has been set. 442 */ 443 @Override 444 public void reset() throws IOException { 445 synchronized (lock) { 446 if (isClosed()) { 447 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 448 } 449 if (markpos == -1) { 450 throw new IOException(Msg.getString("K005c")); //$NON-NLS-1$ 451 } 452 pos = markpos; 453 } 454 } 455 456 /** 457 * Skips <code>amount</code> number of characters in this Reader. 458 * Subsequent <code>read()</code>'s will not return these characters 459 * unless <code>reset()</code> is used. Skipping characters may invalidate 460 * a mark if marklimit is surpassed. 461 * 462 * @param amount 463 * the maximum number of characters to skip. 464 * @return the number of characters actually skipped. 465 * 466 * @throws IOException 467 * If the BufferedReader is already closed or some other IO 468 * error occurs. 469 * @throws IllegalArgumentException 470 * If amount is negative 471 */ 472 @Override 473 public long skip(long amount) throws IOException { 474 if (amount < 0) { 475 throw new IllegalArgumentException(); 476 } 477 synchronized (lock) { 478 if (isClosed()) { 479 throw new IOException(Msg.getString("K005b")); //$NON-NLS-1$ 480 } 481 if (amount < 1) { 482 return 0; 483 } 484 if (count - pos >= amount) { 485 pos += amount; 486 return amount; 487 } 488 489 long read = count - pos; 490 pos = count; 491 while (read < amount) { 492 if (fillbuf() == -1) { 493 return read; 494 } 495 if (count - pos >= amount - read) { 496 pos += amount - read; 497 return amount; 498 } 499 // Couldn't get all the characters, skip what we read 500 read += (count - pos); 501 pos = count; 502 } 503 return amount; 504 } 505 } 506} 507