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