1 /*
2  * Copyright (c) 1996, 2013, 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       JDK1.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      */
read(char cbuf[], int off, int len)106     public int read(char cbuf[], int off, int len) throws IOException {
107         synchronized (lock) {
108             ensureOpen();
109             try {
110                 if (len <= 0) {
111                     if (len < 0) {
112                         throw new IndexOutOfBoundsException();
113                     } else if ((off < 0) || (off > cbuf.length)) {
114                         throw new IndexOutOfBoundsException();
115                     }
116                     return 0;
117                 }
118                 int avail = buf.length - pos;
119                 if (avail > 0) {
120                     if (len < avail)
121                         avail = len;
122                     System.arraycopy(buf, pos, cbuf, off, avail);
123                     pos += avail;
124                     off += avail;
125                     len -= avail;
126                 }
127                 if (len > 0) {
128                     len = super.read(cbuf, off, len);
129                     if (len == -1) {
130                         return (avail == 0) ? -1 : avail;
131                     }
132                     return avail + len;
133                 }
134                 return avail;
135             } catch (ArrayIndexOutOfBoundsException e) {
136                 throw new IndexOutOfBoundsException();
137             }
138         }
139     }
140 
141     /**
142      * Pushes back a single character by copying it to the front of the
143      * pushback buffer. After this method returns, the next character to be read
144      * will have the value <code>(char)c</code>.
145      *
146      * @param  c  The int value representing a character to be pushed back
147      *
148      * @exception  IOException  If the pushback buffer is full,
149      *                          or if some other I/O error occurs
150      */
unread(int c)151     public void unread(int c) throws IOException {
152         synchronized (lock) {
153             ensureOpen();
154             if (pos == 0)
155                 throw new IOException("Pushback buffer overflow");
156             buf[--pos] = (char) c;
157         }
158     }
159 
160     /**
161      * Pushes back a portion of an array of characters by copying it to the
162      * front of the pushback buffer.  After this method returns, the next
163      * character to be read will have the value <code>cbuf[off]</code>, the
164      * character after that will have the value <code>cbuf[off+1]</code>, and
165      * so forth.
166      *
167      * @param  cbuf  Character array
168      * @param  off   Offset of first character to push back
169      * @param  len   Number of characters to push back
170      *
171      * @exception  IOException  If there is insufficient room in the pushback
172      *                          buffer, or if some other I/O error occurs
173      */
unread(char cbuf[], int off, int len)174     public void unread(char cbuf[], int off, int len) throws IOException {
175         synchronized (lock) {
176             ensureOpen();
177             if (len > pos)
178                 throw new IOException("Pushback buffer overflow");
179             pos -= len;
180             System.arraycopy(cbuf, off, buf, pos, len);
181         }
182     }
183 
184     /**
185      * Pushes back an array of characters by copying it to the front of the
186      * pushback buffer.  After this method returns, the next character to be
187      * read will have the value <code>cbuf[0]</code>, the character after that
188      * will have the value <code>cbuf[1]</code>, and so forth.
189      *
190      * @param  cbuf  Character array to push back
191      *
192      * @exception  IOException  If there is insufficient room in the pushback
193      *                          buffer, or if some other I/O error occurs
194      */
unread(char cbuf[])195     public void unread(char cbuf[]) throws IOException {
196         unread(cbuf, 0, cbuf.length);
197     }
198 
199     /**
200      * Tells whether this stream is ready to be read.
201      *
202      * @exception  IOException  If an I/O error occurs
203      */
ready()204     public boolean ready() throws IOException {
205         synchronized (lock) {
206             ensureOpen();
207             return (pos < buf.length) || super.ready();
208         }
209     }
210 
211     /**
212      * Marks the present position in the stream. The <code>mark</code>
213      * for class <code>PushbackReader</code> always throws an exception.
214      *
215      * @exception  IOException  Always, since mark is not supported
216      */
mark(int readAheadLimit)217     public void mark(int readAheadLimit) throws IOException {
218         throw new IOException("mark/reset not supported");
219     }
220 
221     /**
222      * Resets the stream. The <code>reset</code> method of
223      * <code>PushbackReader</code> always throws an exception.
224      *
225      * @exception  IOException  Always, since reset is not supported
226      */
reset()227     public void reset() throws IOException {
228         throw new IOException("mark/reset not supported");
229     }
230 
231     /**
232      * Tells whether this stream supports the mark() operation, which it does
233      * not.
234      */
markSupported()235     public boolean markSupported() {
236         return false;
237     }
238 
239     /**
240      * Closes the stream and releases any system resources associated with
241      * it. Once the stream has been closed, further read(),
242      * unread(), ready(), or skip() invocations will throw an IOException.
243      * Closing a previously closed stream has no effect.
244      *
245      * @exception  IOException  If an I/O error occurs
246      */
close()247     public void close() throws IOException {
248         super.close();
249         buf = null;
250     }
251 
252     /**
253      * Skips characters.  This method will block until some characters are
254      * available, an I/O error occurs, or the end of the stream is reached.
255      *
256      * @param  n  The number of characters to skip
257      *
258      * @return    The number of characters actually skipped
259      *
260      * @exception  IllegalArgumentException  If <code>n</code> is negative.
261      * @exception  IOException  If an I/O error occurs
262      */
skip(long n)263     public long skip(long n) throws IOException {
264         if (n < 0L)
265             throw new IllegalArgumentException("skip value is negative");
266         synchronized (lock) {
267             ensureOpen();
268             int avail = buf.length - pos;
269             if (avail > 0) {
270                 if (n <= avail) {
271                     pos += n;
272                     return n;
273                 } else {
274                     pos = buf.length;
275                     n -= avail;
276                 }
277             }
278             return avail + super.skip(n);
279         }
280     }
281 }
282