001: /*
002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.io;
027:
028: /**
029: * Piped character-input streams.
030: *
031: * @version 1.25, 07/05/05
032: * @author Mark Reinhold
033: * @since JDK1.1
034: */
035:
036: public class PipedReader extends Reader {
037: boolean closedByWriter = false;
038: boolean closedByReader = false;
039: boolean connected = false;
040:
041: /* REMIND: identification of the read and write sides needs to be
042: more sophisticated. Either using thread groups (but what about
043: pipes within a thread?) or using finalization (but it may be a
044: long time until the next GC). */
045: Thread readSide;
046: Thread writeSide;
047:
048: /**
049: * The size of the pipe's circular input buffer.
050: */
051: private static final int DEFAULT_PIPE_SIZE = 1024;
052:
053: /**
054: * The circular buffer into which incoming data is placed.
055: */
056: char buffer[];
057:
058: /**
059: * The index of the position in the circular buffer at which the
060: * next character of data will be stored when received from the connected
061: * piped writer. <code>in<0</code> implies the buffer is empty,
062: * <code>in==out</code> implies the buffer is full
063: */
064: int in = -1;
065:
066: /**
067: * The index of the position in the circular buffer at which the next
068: * character of data will be read by this piped reader.
069: */
070: int out = 0;
071:
072: /**
073: * Creates a <code>PipedReader</code> so
074: * that it is connected to the piped writer
075: * <code>src</code>. Data written to <code>src</code>
076: * will then be available as input from this stream.
077: *
078: * @param src the stream to connect to.
079: * @exception IOException if an I/O error occurs.
080: */
081: public PipedReader(PipedWriter src) throws IOException {
082: this (src, DEFAULT_PIPE_SIZE);
083: }
084:
085: /**
086: * Creates a <code>PipedReader</code> so that it is connected
087: * to the piped writer <code>src</code> and uses the specified
088: * pipe size for the pipe's buffer. Data written to <code>src</code>
089: * will then be available as input from this stream.
090:
091: * @param src the stream to connect to.
092: * @param pipeSize the size of the pipe's buffer.
093: * @exception IOException if an I/O error occurs.
094: * @exception IllegalArgumentException if <code>pipeSize <= 0</code>.
095: * @since 1.6
096: */
097: public PipedReader(PipedWriter src, int pipeSize)
098: throws IOException {
099: initPipe(pipeSize);
100: connect(src);
101: }
102:
103: /**
104: * Creates a <code>PipedReader</code> so
105: * that it is not yet {@linkplain #connect(java.io.PipedWriter)
106: * connected}. It must be {@linkplain java.io.PipedWriter#connect(
107: * java.io.PipedReader) connected} to a <code>PipedWriter</code>
108: * before being used.
109: */
110: public PipedReader() {
111: initPipe(DEFAULT_PIPE_SIZE);
112: }
113:
114: /**
115: * Creates a <code>PipedReader</code> so that it is not yet
116: * {@link #connect(java.io.PipedWriter) connected} and uses
117: * the specified pipe size for the pipe's buffer.
118: * It must be {@linkplain java.io.PipedWriter#connect(
119: * java.io.PipedReader) connected} to a <code>PipedWriter</code>
120: * before being used.
121: *
122: * @param pipeSize the size of the pipe's buffer.
123: * @exception IllegalArgumentException if <code>pipeSize <= 0</code>.
124: * @since 1.6
125: */
126: public PipedReader(int pipeSize) {
127: initPipe(pipeSize);
128: }
129:
130: private void initPipe(int pipeSize) {
131: if (pipeSize <= 0) {
132: throw new IllegalArgumentException("Pipe size <= 0");
133: }
134: buffer = new char[pipeSize];
135: }
136:
137: /**
138: * Causes this piped reader to be connected
139: * to the piped writer <code>src</code>.
140: * If this object is already connected to some
141: * other piped writer, an <code>IOException</code>
142: * is thrown.
143: * <p>
144: * If <code>src</code> is an
145: * unconnected piped writer and <code>snk</code>
146: * is an unconnected piped reader, they
147: * may be connected by either the call:
148: * <p>
149: * <pre><code>snk.connect(src)</code> </pre>
150: * <p>
151: * or the call:
152: * <p>
153: * <pre><code>src.connect(snk)</code> </pre>
154: * <p>
155: * The two
156: * calls have the same effect.
157: *
158: * @param src The piped writer to connect to.
159: * @exception IOException if an I/O error occurs.
160: */
161: public void connect(PipedWriter src) throws IOException {
162: src.connect(this );
163: }
164:
165: /**
166: * Receives a char of data. This method will block if no input is
167: * available.
168: */
169: synchronized void receive(int c) throws IOException {
170: if (!connected) {
171: throw new IOException("Pipe not connected");
172: } else if (closedByWriter || closedByReader) {
173: throw new IOException("Pipe closed");
174: } else if (readSide != null && !readSide.isAlive()) {
175: throw new IOException("Read end dead");
176: }
177:
178: writeSide = Thread.currentThread();
179: while (in == out) {
180: if ((readSide != null) && !readSide.isAlive()) {
181: throw new IOException("Pipe broken");
182: }
183: /* full: kick any waiting readers */
184: notifyAll();
185: try {
186: wait(1000);
187: } catch (InterruptedException ex) {
188: throw new java.io.InterruptedIOException();
189: }
190: }
191: if (in < 0) {
192: in = 0;
193: out = 0;
194: }
195: buffer[in++] = (char) c;
196: if (in >= buffer.length) {
197: in = 0;
198: }
199: }
200:
201: /**
202: * Receives data into an array of characters. This method will
203: * block until some input is available.
204: */
205: synchronized void receive(char c[], int off, int len)
206: throws IOException {
207: while (--len >= 0) {
208: receive(c[off++]);
209: }
210: }
211:
212: /**
213: * Notifies all waiting threads that the last character of data has been
214: * received.
215: */
216: synchronized void receivedLast() {
217: closedByWriter = true;
218: notifyAll();
219: }
220:
221: /**
222: * Reads the next character of data from this piped stream.
223: * If no character is available because the end of the stream
224: * has been reached, the value <code>-1</code> is returned.
225: * This method blocks until input data is available, the end of
226: * the stream is detected, or an exception is thrown.
227: *
228: * @return the next character of data, or <code>-1</code> if the end of the
229: * stream is reached.
230: * @exception IOException if the pipe is
231: * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
232: * {@link #connect(java.io.PipedWriter) unconnected}, closed,
233: * or an I/O error occurs.
234: */
235: public synchronized int read() throws IOException {
236: if (!connected) {
237: throw new IOException("Pipe not connected");
238: } else if (closedByReader) {
239: throw new IOException("Pipe closed");
240: } else if (writeSide != null && !writeSide.isAlive()
241: && !closedByWriter && (in < 0)) {
242: throw new IOException("Write end dead");
243: }
244:
245: readSide = Thread.currentThread();
246: int trials = 2;
247: while (in < 0) {
248: if (closedByWriter) {
249: /* closed by writer, return EOF */
250: return -1;
251: }
252: if ((writeSide != null) && (!writeSide.isAlive())
253: && (--trials < 0)) {
254: throw new IOException("Pipe broken");
255: }
256: /* might be a writer waiting */
257: notifyAll();
258: try {
259: wait(1000);
260: } catch (InterruptedException ex) {
261: throw new java.io.InterruptedIOException();
262: }
263: }
264: int ret = buffer[out++];
265: if (out >= buffer.length) {
266: out = 0;
267: }
268: if (in == out) {
269: /* now empty */
270: in = -1;
271: }
272: return ret;
273: }
274:
275: /**
276: * Reads up to <code>len</code> characters of data from this piped
277: * stream into an array of characters. Less than <code>len</code> characters
278: * will be read if the end of the data stream is reached or if
279: * <code>len</code> exceeds the pipe's buffer size. This method
280: * blocks until at least one character of input is available.
281: *
282: * @param cbuf the buffer into which the data is read.
283: * @param off the start offset of the data.
284: * @param len the maximum number of characters read.
285: * @return the total number of characters read into the buffer, or
286: * <code>-1</code> if there is no more data because the end of
287: * the stream has been reached.
288: * @exception IOException if the pipe is
289: * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
290: * {@link #connect(java.io.PipedWriter) unconnected}, closed,
291: * or an I/O error occurs.
292: */
293: public synchronized int read(char cbuf[], int off, int len)
294: throws IOException {
295: if (!connected) {
296: throw new IOException("Pipe not connected");
297: } else if (closedByReader) {
298: throw new IOException("Pipe closed");
299: } else if (writeSide != null && !writeSide.isAlive()
300: && !closedByWriter && (in < 0)) {
301: throw new IOException("Write end dead");
302: }
303:
304: if ((off < 0) || (off > cbuf.length) || (len < 0)
305: || ((off + len) > cbuf.length) || ((off + len) < 0)) {
306: throw new IndexOutOfBoundsException();
307: } else if (len == 0) {
308: return 0;
309: }
310:
311: /* possibly wait on the first character */
312: int c = read();
313: if (c < 0) {
314: return -1;
315: }
316: cbuf[off] = (char) c;
317: int rlen = 1;
318: while ((in >= 0) && (--len > 0)) {
319: cbuf[off + rlen] = buffer[out++];
320: rlen++;
321: if (out >= buffer.length) {
322: out = 0;
323: }
324: if (in == out) {
325: /* now empty */
326: in = -1;
327: }
328: }
329: return rlen;
330: }
331:
332: /**
333: * Tell whether this stream is ready to be read. A piped character
334: * stream is ready if the circular buffer is not empty.
335: *
336: * @exception IOException if the pipe is
337: * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
338: * {@link #connect(java.io.PipedWriter) unconnected}, or closed.
339: */
340: public synchronized boolean ready() throws IOException {
341: if (!connected) {
342: throw new IOException("Pipe not connected");
343: } else if (closedByReader) {
344: throw new IOException("Pipe closed");
345: } else if (writeSide != null && !writeSide.isAlive()
346: && !closedByWriter && (in < 0)) {
347: throw new IOException("Write end dead");
348: }
349: if (in < 0) {
350: return false;
351: } else {
352: return true;
353: }
354: }
355:
356: /**
357: * Closes this piped stream and releases any system resources
358: * associated with the stream.
359: *
360: * @exception IOException if an I/O error occurs.
361: */
362: public void close() throws IOException {
363: in = -1;
364: closedByReader = true;
365: }
366: }
|