/* CharArrayWriter.java -- Write chars to a buffer
   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
 
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */


package java.io;

/**
  * This class allows data to be written to a char array buffer and
  * and then retrieved by an application.   The internal char array
  * buffer is dynamically resized to hold all the data written.  Please
  * be aware that writing large amounts to data to this stream will
  * cause large amounts of memory to be allocated.
  * <p>
  * The size of the internal buffer defaults to 32 and it is resized
  * in increments of 1024 chars.  This behavior can be over-ridden by using the
  * following two properties:
  * <p>
  * <ul>
  * <li><xmp>gnu.java.io.CharArrayWriter.initialBufferSize</xmp>
  * <li><xmp>gnu.java.io.CharArrayWriter.bufferIncrementSize</xmp>
  * </ul>
  * <p>
  * There is a constructor that specified the initial buffer size and
  * that is the preferred way to set that value because it it portable
  * across all Java class library implementations.
  * <p>
  *
  * @author Aaron M. Renn (arenn@urbanophile.com)
  * @author Tom Tromey <tromey@cygnus.com>
  */
public class CharArrayWriter extends Writer
{
  /**
   * The default initial buffer size
   */
  private static final int DEFAULT_INITIAL_BUFFER_SIZE = 32;

  /**
   * This method initializes a new <code>CharArrayWriter</code> with
   * the default buffer size of 32 chars.  If a different initial
   * buffer size is desired, see the constructor
   * <code>CharArrayWriter(int size)</code>.
   */
  public CharArrayWriter ()
  {
    this (DEFAULT_INITIAL_BUFFER_SIZE);
  }

  /**
   * This method initializes a new <code>CharArrayWriter</code> with
   * a specified initial buffer size.
   *
   * @param size The initial buffer size in chars
   */
  public CharArrayWriter (int size)
  {
    super ();
    buf = new char[size];
  }

  /**
   * Closes the stream.  This method is guaranteed not to free the contents
   * of the internal buffer, which can still be retrieved.
   */
  public void close ()
  {
    closed = true;
  }

  /**
   * This method flushes all buffered chars to the stream.
   */
  public void flush () throws IOException
  {
    synchronized (lock)
      {
	if (closed)
	  throw new IOException ("Stream closed");
      }
  }

  /**
   * This method discards all of the chars that have been written to the
   * internal buffer so far by setting the <code>count</code> variable to
   * 0.  The internal buffer remains at its currently allocated size.
   */
  public void reset ()
  {
    synchronized (lock)
      {
	count = 0;
	// Allow this to reopen the stream.
	// FIXME - what does the JDK do?
	closed = false;
      }
  }

  /**
   * This method returns the number of chars that have been written to
   * the buffer so far.  This is the same as the value of the protected
   * <code>count</code> variable.  If the <code>reset</code> method is
   * called, then this value is reset as well.  Note that this method does
   * not return the length of the internal buffer, but only the number
   * of chars that have been written to it.
   *
   * @return The number of chars in the internal buffer
   *
   * @see reset
   */
  public int size ()
  {
    return count;
  }

  /**
   * This method returns a char array containing the chars that have been
   * written to this stream so far.  This array is a copy of the valid
   * chars in the internal buffer and its length is equal to the number of
   * valid chars, not necessarily to the the length of the current 
   * internal buffer.  Note that since this method allocates a new array,
   * it should be used with caution when the internal buffer is very large.
   */
  public char[] toCharArray ()
  {
    synchronized (lock)
      {      
	char[] nc = new char[count];
	System.arraycopy(buf, 0, nc, 0, count);
	return nc;
      }
  }

  /**
   * Returns the chars in the internal array as a <code>String</code>.  The
   * chars in the buffer are converted to characters using the system default
   * encoding.  There is an overloaded <code>toString()</code> method that
   * allows an application specified character encoding to be used.
   *
   * @return A <code>String</code> containing the data written to this
   *         stream so far
   */
  public String toString ()
  {
    synchronized (lock)
      {
	return new String (buf, 0, count);
      }
  }

  /**
   * This method writes the writes the specified char into the internal
   * buffer.
   *
   * @param oneChar The char to be read passed as an int
   */
  public void write (int oneChar) throws IOException
  {
    synchronized (lock)
      {
	if (closed)
	  throw new IOException ("Stream closed");

	resize (1);
	buf[count++] = (char) oneChar;
      }
  }

  /**
   * This method writes <code>len</code> chars from the passed in array 
   * <code>buf</code> starting at index <code>offset</code> into that buffer
   *
   * @param buffer The char array to write data from
   * @param offset The index into the buffer to start writing data from
   * @param len The number of chars to write
   */
  public void write (char[] buffer, int offset, int len) throws IOException
  {
    synchronized (lock)
      {
	if (closed)
	  throw new IOException ("Stream closed");

	if (len >= 0)
	  resize (len);
	System.arraycopy(buffer, offset, buf, count, len);
	count += len;
      }
  }

  /**
   * This method writes <code>len</code> chars from the passed in
   * <code>String</code> <code>buf</code> starting at index
   * <code>offset</code> into the internal buffer.
   *
   * @param str The <code>String</code> to write data from
   * @param offset The index into the string to start writing data from
   * @param len The number of chars to write
   */
  public void write (String str, int offset, int len) throws IOException
  {
    synchronized (lock)
      {
	if (closed)
	  throw new IOException ("Stream closed");

	if (len >= 0)
	  resize (len);
	str.getChars(offset, offset + len, buf, count);
	count += len;
      }
  }

  /**
   * This method writes all the chars that have been written to this stream
   * from the internal buffer to the specified <code>Writer</code>.
   *
   * @param out The <code>Writer</code> to write to
   *
   * @exception IOException If an error occurs
   */
  public void writeTo (Writer out) throws IOException
  {
    synchronized (lock)
      {
	out.write(buf, 0, count);
      }
  }

  /**
   * This private method makes the buffer bigger when we run out of room
   * by allocating a larger buffer and copying the valid chars from the
   * old array into it.  This is obviously slow and should be avoided by
   * application programmers by setting their initial buffer size big
   * enough to hold everything if possible.
   */
  private final void resize (int len)
  {
    if (count + len >= buf.length)
      {
	int newlen = buf.length * 2;
	if (count + len > newlen)
	  newlen = count + len;
	char[] newbuf = new char[newlen];
	System.arraycopy(buf, 0, newbuf, 0, count);
	buf = newbuf;
      }
  }

  /**
   * The internal buffer where the data written is stored
   */
  protected char[] buf;

  /**
   * The number of chars that have been written to the buffer
   */
  protected int count;

  /**
   * True if the stream has been closed.
   */
  private boolean closed;
}
