import java.io.Reader;

/**
 * A stream of characters created from a reader.  
 *
 * @author Samuel A. Rebelsky
 * @version 0.2 of October 2002
 */
public class NewCharStream 
  implements CharStream
{
  // +-----------+----------------------------------------------------------
  // | Constants |
  // +-----------+

  /** The value used for end of file.  Reader says that this is -1. */
  private static int EOF = -1;
  /** The value used for "not defined" */
  private static int UNDEF = -2;


  // +--------+-------------------------------------------------------------
  // | Fields |
  // +--------+

  /**
   * The reader used for most of the real work.
   */
  protected Reader reader;

  /**
   * The next character to read.  Set to EOF when the end of file
   * is reached.  Set to UNDEF when it's time to read another character.
   */
  protected int buffer;


  // +--------------+-------------------------------------------------------
  // | Constructors |
  // +--------------+

  /**
   * Build a new character stream from a reader.
   */
  public NewCharStream(Reader reader) {
    this.reader = reader;
    this.buffer = UNDEF;
  } // NewCharStream(Reader)


  // +-----------------+----------------------------------------------------
  // | Local Utilities |
  // +-----------------+

  /**
   * Fill the buffer if it's not full.
   */
  private void fillBuffer() {
    try {
      if (this.buffer == UNDEF) this.buffer = reader.read();
    }
    catch (Exception e) {
      this.buffer = EOF;
    }
  } // fillBuffer


  // +-----------+----------------------------------------------------------
  // | Observers |
  // +-----------+

  /**
   * Determine if any more characters remain in the stream.
   *
   * Pre: (none).
   * Post: Returns true if characters remain and false o/w.
   */
  public boolean hasMore() {
    // Fill the buffer if necessary.
    this.fillBuffer();
    // See whether or not the buffer includes an EOF character.
    return (this.buffer != EOF);
  } // hasMore(void)

  /**
   * Look at the next character in the stream, but do not advance
   * the stream.
   *
   * Pre: The stream has a next character.
   * Post: Returns that character.  Does not affect the stream.
   *
   * @exception Exception 
   *   if no characters remain.
   */
  public char peek() 
    throws Exception
  {
    // Make sure the buffer is full.
    this.fillBuffer();
    // Make sure that it's not empty.
    if (this.buffer == EOF) { throw new Exception("end of file"); }
    // Return it
    return (char) this.buffer;
  } // peek(void)


  // +-----------+----------------------------------------------------------
  // | Modifiers |
  // +-----------+

  /**
   * Get the next character from the stream and advance the stream.
   *
   * Pre: The stream has a next character.
   * Post: The stream has advanced beyond that character.
   *
   * @exception Exception 
   *   if no characters remain.
   */
  public char next() 
    throws Exception
  {
    // Get the next character (let peek do the hard work).
    char ch = this.peek();
    // Note that the buffer is now empty.
    this.buffer = UNDEF;
    // That's it.
    return ch;
  } // next()

} // interface NewCharStream

