Frames | No Frames |
1: /* PipedReader.java -- Read portion of piped character streams. 2: Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.io; 39: 40: // NOTE: This implementation is very similar to that of PipedInputStream. 41: // If you fix a bug in here, chances are you should make a similar change to 42: // the PipedInputStream code. 43: 44: /** 45: * An input stream that reads characters from a piped writer to which it is 46: * connected. 47: * <p> 48: * Data is read and written to an internal buffer. It is highly recommended 49: * that the <code>PipedReader</code> and connected <code>PipedWriter</code> 50: * be part of different threads. If they are not, there is a possibility 51: * that the read and write operations could deadlock their thread. 52: * 53: * @specnote The JDK implementation appears to have some undocumented 54: * functionality where it keeps track of what thread is writing 55: * to pipe and throws an IOException if that thread susequently 56: * dies. This behaviour seems dubious and unreliable - we don't 57: * implement it. 58: * 59: * @author Aaron M. Renn (arenn@urbanophile.com) 60: */ 61: public class PipedReader extends Reader 62: { 63: /** PipedWriter to which this is connected. Null only if this 64: * Reader hasn't been connected yet. */ 65: PipedWriter source; 66: 67: /** Set to true if close() has been called on this Reader. */ 68: boolean closed; 69: 70: /** 71: * The size of the internal buffer used for input/output. 72: */ 73: static final int PIPE_SIZE = 2048; 74: 75: /** 76: * This is the internal circular buffer used for storing chars written 77: * to the pipe and from which chars are read by this stream 78: */ 79: char[] buffer = new char[PIPE_SIZE]; 80: 81: /** 82: * The index into buffer where the next char from the connected 83: * <code>PipedWriter</code> will be written. If this variable is 84: * equal to <code>out</code>, then the buffer is full. If set to < 0, 85: * the buffer is empty. 86: */ 87: int in = -1; 88: 89: /** 90: * This index into the buffer where chars will be read from. 91: */ 92: int out = 0; 93: 94: /** Buffer used to implement single-argument read/receive */ 95: char[] read_buf = new char[1]; 96: 97: /** 98: * Creates a new <code>PipedReader</code> that is not connected to a 99: * <code>PipedWriter</code>. It must be connected before chars can 100: * be read from this stream. 101: */ 102: public PipedReader() 103: { 104: } 105: 106: /** 107: * This constructor creates a new <code>PipedReader</code> and connects 108: * it to the passed in <code>PipedWriter</code>. The stream is then 109: * ready for reading. 110: * 111: * @param source The <code>PipedWriter</code> to connect this stream to 112: * 113: * @exception IOException If <code>source</code> is already connected. 114: */ 115: public PipedReader(PipedWriter source) throws IOException 116: { 117: connect(source); 118: } 119: 120: /** 121: * This method connects this stream to the passed in 122: * <code>PipedWriter</code>. 123: * This stream is then ready for reading. If this stream is already 124: * connected or has been previously closed, then an exception is thrown 125: * 126: * @param source The <code>PipedWriter</code> to connect this stream to 127: * 128: * @exception IOException If this PipedReader or <code>source</code> 129: * has been connected already. 130: */ 131: public void connect(PipedWriter source) throws IOException 132: { 133: // The JDK (1.3) does not appear to check for a previously closed 134: // connection here. 135: 136: if (this.source != null || source.sink != null) 137: throw new IOException ("Already connected"); 138: 139: source.sink = this; 140: this.source = source; 141: } 142: 143: /** 144: * This method is used by the connected <code>PipedWriter</code> to 145: * write chars into the buffer. 146: * 147: * @param buf The array containing chars to write to this stream 148: * @param offset The offset into the array to start writing from 149: * @param len The number of chars to write. 150: * 151: * @exception IOException If an error occurs 152: * @specnote This code should be in PipedWriter.write, but we 153: * put it here in order to support that bizarre recieve(int) 154: * method. 155: */ 156: void receive(char[] buf, int offset, int len) 157: throws IOException 158: { 159: synchronized (lock) 160: { 161: if (closed) 162: throw new IOException ("Pipe closed"); 163: 164: int bufpos = offset; 165: int copylen; 166: 167: while (len > 0) 168: { 169: try 170: { 171: while (in == out) 172: { 173: // The pipe is full. Wake up any readers and wait for them. 174: lock.notifyAll(); 175: lock.wait(); 176: // The pipe could have been closed while we were waiting. 177: if (closed) 178: throw new IOException ("Pipe closed"); 179: } 180: } 181: catch (InterruptedException ix) 182: { 183: throw new InterruptedIOException (); 184: } 185: 186: if (in < 0) // The pipe is empty. 187: in = 0; 188: 189: // Figure out how many chars from buf can be copied without 190: // overrunning out or going past the length of the buffer. 191: if (in < out) 192: copylen = Math.min (len, out - in); 193: else 194: copylen = Math.min (len, buffer.length - in); 195: 196: // Copy chars until the pipe is filled, wrapping if necessary. 197: System.arraycopy(buf, bufpos, buffer, in, copylen); 198: len -= copylen; 199: bufpos += copylen; 200: in += copylen; 201: if (in == buffer.length) 202: in = 0; 203: } 204: // Notify readers that new data is in the pipe. 205: lock.notifyAll(); 206: } 207: } 208: 209: /** 210: * This method reads chars from the stream into a caller supplied buffer. 211: * It starts storing chars at position <code>offset</code> into the 212: * buffer and 213: * reads a maximum of <code>len</code> chars. Note that this method 214: * can actually 215: * read fewer than <code>len</code> chars. The actual number of chars 216: * read is 217: * returned. A -1 is returned to indicated that no chars can be read 218: * because the end of the stream was reached. If the stream is already 219: * closed, a -1 will again be returned to indicate the end of the stream. 220: * <p> 221: * This method will block if no char is available to be read. 222: */ 223: public int read() throws IOException 224: { 225: // Method operates by calling the multichar overloaded read method 226: // Note that read_buf is an internal instance variable. I allocate it 227: // there to avoid constant reallocation overhead for applications that 228: // call this method in a loop at the cost of some unneeded overhead 229: // if this method is never called. 230: 231: int r = read(read_buf, 0, 1); 232: return r != -1 ? read_buf[0] : -1; 233: } 234: 235: /** 236: * This method reads characters from the stream into a caller supplied 237: * buffer. It starts storing chars at position <code>offset</code> into 238: * the buffer and reads a maximum of <code>len</code> chars. Note that 239: * this method can actually read fewer than <code>len</code> chars. 240: * The actual number of chars read is 241: * returned. A -1 is returned to indicated that no chars can be read 242: * because the end of the stream was reached - ie close() was called on the 243: * connected PipedWriter. 244: * <p> 245: * This method will block if no chars are available to be read. 246: * 247: * @param buf The buffer into which chars will be stored 248: * @param offset The index into the buffer at which to start writing. 249: * @param len The maximum number of chars to read. 250: * 251: * @exception IOException If <code>close()</code> was called on this Piped 252: * Reader. 253: */ 254: public int read(char[] buf, int offset, int len) 255: throws IOException 256: { 257: synchronized (lock) 258: { 259: if (source == null) 260: throw new IOException ("Not connected"); 261: if (closed) 262: throw new IOException ("Pipe closed"); 263: 264: // Don't block if nothing was requested. 265: if (len == 0) 266: return 0; 267: 268: // If the buffer is empty, wait until there is something in the pipe 269: // to read. 270: try 271: { 272: while (in < 0) 273: { 274: if (source.closed) 275: return -1; 276: lock.wait(); 277: } 278: } 279: catch (InterruptedException ix) 280: { 281: throw new InterruptedIOException(); 282: } 283: 284: int total = 0; 285: int copylen; 286: 287: while (true) 288: { 289: // Figure out how many chars from the pipe can be copied without 290: // overrunning in or going past the length of buf. 291: if (out < in) 292: copylen = Math.min (len, in - out); 293: else 294: copylen = Math.min (len, buffer.length - out); 295: 296: System.arraycopy (buffer, out, buf, offset, copylen); 297: offset += copylen; 298: len -= copylen; 299: out += copylen; 300: total += copylen; 301: 302: if (out == buffer.length) 303: out = 0; 304: 305: if (out == in) 306: { 307: // Pipe is now empty. 308: in = -1; 309: out = 0; 310: } 311: 312: // If output buffer is filled or the pipe is empty, we're done. 313: if (len == 0 || in == -1) 314: { 315: // Notify any waiting Writer that there is now space 316: // to write. 317: lock.notifyAll(); 318: return total; 319: } 320: } 321: } 322: } 323: 324: public boolean ready() throws IOException 325: { 326: // The JDK 1.3 implementation does not appear to check for the closed or 327: // unconnected stream conditions here. However, checking for a 328: // closed stream is explicitly required by the JDK 1.2 and 1.3 329: // documentation (for Reader.close()), so we do it. 330: 331: synchronized (lock) 332: { 333: if (closed) 334: throw new IOException("Pipe closed"); 335: 336: if (in < 0) 337: return false; 338: 339: int count; 340: if (out < in) 341: count = in - out; 342: else 343: count = (buffer.length - out) - in; 344: 345: return (count > 0); 346: } 347: } 348: 349: /** 350: * This methods closes the stream so that no more data can be read 351: * from it. 352: * 353: * @exception IOException If an error occurs 354: */ 355: public void close() throws IOException 356: { 357: synchronized (lock) 358: { 359: closed = true; 360: // Wake any thread which may be in receive() waiting to write data. 361: lock.notifyAll(); 362: } 363: } 364: }