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 }
|