Fix for PR java.io/204:
authorTom Tromey <tromey@cygnus.com>
Fri, 21 Apr 2000 01:18:16 +0000 (01:18 +0000)
committerTom Tromey <tromey@gcc.gnu.org>
Fri, 21 Apr 2000 01:18:16 +0000 (01:18 +0000)
* java/io/PipedInputStream.java, java/io/PipedReader.java,
java/io/PipedOutputStream.java, java/io/PipedWriter.java: Imported
from Classpath.

From-SVN: r33300

libjava/ChangeLog
libjava/java/io/PipedInputStream.java
libjava/java/io/PipedOutputStream.java
libjava/java/io/PipedReader.java
libjava/java/io/PipedWriter.java

index 212e9aa2aacc984c1401e0d7b91daedb5c33eada..d5e16880ad694d4af803036a9ec59beb4995dff7 100644 (file)
@@ -1,5 +1,10 @@
 2000-04-20  Tom Tromey  <tromey@cygnus.com>
 
+       Fix for PR java.io/204:
+       * java/io/PipedInputStream.java, java/io/PipedReader.java,
+       java/io/PipedOutputStream.java, java/io/PipedWriter.java: Imported
+       from Classpath.
+
        Fix for PR libgcj/212:
        * gcj/javaprims.h (_Jv_word, _Jv_word2): Removed definitions.
        * include/jvm.h (_Jv_word, _Jv_word2): Define.
