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