blob: 5c47981792112c6f39845c3754bf42391594388b [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package java.io;
27
28
29/**
30 * Piped character-input streams.
31 *
32 * @author Mark Reinhold
33 * @since JDK1.1
34 */
35
36public class PipedReader extends Reader {
37 boolean closedByWriter = false;
38 boolean closedByReader = false;
39 boolean connected = false;
40
41 /* REMIND: identification of the read and write sides needs to be
42 more sophisticated. Either using thread groups (but what about
43 pipes within a thread?) or using finalization (but it may be a
44 long time until the next GC). */
45 Thread readSide;
46 Thread writeSide;
47
48 /**
49 * The size of the pipe's circular input buffer.
50 */
51 private static final int DEFAULT_PIPE_SIZE = 1024;
52
53 /**
54 * The circular buffer into which incoming data is placed.
55 */
56 char buffer[];
57
58 /**
59 * The index of the position in the circular buffer at which the
60 * next character of data will be stored when received from the connected
61 * piped writer. <code>in&lt;0</code> implies the buffer is empty,
62 * <code>in==out</code> implies the buffer is full
63 */
64 int in = -1;
65
66 /**
67 * The index of the position in the circular buffer at which the next
68 * character of data will be read by this piped reader.
69 */
70 int out = 0;
71
72 /**
73 * Creates a <code>PipedReader</code> so
74 * that it is connected to the piped writer
75 * <code>src</code>. Data written to <code>src</code>
76 * will then be available as input from this stream.
77 *
78 * @param src the stream to connect to.
79 * @exception IOException if an I/O error occurs.
80 */
81 public PipedReader(PipedWriter src) throws IOException {
82 this(src, DEFAULT_PIPE_SIZE);
83 }
84
85 /**
86 * Creates a <code>PipedReader</code> so that it is connected
87 * to the piped writer <code>src</code> and uses the specified
88 * pipe size for the pipe's buffer. Data written to <code>src</code>
89 * will then be available as input from this stream.
90
91 * @param src the stream to connect to.
92 * @param pipeSize the size of the pipe's buffer.
93 * @exception IOException if an I/O error occurs.
94 * @exception IllegalArgumentException if <code>pipeSize <= 0</code>.
95 * @since 1.6
96 */
97 public PipedReader(PipedWriter src, int pipeSize) throws IOException {
98 initPipe(pipeSize);
99 connect(src);
100 }
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) throws IOException {
206 while (--len >= 0) {
207 receive(c[off++]);
208 }
209 }
210
211 /**
212 * Notifies all waiting threads that the last character of data has been
213 * received.
214 */
215 synchronized void receivedLast() {
216 closedByWriter = true;
217 notifyAll();
218 }
219
220 /**
221 * Reads the next character of data from this piped stream.
222 * If no character is available because the end of the stream
223 * has been reached, the value <code>-1</code> is returned.
224 * This method blocks until input data is available, the end of
225 * the stream is detected, or an exception is thrown.
226 *
227 * @return the next character of data, or <code>-1</code> if the end of the
228 * stream is reached.
229 * @exception IOException if the pipe is
230 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
231 * {@link #connect(java.io.PipedWriter) unconnected}, closed,
232 * or an I/O error occurs.
233 */
234 public synchronized int read() throws IOException {
235 if (!connected) {
236 throw new IOException("Pipe not connected");
237 } else if (closedByReader) {
238 throw new IOException("Pipe closed");
239 } else if (writeSide != null && !writeSide.isAlive()
240 && !closedByWriter && (in < 0)) {
241 throw new IOException("Write end dead");
242 }
243
244 readSide = Thread.currentThread();
245 int trials = 2;
246 while (in < 0) {
247 if (closedByWriter) {
248 /* closed by writer, return EOF */
249 return -1;
250 }
251 if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
252 throw new IOException("Pipe broken");
253 }
254 /* might be a writer waiting */
255 notifyAll();
256 try {
257 wait(1000);
258 } catch (InterruptedException ex) {
259 throw new java.io.InterruptedIOException();
260 }
261 }
262 int ret = buffer[out++];
263 if (out >= buffer.length) {
264 out = 0;
265 }
266 if (in == out) {
267 /* now empty */
268 in = -1;
269 }
270 return ret;
271 }
272
273 /**
274 * Reads up to <code>len</code> characters of data from this piped
275 * stream into an array of characters. Less than <code>len</code> characters
276 * will be read if the end of the data stream is reached or if
277 * <code>len</code> exceeds the pipe's buffer size. This method
278 * blocks until at least one character of input is available.
279 *
280 * @param cbuf the buffer into which the data is read.
281 * @param off the start offset of the data.
282 * @param len the maximum number of characters read.
283 * @return the total number of characters read into the buffer, or
284 * <code>-1</code> if there is no more data because the end of
285 * the stream has been reached.
286 * @exception IOException if the pipe is
287 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
288 * {@link #connect(java.io.PipedWriter) unconnected}, closed,
289 * or an I/O error occurs.
290 */
291 public synchronized int read(char cbuf[], int off, int len) throws IOException {
292 if (!connected) {
293 throw new IOException("Pipe not connected");
294 } else if (closedByReader) {
295 throw new IOException("Pipe closed");
296 } else if (writeSide != null && !writeSide.isAlive()
297 && !closedByWriter && (in < 0)) {
298 throw new IOException("Write end dead");
299 }
300
301 if ((off < 0) || (off > cbuf.length) || (len < 0) ||
302 ((off + len) > cbuf.length) || ((off + len) < 0)) {
303 throw new IndexOutOfBoundsException();
304 } else if (len == 0) {
305 return 0;
306 }
307
308 /* possibly wait on the first character */
309 int c = read();
310 if (c < 0) {
311 return -1;
312 }
313 cbuf[off] = (char)c;
314 int rlen = 1;
315 while ((in >= 0) && (--len > 0)) {
316 cbuf[off + rlen] = buffer[out++];
317 rlen++;
318 if (out >= buffer.length) {
319 out = 0;
320 }
321 if (in == out) {
322 /* now empty */
323 in = -1;
324 }
325 }
326 return rlen;
327 }
328
329 /**
330 * Tell whether this stream is ready to be read. A piped character
331 * stream is ready if the circular buffer is not empty.
332 *
333 * @exception IOException if the pipe is
334 * <a href=PipedInputStream.html#BROKEN> <code>broken</code></a>,
335 * {@link #connect(java.io.PipedWriter) unconnected}, or closed.
336 */
337 public synchronized boolean ready() throws IOException {
338 if (!connected) {
339 throw new IOException("Pipe not connected");
340 } else if (closedByReader) {
341 throw new IOException("Pipe closed");
342 } else if (writeSide != null && !writeSide.isAlive()
343 && !closedByWriter && (in < 0)) {
344 throw new IOException("Write end dead");
345 }
346 if (in < 0) {
347 return false;
348 } else {
349 return true;
350 }
351 }
352
353 /**
354 * Closes this piped stream and releases any system resources
355 * associated with the stream.
356 *
357 * @exception IOException if an I/O error occurs.
358 */
359 public void close() throws IOException {
360 in = -1;
361 closedByReader = true;
362 }
363}