index d2dbecbba119a0150bc4227c3d82e0f642055359..d1081a2ce3d9bd15a152513ade59a850c558e991 100644 (file)
-/* Copyright (C) 1998, 1999  Free Software Foundation
+/* PipedInputStream.java -- Input stream that reads from an output stream
+   Copyright (C) 1998, 1999 Free Software Foundation, Inc.
 
-   This file is part of libgcj.
+This file is part of GNU Classpath.
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
+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.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+
 package java.io;
 
 /**
- * @author Warren Levy <warrenl@cygnus.com>
- * @date October 29, 1998.  
+  * This class is an input stream that reads its bytes from an output stream
+  * to which it is connected. 
+  * <p>
+  * Data is read and written to an internal buffer.  It is highly recommended
+  * that the <code>PipedInputStream</code> and connected <code>PipedOutputStream</code>
+  * be part of different threads.  If they are not, there is a possibility
+  * that the read and write operations could deadlock their thread.
+  *
+  * @version 0.0
+  *
+  * @author Aaron M. Renn (arenn@urbanophile.com)
+  */
+public class PipedInputStream extends InputStream
+{
+
+/*************************************************************************/
+
+/*
+ * Class Variables
  */
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
- * Status:  Believed complete and correct.
+
+/**
+  * The size of the internal buffer used for input/output.  Note that this
+  * can be overriden by setting the system property 
+  * <code>gnu.java.io.PipedInputStream.pipe_size</code> to the desired size shown 
+  * in bytes.  This is not a standard part of the class library.  Note that 
+  * since this variable is <code>final</code>, it cannot be changed to refect 
+  * the size specified in the property.
+  * <p>
+  * The value for this variable is 2048.
+  */
+protected static final int PIPE_SIZE = 2048;
+
+/**
+  * This is the real pipe size.  It defaults to PIPE_SIZE, unless overridden
+  * by use of the system property <code>gnu.java.io.PipedInputStream.pipe_size</code>.
+  */
+private static int pipe_size;
+
+/**
+  * This variable indicates whether or not the <code>read()</code> method will attempt
+  * return a short count if this will possibly keep the stream from blocking.  
+  * The default for this is <code>false</code> because that is what what the JDK seems
+  * to imply in its javadocs.  We set this to <code>false</code> if the system 
+  * property <code>gnu.java.io.try_not_to_block</code> is set.
+  */
+private static boolean try_not_to_block = false;
+
+static
+{
+  pipe_size = Integer.getInteger("gnu.java.io.PipedInputStream.pipe_size",
+                                 PIPE_SIZE).intValue();
+
+  String block_prop = System.getProperty("gnu.java.io.try_not_to_block");
+  if (block_prop != null)
+    try_not_to_block = true;
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
  */
-public class PipedInputStream extends InputStream
+
+/**
+  * This is the internal circular buffer used for storing bytes written
+  * to the pipe and from which bytes are read by this stream
+  */
+protected byte[] buffer = new byte[pipe_size];
+
+/**
+  * The index into buffer where the bytes written byte the connected
+  * <code>PipedOutputStream</code> will be written.  If this variables is less
+  * than 0, then the buffer is empty.  If this variable is equal to 
+  * <code>out</code>, then the buffer is full
+  */
+protected int in = -1;
+
+/**
+  * This index into the buffer where bytes will be read from.
+  */
+protected int out = 0;
+
+/**
+  * This variable is <code>true</code> if this object has ever been connected
+  * to a <code>PipedOutputStream</code>, and <code>false</code> otherwise.  It is used
+  * to detect an attempt to connect an already connected stream or to
+  * otherwise use the stream before it is connected.
+  */
+private boolean ever_connected = false;
+
+/**
+  * This variable is set to <code>true</code> if the <code>close()</code> method is
+  * called.  This value is checked prevents a caller from re-opening the
+  * stream.
+  */
+private boolean closed = false;
+
+/**
+  * This variable is the PipedOutputStream to which this stream is connected.
+  */
+PipedOutputStream src;
+
+/**
+  * Used by <code>read()</code> to call an overloaded method
+  */
+private byte[] read_buf = new byte[1];
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+/**
+  * This constructor creates a new <code>PipedInputStream</code> that is not 
+  * connected to a <code>PipedOutputStream</code>.  It must be connected before
+  * bytes can be read from this stream.
+  */
+public
+PipedInputStream()
 {
-  /* The size of the pipe's circular input buffer. */
-  protected static final int PIPE_SIZE = 1024;
+  return;
+}
 
-  /* The circular buffer into which incoming data is placed. */
-  protected byte[] buffer;
+/*************************************************************************/
 
-  /* The index in the buffer at which the next byte of data will be stored. */
-  protected int in = -1;
+/**
+  * This constructor creates a new <code>PipedInputStream</code> and connects
+  * it to the passed in <code>PipedOutputStream</code>. The stream is then read
+  * for reading.
+  *
+  * @param src The <code>PipedOutputStream</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public
+PipedInputStream(PipedOutputStream src) throws IOException
+{
+  connect(src);
+}
 
-  /* The index in the buffer at which the next byte of data will be read. */
-  protected int out = 0;
+/*************************************************************************/
 
-  /* The output stream this is connected to; used to check for errors. */
-  private PipedOutputStream po = null;
+/*
+ * Instance Variables
+ */
 
-  /* Flag to indicate that the output stream was closed. */
-  private boolean outClosed = false;
+/**
+  * This method connects this stream to the passed in <code>PipedOutputStream</code>.
+  * This stream is then ready for reading.  If this stream is already
+  * connected or has been previously closed, then an exception is thrown
+  *
+  * @param src The <code>PipedOutputStream</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized void
+connect(PipedOutputStream src) throws IOException
+{
+  if (src == this.src)
+    return;
 
-  public PipedInputStream(PipedOutputStream src) throws IOException
-  {
-    buffer = new byte[PIPE_SIZE];
-    connect(src);
-  }
+  if (ever_connected)
+    throw new IOException("Already connected");
 
-  public PipedInputStream()
-  {
-    buffer = new byte[PIPE_SIZE];
-  }
+  if (closed)
+    throw new IOException("Stream is closed and cannot be reopened");
 
-  public synchronized int available() throws IOException
-  {
-    if (in < 0)
-      return 0;
+  src.connect(this);
 
-    if (in > out)
-      return in - out;
+  ever_connected = true;
+}
 
-    // Buffer has wrapped around.
-    return buffer.length - out + in;
-  }
+/*************************************************************************/
 
-  public void close() throws IOException
-  {
-    buffer = null;
-    po = null;
+/**
+  * This methods closes the stream so that no more data can be read
+  * from it.
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized void
+close() throws IOException
+{
+  closed = true;
+  notifyAll();
+}
 
-    // Mark as empty for available method.
-    in = -1;
-  }
+/*************************************************************************/
 
-  public void connect(PipedOutputStream src) throws IOException
-  {
-    if (buffer == null)
-      throw new IOException("pipe closed");
+/**
+  * This method returns the number of bytes that can be read from this stream
+  * before blocking could occur.  This is the number of bytes that are
+  * currently unread in the internal circular buffer.  Note that once this
+  * many additional bytes are read, the stream may block on a subsequent
+  * read, but it not guaranteed to block.
+  *
+  * @return The number of bytes that can be read before blocking might occur
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized int
+available() throws IOException
+{
+  if (in == -1)
+    return(0);
+  else if (in > out)
+    return(in - out);
+  else
+    return(in + (pipe_size - out));
+}
 
-    if (po != null)
-      if (po == src)
-       return;
-      else
-        throw new IOException("pipe already connected");
+/*************************************************************************/
+
+/**
+  * Reads the next byte from the stream.  The byte read is returned as
+  * and int in the range of 0-255.  If a byte cannot be read because of an
+  * end of stream condition, -1 is returned.  If the stream is already
+  * closed, an IOException will be thrown.
+  *  <code>
+  * This method will block if no bytes are available to be read.
+  *
+  * @return The byte read or -1 if end of stream.
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized int
+read() throws IOException
+{
+  // Method operates by calling the multibyte overloaded read method
+  // Note that read_buf is an internal instance variable.  I allocate it
+  // there to avoid constant reallocation overhead for applications that
+  // call this method in a loop at the cost of some unneeded overhead
+  // if this method is never called.
+  int bytes_read = read(read_buf, 0, read_buf.length);
+
+  if (bytes_read == -1)
+    return(-1);
+  else
+    return((read_buf[0] & 0xFF));
+}
+
+/*************************************************************************/
 
-    po = src;
-    try
+/**
+  * This method reads bytes from the stream into a caller supplied buffer.
+  * It starts storing bytes at position <code>offset</code> into the buffer and
+  * reads a maximum of <cod>>len</code> bytes.  Note that this method can actually
+  * read fewer than <code>len</code> bytes.  The actual number of bytes read is
+  * returned.  A -1 is returned to indicated that no bytes can be read
+  * because the end of the stream was reached.  If the stream is already
+  * closed, a -1 will again be returned to indicate the end of the stream.
+  * <p>
+  * This method will block if no bytes are available to be read.
+  *
+  * @param buf The buffer into which bytes will be stored
+  * @param offset The index into the buffer at which to start writing.
+  * @param len The maximum number of bytes to read.
+  */
+public synchronized int
+read(byte[] buf, int offset, int len) throws IOException
+{
+  if (!ever_connected)
+    throw new IOException("Not connected"); 
+
+  int bytes_read = 0;
+  for (;;)
     {
-      src.connect(this);
-    }
-    catch (IOException ex)
+      // If there are bytes, take them
+      if (in != -1)
+        {
+          int desired_bytes = len - bytes_read;
+
+          // We are in a "wrap" condition
+          if (out > in)
+            {
+              if (desired_bytes > (pipe_size - out))
+                {
+                  if (in == 0)
+                    desired_bytes = (pipe_size - out) - 1;
+                  else
+                    desired_bytes = pipe_size - out;
+
+                  System.arraycopy(buffer, out, buf, offset + bytes_read,
+                                   desired_bytes);
+
+                  bytes_read += desired_bytes;
+                  out += desired_bytes;
+                  desired_bytes = len - bytes_read;
+
+                  if (out == pipe_size)
+                    out = 0;
+
+                  notifyAll();
+                }
+              else
+                {
+                  if ((out + desired_bytes) == in)
+                    --desired_bytes;
+
+                  if (((out + desired_bytes) == pipe_size) && (in == 0)) 
+                    desired_bytes = (pipe_size - out) - 1;
+
+                  System.arraycopy(buffer, out, buf, offset + bytes_read,
+                                   desired_bytes); 
+
+                  bytes_read += desired_bytes;
+                  out += desired_bytes;
+                  desired_bytes = len - bytes_read;
+
+                  if (out == pipe_size)
+                    out = 0;
+
+                  notifyAll();
+                }
+            }
+          // We are in a "no wrap" or condition (can also be fall through
+          // from above
+          if (in > out)
+            {
+              if (desired_bytes >= ((in - out) - 1))
+                desired_bytes = (in - out) - 1;
+
+              System.arraycopy(buffer, out, buf, offset + bytes_read, 
+                               desired_bytes);
+
+              bytes_read += desired_bytes;
+              out += desired_bytes;
+              desired_bytes = len - bytes_read;
+
+              if (out == pipe_size)
+                out = 0;
+
+              notifyAll();
+            }
+        }
+
+      // If we are done, return
+      if (bytes_read == len)
+        return(bytes_read);
+
+      // Return a short count if necessary
+      if (bytes_read < len)
+        if (try_not_to_block)
+           return(bytes_read);
+
+      // Handle the case where the end of stream was encountered.
+      if (closed)
+        {
+          // We never let in == out so there might be one last byte
+          // available that we have not copied yet.
+          if (in != -1)
+            {
+              buf[offset + bytes_read] = buffer[out];
+              in = -1;
+              ++out;
+              ++bytes_read;
+            }
+
+          if (bytes_read != 0)
+            return(bytes_read);
+          else
+            return(-1);
+        }
+
+      // Wait for a byte to be read
+      try
+        {
+          wait();
+        }
+      catch(InterruptedException e) { ; }
+    } 
+}
+
+/*************************************************************************/
+
+/**
+  * This method receives a byte of input from the source PipedOutputStream.
+  * If there is no data ready to be written, or if the internal circular
+  * buffer is full, this method blocks.
+  *
+  * *****What is this method really supposed to do *********
+  */
+protected synchronized void
+receive(int byte_received) throws IOException
+{
+  int orig_in = in;
+
+  for (;;)
     {
-      po = null;
-      throw ex;
+      // Wait for something to happen
+      try
+        {
+          wait();
+        }
+      catch(InterruptedException e) { ; }
+
+      // See if we woke up because the stream was closed on us
+      if (closed)
+        throw new IOException("Stream closed before receiving byte");
+
+      // See if a byte of data was received
+      if (in != orig_in)
+        return;
     }
-  }
-
-  public synchronized int read() throws IOException
-  {
-    // TBD: Spec says to throw IOException if thread writing to output stream
-    // died.  What does this really mean?  Theoretically, multiple threads
-    // could be writing to this object.  Do you track the first, last, or
-    // all of them?
-    if (po == null)
-      if (buffer == null)
-        throw new IOException("pipe closed");
-      else
-        throw new IOException("pipe unconnected");
+}
 
-    // Block until there's something to read or output stream was closed.
-    while (in < 0)
-      try
-       {
-         if (outClosed)
-           return -1;
-         wait();
-       }
-      catch (InterruptedException ex)
-       {
-        throw new InterruptedIOException();
-       }
-
-    // Let other threads know there's room to write now.
-    notifyAll();
-
-    int retval = buffer[out++] & 0xFF;
-
-    // Wrap back around if at end of the array.
-    if (out >= buffer.length)
-      out = 0;
-
-    // When the last byte available is read, mark the buffer as empty.
-    if (out == in)
-      {
-        in = -1;
-       out = 0;
-      }
-      
-    return retval;
-  }
-
-  public synchronized int read(byte[] b, int off, int len) throws IOException
-  {
-    if (off < 0 || len < 0 || off + len > b.length)
-      throw new ArrayIndexOutOfBoundsException();
-
-    // TBD: Spec says to throw IOException if thread writing to output stream
-    // died.  What does this really mean?  Theoretically, multiple threads
-    // could be writing to this object.  Do you track the first, last, or
-    // all of them?
-    if (po == null)
-      if (buffer == null)
-        throw new IOException("pipe closed");
-      else
-        throw new IOException("pipe unconnected");
+/*************************************************************************/
 
-    // Block until there's something to read or output stream was closed.
-    while (in < 0)
-      try
-       {
-         if (outClosed)
-           return -1;
-         wait();
-       }
-      catch (InterruptedException ex)
-       {
-        throw new InterruptedIOException();
-       }
-
-    // Let other threads know there's room to write now.
-    notifyAll();
-
-    int numRead;
-    len = Math.min(len, available());
-    if (in <= out && len >= (numRead = buffer.length - out))
-      {
-       // Buffer has wrapped around; need to copy in 2 steps.
-       // Copy to the end of the buffer first; second copy may be of zero
-       // bytes but that is ok.  Doing it that way saves having to check
-       // later if 'out' has grown to buffer.length.
-        System.arraycopy(buffer, out, b, off, numRead);
-       len -= numRead;
-       off += numRead;
-       out = 0;
-      }
-    else
-      numRead = 0;
-
-    System.arraycopy(buffer, out, b, off, len);
-    numRead += len;
-    out += len;
-
-    // When the last byte available is read, mark the buffer as empty.
-    if (out == in)
-      {
-        in = -1;
-       out = 0;
-      }
-
-    return numRead;
-  }
-
-  protected synchronized void receive(int b) throws IOException
-  {
-    if (buffer == null)
-      throw new IOException("pipe closed");
-
-    // TBD: Spec says to throw IOException if thread reading from input stream
-    // died.  What does this really mean?  Theoretically, multiple threads
-    // could be reading to this object (why else would 'read' be synchronized?).
-    // Do you track the first, last, or all of them?
-
-    if (b < 0)
-      {
-        outClosed = true;
-       notifyAll();    // In case someone was blocked in a read.
-       return;
-      }
-
-    // Block until there's room in the pipe.
-    while (in == out)
-      try
-       {
-         wait();
-       }
-      catch (InterruptedException ex)
-       {
-        throw new InterruptedIOException();
-       }
-
-    // Check if buffer is empty.
-    if (in < 0)
-      in = 0;
-
-    buffer[in++] = (byte) b;
-
-    // Wrap back around if at end of the array.
-    if (in >= buffer.length)
-      in = 0;
-
-    // Let other threads know there's something to read when this returns.
-    notifyAll();
-  }
+/**
+  * This method is used by the connected <code>PipedOutputStream</code> to
+  * write bytes into the buffer.  It uses this method instead of directly
+  * writing the bytes in order to obtain ownership of the object's monitor
+  * for the purposes of calling <code>notify</code>.
+  *
+  * @param buf The array containing bytes to write to this stream
+  * @param offset The offset into the array to start writing from
+  * @param len The number of bytes to write.
+  *
+  * @exception IOException If an error occurs
+  */
+synchronized void
+write(byte[] buf, int offset, int len) throws IOException
+{
+  if (len <= 0)
+    return;
+
+  int total_written = 0;
+  while (total_written < len)
+    {
+      // If we are not at the end of the buffer with out = 0
+      if (!((in == (buffer.length - 1)) && (out == 0)))
+        {
+          // This is the "no wrap" situation
+          if ((in - 1) >= out)
+            {
+              int bytes_written = 0;
+              if ((buffer.length - in) > (len - total_written))
+                bytes_written = (len - total_written);
+              else if (out == 0)
+                bytes_written = (buffer.length - in) - 1;
+              else 
+                bytes_written = (buffer.length - in);
+
+              if (bytes_written > 0) 
+                System.arraycopy(buf, offset + total_written, buffer, in, 
+                                 bytes_written);
+              total_written += bytes_written;
+              in += bytes_written;
+
+              if (in == buffer.length)
+                in = 0;
+
+              notifyAll();
+            }
+          // This is the "wrap" situtation
+          if ((out > in) && (total_written != len))
+            {
+              int bytes_written = 0;
+
+              // Do special processing if we are at the beginning
+              if (in == -1)
+                {
+                  in = 0;
+
+                  if (buffer.length > len)
+                    bytes_written = len;
+                  else
+                    bytes_written = buffer.length - 1;
+                }
+              else if (((out - in) - 1) < (len - total_written))
+                {
+                  bytes_written = (out - in) - 1;
+                }
+              else
+                {
+                  bytes_written = len - total_written;
+                }
+
+              // If the buffer is full, wait for it to empty out
+              if ((out - 1) == in)
+                {
+                  try
+                    {         
+                      wait(); 
+                    }
+                  catch (InterruptedException e) 
+                    { 
+                      continue; 
+                    }
+                }
+
+              System.arraycopy(buf, offset + total_written, buffer, in,
+                               bytes_written);
+              total_written += bytes_written;
+              in += bytes_written;
+
+              if (in == buffer.length)
+                in = 0;
+
+              notifyAll();
+            }
+        }
+      // Wait for some reads to occur before we write anything.
+      else
+        {
+          try
+            {
+              wait();
+            }
+          catch (InterruptedException e) { ; }
+        }
+    }
 }
+
+} // class PipedInputStream
+
index 901fb9e54840cac6ba22fa3d8f9c55c139a427f0..6d1061230bea2ebbbce2efbc607a8e4d6f07eaa8 100644 (file)
-// PipedOutputStream.java - Write bytes to a pipe.
+/* PipedOutputStream.java -- Write portion of piped streams.
+   Copyright (C) 1998 Free Software Foundation, Inc.
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+This file is part of GNU Classpath.
 
-   This file is part of libgcj.
+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.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
 
 package java.io;
 
 /**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date September 24, 1998 
+  * This class writes its bytes to a <code>PipedInputStream</code> to 
+  * which it is connected.
+  * <p>
+  * It is highly recommended that a <code>PipedOutputStream</code> and its
+  * connected <code>PipedInputStream</code> be in different threads.  If 
+  * they are in the same thread, read and write operations could deadlock
+  * the thread.
+  *
+  * @version 0.0
+  *
+  * @author Aaron M. Renn (arenn@urbanophile.com)
+  */
+public class PipedOutputStream extends OutputStream
+{
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
  */
 
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * Status:  Believed complete and correct.
+/**
+  * This is the <code>PipedInputStream</code> to which this object
+  * is connected.
+  */
+private PipedInputStream snk;
+
+/**
+  * This flag indicates whether or not this stream has ever been
+  * connected to an input stream
+  */
+private boolean ever_connected;
+
+/**
+  * This flag indicates whether the <code>close</code> method has ever
+  * been called for this stream.
+  */
+private boolean closed;
+
+/*************************************************************************/
+
+/**
+  * This method initializes a new <code>PipedOutputStream</code> instance.
+  * This constructor creates an unconnected object.  It must be connected
+  * to a <code>PipedInputStream</code> object using the <code>connect</code>
+  * method prior to writing any data or an exception will be thrown.
+  */
+public
+PipedOutputStream()
+{
+  ; // Do Nothing
+}
+
+/*************************************************************************/
+
+/**
+  * This method initializes a new <code>PipedOutputStream</code> instance
+  * to write to the specified <code>PipedInputStream</code>.  This stream
+  * is then ready for writing.
+  *
+  * @param snk The <code>PipedInputStream</code> to connect this stream to.
+  *
+  * @exception IOException If an error occurs
+  */
+public
+PipedOutputStream(PipedInputStream snk) throws IOException
+{
+  connect(snk);
+} 
+
+/*************************************************************************/
+
+/*
+ * Instance Methods
  */
 
-public class PipedOutputStream extends OutputStream
+/**
+  * This method connects this object to the specified 
+  * <code>PipedInputStream</code> object.  This stream will then be ready 
+  * for writing.  If this stream is already connected or has been 
+  * previously closed, then an exception is thrown.
+  *
+  * @param snk The <code>PipedInputStream</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized void
+connect(PipedInputStream snk) throws IOException
 {
-  public void close () throws IOException
-  {
-    closed = true;
-
-    // Notify PipedInputStream that there is no more data to be had.
-    destination.receive(-1);
-  }
-
-  public void connect (PipedInputStream dest) throws IOException
-  {
-    if (closed)
-      throw new IOException("pipe closed");
-
-    if (destination != null)
-      if (destination == dest)
-       return;
-      else
-        throw new IOException("pipe already connected");
-
-    destination = dest;
-    try
-    {
-      dest.connect(this);
-    }
-    catch (IOException ex)
-    {
-      destination = null;
-      throw ex;
-    }
-  }
-
-  public synchronized void flush () throws IOException
-  {
-    // There doesn't seem to be anything to do here.
-
-    // TBD: Should this maybe do a notifyAll as a way for the user
-    // to wake up the input stream to check for bytes to read?  Shouldn't
-    // be necessary but if there aren't any bytes, other threads will just
-    // go blocak again anyway so it wouldn't hurt.
-  }
-
-  public PipedOutputStream ()
-  {
-    closed = false;
-  }
-
-  public PipedOutputStream (PipedInputStream dest) throws IOException
-  {
-    closed = false;
-    connect (dest);
-  }
-
-  public void write (int oneByte) throws IOException
-  {
-    if (closed)
-      throw new IOException ();
-    destination.receive(oneByte);
-  }
-
-  public void write (byte[] buffer, int offset, int count) throws IOException
-  {
-    if (closed)
-      throw new IOException ();
-    if (offset < 0 || count < 0 || offset + count > buffer.length)
-      throw new ArrayIndexOutOfBoundsException ();
-    for (int i = 0; i < count; ++i)
-      destination.receive (buffer[offset + i]);
-  }
-
-  // Instance variables.
-  private PipedInputStream destination;
-  private boolean closed;
+  if (snk == this.snk)
+    return;
+
+  if (ever_connected)
+    throw new IOException("Already connected");
+
+  if (closed)
+    throw new IOException("Stream is closed and cannot be reopened");
+
+  this.snk = snk;
+  ever_connected = true;
+  snk.src = this;
+
+  snk.connect(this);
+}
+
+/*************************************************************************/
+
+/**
+  * This method closes this stream so that no more data can be written
+  * to it. Any further attempts to write to this stream may throw an
+  * exception
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized void
+close() throws IOException
+{
+  closed = true;
+  snk.close();
+  notifyAll();
+}
+
+/*************************************************************************/
+
+/**
+  * This method writes a single byte of date to the stream.  Note that
+  * this method will block if the <code>PipedInputStream</code> to which
+  * this object is connected has a full buffer.
+  *
+  * @param b The byte of data to be written, passed as an <code>int</code>.
+  *
+  * @exception IOException If an error occurs
+  */
+public synchronized void
+write(int b) throws IOException
+{
+  byte[] buf = new byte[1];
+  buf[0] = (byte)(b & 0xFF);
+
+  snk.write(buf, 0, buf.length);
 }
+
+/*************************************************************************/
+
+/**
+  * This method writes <code>len</code> bytes of data from the byte array
+  * <code>buf</code> starting at index <code>offset</code> in the array
+  * to the stream.  Note that this method will block if the  
+  * <code>PipedInputStream</code> to which this object is connected has
+  * a buffer that cannot hold all of the bytes to be written.
+  *
+  * @param buf The array containing bytes to write to thes stream.
+  * @param offset The index into the array to start writing bytes from.
+  * @param len The number of bytes to write.
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+write(byte[] buf, int offset, int len) throws IOException
+{
+  snk.write(buf, 0, len);
+}
+
+/*************************************************************************/
+
+/**
+  * This method flushes any unwritten bytes to the output and notifies
+  * any waiting readers that the pipe is ready to be read.
+  *
+  * @exception IOException If an error occurs.
+  */
+public void
+flush() throws IOException
+{
+  return;
+}
+
+} // class PipedOutputStream
+
index faac9865cb8e2c2d40e5bdfdec5444d429991ec4..72e516a2c102c1d5ee094e285484e59fe3b97151 100644 (file)
-// PipedReader.java - Piped character stream.
+/* PipedReader.java -- Input stream that reads from an output stream
+   Copyright (C) 1998, 1999 Free Software Foundation, Inc.
 
-/* Copyright (C) 1998, 1999  Free Software Foundation
+This file is part of GNU Classpath.
 
-   This file is part of libgcj.
+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.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
 
 package java.io;
 
 /**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date September 25, 1998 
+  * This class is an input stream that reads its chars from an output stream
+  * to which it is connected. 
+  * <p>
+  * Data is read and written to an internal buffer.  It is highly recommended
+  * that the <code>PipedReader</code> and connected <code>PipedWriter</code>
+  * be part of different threads.  If they are not, there is a possibility
+  * that the read and write operations could deadlock their thread.
+  *
+  * @version 0.0
+  *
+  * @author Aaron M. Renn (arenn@urbanophile.com)
+  */
+public class PipedReader extends Reader
+{
+
+/*************************************************************************/
+
+/*
+ * Class Variables
  */
 
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * Status:  Complete to 1.1.
+/**
+  * The size of the internal buffer used for input/output.  Note that this
+  * can be overriden by setting the system property 
+  * <code>gnu.java.io.PipedReader.pipe_size</code> to the desired size shown 
+  * in chars.  This is not a standard part of the class library.  Note that 
+  * since this variable is <code>final</code>, it cannot be changed to refect 
+  * the size specified in the property.
+  * <p>
+  * The value for this variable is 2048.
+  */
+private static final int PIPE_SIZE = 2048;
+
+/**
+  * This is the real pipe size.  It defaults to PIPE_SIZE, unless overridden
+  * by use of the system property <code>gnu.java.io.PipedReader.pipe_size</code>.
+  */
+private static int pipe_size;
+
+/**
+  * This variable indicates whether or not the <code>read()</code> method will attempt
+  * return a short count if this will possibly keep the stream from blocking.  
+  * The default for this is <code>false</code> because that is what what the JDK seems
+  * to imply in its javadocs.  We set this to <code>false</code> if the system 
+  * property <code>gnu.java.io.try_not_to_block</code> is set.
+  */
+private static boolean try_not_to_block = false;
+
+static
+{
+  pipe_size =  Integer.getInteger("gnu.java.io.PipedReader.pipe_size",
+                                  PIPE_SIZE).intValue();
+
+  String block_prop = System.getProperty("gnu.java.io.try_not_to_block");
+  if (block_prop != null)
+    try_not_to_block = true;
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
  */
 
-public class PipedReader extends Reader
+/**
+  * This is the internal circular buffer used for storing chars written
+  * to the pipe and from which chars are read by this stream
+  */
+private char[] buffer = new char[pipe_size];
+
+/**
+  * The index into buffer where the chars written char the connected
+  * <code>PipedWriter</code> will be written.  If this variables is less
+  * than 0, then the buffer is empty.  If this variable is equal to 
+  * <code>out</code>, then the buffer is full
+  */
+private int in = -1;
+
+/**
+  * This index into the buffer where chars will be read from.
+  */
+private int out = 0;
+
+/**
+  * This variable is <code>true</code> if this object has ever been connected
+  * to a <code>PipedWriter</code>, and <code>false</code> otherwise.  It is used
+  * to detect an attempt to connect an already connected stream or to
+  * otherwise use the stream before it is connected.
+  */
+private boolean ever_connected = false;
+
+/**
+  * This variable is set to <code>true</code> if the <code>close()</code> method is
+  * called.  This value is checked prevents a caller from re-opening the
+  * stream.
+  */
+private boolean closed = false;
+
+/**
+  * This variable is the PipedWriter to which this stream is connected.
+  */
+PipedWriter src;
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+/**
+  * This constructor creates a new <code>PipedReader</code> that is not 
+  * connected to a <code>PipedWriter</code>.  It must be connected before
+  * chars can be read from this stream.
+  */
+public
+PipedReader()
+{
+  return;
+}
+
+/*************************************************************************/
+
+/**
+  * This constructor creates a new <code>PipedReader</code> and connects
+  * it to the passed in <code>PipedWriter</code>. The stream is then read
+  * for reading.
+  *
+  * @param src The <code>PipedWriter</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public
+PipedReader(PipedWriter src) throws IOException
+{
+  connect(src);
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
+ */
+
+/**
+  * This method connects this stream to the passed in <code>PipedWriter</code>.
+  * This stream is then ready for reading.  If this stream is already
+  * connected or has been previously closed, then an exception is thrown
+  *
+  * @param src The <code>PipedWriter</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+connect(PipedWriter src) throws IOException
+{
+  if (src == this.src)
+    return;
+
+  if (ever_connected)
+    throw new IOException("Already connected");
+
+  if (closed)
+    throw new IOException("Stream is closed and cannot be reopened");
+
+  synchronized (lock) {
+
+  src.connect(this);
+
+  ever_connected = true;
+
+  } // synchronized
+}
+
+/*************************************************************************/
+
+/**
+  * This methods closes the stream so that no more data can be read
+  * from it.
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+close() throws IOException
+{
+  synchronized (lock) {
+
+  closed = true;
+  notifyAll();
+
+  } // synchronized
+}
+
+/*************************************************************************/
+
+/**
+  * This method determines whether or not this stream is ready to be read.
+  * If this metho returns <code>false</code> an attempt to read may (but is
+  * not guaranteed to) block.
+  *
+  * @return <code>true</code> if this stream is ready to be read, <code>false</code> otherwise
+  *
+  * @exception IOException If an error occurs
+  */
+public boolean
+ready() throws IOException
 {
-  public void close () throws IOException
-  {
-    closed = true;
-  }
-
-  public void connect (PipedWriter src) throws IOException
-  {
-    if (closed)
-      throw new IOException ("already closed");
-    if (writer != null)
-      {
-       if (writer == src)
-         return;
-       throw new IOException ("already connected");
-      }
-    try
-      {
-       writer = src;
-       writer.connect(this);
-      }
-    catch (IOException e)
-      {
-       writer = null;
-       throw e;
-      }
-  }
-
-  public PipedReader ()
-  {
-    super ();
-    writer = null;
-    closed = false;
-    in = -1;
-    out = 0;
-    pipeBuffer = new char[1024];
-  }
-
-  public PipedReader (PipedWriter src) throws IOException
-  {
-    super ();
-    closed = false;
-    in = -1;
-    out = 0;
-    pipeBuffer = new char[1024];
-    connect (src);
-  }
-
-  public int read (char buf[], int offset, int count) throws IOException
-  {
-    if (closed)
-      throw new IOException ("closed");
-    if (count < 0)
-      throw new ArrayIndexOutOfBoundsException ();
-    int toCopy = count;
-    synchronized (lock)
-      {
-       while (toCopy > 0)
-         {
-           // Wait for data in the pipe.  If the writer is closed and
-           // no data has been copied into the output buffer, return
-           // the magic EOF number.
-           while (in == -1)
-             {
-               if (writer.isClosed())
-                 {
-                   if (toCopy < count)
-                     return count - toCopy;
-                   return -1;
-                 }
-
-               // Note that JCL doesn't say this is the right thing
-               // to do.  Still, it feels right, and we must deal
-               // with an interrupt somehow.
-               try
-                 {
-                   lock.wait();
-                 }
-               catch (InterruptedException e)
-                 {
-                   InterruptedIOException io
-                     = new InterruptedIOException (e.getMessage());
-                   io.bytesTransferred = count - toCopy;
-                   throw io;
-                 }
-             }
-           // Now copy some data from pipe into user buffer.
-           int len;
-           if (in < out)
-             len = pipeBuffer.length - out;
-           else
-             len = in - out;
-           len = len > toCopy ? toCopy : len;
-           System.arraycopy(pipeBuffer, out, buf, offset, len);
-           out += len;
-           if (out == pipeBuffer.length)
-             out = 0;
-           toCopy -= len;
-           offset += len;
-           // If we've read all the data, then reset so that we know
-           // there is nothing left to be read.
-           if (in == out)
-             in = -1;
-           // Tell anybody waiting for space in the buffer.
-           lock.notifyAll();
-         }
-      }
-    return count;
-  }
-
-  void receive (char buf[], int offset, int count) throws IOException
-  {
-    if (count < 0)
-      throw new ArrayIndexOutOfBoundsException ();
-    int original = count;
-    synchronized (lock)
-      {
-       while (count > 0)
-         {
-           // Wait until there is some space in the buffer.
-           while (in == out)
-             {
-               try
-                 {
-                   lock.wait();
-                 }
-               catch (InterruptedException e)
-                 {
-                   // Turn interrupts into IO interrupts.
-                   InterruptedIOException io
-                     = new InterruptedIOException (e.getMessage());
-                   io.bytesTransferred = original - count;
-                   throw io;
-                 }
-             }
-
-           // Compute destination in the pipe.
-           int base, len;
-           if (in == -1)
-             {
-               base = 0;
-               len = pipeBuffer.length;
-             }
-           else if (in < out)
-             {
-               base = in;
-               len = out - in;
-             }
-           else
-             {
-               base = in;
-               len = pipeBuffer.length - in;
-             }
-           int copyLen = len > count ? count : len;
-           // Copy data and update local state.
-           System.arraycopy(buf, offset, pipeBuffer, base, copyLen);
-           in = base + copyLen;
-           if (in == pipeBuffer.length)
-             in = 0;
-           count -= copyLen;
-           offset += copyLen;
-           // Tell anybody waiting for data.
-           lock.notifyAll();
-         }
-      }
-  }
-
-
-  boolean isClosed ()
-  {
-    return closed;
-  }
-
-  // The associated writer.
-  private PipedWriter writer;
-  // True if this reader has been closed.
-  boolean closed;
-
-  // Index of next character to overwrite when receive() is called.
-  // If -1, then that means the buffer is empty.
-  private int in;
-  // Index of next character to return from read().
-  private int out;
-
-  // The pipe buffer itself.
-  private char[] pipeBuffer;
+  if (in == -1)
+    return(false);
+
+  if (out == (in - 1))
+    return(false);
+
+  if ((out == pipe_size) && (in == 0))
+    return(false);
+
+  return(true);
 }
+
+/*************************************************************************/
+
+/**
+  * This method reads a single char from the pipe and returns it as an
+  * <code>int</code>.
+  * <p>
+  * This method will block if no chars are available to be read.
+  *
+  * @return An char read from the pipe, or -1 if the end of stream is 
+  * reached.
+  *
+  * @exception IOException If an error occurs.
+  */
+public int
+read() throws IOException
+{
+  char[] buf = new char[1];
+
+  return(read(buf, 0, buf.length));
+}
+
+/*************************************************************************/
+
+/**
+  * This method reads chars from the stream into a caller supplied buffer.
+  * It starts storing chars at position <code>offset</code> into the buffer and
+  * reads a maximum of <cod>>len</code> chars.  Note that this method can actually
+  * read fewer than <code>len</code> chars.  The actual number of chars read is
+  * returned.  A -1 is returned to indicated that no chars can be read
+  * because the end of the stream was reached.  If the stream is already
+  * closed, a -1 will again be returned to indicate the end of the stream.
+  * <p>
+  * This method will block if no chars are available to be read.
+  *
+  * @param buf The buffer into which chars will be stored
+  * @param offset The index into the buffer at which to start writing.
+  * @param len The maximum number of chars to read.
+  */
+public int
+read(char[] buf, int offset, int len) throws IOException
+{
+  if (!ever_connected)
+    throw new IOException("Not connected"); 
+
+  synchronized (lock) {
+
+  int chars_read = 0;
+  for (;;)
+    {
+      // If there are chars, take them
+      if (in != -1)
+        {
+          int desired_chars = len - chars_read;
+
+          // We are in a "wrap" condition
+          if (out > in)
+            {
+              if (desired_chars > (pipe_size - out))
+                {
+                  if (in == 0)
+                    desired_chars = (pipe_size - out) - 1;
+                  else
+                    desired_chars = pipe_size - out;
+
+                  System.arraycopy(buffer, out, buf, offset + chars_read,
+                                   desired_chars);
+
+                  chars_read += desired_chars;
+                  out += desired_chars;
+                  desired_chars = len - chars_read;
+
+                  if (out == pipe_size)
+                    out = 0;
+
+                  notifyAll();
+                }
+              else
+                {
+                  if ((out + desired_chars) == in)
+                    --desired_chars;
+
+                  if (((out + desired_chars) == pipe_size) && (in == 0)) 
+                    desired_chars = (pipe_size - out) - 1;
+
+                  System.arraycopy(buffer, out, buf, offset + chars_read,
+                                   desired_chars); 
+
+                  chars_read += desired_chars;
+                  out += desired_chars;
+                  desired_chars = len - chars_read;
+
+                  if (out == pipe_size)
+                    out = 0;
+
+                  notifyAll();
+                }
+            }
+          // We are in a "no wrap" or condition (can also be fall through
+          // from above
+          if (in > out)
+            {
+              if (desired_chars >= ((in - out) - 1))
+                desired_chars = (in - out) - 1;
+
+              System.arraycopy(buffer, out, buf, offset + chars_read, 
+                               desired_chars);
+
+              chars_read += desired_chars;
+              out += desired_chars;
+              desired_chars = len - chars_read;
+
+              if (out == pipe_size)
+                out = 0;
+
+              notifyAll();
+            }
+        }
+
+      // If we are done, return
+      if (chars_read == len)
+        return(chars_read);
+
+      // Return a short count if necessary
+      if (chars_read < len)
+        if (try_not_to_block)
+           return(chars_read);
+
+      // Handle the case where the end of stream was encountered.
+      if (closed)
+        {
+          // We never let in == out so there might be one last char
+          // available that we have not copied yet.
+          if (in != -1)
+            {
+              buf[offset + chars_read] = buffer[out];
+              in = -1;
+              ++out;
+              ++chars_read;
+            }
+
+          if (chars_read != 0)
+            return(chars_read);
+          else
+            return(-1);
+        }
+
+      // Wait for a char to be read
+      try
+        {
+          wait();
+        }
+      catch(InterruptedException e) { ; }
+    } 
+
+  } // synchronized
+}
+
+/*************************************************************************/
+
+/**
+  * This method is used by the connected <code>PipedWriter</code> to
+  * write chars into the buffer.  It uses this method instead of directly
+  * writing the chars in order to obtain ownership of the object's monitor
+  * for the purposes of calling <code>notify</code>.
+  *
+  * @param buf The array containing chars to write to this stream
+  * @param offset The offset into the array to start writing from
+  * @param len The number of chars to write.
+  *
+  * @exception IOException If an error occurs
+  */
+void
+write(char[] buf, int offset, int len) throws IOException
+{
+  if (len <= 0)
+    return;
+
+  synchronized (lock) {
+
+  int total_written = 0;
+  while (total_written < len)
+    {
+      // If we are not at the end of the buffer with out = 0
+      if (!((in == (buffer.length - 1)) && (out == 0)))
+        {
+          // This is the "no wrap" situation
+          if ((in - 1) >= out)
+            {
+              int chars_written = 0;
+              if ((buffer.length - in) > (len - total_written))
+                chars_written = (len - total_written);
+              else if (out == 0)
+                chars_written = (buffer.length - in) - 1;
+              else 
+                chars_written = (buffer.length - in);
+
+              if (chars_written > 0) 
+                System.arraycopy(buf, offset + total_written, buffer, in, 
+                                 chars_written);
+              total_written += chars_written;
+              in += chars_written;
+
+              if (in == buffer.length)
+                in = 0;
+
+              notifyAll();
+            }
+          // This is the "wrap" situtation
+          if ((out > in) && (total_written != len))
+            {
+              int chars_written = 0;
+
+              // Do special processing if we are at the beginning
+              if (in == -1)
+                {
+                  in = 0;
+
+                  if (buffer.length > len)
+                    chars_written = len;
+                  else
+                    chars_written = buffer.length - 1;
+                }
+              else if (((out - in) - 1) < (len - total_written))
+                {
+                  chars_written = (out - in) - 1;
+                }
+              else
+                {
+                  chars_written = len - total_written;
+                }
+
+              // If the buffer is full, wait for it to empty out
+              if ((out - 1) == in)
+                {
+                  try
+                    {         
+                      wait(); 
+                    }
+                  catch (InterruptedException e) 
+                    { 
+                      continue; 
+                    }
+                }
+
+              System.arraycopy(buf, offset + total_written, buffer, in,
+                               chars_written);
+              total_written += chars_written;
+              in += chars_written;
+
+              if (in == buffer.length)
+                in = 0;
+
+              notifyAll();
+            }
+        }
+      // Wait for some reads to occur before we write anything.
+      else
+        {
+          try
+            {
+              wait();
+            }
+          catch (InterruptedException e) { ; }
+        }
+    }
+  } // synchronized
+}
+
+} // class PipedReader
+
index 7efed3976da30a0f094fa1ff20d8dd0e06ad6879..cc36acdf0d5cffb01a55ee149fa486198d6f3606 100644 (file)
-// PipedWriter.java - Piped character stream.
+/* PipedWriter.java -- Write portion of piped streams.
+   Copyright (C) 1998 Free Software Foundation, Inc.
 
-/* Copyright (C) 1998, 1999  Free Software Foundation
+This file is part of GNU Classpath.
 
-   This file is part of libgcj.
+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.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
 
 package java.io;
 
 /**
- * @author Tom Tromey <tromey@cygnus.com>
- * @date September 25, 1998 
+  * This class writes its chars to a <code>PipedReader</code> to 
+  * which it is connected.
+  * <p>
+  * It is highly recommended that a <code>PipedWriter</code> and its
+  * connected <code>PipedReader</code> be in different threads.  If 
+  * they are in the same thread, read and write operations could deadlock
+  * the thread.
+  *
+  * @version 0.0
+  *
+  * @author Aaron M. Renn (arenn@urbanophile.com)
+  */
+public class PipedWriter extends Writer
+{
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
  */
 
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * Status:  Complete to 1.1.
+/**
+  * This is the <code>PipedReader</code> to which this object
+  * is connected.
+  */
+private PipedReader snk;
+
+/**
+  * This flag indicates whether or not this stream has ever been
+  * connected to an input stream
+  */
+private boolean ever_connected;
+
+/**
+  * This flag indicates whether the <code>close</code> method has ever
+  * been called for this stream.
+  */
+private boolean closed;
+
+/*************************************************************************/
+
+/**
+  * This method initializes a new <code>PipedWriter</code> instance.
+  * This constructor creates an unconnected object.  It must be connected
+  * to a <code>PipedReader</code> object using the <code>connect</code>
+  * method prior to writing any data or an exception will be thrown.
+  */
+public
+PipedWriter()
+{
+  ; // Do Nothing
+}
+
+/*************************************************************************/
+
+/**
+  * This method initializes a new <code>PipedWriter</code> instance
+  * to write to the specified <code>PipedReader</code>.  This stream
+  * is then ready for writing.
+  *
+  * @param snk The <code>PipedReader</code> to connect this stream to.
+  *
+  * @exception IOException If an error occurs
+  */
+public
+PipedWriter(PipedReader snk) throws IOException
+{
+  connect(snk);
+} 
+
+/*************************************************************************/
+
+/*
+ * Instance Methods
  */
 
-public class PipedWriter extends Writer
+/**
+  * This method connects this object to the specified 
+  * <code>PipedReader</code> object.  This stream will then be ready 
+  * for writing.  If this stream is already connected or has been 
+  * previously closed, then an exception is thrown.
+  *
+  * @param snk The <code>PipedReader</code> to connect this stream to
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+connect(PipedReader snk) throws IOException
+{
+  if (snk == this.snk)
+    return;
+
+  if (ever_connected)
+    throw new IOException("Already connected");
+
+  if (closed)
+    throw new IOException("Stream is closed and cannot be reopened");
+
+  synchronized (lock) {
+
+  this.snk = snk;
+  ever_connected = true;
+  snk.src = this;
+
+  snk.connect(this);
+
+  } // synchronized
+}
+
+/*************************************************************************/
+
+/**
+  * This method closes this stream so that no more data can be written
+  * to it. Any further attempts to write to this stream may throw an
+  * exception
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+close() throws IOException
 {
-  public void close () throws IOException
-  {
-    closed = true;
-  }
-
-  public void connect (PipedReader sink) throws IOException
-  {
-    if (closed)
-      throw new IOException ("already closed");
-    if (reader != null)
-      {
-       if (reader == sink)
-         return;
-       throw new IOException ("already connected");
-      }
-    try
-      {
-       reader = sink;
-       reader.connect(this);
-      }
-    catch (IOException e)
-      {
-       reader = null;
-       throw e;
-      }
-  }
-
-  public void flush () throws IOException
-  {
-    // We'll throw an exception if we're closed, but there's nothing
-    // else to do here.
-    if (closed)
-      throw new IOException ("closed");
-  }
-
-  public PipedWriter ()
-  {
-    super ();
-    closed = false;
-  }
-
-  public PipedWriter (PipedReader sink) throws IOException
-  {
-    super ();
-    closed = false;
-    connect (sink);
-  }
-
-  public void write (char buffer[], int offset, int count) throws IOException
-  {
-    if (closed)
-      throw new IOException ("closed");
-    reader.receive(buffer, offset, count);
-  }
-
-  boolean isClosed ()
-  {
-    return closed;
-  }
-
-  // The associated reader.
-  private PipedReader reader;
-  private boolean closed;
+  synchronized (lock) {
+
+  closed = true;
+  snk.close();
+  notifyAll();
+
+  } // synchronized
 }
+
+/*************************************************************************/
+
+/**
+  * This methods writes a single byte of data to the pipe.  This call may
+  * block if the pipe is full.
+  *
+  * @param c The <code>char</code> to write, passed as an <code>int</code>.
+  *
+  * @exception IOException If an error occurs.
+  */
+public void
+write(int c) throws IOException
+{
+  char[] buf = new char[1];
+  buf[0] = (char)c;
+
+  write(buf, 0, buf.length);
+} 
+
+/*************************************************************************/
+
+/**
+  * This method writes <code>len</code> chars of data from the char array
+  * <code>buf</code> starting at index <code>offset</code> in the array
+  * to the stream.  Note that this method will block if the  
+  * <code>PipedReader</code> to which this object is connected has
+  * a buffer that cannot hold all of the chars to be written.
+  *
+  * @param buf The array containing chars to write to thes stream.
+  * @param offset The index into the array to start writing chars from.
+  * @param len The number of chars to write.
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+write(char[] buf, int offset, int len) throws IOException
+{
+  snk.write(buf, 0, len);
+}
+
+/*************************************************************************/
+
+/**
+  * This method flushes any unwritten chars to the underlying output
+  * sink.  This method does nothing in this class because this class does
+  * not buffer any chars.
+  *
+  * @exception IOException If an error occurs
+  */
+public void
+flush() throws IOException
+{
+  ; // Do Nothing
+}
+
+} // class PipedWriter
+