Source for java.io.BufferedReader

   1: /* BufferedReader.java
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
   3:      Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11:  
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package java.io;
  41: 
  42: /* Written using "Java Class Libraries", 2nd edition, plus online
  43:  * API docs for JDK 1.2 beta from http://www.javasoft.com.
  44:  * Status:  Believed complete and correct.
  45:  */
  46: 
  47: /**
  48:  * This subclass of <code>FilterReader</code> buffers input from an 
  49:  * underlying implementation to provide a possibly more efficient read
  50:  * mechanism.  It maintains the buffer and buffer state in instance 
  51:  * variables that are available to subclasses.  The default buffer size
  52:  * of 8192 chars can be overridden by the creator of the stream.
  53:  * <p>
  54:  * This class also implements mark/reset functionality.  It is capable
  55:  * of remembering any number of input chars, to the limits of
  56:  * system memory or the size of <code>Integer.MAX_VALUE</code>
  57:  *
  58:  * @author Per Bothner (bothner@cygnus.com)
  59:  * @author Aaron M. Renn (arenn@urbanophile.com)
  60:  */
  61: public class BufferedReader extends Reader
  62: {
  63:   Reader in;
  64:   char[] buffer;
  65:   /* Index of current read position.  Must be >= 0 and <= limit. */
  66:   /* There is a special case where pos may be equal to limit+1; this
  67:    * is used as an indicator that a readLine was done with a '\r' was
  68:    * the very last char in the buffer.  Since we don't want to read-ahead
  69:    * and potentially block, we set pos this way to indicate the situation
  70:    * and deal with it later.  Doing it this way rather than having a
  71:    * separate boolean field to indicate the condition has the advantage
  72:    * that it is self-clearing on things like mark/reset.
  73:    */
  74:   int pos;
  75:   /* Limit of valid data in buffer.  Must be >= pos and <= buffer.length. */
  76:   /* This can be < pos in the one special case described above. */
  77:   int limit;
  78: 
  79:   /* The value -1 means there is no mark, or the mark has been invalidated.
  80:      Otherwise, markPos is the index in the buffer of the marked position.
  81:      Must be >= 0 and <= pos.
  82:      Note we do not explicitly store the read-limit.
  83:      The implicit read-limit is (buffer.length - markPos), which is
  84:      guaranteed to be >= the read-limit requested in the call to mark. */
  85:   int markPos = -1;
  86: 
  87:   // The JCL book specifies the default buffer size as 8K characters.
  88:   // This is package-private because it is used by LineNumberReader.
  89:   static final int DEFAULT_BUFFER_SIZE = 8192;
  90: 
  91:   /**
  92:     * Create a new <code>BufferedReader</code> that will read from the 
  93:     * specified subordinate stream with a default buffer size of 8192 chars.
  94:     *
  95:     * @param in The subordinate stream to read from
  96:     */
  97:   public BufferedReader(Reader in)
  98:   {
  99:     this(in, DEFAULT_BUFFER_SIZE);
 100:   }
 101: 
 102:   /**
 103:    * Create a new <code>BufferedReader</code> that will read from the 
 104:    * specified subordinate stream with a buffer size that is specified by the 
 105:    * caller.
 106:    *
 107:    * @param in The subordinate stream to read from
 108:    * @param size The buffer size to use
 109:    *
 110:    * @exception IllegalArgumentException if size &lt;= 0
 111:    */
 112:   public BufferedReader(Reader in, int size)
 113:   {
 114:     super(in.lock);
 115:     if (size <= 0)
 116:       throw new IllegalArgumentException("Illegal buffer size: " + size);
 117:     this.in = in;
 118:     buffer = new char[size];
 119:   }
 120: 
 121:   /**
 122:    * This method closes the underlying stream and frees any associated
 123:    * resources.
 124:    *
 125:    * @exception IOException If an error occurs
 126:    */
 127:   public void close() throws IOException
 128:   {
 129:     synchronized (lock)
 130:       {
 131:     if (in != null)
 132:       in.close();
 133:     in = null;
 134:     buffer = null;
 135:       }
 136:   }
 137: 
 138:   /**
 139:    * Returns <code>true</code> to indicate that this class supports mark/reset 
 140:    * functionality.
 141:    *
 142:    * @return <code>true</code>
 143:    */
 144:   public boolean markSupported()
 145:   {
 146:     return true;
 147:   }
 148: 
 149:   /**
 150:    * Mark a position in the input to which the stream can be
 151:    * "reset" by calling the <code>reset()</code> method.  The parameter
 152:    * <code>readLimit</code> is the number of chars that can be read from the 
 153:    * stream after setting the mark before the mark becomes invalid.  For
 154:    * example, if <code>mark()</code> is called with a read limit of 10, then 
 155:    * when 11 chars of data are read from the stream before the 
 156:    * <code>reset()</code> method is called, then the mark is invalid and the 
 157:    * stream object instance is not required to remember the mark.
 158:    * <p>
 159:    * Note that the number of chars that can be remembered by this method
 160:    * can be greater than the size of the internal read buffer.  It is also
 161:    * not dependent on the subordinate stream supporting mark/reset
 162:    * functionality.
 163:    *
 164:    * @param readLimit The number of chars that can be read before the mark 
 165:    *        becomes invalid
 166:    *
 167:    * @exception IOException If an error occurs
 168:    * @exception IllegalArgumentException if readLimit is negative.
 169:    */
 170:   public void mark(int readLimit) throws IOException
 171:   {
 172:     if (readLimit < 0)
 173:       throw new IllegalArgumentException("Read-ahead limit is negative");
 174: 
 175:     synchronized (lock)
 176:       {
 177:     checkStatus();
 178:     // In this method we need to be aware of the special case where
 179:     // pos + 1 == limit.  This indicates that a '\r' was the last char
 180:     // in the buffer during a readLine.  We'll want to maintain that
 181:     // condition after we shift things around and if a larger buffer is
 182:     // needed to track readLimit, we'll have to make it one element
 183:     // larger to ensure we don't invalidate the mark too early, if the
 184:     // char following the '\r' is NOT a '\n'.  This is ok because, per
 185:     // the spec, we are not required to invalidate when passing readLimit.
 186:     //
 187:     // Note that if 'pos > limit', then doing 'limit -= pos' will cause
 188:     // limit to be negative.  This is the only way limit will be < 0.
 189: 
 190:     if (pos + readLimit > limit)
 191:       {
 192:         char[] old_buffer = buffer;
 193:         int extraBuffSpace = 0;
 194:         if (pos > limit)
 195:           extraBuffSpace = 1;
 196:         if (readLimit + extraBuffSpace > limit)
 197:           buffer = new char[readLimit + extraBuffSpace];
 198:         limit -= pos;
 199:         if (limit >= 0)
 200:           {
 201:             System.arraycopy(old_buffer, pos, buffer, 0, limit);
 202:             pos = 0;
 203:           }
 204:       }
 205: 
 206:     if (limit < 0)
 207:       {
 208:         // Maintain the relationship of 'pos > limit'.
 209:         pos = 1;
 210:         limit = markPos = 0;
 211:       }
 212:     else
 213:       markPos = pos;
 214:     // Now pos + readLimit <= buffer.length. thus if we need to read
 215:     // beyond buffer.length, then we are allowed to invalidate markPos.
 216:       }
 217:   }
 218: 
 219:   /**
 220:    * Reset the stream to the point where the <code>mark()</code> method
 221:    * was called.  Any chars that were read after the mark point was set will
 222:    * be re-read during subsequent reads.
 223:    * <p>
 224:    * This method will throw an IOException if the number of chars read from
 225:    * the stream since the call to <code>mark()</code> exceeds the mark limit
 226:    * passed when establishing the mark.
 227:    *
 228:    * @exception IOException If an error occurs;
 229:    */
 230:   public void reset() throws IOException
 231:   {
 232:     synchronized (lock)
 233:       {
 234:     checkStatus();
 235:     if (markPos < 0)
 236:       throw new IOException("mark never set or invalidated");
 237: 
 238:     // Need to handle the extremely unlikely case where a readLine was
 239:     // done with a '\r' as the last char in the buffer; which was then
 240:     // immediately followed by a mark and a reset with NO intervening
 241:     // read of any sort.  In that case, setting pos to markPos would
 242:     // lose that info and a subsequent read would thus not skip a '\n'
 243:     // (if one exists).  The value of limit in this rare case is zero.
 244:     // We can assume that if limit is zero for other reasons, then
 245:     // pos is already set to zero and doesn't need to be readjusted.
 246:     if (limit > 0)
 247:       pos = markPos;
 248:       }
 249:   }
 250: 
 251:   /**
 252:    * This method determines whether or not a stream is ready to be read.  If
 253:    * this method returns <code>false</code> then this stream could (but is
 254:    * not guaranteed to) block on the next read attempt.
 255:    *
 256:    * @return <code>true</code> if this stream is ready to be read, 
 257:    * <code>false</code> otherwise
 258:    *
 259:    * @exception IOException If an error occurs
 260:    */
 261:   public boolean ready() throws IOException
 262:   {
 263:     synchronized (lock)
 264:       {
 265:     checkStatus();
 266:     return pos < limit || in.ready();
 267:       }
 268:   }
 269: 
 270:   /**
 271:    * This method read chars from a stream and stores them into a caller
 272:    * supplied buffer.  It starts storing the data at index 
 273:    * <code>offset</code> into
 274:    * the buffer and attempts to read <code>len</code> chars.  This method can
 275:    * return before reading the number of chars requested.  The actual number
 276:    * of chars read is returned as an int.  A -1 is returned to indicate the
 277:    * end of the stream.
 278:    * <p>
 279:    * This method will block until some data can be read.
 280:    *
 281:    * @param buf The array into which the chars read should be stored
 282:    * @param offset The offset into the array to start storing chars
 283:    * @param count The requested number of chars to read
 284:    *
 285:    * @return The actual number of chars read, or -1 if end of stream.
 286:    *
 287:    * @exception IOException If an error occurs.
 288:    * @exception IndexOutOfBoundsException If offset and count are not
 289:    * valid regarding buf.
 290:    */
 291:   public int read(char[] buf, int offset, int count) throws IOException
 292:   {
 293:     if (offset < 0 || offset + count > buf.length || count < 0)
 294:       throw new IndexOutOfBoundsException();
 295: 
 296:     synchronized (lock)
 297:       {
 298:     checkStatus();
 299:     // Once again, we need to handle the special case of a readLine
 300:     // that has a '\r' at the end of the buffer.  In this case, we'll
 301:     // need to skip a '\n' if it is the next char to be read.
 302:     // This special case is indicated by 'pos > limit'.
 303:     boolean retAtEndOfBuffer = false;
 304: 
 305:     int avail = limit - pos;
 306:     if (count > avail)
 307:       {
 308:         if (avail > 0)
 309:           count = avail;
 310:         else // pos >= limit
 311:           {
 312:         if (limit == buffer.length)
 313:           markPos = -1; // read too far - invalidate the mark.
 314:         if (pos > limit)
 315:           {
 316:             // Set a boolean and make pos == limit to simplify things.
 317:             retAtEndOfBuffer = true;
 318:             --pos;
 319:           }
 320:         if (markPos < 0)
 321:           {
 322:             // Optimization:  can read directly into buf.
 323:             if (count >= buffer.length && !retAtEndOfBuffer)
 324:               return in.read(buf, offset, count);
 325:             pos = limit = 0;
 326:           }
 327:         avail = in.read(buffer, limit, buffer.length - limit);
 328:         if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n')
 329:           {
 330:             --avail;
 331:             limit++;
 332:           }
 333:         if (avail < count)
 334:           {
 335:             if (avail <= 0)
 336:               return avail;
 337:             count = avail;
 338:           }
 339:         limit += avail;
 340:           }
 341:       }
 342:     System.arraycopy(buffer, pos, buf, offset, count);
 343:     pos += count;
 344:     return count;
 345:       }
 346:   }
 347: 
 348:   /* Read more data into the buffer.  Update pos and limit appropriately.
 349:      Assumes pos==limit initially.  May invalidate the mark if read too much.
 350:      Return number of chars read (never 0), or -1 on eof. */
 351:   private int fill() throws IOException
 352:   {
 353:     checkStatus();
 354:     // Handle the special case of a readLine that has a '\r' at the end of
 355:     // the buffer.  In this case, we'll need to skip a '\n' if it is the
 356:     // next char to be read.  This special case is indicated by 'pos > limit'.
 357:     boolean retAtEndOfBuffer = false;
 358:     if (pos > limit)
 359:       {
 360:         retAtEndOfBuffer = true;
 361:     --pos;
 362:       }
 363: 
 364:     if (markPos >= 0 && limit == buffer.length)
 365:       markPos = -1;
 366:     if (markPos < 0)
 367:       pos = limit = 0;
 368:     int count = in.read(buffer, limit, buffer.length - limit);
 369:     if (count > 0)
 370:       limit += count;
 371: 
 372:     if (retAtEndOfBuffer && buffer[pos] == '\n')
 373:       {
 374:     --count;
 375:     // If the mark was set to the location of the \n, then we
 376:     // must change it to fully pretend that the \n does not
 377:     // exist.
 378:     if (markPos == pos)
 379:       ++markPos;
 380:     ++pos;
 381:       }
 382: 
 383:     return count;
 384:   }
 385:   
 386:   public int read() throws IOException
 387:   {
 388:     synchronized (lock)
 389:       {
 390:     checkStatus();
 391:     if (pos >= limit && fill () <= 0)
 392:       return -1;
 393:     return buffer[pos++];
 394:       }
 395:   }
 396: 
 397:   /* Return the end of the line starting at this.pos and ending at limit.
 398:    * The index returns is *before* any line terminators, or limit
 399:    * if no line terminators were found.
 400:    */
 401:   private int lineEnd(int limit)
 402:   {
 403:     int i = pos;
 404:     for (; i < limit; i++)
 405:       {
 406:     char ch = buffer[i];
 407:     if (ch == '\n' || ch == '\r')
 408:       break;
 409:       }
 410:     return i;
 411:   }
 412: 
 413:   /**
 414:    * This method reads a single line of text from the input stream, returning
 415:    * it as a <code>String</code>.  A line is terminated by "\n", a "\r", or
 416:    * an "\r\n" sequence.  The system dependent line separator is not used.
 417:    * The line termination characters are not returned in the resulting
 418:    * <code>String</code>.
 419:    * 
 420:    * @return The line of text read, or <code>null</code> if end of stream.
 421:    * 
 422:    * @exception IOException If an error occurs
 423:    */
 424:   public String readLine() throws IOException
 425:   {
 426:     checkStatus();
 427:     // Handle the special case where a previous readLine (with no intervening
 428:     // reads/skips) had a '\r' at the end of the buffer.
 429:     // In this case, we'll need to skip a '\n' if it's the next char to be read.
 430:     // This special case is indicated by 'pos > limit'.
 431:     if (pos > limit)
 432:       {
 433:     int ch = read();
 434:     if (ch < 0)
 435:       return null;
 436:     if (ch != '\n')
 437:       --pos;
 438:       }
 439:     int i = lineEnd(limit);
 440:     if (i < limit)
 441:       {
 442:     String str = String.valueOf(buffer, pos, i - pos);
 443:     pos = i + 1;
 444:     // If the last char in the buffer is a '\r', we must remember
 445:     // to check if the next char to be read after the buffer is refilled
 446:     // is a '\n'.  If so, skip it.  To indicate this condition, we set pos
 447:     // to be limit + 1, which normally is never possible.
 448:     if (buffer[i] == '\r')
 449:       if (pos == limit || buffer[pos] == '\n')
 450:         pos++;
 451:     return str;
 452:       }
 453:     StringBuilder sbuf = new StringBuilder(200);
 454:     sbuf.append(buffer, pos, i - pos);
 455:     pos = i;
 456:     // We only want to return null when no characters were read before
 457:     // EOF.  So we must keep track of this separately.  Otherwise we
 458:     // would treat an empty `sbuf' as an EOF condition, which is wrong
 459:     // when there is just a newline.
 460:     boolean eof = false;
 461:     for (;;)
 462:       {
 463:     // readLine should block. So we must not return until a -1 is reached.
 464:     if (pos >= limit)
 465:       {
 466:         // here count == 0 isn't sufficient to give a failure.
 467:         int count = fill();
 468:         if (count < 0)
 469:           {
 470:         eof = true;
 471:         break;
 472:           }
 473:         continue;
 474:       }
 475:     int ch = buffer[pos++];
 476:     if (ch == '\n' || ch == '\r')
 477:       {
 478:         // Check here if a '\r' was the last char in the buffer; if so,
 479:         // mark it as in the comment above to indicate future reads
 480:         // should skip a newline that is the next char read after
 481:         // refilling the buffer.
 482:         if (ch == '\r')
 483:           if (pos == limit || buffer[pos] == '\n')
 484:             pos++;
 485:         break;
 486:       }
 487:     i = lineEnd(limit);
 488:     sbuf.append(buffer, pos - 1, i - (pos - 1));
 489:     pos = i;
 490:       }
 491:     return (sbuf.length() == 0 && eof) ? null : sbuf.toString();
 492:   }
 493: 
 494:   /**
 495:    * This method skips the specified number of chars in the stream.  It
 496:    * returns the actual number of chars skipped, which may be less than the
 497:    * requested amount.
 498:    * <p>
 499:    * This method first discards chars in the buffer, then calls the
 500:    * <code>skip</code> method on the underlying stream to skip the 
 501:    * remaining chars.
 502:    *
 503:    * @param count The requested number of chars to skip
 504:    *
 505:    * @return The actual number of chars skipped.
 506:    *
 507:    * @exception IOException If an error occurs.
 508:    * @exception IllegalArgumentException If count is negative.
 509:    */
 510:   public long skip(long count) throws IOException
 511:   {
 512:     synchronized (lock)
 513:       {
 514:     checkStatus();
 515:     if (count < 0)
 516:       throw new IllegalArgumentException("skip value is negative");
 517:     if (count == 0)
 518:       return 0;
 519:     // Yet again, we need to handle the special case of a readLine
 520:     // that has a '\r' at the end of the buffer.  In this case, we need
 521:     // to ignore a '\n' if it is the next char to be read.
 522:     // This special case is indicated by 'pos > limit' (i.e. avail < 0).
 523:     // To simplify things, if we're dealing with the special case for
 524:     // readLine, just read the next char (since the fill method will
 525:     // skip the '\n' for us).  By doing this, we'll have to back up pos.
 526:     // That's easier than trying to keep track of whether we've skipped
 527:     // one element or not.
 528:     if (pos > limit)
 529:       {
 530:         if (read() < 0)
 531:           return 0;
 532:         else
 533:           --pos; 
 534:       }
 535: 
 536:     int avail = limit - pos;
 537: 
 538:     if (count < avail)
 539:       {
 540:         pos += count;
 541:         return count;
 542:       }
 543: 
 544:     pos = limit;
 545:     long todo = count - avail;
 546:     if (todo > buffer.length)
 547:       {
 548:         markPos = -1;
 549:         todo -= in.skip(todo);
 550:       }
 551:     else
 552:       {
 553:         while (todo > 0)
 554:           {
 555:         avail = fill();
 556:         if (avail <= 0)
 557:           break;
 558:         if (avail > todo)
 559:           avail = (int) todo;
 560:         pos += avail;
 561:         todo -= avail;
 562:           }
 563:       }
 564:     return count - todo;
 565:       }
 566:   }
 567:   
 568:   private void checkStatus() throws IOException
 569:   {
 570:     if (in == null)
 571:       throw new IOException("Stream closed");
 572:   }  
 573: }