1. /*
  2. * @(#)PipedReader.java 1.12 00/02/02
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.io;
  11. /**
  12. * Piped character-input streams.
  13. *
  14. * @version 1.12, 00/02/02
  15. * @author Mark Reinhold
  16. * @since JDK1.1
  17. */
  18. public class PipedReader extends Reader {
  19. boolean closedByWriter = false;
  20. boolean closedByReader = false;
  21. boolean connected = false;
  22. /* REMIND: identification of the read and write sides needs to be
  23. more sophisticated. Either using thread groups (but what about
  24. pipes within a thread?) or using finalization (but it may be a
  25. long time until the next GC). */
  26. Thread readSide;
  27. Thread writeSide;
  28. /**
  29. * The size of the pipe's circular input buffer.
  30. */
  31. static final int PIPE_SIZE = 1024;
  32. /**
  33. * The circular buffer into which incoming data is placed.
  34. */
  35. char buffer[] = new char[PIPE_SIZE];
  36. /**
  37. * The index of the position in the circular buffer at which the
  38. * next character of data will be stored when received from the connected
  39. * piped writer. <code>in<0</code> implies the buffer is empty,
  40. * <code>in==out</code> implies the buffer is full
  41. */
  42. int in = -1;
  43. /**
  44. * The index of the position in the circular buffer at which the next
  45. * character of data will be read by this piped reader.
  46. */
  47. int out = 0;
  48. /**
  49. * Creates a <code>PipedReader</code> so
  50. * that it is connected to the piped writer
  51. * <code>src</code>. Data written to <code>src</code>
  52. * will then be available as input from this stream.
  53. *
  54. * @param src the stream to connect to.
  55. * @exception IOException if an I/O error occurs.
  56. */
  57. public PipedReader(PipedWriter src) throws IOException {
  58. connect(src);
  59. }
  60. /**
  61. * Creates a <code>PipedReader</code> so
  62. * that it is not yet connected. It must be
  63. * connected to a <code>PipedWriter</code>
  64. * before being used.
  65. *
  66. * @see java.io.PipedReader#connect(java.io.PipedWriter)
  67. * @see java.io.PipedWriter#connect(java.io.PipedReader)
  68. */
  69. public PipedReader() {
  70. }
  71. /**
  72. * Causes this piped reader to be connected
  73. * to the piped writer <code>src</code>.
  74. * If this object is already connected to some
  75. * other piped writer, an <code>IOException</code>
  76. * is thrown.
  77. * <p>
  78. * If <code>src</code> is an
  79. * unconnected piped writer and <code>snk</code>
  80. * is an unconnected piped reader, they
  81. * may be connected by either the call:
  82. * <p>
  83. * <pre><code>snk.connect(src)</code> </pre>
  84. * <p>
  85. * or the call:
  86. * <p>
  87. * <pre><code>src.connect(snk)</code> </pre>
  88. * <p>
  89. * The two
  90. * calls have the same effect.
  91. *
  92. * @param src The piped writer to connect to.
  93. * @exception IOException if an I/O error occurs.
  94. */
  95. public void connect(PipedWriter src) throws IOException {
  96. src.connect(this);
  97. }
  98. /**
  99. * Receives a char of data. This method will block if no input is
  100. * available.
  101. */
  102. synchronized void receive(int c) throws IOException {
  103. if (!connected) {
  104. throw new IOException("Pipe not connected");
  105. } else if (closedByWriter || closedByReader) {
  106. throw new IOException("Pipe closed");
  107. } else if (readSide != null && !readSide.isAlive()) {
  108. throw new IOException("Read end dead");
  109. }
  110. writeSide = Thread.currentThread();
  111. while (in == out) {
  112. if ((readSide != null) && !readSide.isAlive()) {
  113. throw new IOException("Pipe broken");
  114. }
  115. /* full: kick any waiting readers */
  116. notifyAll();
  117. try {
  118. wait(1000);
  119. } catch (InterruptedException ex) {
  120. throw new java.io.InterruptedIOException();
  121. }
  122. }
  123. if (in < 0) {
  124. in = 0;
  125. out = 0;
  126. }
  127. buffer[in++] = (char) c;
  128. if (in >= buffer.length) {
  129. in = 0;
  130. }
  131. }
  132. /**
  133. * Receives data into an array of characters. This method will
  134. * block until some input is available.
  135. */
  136. synchronized void receive(char c[], int off, int len) throws IOException {
  137. while (--len >= 0) {
  138. receive(c[off++]);
  139. }
  140. }
  141. /**
  142. * Notifies all waiting threads that the last character of data has been
  143. * received.
  144. */
  145. synchronized void receivedLast() {
  146. closedByWriter = true;
  147. notifyAll();
  148. }
  149. /**
  150. * Reads the next character of data from this piped stream.
  151. * If no character is available because the end of the stream
  152. * has been reached, the value <code>-1</code> is returned.
  153. * This method blocks until input data is available, the end of
  154. * the stream is detected, or an exception is thrown.
  155. *
  156. * If a thread was providing data characters
  157. * to the connected piped writer, but
  158. * the thread is no longer alive, then an
  159. * <code>IOException</code> is thrown.
  160. *
  161. * @return the next character of data, or <code>-1</code> if the end of the
  162. * stream is reached.
  163. * @exception IOException if the pipe is broken.
  164. */
  165. public synchronized int read() throws IOException {
  166. if (!connected) {
  167. throw new IOException("Pipe not connected");
  168. } else if (closedByReader) {
  169. throw new IOException("Pipe closed");
  170. } else if (writeSide != null && !writeSide.isAlive()
  171. && !closedByWriter && (in < 0)) {
  172. throw new IOException("Write end dead");
  173. }
  174. readSide = Thread.currentThread();
  175. int trials = 2;
  176. while (in < 0) {
  177. if (closedByWriter) {
  178. /* closed by writer, return EOF */
  179. return -1;
  180. }
  181. if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
  182. throw new IOException("Pipe broken");
  183. }
  184. /* might be a writer waiting */
  185. notifyAll();
  186. try {
  187. wait(1000);
  188. } catch (InterruptedException ex) {
  189. throw new java.io.InterruptedIOException();
  190. }
  191. }
  192. int ret = buffer[out++];
  193. if (out >= buffer.length) {
  194. out = 0;
  195. }
  196. if (in == out) {
  197. /* now empty */
  198. in = -1;
  199. }
  200. return ret;
  201. }
  202. /**
  203. * Reads up to <code>len</code> characters of data from this piped
  204. * stream into an array of characters. Less than <code>len</code> characters
  205. * will be read if the end of the data stream is reached. This method
  206. * blocks until at least one character of input is available.
  207. * If a thread was providing data characters to the connected piped output,
  208. * but the thread is no longer alive, then an <code>IOException</code>
  209. * is thrown.
  210. *
  211. * @param cbuf the buffer into which the data is read.
  212. * @param off the start offset of the data.
  213. * @param len the maximum number of characters read.
  214. * @return the total number of characters read into the buffer, or
  215. * <code>-1</code> if there is no more data because the end of
  216. * the stream has been reached.
  217. * @exception IOException if an I/O error occurs.
  218. */
  219. public synchronized int read(char cbuf[], int off, int len) throws IOException {
  220. if (!connected) {
  221. throw new IOException("Pipe not connected");
  222. } else if (closedByReader) {
  223. throw new IOException("Pipe closed");
  224. } else if (writeSide != null && !writeSide.isAlive()
  225. && !closedByWriter && (in < 0)) {
  226. throw new IOException("Write end dead");
  227. }
  228. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  229. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  230. throw new IndexOutOfBoundsException();
  231. } else if (len == 0) {
  232. return 0;
  233. }
  234. /* possibly wait on the first character */
  235. int c = read();
  236. if (c < 0) {
  237. return -1;
  238. }
  239. cbuf[off] = (char)c;
  240. int rlen = 1;
  241. while ((in >= 0) && (--len > 0)) {
  242. cbuf[off + rlen] = buffer[out++];
  243. rlen++;
  244. if (out >= buffer.length) {
  245. out = 0;
  246. }
  247. if (in == out) {
  248. /* now empty */
  249. in = -1;
  250. }
  251. }
  252. return rlen;
  253. }
  254. /**
  255. * Tell whether this stream is ready to be read. A piped character
  256. * stream is ready if the circular buffer is not empty.
  257. *
  258. * @exception IOException If an I/O error occurs
  259. */
  260. public synchronized boolean ready() throws IOException {
  261. if (!connected) {
  262. throw new IOException("Pipe not connected");
  263. } else if (closedByReader) {
  264. throw new IOException("Pipe closed");
  265. } else if (writeSide != null && !writeSide.isAlive()
  266. && !closedByWriter && (in < 0)) {
  267. throw new IOException("Write end dead");
  268. }
  269. if (in < 0) {
  270. return false;
  271. } else {
  272. return true;
  273. }
  274. }
  275. /**
  276. * Closes this piped stream and releases any system resources
  277. * associated with the stream.
  278. *
  279. * @exception IOException if an I/O error occurs.
  280. */
  281. public void close() throws IOException {
  282. in = -1;
  283. closedByReader = true;
  284. }
  285. }