1. /*
  2. * @(#)PushbackInputStream.java 1.31 00/02/02
  3. *
  4. * Copyright 1994-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. * A <code>PushbackInputStream</code> adds
  13. * functionality to another input stream, namely
  14. * the ability to "push back" or "unread"
  15. * one byte. This is useful in situations where
  16. * it is convenient for a fragment of code
  17. * to read an indefinite number of data bytes
  18. * that are delimited by a particular byte
  19. * value; after reading the terminating byte,
  20. * the code fragment can "unread" it, so that
  21. * the next read operation on the input stream
  22. * will reread the byte that was pushed back.
  23. * For example, bytes representing the characters
  24. * constituting an identifier might be terminated
  25. * by a byte representing an operator character;
  26. * a method whose job is to read just an identifier
  27. * can read until it sees the operator and
  28. * then push the operator back to be re-read.
  29. *
  30. * @author David Connelly
  31. * @author Jonathan Payne
  32. * @version 1.31, 02/02/00
  33. * @since JDK1.0
  34. */
  35. public
  36. class PushbackInputStream extends FilterInputStream {
  37. /**
  38. * The pushback buffer.
  39. * @since JDK1.1
  40. */
  41. protected byte[] buf;
  42. /**
  43. * The position within the pushback buffer from which the next byte will
  44. * be read. When the buffer is empty, <code>pos</code> is equal to
  45. * <code>buf.length</code> when the buffer is full, <code>pos</code> is
  46. * equal to zero.
  47. *
  48. * @since JDK1.1
  49. */
  50. protected int pos;
  51. /**
  52. * Check to make sure that this stream has not been closed
  53. */
  54. private void ensureOpen() throws IOException {
  55. if (in == null)
  56. throw new IOException("Stream closed");
  57. }
  58. /**
  59. * Creates a <code>PushbackInputStream</code>
  60. * with a pushback buffer of the specified <code>size</code>,
  61. * and saves its argument, the input stream
  62. * <code>in</code>, for later use. Initially,
  63. * there is no pushed-back byte (the field
  64. * <code>pushBack</code> is initialized to
  65. * <code>-1</code>).
  66. *
  67. * @param in the input stream from which bytes will be read.
  68. * @param size the size of the pushback buffer.
  69. * @exception IllegalArgumentException if size is <= 0
  70. * @since JDK1.1
  71. */
  72. public PushbackInputStream(InputStream in, int size) {
  73. super(in);
  74. if (size <= 0) {
  75. throw new IllegalArgumentException("size <= 0");
  76. }
  77. this.buf = new byte[size];
  78. this.pos = size;
  79. }
  80. /**
  81. * Creates a <code>PushbackInputStream</code>
  82. * and saves its argument, the input stream
  83. * <code>in</code>, for later use. Initially,
  84. * there is no pushed-back byte (the field
  85. * <code>pushBack</code> is initialized to
  86. * <code>-1</code>).
  87. *
  88. * @param in the input stream from which bytes will be read.
  89. */
  90. public PushbackInputStream(InputStream in) {
  91. this(in, 1);
  92. }
  93. /**
  94. * Reads the next byte of data from this input stream. The value
  95. * byte is returned as an <code>int</code> in the range
  96. * <code>0</code> to <code>255</code>. If no byte is available
  97. * because the end of the stream has been reached, the value
  98. * <code>-1</code> is returned. This method blocks until input data
  99. * is available, the end of the stream is detected, or an exception
  100. * is thrown.
  101. *
  102. * <p> This method returns the most recently pushed-back byte, if there is
  103. * one, and otherwise calls the <code>read</code> method of its underlying
  104. * input stream and returns whatever value that method returns.
  105. *
  106. * @return the next byte of data, or <code>-1</code> if the end of the
  107. * stream has been reached.
  108. * @exception IOException if an I/O error occurs.
  109. * @see java.io.InputStream#read()
  110. */
  111. public int read() throws IOException {
  112. ensureOpen();
  113. if (pos < buf.length) {
  114. return buf[pos++] & 0xff;
  115. }
  116. return super.read();
  117. }
  118. /**
  119. * Reads up to <code>len</code> bytes of data from this input stream into
  120. * an array of bytes. This method first reads any pushed-back bytes; after
  121. * that, if fewer than than <code>len</code> bytes have been read then it
  122. * reads from the underlying input stream. This method blocks until at
  123. * least 1 byte of input is available.
  124. *
  125. * @param b the buffer into which the data is read.
  126. * @param off the start offset of the data.
  127. * @param len the maximum number of bytes read.
  128. * @return the total number of bytes read into the buffer, or
  129. * <code>-1</code> if there is no more data because the end of
  130. * the stream has been reached.
  131. * @exception IOException if an I/O error occurs.
  132. * @see java.io.InputStream#read(byte[], int, int)
  133. */
  134. public int read(byte[] b, int off, int len) throws IOException {
  135. ensureOpen();
  136. if ((off < 0) || (off > b.length) || (len < 0) ||
  137. ((off + len) > b.length) || ((off + len) < 0)) {
  138. throw new IndexOutOfBoundsException();
  139. }
  140. if (len == 0) {
  141. return 0;
  142. }
  143. int avail = buf.length - pos;
  144. if (avail > 0) {
  145. if (len < avail) {
  146. avail = len;
  147. }
  148. System.arraycopy(buf, pos, b, off, avail);
  149. pos += avail;
  150. off += avail;
  151. len -= avail;
  152. }
  153. if (len > 0) {
  154. len = super.read(b, off, len);
  155. if (len == -1) {
  156. return avail == 0 ? -1 : avail;
  157. }
  158. return avail + len;
  159. }
  160. return avail;
  161. }
  162. /**
  163. * Pushes back a byte by copying it to the front of the pushback buffer.
  164. * After this method returns, the next byte to be read will have the value
  165. * <code>(byte)b</code>.
  166. *
  167. * @param b the <code>int</code> value whose low-order
  168. * byte is to be pushed back.
  169. * @exception IOException If there is not enough room in the pushback
  170. * buffer for the byte.
  171. */
  172. public void unread(int b) throws IOException {
  173. ensureOpen();
  174. if (pos == 0) {
  175. throw new IOException("Push back buffer is full");
  176. }
  177. buf[--pos] = (byte)b;
  178. }
  179. /**
  180. * Pushes back a portion of an array of bytes by copying it to the front
  181. * of the pushback buffer. After this method returns, the next byte to be
  182. * read will have the value <code>b[off]</code>, the byte after that will
  183. * have the value <code>b[off+1]</code>, and so forth.
  184. *
  185. * @param b the byte array to push back.
  186. * @param off the start offset of the data.
  187. * @param len the number of bytes to push back.
  188. * @exception IOException If there is not enough room in the pushback
  189. * buffer for the specified number of bytes.
  190. * @since JDK1.1
  191. */
  192. public void unread(byte[] b, int off, int len) throws IOException {
  193. ensureOpen();
  194. if (len > pos) {
  195. throw new IOException("Push back buffer is full");
  196. }
  197. pos -= len;
  198. System.arraycopy(b, off, buf, pos, len);
  199. }
  200. /**
  201. * Pushes back an array of bytes by copying it to the front of the
  202. * pushback buffer. After this method returns, the next byte to be read
  203. * will have the value <code>b[0]</code>, the byte after that will have the
  204. * value <code>b[1]</code>, and so forth.
  205. *
  206. * @param b the byte array to push back
  207. * @exception IOException If there is not enough room in the pushback
  208. * buffer for the specified number of bytes.
  209. * @since JDK1.1
  210. */
  211. public void unread(byte[] b) throws IOException {
  212. unread(b, 0, b.length);
  213. }
  214. /**
  215. * Returns the number of bytes that can be read from this input stream
  216. * without blocking. This method calls the <code>available</code> method
  217. * of the underlying input stream; it returns that value plus the number of
  218. * bytes that have been pushed back.
  219. *
  220. * @return the number of bytes that can be read from the input stream
  221. * without blocking.
  222. * @exception IOException if an I/O error occurs.
  223. * @see java.io.FilterInputStream#in
  224. * @see java.io.InputStream#available()
  225. */
  226. public int available() throws IOException {
  227. ensureOpen();
  228. return (buf.length - pos) + super.available();
  229. }
  230. /**
  231. * Skips over and discards <code>n</code> bytes of data from this
  232. * input stream. The <code>skip</code> method may, for a variety of
  233. * reasons, end up skipping over some smaller number of bytes,
  234. * possibly zero. If <code>n</code> is negative, no bytes are skipped.
  235. *
  236. * <p> The <code>skip</code> method of <code>PushbackInputStream</code>
  237. * first skips over the bytes in the pushback buffer, if any. It then
  238. * calls the <code>skip</code> method of the underlying input stream if
  239. * more bytes need to be skipped. The actual number of bytes skipped
  240. * is returned.
  241. *
  242. * @param n the number of bytes to be skipped.
  243. * @return the actual number of bytes skipped.
  244. * @exception IOException if an I/O error occurs.
  245. * @see java.io.FilterInputStream#in
  246. * @see java.io.InputStream#skip(long n)
  247. * @since 1.2
  248. */
  249. public long skip(long n) throws IOException {
  250. ensureOpen();
  251. if (n <= 0) {
  252. return 0;
  253. }
  254. long pskip = buf.length - pos;
  255. if (pskip > 0) {
  256. if (n < pskip) {
  257. pskip = n;
  258. }
  259. pos += pskip;
  260. n -= pskip;
  261. }
  262. if (n > 0) {
  263. pskip += super.skip(n);
  264. }
  265. return pskip;
  266. }
  267. /**
  268. * Tests if this input stream supports the <code>mark</code> and
  269. * <code>reset</code> methods, which it does not.
  270. *
  271. * @return <code>false</code>, since this class does not support the
  272. * <code>mark</code> and <code>reset</code> methods.
  273. * @see java.io.InputStream#mark(int)
  274. * @see java.io.InputStream#reset()
  275. */
  276. public boolean markSupported() {
  277. return false;
  278. }
  279. /**
  280. * Closes this input stream and releases any system resources
  281. * associated with the stream.
  282. *
  283. * @exception IOException if an I/O error occurs.
  284. */
  285. public synchronized void close() throws IOException {
  286. if (in == null)
  287. return;
  288. in.close();
  289. in = null;
  290. buf = null;
  291. }
  292. }