001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import org.apache.harmony.luni.util.Msg;
021:
022: /**
023: * PushbackReader is a filter class which allows chars read to be pushed back
024: * into the stream so that they can be reread. Parsers may find this useful.
025: * There is a progammable limit to the number of chars which may be pushed back.
026: * If the buffer of pushed back chars is empty, chars are read from the source
027: * reader.
028: *
029: */
030: public class PushbackReader extends FilterReader {
031: /**
032: * The <code>char</code> array containing the chars to read.
033: */
034: char[] buf;
035:
036: /**
037: * The current position within the char array <code>buf</code>. A value
038: * equal to buf.length indicates no chars available. A value of 0 indicates
039: * the buffer is full.
040: */
041: int pos;
042:
043: /**
044: * Constructs a new PushbackReader on the Reader <code>in</code>. The
045: * size of the pushback buffer is set to the default, or 1 character.
046: *
047: * @param in
048: * the Reader to allow pushback operations on.
049: *
050: */
051: public PushbackReader(Reader in) {
052: super (in);
053: buf = new char[1];
054: pos = 1;
055: }
056:
057: /**
058: * Constructs a new PushbackReader on the Reader <code>in</code>. The
059: * size of the pushback buffer is set to <code>size</code> characters.
060: *
061: * @param in
062: * the Reader to allow pushback operations on.
063: * @param size
064: * the size of the pushback buffer (<code>size>=0</code>) in
065: * characters.
066: * @throws IllegalArgumentException
067: * if size <= 0
068: */
069: public PushbackReader(Reader in, int size) {
070: super (in);
071: if (size <= 0) {
072: throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
073: }
074: buf = new char[size];
075: pos = size;
076: }
077:
078: /**
079: * Close this PushbackReader. This implementation closes this reader,
080: * releases the buffer used to pushback characters, and closes the target
081: * reader.
082: *
083: * @throws IOException
084: * If an error occurs attempting to close this Reader.
085: */
086: @Override
087: public void close() throws IOException {
088: synchronized (lock) {
089: buf = null;
090: in.close();
091: }
092: }
093:
094: /**
095: * Mark this PushbackReader. Since mark is not supported, this method will
096: * always throw IOException.
097: *
098: * @param readAheadLimit
099: * ignored, this method always throws IOException.
100: *
101: * @throws IOException
102: * Since mark is not supported byt PushbackReader.
103: */
104: @Override
105: public void mark(int readAheadLimit) throws IOException {
106: throw new IOException(Msg.getString("K007f")); //$NON-NLS-1$
107: }
108:
109: /**
110: * Answers a boolean indicating whether or not this PushbackReader supports
111: * mark() and reset(). This implementation always answers false since
112: * PushbackReaders do not support mark/reset.
113: *
114: * @return boolean indicates whether or not mark() and reset() are
115: * supported.
116: */
117: @Override
118: public boolean markSupported() {
119: return false;
120: }
121:
122: /**
123: * Reads a single character from this PushbackReader and returns the result
124: * as an int. The 2 lowest-order bytes are returned or -1 of the end of
125: * stream was encountered. If the pushback buffer does not contain any
126: * available chars then a char from the target input reader is returned.
127: *
128: * @return int The char read or -1 if end of stream.
129: *
130: * @throws IOException
131: * If an IOException occurs.
132: */
133: @Override
134: public int read() throws IOException {
135: synchronized (lock) {
136: if (buf == null) {
137: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
138: }
139: /* Is there a pushback character available? */
140: if (pos < buf.length) {
141: return buf[pos++];
142: }
143: /**
144: * Assume read() in the InputStream will return 2 lowest-order bytes
145: * or -1 if end of stream.
146: */
147: return in.read();
148: }
149: }
150:
151: /**
152: * Reads at most <code>count</code> chars from this PushbackReader and
153: * stores them in char array <code>buffer</code> starting at
154: * <code>offset</code>. Answer the number of chars actually read or -1 if
155: * no chars were read and end of stream was encountered. This implementation
156: * reads chars from the pushback buffer first, then the target stream if
157: * more chars are required to satisfy <code>count</code>.
158: *
159: * @param buffer
160: * the char array in which to store the read chars.
161: * @param offset
162: * the offset in <code>buffer</code> to store the read chars.
163: * @param count
164: * the maximum number of chars to store in <code>buffer</code>.
165: * @return the number of chars actually read or -1 if end of stream.
166: *
167: * @throws IOException
168: * If an IOException occurs.
169: */
170: @Override
171: public int read(char[] buffer, int offset, int count)
172: throws IOException {
173: synchronized (lock) {
174: if (null == buf) {
175: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
176: }
177: // avoid int overflow
178: if (offset < 0 || count < 0
179: || offset > buffer.length - count) {
180: throw new IndexOutOfBoundsException();
181: }
182:
183: int copiedChars = 0;
184: int copyLength = 0;
185: int newOffset = offset;
186: /* Are there pushback chars available? */
187: if (pos < buf.length) {
188: copyLength = (buf.length - pos >= count) ? count
189: : buf.length - pos;
190: System.arraycopy(buf, pos, buffer, newOffset,
191: copyLength);
192: newOffset += copyLength;
193: copiedChars += copyLength;
194: /* Use up the chars in the local buffer */
195: pos += copyLength;
196: }
197: /* Have we copied enough? */
198: if (copyLength == count) {
199: return count;
200: }
201: int inCopied = in.read(buffer, newOffset, count
202: - copiedChars);
203: if (inCopied > 0) {
204: return inCopied + copiedChars;
205: }
206: if (copiedChars == 0) {
207: return inCopied;
208: }
209: return copiedChars;
210: }
211: }
212:
213: /**
214: * Answers a <code>boolean</code> indicating whether or not this
215: * PushbackReader is ready to be read without blocking. If the result is
216: * <code>true</code>, the next <code>read()</code> will not block. If
217: * the result is <code>false</code> this Reader may or may not block when
218: * <code>read()</code> is sent.
219: *
220: * @return boolean <code>true</code> if the receiver will not block when
221: * <code>read()</code> is called, <code>false</code> if unknown
222: * or blocking will occur.
223: *
224: * @throws IOException
225: * If the Reader is already closed or some other IO error
226: * occurs.
227: */
228: @Override
229: public boolean ready() throws IOException {
230: synchronized (lock) {
231: if (buf == null) {
232: throw new IOException(Msg.getString("K0080")); //$NON-NLS-1$
233: }
234: return (buf.length - pos > 0 || in.ready());
235: }
236: }
237:
238: /**
239: * Resets this PushbackReader. Since mark is not supported, always throw
240: * IOException.
241: *
242: * @throws IOException
243: * Since mark is not supported.
244: */
245: @Override
246: public void reset() throws IOException {
247: throw new IOException(Msg.getString("K007f")); //$NON-NLS-1$
248: }
249:
250: /**
251: * Push back all the chars in <code>buffer</code>. The chars are pushed
252: * so that they would be read back buffer[0], buffer[1], etc. If the push
253: * back buffer cannot handle the entire contents of <code>buffer</code>,
254: * an IOException will be thrown. Some of the buffer may already be in the
255: * buffer after the exception is thrown.
256: *
257: * @param buffer
258: * the char array containing chars to push back into the reader.
259: *
260: * @throws IOException
261: * If the pushback buffer becomes, or is, full.
262: */
263: public void unread(char[] buffer) throws IOException {
264: unread(buffer, 0, buffer.length);
265: }
266:
267: /**
268: * Push back <code>count</code> number of chars in <code>buffer</code>
269: * starting at <code>offset</code>. The chars are pushed so that they
270: * would be read back buffer[offset], buffer[offset+1], etc. If the push
271: * back buffer cannot handle the chars copied from <code>buffer</code>,
272: * an IOException will be thrown. Some of the chars may already be in the
273: * buffer after the exception is thrown.
274: *
275: * @param buffer
276: * the char array containing chars to push back into the reader.
277: * @param offset
278: * the location to start taking chars to push back.
279: * @param count
280: * the number of chars to push back.
281: *
282: * @throws IOException
283: * If the pushback buffer becomes, or is, full.
284: */
285: public void unread(char[] buffer, int offset, int count)
286: throws IOException {
287: synchronized (lock) {
288: if (buf == null) {
289: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
290: }
291: if (count > pos) {
292: // Pushback buffer full
293: throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
294: }
295: if (buffer == null) {
296: throw new NullPointerException();
297: }
298: // avoid int overflow
299: if (offset < 0 || count < 0
300: || offset > buffer.length - count) {
301: throw new ArrayIndexOutOfBoundsException();
302: }
303: for (int i = offset + count - 1; i >= offset; i--) {
304: unread(buffer[i]);
305: }
306: }
307: }
308:
309: /**
310: * Push back one <code>char</code>. Takes the char <code>oneChar</code>
311: * and puts in in the local buffer of chars to read back before accessing
312: * the target input stream.
313: *
314: * @param oneChar
315: * the char to push back into the stream.
316: *
317: * @throws IOException
318: * If the pushback buffer is already full.
319: */
320: public void unread(int oneChar) throws IOException {
321: synchronized (lock) {
322: if (buf == null) {
323: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
324: }
325: if (pos == 0) {
326: throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
327: }
328: buf[--pos] = (char) oneChar;
329: }
330: }
331:
332: /**
333: * Skips <code>count</code> number of characters in this Reader.
334: * Subsequent <code>read()</code>'s will not return these characters
335: * unless <code>reset()</code> is used.
336: *
337: * @param count
338: * the maximum number of characters to skip.
339: * @return the number of characters actually skipped.
340: *
341: * @throws IOException
342: * If the Reader is already closed or some other IO error
343: * occurs.
344: * @throws IllegalArgumentException
345: * If count is negative.
346: */
347: @Override
348: public long skip(long count) throws IOException {
349: if (count < 0) {
350: throw new IllegalArgumentException();
351: }
352: synchronized (lock) {
353: if (buf == null) {
354: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
355: }
356: if (count == 0) {
357: return 0;
358: }
359: long inSkipped;
360: int availableFromBuffer = buf.length - pos;
361: if (availableFromBuffer > 0) {
362: long requiredFromIn = count - availableFromBuffer;
363: if (requiredFromIn <= 0) {
364: pos += count;
365: return count;
366: }
367: pos += availableFromBuffer;
368: inSkipped = in.skip(requiredFromIn);
369: } else {
370: inSkipped = in.skip(count);
371: }
372: return inSkipped + availableFromBuffer;
373: }
374: }
375: }
|