1 /*
2  * Copyright (c) 1996, 2015, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.io;
27 
28 
29 /**
30  * A character-stream reader that allows characters to be pushed back into the
31  * stream.
32  *
33  * @author      Mark Reinhold
34  * @since       1.1
35  */
36 
37 public class PushbackReader extends FilterReader {
38 
39     /** Pushback buffer */
40     private char[] buf;
41 
42     /** Current position in buffer */
43     private int pos;
44 
45     /**
46      * Creates a new pushback reader with a pushback buffer of the given size.
47      *
48      * @param   in   The reader from which characters will be read
49      * @param   size The size of the pushback buffer
50      * @exception IllegalArgumentException if {@code size <= 0}
51      */
PushbackReader(Reader in, int size)52     public PushbackReader(Reader in, int size) {
53         super(in);
54         if (size <= 0) {
55             throw new IllegalArgumentException("size <= 0");
56         }
57         this.buf = new char[size];
58         this.pos = size;
59     }
60 
61     /**
62      * Creates a new pushback reader with a one-character pushback buffer.
63      *
64      * @param   in  The reader from which characters will be read
65      */
PushbackReader(Reader in)66     public PushbackReader(Reader in) {
67         this(in, 1);
68     }
69 
70     /** Checks to make sure that the stream has not been closed. */
ensureOpen()71     private void ensureOpen() throws IOException {
72         if (buf == null)
73             throw new IOException("Stream closed");
74     }
75 
76     /**
77      * Reads a single character.
78      *
79      * @return     The character read, or -1 if the end of the stream has been
80      *             reached
81      *
82      * @exception  IOException  If an I/O error occurs
83      */
read()84     public int read() throws IOException {
85         synchronized (lock) {
86             ensureOpen();
87             if (pos < buf.length)
88                 return buf[pos++];
89             else
90                 return super.read();
91         }
92     }
93 
94     /**
95      * Reads characters into a portion of an array.
96      *
97      * @param      cbuf  Destination buffer
98      * @param      off   Offset at which to start writing characters
99      * @param      len   Maximum number of characters to read
100      *
101      * @return     The number of characters read, or -1 if the end of the
102      *             stream has been reached
103      *
104      * @exception  IOException  If an I/O error occurs
105      * @exception  IndexOutOfBoundsException {@inheritDoc}
106      */
read(char cbuf[], int off, int len)107     public int read(char cbuf[], int off, int len) throws IOException {
108         synchronized (lock) {
109             ensureOpen();
110             try {
111                 if (len <= 0) {
112                     if (len < 0) {
113                         throw new IndexOutOfBoundsException();
114                     } else if ((off < 0) || (off > cbuf.length)) {
115                         throw new IndexOutOfBoundsException();
116                     }
117                     return 0;
118                 }
119                 int avail = buf.length - pos;
120                 if (avail > 0) {
121                     if (len < avail)
122                         avail = len;
123                     System.arraycopy(buf, pos, cbuf, off, avail);
124                     pos += avail;
125                     off += avail;
126                     len -= avail;
127                 }
128                 if (len > 0) {
129                     len = super.read(cbuf, off, len);
130                     if (len == -1) {
131                         return (avail == 0) ? -1 : avail;
132                     }
133                     return avail + len;
134                 }
135                 return avail;
136             } catch (ArrayIndexOutOfBoundsException e) {
137                 throw new IndexOutOfBoundsException();
138             }
139         }
140     }
141 
142     /**
143      * Pushes back a single character by copying it to the front of the
144      * pushback buffer. After this method returns, the next character to be read
145      * will have the value <code>(char)c</code>.
146      *
147      * @param  c  The int value representing a character to be pushed back
148      *
149      * @exception  IOException  If the pushback buffer is full,
150      *                          or if some other I/O error occurs
151      */
unread(int c)152     public void unread(int c) throws IOException {
153         synchronized (lock) {
154             ensureOpen();
155             if (pos == 0)
156                 throw new IOException("Pushback buffer overflow");
157             buf[--pos] = (char) c;
158         }
159     }
160 
161     /**
162      * Pushes back a portion of an array of characters by copying it to the
163      * front of the pushback buffer.  After this method returns, the next
164      * character to be read will have the value <code>cbuf[off]</code>, the
165      * character after that will have the value <code>cbuf[off+1]</code>, and
166      * so forth.
167      *
168      * @param  cbuf  Character array
169      * @param  off   Offset of first character to push back
170      * @param  len   Number of characters to push back
171      *
172      * @exception  IOException  If there is insufficient room in the pushback
173      *                          buffer, or if some other I/O error occurs
174      */
unread(char cbuf[], int off, int len)175     public void unread(char cbuf[], int off, int len) throws IOException {
176         synchronized (lock) {
177             ensureOpen();
178             if (len > pos)
179                 throw new IOException("Pushback buffer overflow");
180             pos -= len;
181             System.arraycopy(cbuf, off, buf, pos, len);
182         }
183     }
184 
185     /**
186      * Pushes back an array of characters by copying it to the front of the
187      * pushback buffer.  After this method returns, the next character to be
188      * read will have the value <code>cbuf[0]</code>, the character after that
189      * will have the value <code>cbuf[1]</code>, and so forth.
190      *
191      * @param  cbuf  Character array to push back
192      *
193      * @exception  IOException  If there is insufficient room in the pushback
194      *                          buffer, or if some other I/O error occurs
195      */
unread(char cbuf[])196     public void unread(char cbuf[]) throws IOException {
197         unread(cbuf, 0, cbuf.length);
198     }
199 
200     /**
201      * Tells whether this stream is ready to be read.
202      *
203      * @exception  IOException  If an I/O error occurs
204      */
ready()205     public boolean ready() throws IOException {
206         synchronized (lock) {
207             ensureOpen();
208             return (pos < buf.length) || super.ready();
209         }
210     }
211 
212     /**
213      * Marks the present position in the stream. The <code>mark</code>
214      * for class <code>PushbackReader</code> always throws an exception.
215      *
216      * @exception  IOException  Always, since mark is not supported
217      */
mark(int readAheadLimit)218     public void mark(int readAheadLimit) throws IOException {
219         throw new IOException("mark/reset not supported");
220     }
221 
222     /**
223      * Resets the stream. The <code>reset</code> method of
224      * <code>PushbackReader</code> always throws an exception.
225      *
226      * @exception  IOException  Always, since reset is not supported
227      */
reset()228     public void reset() throws IOException {
229         throw new IOException("mark/reset not supported");
230     }
231 
232     /**
233      * Tells whether this stream supports the mark() operation, which it does
234      * not.
235      */
markSupported()236     public boolean markSupported() {
237         return false;
238     }
239 
240     /**
241      * Closes the stream and releases any system resources associated with
242      * it. Once the stream has been closed, further read(),
243      * unread(), ready(), or skip() invocations will throw an IOException.
244      * Closing a previously closed stream has no effect. This method will block
245      * while there is another thread blocking on the reader.
246      *
247      * @exception  IOException  If an I/O error occurs
248      */
close()249     public void close() throws IOException {
250         synchronized (lock) {
251             super.close();
252             buf = null;
253         }
254     }
255 
256     /**
257      * Skips characters.  This method will block until some characters are
258      * available, an I/O error occurs, or the end of the stream is reached.
259      *
260      * @param  n  The number of characters to skip
261      *
262      * @return    The number of characters actually skipped
263      *
264      * @exception  IllegalArgumentException  If <code>n</code> is negative.
265      * @exception  IOException  If an I/O error occurs
266      */
skip(long n)267     public long skip(long n) throws IOException {
268         if (n < 0L)
269             throw new IllegalArgumentException("skip value is negative");
270         synchronized (lock) {
271             ensureOpen();
272             int avail = buf.length - pos;
273             if (avail > 0) {
274                 if (n <= avail) {
275                     pos += n;
276                     return n;
277                 } else {
278                     pos = buf.length;
279                     n -= avail;
280                 }
281             }
282             return avail + super.skip(n);
283         }
284     }
285 }
286