001: /*
002: * @(#)PushbackReader.java 1.21 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.io;
029:
030: /**
031: * A character-stream reader that allows characters to be pushed back into the
032: * stream.
033: *
034: * @version 1.14, 00/02/02
035: * @author Mark Reinhold
036: * @since JDK1.1
037: */
038:
039: public class PushbackReader extends FilterReader {
040:
041: /** Pushback buffer */
042: private char[] buf;
043:
044: /** Current position in buffer */
045: private int pos;
046:
047: /**
048: * Create a new pushback reader with a pushback buffer of the given size.
049: *
050: * @param in The reader from which characters will be read
051: * @param size The size of the pushback buffer
052: * @exception IllegalArgumentException if size is <= 0
053: */
054: public PushbackReader(Reader in, int size) {
055: super (in);
056: if (size <= 0) {
057: throw new IllegalArgumentException("size <= 0");
058: }
059: this .buf = new char[size];
060: this .pos = size;
061: }
062:
063: /**
064: * Create a new pushback reader with a one-character pushback buffer.
065: *
066: * @param in The reader from which characters will be read
067: */
068: public PushbackReader(Reader in) {
069: this (in, 1);
070: }
071:
072: /** Check to make sure that the stream has not been closed. */
073: private void ensureOpen() throws IOException {
074: if (buf == null)
075: throw new IOException("Stream closed");
076: }
077:
078: /**
079: * Read a single character.
080: *
081: * @return The character read, or -1 if the end of the stream has been
082: * reached
083: *
084: * @exception IOException If an I/O error occurs
085: */
086: public int read() throws IOException {
087: synchronized (lock) {
088: ensureOpen();
089: if (pos < buf.length)
090: return buf[pos++];
091: else
092: return super .read();
093: }
094: }
095:
096: /**
097: * Read characters into a portion of an array.
098: *
099: * @param cbuf Destination buffer
100: * @param off Offset at which to start writing characters
101: * @param len Maximum number of characters to read
102: *
103: * @return The number of characters read, or -1 if the end of the
104: * stream has been reached
105: *
106: * @exception IOException If an I/O error occurs
107: */
108: public int read(char cbuf[], int off, int len) throws IOException {
109: synchronized (lock) {
110: ensureOpen();
111: try {
112: if (len <= 0) {
113: if (len < 0) {
114: throw new IndexOutOfBoundsException();
115: } else if ((off < 0) || (off > cbuf.length)) {
116: throw new IndexOutOfBoundsException();
117: }
118: return 0;
119: }
120: int avail = buf.length - pos;
121: if (avail > 0) {
122: if (len < avail)
123: avail = len;
124: System.arraycopy(buf, pos, cbuf, off, avail);
125: pos += avail;
126: off += avail;
127: len -= avail;
128: }
129: if (len > 0) {
130: len = super .read(cbuf, off, len);
131: if (len == -1) {
132: return (avail == 0) ? -1 : avail;
133: }
134: return avail + len;
135: }
136: return avail;
137: } catch (ArrayIndexOutOfBoundsException e) {
138: throw new IndexOutOfBoundsException();
139: }
140: }
141: }
142:
143: /**
144: * Push back a single character.
145: *
146: * @param c The character to push 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: * Push 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: * Push 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: * Tell 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: * Mark 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: * Reset 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: * Tell whether this stream supports the mark() operation, which it does
234: * not.
235: */
236: public boolean markSupported() {
237: return false;
238: }
239:
240: /**
241: * Close the stream.
242: *
243: * @exception IOException If an I/O error occurs
244: */
245: public void close() throws IOException {
246: super .close();
247: buf = null;
248: }
249:
250: /**
251: * Skip characters. This method will block until some characters are
252: * available, an I/O error occurs, or the end of the stream is reached.
253: *
254: * @param n The number of characters to skip
255: *
256: * @return The number of characters actually skipped
257: *
258: * @exception IllegalArgumentException If <code>n</code> is negative.
259: * @exception IOException If an I/O error occurs
260: */
261: public long skip(long n) throws IOException {
262: if (n < 0L)
263: throw new IllegalArgumentException("skip value is negative");
264: synchronized (lock) {
265: ensureOpen();
266: int avail = buf.length - pos;
267: if (avail > 0) {
268: if (n <= avail) {
269: pos += n;
270: return n;
271: } else {
272: pos = buf.length;
273: n -= avail;
274: }
275: }
276: return avail + super.skip(n);
277: }
278: }
279:
280: }
|