001: /*
002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.io;
027:
028: /**
029: * A character-stream reader that allows characters to be pushed back into the
030: * stream.
031: *
032: * @version 1.27, 07/05/05
033: * @author Mark Reinhold
034: * @since JDK1.1
035: */
036:
037: public class PushbackReader extends FilterReader {
038:
039: /** Pushback buffer */
040: private char[] buf;
041:
042: /** Current position in buffer */
043: private int pos;
044:
045: /**
046: * Creates a new pushback reader with a pushback buffer of the given size.
047: *
048: * @param in The reader from which characters will be read
049: * @param size The size of the pushback buffer
050: * @exception IllegalArgumentException if size is <= 0
051: */
052: public PushbackReader(Reader in, int size) {
053: super (in);
054: if (size <= 0) {
055: throw new IllegalArgumentException("size <= 0");
056: }
057: this .buf = new char[size];
058: this .pos = size;
059: }
060:
061: /**
062: * Creates a new pushback reader with a one-character pushback buffer.
063: *
064: * @param in The reader from which characters will be read
065: */
066: public PushbackReader(Reader in) {
067: this (in, 1);
068: }
069:
070: /** Checks to make sure that the stream has not been closed. */
071: private void ensureOpen() throws IOException {
072: if (buf == null)
073: throw new IOException("Stream closed");
074: }
075:
076: /**
077: * Reads a single character.
078: *
079: * @return The character read, or -1 if the end of the stream has been
080: * reached
081: *
082: * @exception IOException If an I/O error occurs
083: */
084: public int read() throws IOException {
085: synchronized (lock) {
086: ensureOpen();
087: if (pos < buf.length)
088: return buf[pos++];
089: else
090: return super .read();
091: }
092: }
093:
094: /**
095: * Reads characters into a portion of an array.
096: *
097: * @param cbuf Destination buffer
098: * @param off Offset at which to start writing characters
099: * @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: */
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: */
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: */
174: public void unread(char cbuf[], int off, int len)
175: 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: */
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: */
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: */
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: */
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: */
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.
245: *
246: * @exception IOException If an I/O error occurs
247: */
248: public void close() throws IOException {
249: super .close();
250: buf = null;
251: }
252:
253: /**
254: * Skips characters. This method will block until some characters are
255: * available, an I/O error occurs, or the end of the stream is reached.
256: *
257: * @param n The number of characters to skip
258: *
259: * @return The number of characters actually skipped
260: *
261: * @exception IllegalArgumentException If <code>n</code> is negative.
262: * @exception IOException If an I/O error occurs
263: */
264: public long skip(long n) throws IOException {
265: if (n < 0L)
266: throw new IllegalArgumentException("skip value is negative");
267: synchronized (lock) {
268: ensureOpen();
269: int avail = buf.length - pos;
270: if (avail > 0) {
271: if (n <= avail) {
272: pos += n;
273: return n;
274: } else {
275: pos = buf.length;
276: n -= avail;
277: }
278: }
279: return avail + super.skip(n);
280: }
281: }
282: }
|