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:    * The line buffer for <code>readLine</code>.
  93:    */
  94:   private StringBuffer sbuf = null;
  95: 
  96:   /**
  97:     * Create a new <code>BufferedReader</code> that will read from the 
  98:     * specified subordinate stream with a default buffer size of 8192 chars.
  99:     *
 100:     * @param in The subordinate stream to read from
 101:     */
 102:   public BufferedReader(Reader in)
 103:   {
 104:     this(in, DEFAULT_BUFFER_SIZE);
 105:   }
 106: 
 107:   /**
 108:    * Create a new <code>BufferedReader</code> that will read from the 
 109:    * specified subordinate stream with a buffer size that is specified by the 
 110:    * caller.
 111:    *
 112:    * @param in The subordinate stream to read from
 113:    * @param size The buffer size to use
 114:    *
 115:    * @exception IllegalArgumentException if size &lt;= 0
 116:    */
 117:   public BufferedReader(Reader in, int size)
 118:   {
 119:     super(in.lock);
 120:     if (size <= 0)
 121:       throw new IllegalArgumentException("Illegal buffer size: " + size);
 122:     this.in = in;
 123:     buffer = new char[size];
 124:   }
 125: 
 126:   /**
 127:    * This method closes the underlying stream and frees any associated
 128:    * resources.
 129:    *
 130:    * @exception IOException If an error occurs
 131:    */
 132:   public void close() throws IOException
 133:   {
 134:     synchronized (lock)
 135:       {
 136:     if (in != null)
 137:       in.close();
 138:     in = null;
 139:     buffer = null;
 140:       }
 141:   }
 142: 
 143:   /**
 144:    * Returns <code>true</code> to indicate that this class supports mark/reset 
 145:    * functionality.
 146:    *
 147:    * @return <code>true</code>
 148:    */
 149:   public boolean markSupported()
 150:   {
 151:     return true;
 152:   }
 153: 
 154:   /**
 155:    * Mark a position in the input to which the stream can be
 156:    * "reset" by calling the <code>reset()</code> method.  The parameter
 157:    * <code>readLimit</code> is the number of chars that can be read from the 
 158:    * stream after setting the mark before the mark becomes invalid.  For
 159:    * example, if <code>mark()</code> is called with a read limit of 10, then 
 160:    * when 11 chars of data are read from the stream before the 
 161:    * <code>reset()</code> method is called, then the mark is invalid and the 
 162:    * stream object instance is not required to remember the mark.
 163:    * <p>
 164:    * Note that the number of chars that can be remembered by this method
 165:    * can be greater than the size of the internal read buffer.  It is also
 166:    * not dependent on the subordinate stream supporting mark/reset
 167:    * functionality.
 168:    *
 169:    * @param readLimit The number of chars that can be read before the mark 
 170:    *        becomes invalid
 171:    *
 172:    * @exception IOException If an error occurs
 173:    * @exception IllegalArgumentException if readLimit is negative.
 174:    */
 175:   public void mark(int readLimit) throws IOException
 176:   {
 177:     if (readLimit < 0)
 178:       throw new IllegalArgumentException("Read-ahead limit is negative");
 179: 
 180:     synchronized (lock)
 181:       {
 182:     checkStatus();
 183:     // In this method we need to be aware of the special case where
 184:     // pos + 1 == limit.  This indicates that a '\r' was the last char
 185:     // in the buffer during a readLine.  We'll want to maintain that
 186:     // condition after we shift things around and if a larger buffer is
 187:     // needed to track readLimit, we'll have to make it one element
 188:     // larger to ensure we don't invalidate the mark too early, if the
 189:     // char following the '\r' is NOT a '\n'.  This is ok because, per
 190:     // the spec, we are not required to invalidate when passing readLimit.
 191:     //
 192:     // Note that if 'pos > limit', then doing 'limit -= pos' will cause
 193:     // limit to be negative.  This is the only way limit will be < 0.
 194: 
 195:     if (pos + readLimit > limit)
 196:       {
 197:         char[] old_buffer = buffer;
 198:         int extraBuffSpace = 0;
 199:         if (pos > limit)
 200:           extraBuffSpace = 1;
 201:         if (readLimit + extraBuffSpace > limit)
 202:           buffer = new char[readLimit + extraBuffSpace];
 203:         limit -= pos;
 204:         if (limit >= 0)
 205:           {
 206:             System.arraycopy(old_buffer, pos, buffer, 0, limit);
 207:             pos = 0;
 208:           }
 209:       }
 210: 
 211:     if (limit < 0)
 212:       {
 213:         // Maintain the relationship of 'pos > limit'.
 214:         pos = 1;
 215:         limit = markPos = 0;
 216:       }
 217:     else
 218:       markPos = pos;
 219:     // Now pos + readLimit <= buffer.length. thus if we need to read
 220:     // beyond buffer.length, then we are allowed to invalidate markPos.
 221:       }
 222:   }
 223: 
 224:   /**
 225:    * Reset the stream to the point where the <code>mark()</code> method
 226:    * was called.  Any chars that were read after the mark point was set will
 227:    * be re-read during subsequent reads.
 228:    * <p>
 229:    * This method will throw an IOException if the number of chars read from
 230:    * the stream since the call to <code>mark()</code> exceeds the mark limit
 231:    * passed when establishing the mark.
 232:    *
 233:    * @exception IOException If an error occurs;
 234:    */
 235:   public void reset() throws IOException
 236:   {
 237:     synchronized (lock)
 238:       {
 239:     checkStatus();
 240:     if (markPos < 0)
 241:       throw new IOException("mark never set or invalidated");
 242: 
 243:     // Need to handle the extremely unlikely case where a readLine was
 244:     // done with a '\r' as the last char in the buffer; which was then
 245:     // immediately followed by a mark and a reset with NO intervening
 246:     // read of any sort.  In that case, setting pos to markPos would
 247:     // lose that info and a subsequent read would thus not skip a '\n'
 248:     // (if one exists).  The value of limit in this rare case is zero.
 249:     // We can assume that if limit is zero for other reasons, then
 250:     // pos is already set to zero and doesn't need to be readjusted.
 251:     if (limit > 0)
 252:       pos = markPos;
 253:       }
 254:   }
 255: 
 256:   /**
 257:    * This method determines whether or not a stream is ready to be read.  If
 258:    * this method returns <code>false</code> then this stream could (but is
 259:    * not guaranteed to) block on the next read attempt.
 260:    *
 261:    * @return <code>true</code> if this stream is ready to be read, 
 262:    * <code>false</code> otherwise
 263:    *
 264:    * @exception IOException If an error occurs
 265:    */
 266:   public boolean ready() throws IOException
 267:   {
 268:     synchronized (lock)
 269:       {
 270:     checkStatus();
 271:     return pos < limit || in.ready();
 272:       }
 273:   }
 274: 
 275:   /**
 276:    * This method read chars from a stream and stores them into a caller
 277:    * supplied buffer.  It starts storing the data at index 
 278:    * <code>offset</code> into
 279:    * the buffer and attempts to read <code>len</code> chars.  This method can
 280:    * return before reading the number of chars requested.  The actual number
 281:    * of chars read is returned as an int.  A -1 is returned to indicate the
 282:    * end of the stream.
 283:    * <p>
 284:    * This method will block until some data can be read.
 285:    *
 286:    * @param buf The array into which the chars read should be stored
 287:    * @param offset The offset into the array to start storing chars
 288:    * @param count The requested number of chars to read
 289:    *
 290:    * @return The actual number of chars read, or -1 if end of stream.
 291:    *
 292:    * @exception IOException If an error occurs.
 293:    * @exception IndexOutOfBoundsException If offset and count are not
 294:    * valid regarding buf.
 295:    */
 296:   public int read(char[] buf, int offset, int count) throws IOException
 297:   {
 298:     if (offset < 0 || offset + count > buf.length || count < 0)
 299:       throw new IndexOutOfBoundsException();
 300: 
 301:     synchronized (lock)
 302:       {
 303:     checkStatus();
 304:     // Once again, we need to handle the special case of a readLine
 305:     // that has a '\r' at the end of the buffer.  In this case, we'll
 306:     // need to skip a '\n' if it is the next char to be read.
 307:     // This special case is indicated by 'pos > limit'.
 308:     boolean retAtEndOfBuffer = false;
 309: 
 310:     int avail = limit - pos;
 311:     if (count > avail)
 312:       {
 313:         if (avail > 0)
 314:           count = avail;
 315:         else // pos >= limit
 316:           {
 317:         if (limit == buffer.length)
 318:           markPos = -1; // read too far - invalidate the mark.
 319:         if (pos > limit)
 320:           {
 321:             // Set a boolean and make pos == limit to simplify things.
 322:             retAtEndOfBuffer = true;
 323:             --pos;
 324:           }
 325:         if (markPos < 0)
 326:           {
 327:             // Optimization:  can read directly into buf.
 328:             if (count >= buffer.length && !retAtEndOfBuffer)
 329:               return in.read(buf, offset, count);
 330:             pos = limit = 0;
 331:           }
 332:         avail = in.read(buffer, limit, buffer.length - limit);
 333:         if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n')
 334:           {
 335:             --avail;
 336:             limit++;
 337:           }
 338:         if (avail < count)
 339:           {
 340:             if (avail <= 0)
 341:               return avail;
 342:             count = avail;
 343:           }
 344:         limit += avail;
 345:           }
 346:       }
 347:     System.arraycopy(buffer, pos, buf, offset, count);
 348:     pos += count;
 349:     return count;
 350:       }
 351:   }
 352: 
 353:   /* Read more data into the buffer.  Update pos and limit appropriately.
 354:      Assumes pos==limit initially.  May invalidate the mark if read too much.
 355:      Return number of chars read (never 0), or -1 on eof. */
 356:   private int fill() throws IOException
 357:   {
 358:     checkStatus();
 359:     // Handle the special case of a readLine that has a '\r' at the end of
 360:     // the buffer.  In this case, we'll need to skip a '\n' if it is the
 361:     // next char to be read.  This special case is indicated by 'pos > limit'.
 362:     boolean retAtEndOfBuffer = false;
 363:     if (pos > limit)
 364:       {
 365:         retAtEndOfBuffer = true;
 366:     --pos;
 367:       }
 368: 
 369:     if (markPos >= 0 && limit == buffer.length)
 370:       markPos = -1;
 371:     if (markPos < 0)
 372:       pos = limit = 0;
 373:     int count = in.read(buffer, limit, buffer.length - limit);
 374:     if (count > 0)
 375:       limit += count;
 376: 
 377:     if (retAtEndOfBuffer && buffer[pos] == '\n')
 378:       {
 379:     --count;
 380:     // If the mark was set to the location of the \n, then we
 381:     // must change it to fully pretend that the \n does not
 382:     // exist.
 383:     if (markPos == pos)
 384:       ++markPos;
 385:     ++pos;
 386:       }
 387: 
 388:     return count;
 389:   }
 390:   
 391:   public int read() throws IOException
 392:   {
 393:     synchronized (lock)
 394:       {
 395:     checkStatus();
 396:     if (pos >= limit && fill () <= 0)
 397:       return -1;
 398:     return buffer[pos++];
 399:       }
 400:   }
 401: 
 402:   /* Return the end of the line starting at this.pos and ending at limit.
 403:    * The index returns is *before* any line terminators, or limit
 404:    * if no line terminators were found.
 405:    */
 406:   private int lineEnd(int limit)
 407:   {
 408:     int i = pos;
 409:     for (; i < limit; i++)
 410:       {
 411:     char ch = buffer[i];
 412:     if (ch == '\n' || ch == '\r')
 413:       break;
 414:       }
 415:     return i;
 416:   }
 417: 
 418:   /**
 419:    * This method reads a single line of text from the input stream, returning
 420:    * it as a <code>String</code>.  A line is terminated by "\n", a "\r", or
 421:    * an "\r\n" sequence.  The system dependent line separator is not used.
 422:    * The line termination characters are not returned in the resulting
 423:    * <code>String</code>.
 424:    * 
 425:    * @return The line of text read, or <code>null</code> if end of stream.
 426:    * 
 427:    * @exception IOException If an error occurs
 428:    */
 429:   public String readLine() throws IOException
 430:   {
 431:     checkStatus();
 432:     // Handle the special case where a previous readLine (with no intervening
 433:     // reads/skips) had a '\r' at the end of the buffer.
 434:     // In this case, we'll need to skip a '\n' if it's the next char to be read.
 435:     // This special case is indicated by 'pos > limit'.
 436:     if (pos > limit)
 437:       {
 438:     int ch = read();
 439:     if (ch < 0)
 440:       return null;
 441:     if (ch != '\n')
 442:       --pos;
 443:       }
 444:     int i = lineEnd(limit);
 445:     if (i < limit)
 446:       {
 447:     String str = String.valueOf(buffer, pos, i - pos);
 448:     pos = i + 1;
 449:     // If the last char in the buffer is a '\r', we must remember
 450:     // to check if the next char to be read after the buffer is refilled
 451:     // is a '\n'.  If so, skip it.  To indicate this condition, we set pos
 452:     // to be limit + 1, which normally is never possible.
 453:     if (buffer[i] == '\r')
 454:       if (pos == limit || buffer[pos] == '\n')
 455:         pos++;
 456:     return str;
 457:       }
 458:     if (sbuf == null)
 459:       sbuf = new StringBuffer(200);
 460:     else
 461:       sbuf.setLength(0);
 462:     sbuf.append(buffer, pos, i - pos);
 463:     pos = i;
 464:     // We only want to return null when no characters were read before
 465:     // EOF.  So we must keep track of this separately.  Otherwise we
 466:     // would treat an empty `sbuf' as an EOF condition, which is wrong
 467:     // when there is just a newline.
 468:     boolean eof = false;
 469:     for (;;)
 470:       {
 471:     // readLine should block. So we must not return until a -1 is reached.
 472:     if (pos >= limit)
 473:       {
 474:         // here count == 0 isn't sufficient to give a failure.
 475:         int count = fill();
 476:         if (count < 0)
 477:           {
 478:         eof = true;
 479:         break;
 480:           }
 481:         continue;
 482:       }
 483:     int ch = buffer[pos++];
 484:     if (ch == '\n' || ch == '\r')
 485:       {
 486:         // Check here if a '\r' was the last char in the buffer; if so,
 487:         // mark it as in the comment above to indicate future reads
 488:         // should skip a newline that is the next char read after
 489:         // refilling the buffer.
 490:         if (ch == '\r')
 491:           if (pos == limit || buffer[pos] == '\n')
 492:             pos++;
 493:         break;
 494:       }
 495:     i = lineEnd(limit);
 496:     sbuf.append(buffer, pos - 1, i - (pos - 1));
 497:     pos = i;
 498:       }
 499:     return (sbuf.length() == 0 && eof) ? null : sbuf.toString();
 500:   }
 501: 
 502:   /**
 503:    * This method skips the specified number of chars in the stream.  It
 504:    * returns the actual number of chars skipped, which may be less than the
 505:    * requested amount.
 506:    * <p>
 507:    * This method first discards chars in the buffer, then calls the
 508:    * <code>skip</code> method on the underlying stream to skip the 
 509:    * remaining chars.
 510:    *
 511:    * @param count The requested number of chars to skip
 512:    *
 513:    * @return The actual number of chars skipped.
 514:    *
 515:    * @exception IOException If an error occurs.
 516:    * @exception IllegalArgumentException If count is negative.
 517:    */
 518:   public long skip(long count) throws IOException
 519:   {
 520:     synchronized (lock)
 521:       {
 522:     checkStatus();
 523:     if (count < 0)
 524:       throw new IllegalArgumentException("skip value is negative");
 525:     if (count == 0)
 526:       return 0;
 527:     // Yet again, we need to handle the special case of a readLine
 528:     // that has a '\r' at the end of the buffer.  In this case, we need
 529:     // to ignore a '\n' if it is the next char to be read.
 530:     // This special case is indicated by 'pos > limit' (i.e. avail < 0).
 531:     // To simplify things, if we're dealing with the special case for
 532:     // readLine, just read the next char (since the fill method will
 533:     // skip the '\n' for us).  By doing this, we'll have to back up pos.
 534:     // That's easier than trying to keep track of whether we've skipped
 535:     // one element or not.
 536:     if (pos > limit)
 537:       {
 538:         if (read() < 0)
 539:           return 0;
 540:         else
 541:           --pos; 
 542:       }
 543: 
 544:     int avail = limit - pos;
 545: 
 546:     if (count < avail)
 547:       {
 548:         pos += count;
 549:         return count;
 550:       }
 551: 
 552:     pos = limit;
 553:     long todo = count - avail;
 554:     if (todo > buffer.length)
 555:       {
 556:         markPos = -1;
 557:         todo -= in.skip(todo);
 558:       }
 559:     else
 560:       {
 561:         while (todo > 0)
 562:           {
 563:         avail = fill();
 564:         if (avail <= 0)
 565:           break;
 566:         if (avail > todo)
 567:           avail = (int) todo;
 568:         pos += avail;
 569:         todo -= avail;
 570:           }
 571:       }
 572:     return count - todo;
 573:       }
 574:   }
 575:   
 576:   private void checkStatus() throws IOException
 577:   {
 578:     if (in == null)
 579:       throw new IOException("Stream closed");
 580:   }  
 581: }