mirror of
https://github.com/moparisthebest/mailiverse
synced 2024-11-29 19:52:15 -05:00
335 lines
12 KiB
Java
335 lines
12 KiB
Java
|
/*
|
||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||
|
* contributor license agreements. See the NOTICE file distributed with
|
||
|
* this work for additional information regarding copyright ownership.
|
||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||
|
* (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package java.io;
|
||
|
|
||
|
/**
|
||
|
* PushbackInputStream is a filter class which allows bytes read to be pushed
|
||
|
* back into the stream so that they can be reread. Parsers may find this
|
||
|
* useful. There is a progammable limit to the number of bytes which may be
|
||
|
* pushed back. If the buffer of pushed back bytes is empty, bytes are read from
|
||
|
* the source input stream.
|
||
|
*/
|
||
|
public class PushbackInputStream extends FilterInputStream {
|
||
|
/**
|
||
|
* The <code>byte</code> array containing the bytes to read.
|
||
|
*/
|
||
|
protected byte[] buf;
|
||
|
|
||
|
/**
|
||
|
* The current position within the byte array <code>buf</code>. A value
|
||
|
* equal to buf.length indicates no bytes available. A value of 0 indicates
|
||
|
* the buffer is full.
|
||
|
*/
|
||
|
protected int pos;
|
||
|
|
||
|
/**
|
||
|
* Constructs a new PushbackInputStream on the InputStream <code>in</code>.
|
||
|
* The size of the pushback buffer is set to the default, or 1 byte.
|
||
|
*
|
||
|
* @param in
|
||
|
* the InputStream to allow pushback operations on.
|
||
|
*/
|
||
|
public PushbackInputStream(InputStream in) {
|
||
|
super(in);
|
||
|
buf = (in == null) ? null : new byte[1];
|
||
|
pos = 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a new PushbackInputStream on the InputStream <code>in</code>.
|
||
|
* The size of the pushback buffer is set to <code>size</code>.
|
||
|
*
|
||
|
* @param in
|
||
|
* the InputStream to allow pushback operations on.
|
||
|
* @param size
|
||
|
* the size of the pushback buffer (<code>size>=0</code>).
|
||
|
*/
|
||
|
public PushbackInputStream(InputStream in, int size) {
|
||
|
super(in);
|
||
|
if (size <= 0) {
|
||
|
throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
|
||
|
}
|
||
|
buf = (in == null) ? null : new byte[size];
|
||
|
pos = size;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Answers a int representing then number of bytes that are available before
|
||
|
* this PushbackInputStream will block. This method returns the number of
|
||
|
* bytes available in the pushback buffer plus those available in the target
|
||
|
* stream.
|
||
|
*
|
||
|
* @return int the number of bytes available before blocking.
|
||
|
*
|
||
|
* @throws java.io.IOException
|
||
|
* If an error occurs in this stream.
|
||
|
*/
|
||
|
@Override
|
||
|
public int available() throws IOException {
|
||
|
if (buf == null) {
|
||
|
throw new IOException();
|
||
|
}
|
||
|
return buf.length - pos + in.available();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Close this PushbackInputStream. This implementation closes the target
|
||
|
* stream.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If an error occurs attempting to close this stream.
|
||
|
*/
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
if (in != null) {
|
||
|
in.close();
|
||
|
in = null;
|
||
|
buf = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Answers a boolean indicating whether or not this PushbackInputStream
|
||
|
* supports mark() and reset(). This implementation always answers false
|
||
|
* since PushbackInputStreams do not support mark/reset.
|
||
|
*
|
||
|
* @return boolean indicates whether or not mark() and reset() are
|
||
|
* supported.
|
||
|
*/
|
||
|
@Override
|
||
|
public boolean markSupported() {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads a single byte from this PushbackInputStream and returns the result
|
||
|
* as an int. The low-order byte is returned or -1 of the end of stream was
|
||
|
* encountered. If the pushback buffer does not contain any available bytes
|
||
|
* then a byte from the target input stream is returned.
|
||
|
*
|
||
|
* @return int The byte read or -1 if end of stream.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If an IOException occurs.
|
||
|
*/
|
||
|
@Override
|
||
|
public int read() throws IOException {
|
||
|
if (buf == null) {
|
||
|
throw new IOException();
|
||
|
}
|
||
|
// Is there a pushback byte available?
|
||
|
if (pos < buf.length) {
|
||
|
return (buf[pos++] & 0xFF);
|
||
|
}
|
||
|
// Assume read() in the InputStream will return low-order byte or -1
|
||
|
// if end of stream.
|
||
|
return in.read();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads at most <code>length</code> bytes from this PushbackInputStream
|
||
|
* and stores them in byte array <code>buffer</code> starting at
|
||
|
* <code>offset</code>. Answer the number of bytes actually read or -1 if
|
||
|
* no bytes were read and end of stream was encountered. This implementation
|
||
|
* reads bytes from the pushback buffer first, then the target stream if
|
||
|
* more bytes are required to satisfy <code>count</code>.
|
||
|
*
|
||
|
* @param buffer
|
||
|
* the byte array in which to store the read bytes.
|
||
|
* @param offset
|
||
|
* the offset in <code>buffer</code> to store the read bytes.
|
||
|
* @param length
|
||
|
* the maximum number of bytes to store in <code>buffer</code>.
|
||
|
* @return the number of bytes actually read or -1 if end of stream.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If an IOException occurs.
|
||
|
*/
|
||
|
@Override
|
||
|
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||
|
if (buf == null) {
|
||
|
throw new IOException();
|
||
|
}
|
||
|
if (buffer == null) {
|
||
|
throw new NullPointerException();
|
||
|
}
|
||
|
// avoid int overflow
|
||
|
if (offset < 0 || offset > buffer.length || length < 0
|
||
|
|| length > buffer.length - offset) {
|
||
|
throw new ArrayIndexOutOfBoundsException();
|
||
|
}
|
||
|
|
||
|
int copiedBytes = 0, copyLength = 0, newOffset = offset;
|
||
|
// Are there pushback bytes available?
|
||
|
if (pos < buf.length) {
|
||
|
copyLength = (buf.length - pos >= length) ? length : buf.length
|
||
|
- pos;
|
||
|
System.arraycopy(buf, pos, buffer, newOffset, copyLength);
|
||
|
newOffset += copyLength;
|
||
|
copiedBytes += copyLength;
|
||
|
// Use up the bytes in the local buffer
|
||
|
pos += copyLength;
|
||
|
}
|
||
|
// Have we copied enough?
|
||
|
if (copyLength == length) {
|
||
|
return length;
|
||
|
}
|
||
|
int inCopied = in.read(buffer, newOffset, length - copiedBytes);
|
||
|
if (inCopied > 0) {
|
||
|
return inCopied + copiedBytes;
|
||
|
}
|
||
|
if (copiedBytes == 0) {
|
||
|
return inCopied;
|
||
|
}
|
||
|
return copiedBytes;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Skips <code>count</code> number of bytes in this PushbackInputStream.
|
||
|
* Subsequent <code>read()</code>'s will not return these bytes unless
|
||
|
* <code>reset()</code> is used. This implementation skips
|
||
|
* <code>count</code> number of bytes in the buffer and/or the target
|
||
|
* stream.
|
||
|
*
|
||
|
* @param count
|
||
|
* the number of bytes to skip.
|
||
|
* @return the number of bytes actually skipped.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If the stream is already closed or another IOException
|
||
|
* occurs.
|
||
|
*/
|
||
|
@Override
|
||
|
public long skip(long count) throws IOException {
|
||
|
if (in == null) {
|
||
|
throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
|
||
|
}
|
||
|
if (count <= 0) {
|
||
|
return 0;
|
||
|
}
|
||
|
int numSkipped = 0;
|
||
|
if (pos < buf.length) {
|
||
|
numSkipped += (count < buf.length - pos) ? count : buf.length - pos;
|
||
|
pos += numSkipped;
|
||
|
}
|
||
|
if (numSkipped < count) {
|
||
|
numSkipped += in.skip(count - numSkipped);
|
||
|
}
|
||
|
return numSkipped;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Push back all the bytes in <code>buffer</code>. The bytes are pushed
|
||
|
* so that they would be read back buffer[0], buffer[1], etc. If the push
|
||
|
* back buffer cannot handle the entire contents of <code>buffer</code>,
|
||
|
* an IOException will be thrown. Some of the buffer may already be in the
|
||
|
* buffer after the exception is thrown.
|
||
|
*
|
||
|
* @param buffer
|
||
|
* the byte array containing bytes to push back into the stream.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If the pushback buffer becomes, or is, full.
|
||
|
*/
|
||
|
public void unread(byte[] buffer) throws IOException {
|
||
|
unread(buffer, 0, buffer.length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Push back <code>length</code> number of bytes in <code>buffer</code>
|
||
|
* starting at <code>offset</code>. The bytes are pushed so that they
|
||
|
* would be read back buffer[offset], buffer[offset+1], etc. If the push
|
||
|
* back buffer cannot handle the bytes copied from <code>buffer</code>,
|
||
|
* an IOException will be thrown. Some of the bytes may already be in the
|
||
|
* buffer after the exception is thrown.
|
||
|
*
|
||
|
* @param buffer
|
||
|
* the byte array containing bytes to push back into the stream.
|
||
|
* @param offset
|
||
|
* the location to start taking bytes to push back.
|
||
|
* @param length
|
||
|
* the number of bytes to push back.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If the pushback buffer becomes, or is, full.
|
||
|
*/
|
||
|
public void unread(byte[] buffer, int offset, int length)
|
||
|
throws IOException {
|
||
|
if (length > pos) {
|
||
|
// Pushback buffer full
|
||
|
throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
|
||
|
}
|
||
|
// avoid int overflow
|
||
|
if (offset < 0 || offset > buffer.length || length < 0
|
||
|
|| length > buffer.length - offset) {
|
||
|
throw new ArrayIndexOutOfBoundsException();
|
||
|
}
|
||
|
|
||
|
for (int i = offset + length - 1; i >= offset; i--) {
|
||
|
unread(buffer[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Push back one <code>byte</code>. Takes the byte <code>oneByte</code>
|
||
|
* and puts in in the local buffer of bytes to read back before accessing
|
||
|
* the target input stream.
|
||
|
*
|
||
|
* @param oneByte
|
||
|
* the byte to push back into the stream.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
* If the pushback buffer is already full.
|
||
|
*/
|
||
|
public void unread(int oneByte) throws IOException {
|
||
|
if (buf == null) {
|
||
|
throw new IOException();
|
||
|
}
|
||
|
if (pos == 0) {
|
||
|
throw new IOException(Msg.getString("K007e")); //$NON-NLS-1$
|
||
|
}
|
||
|
buf[--pos] = (byte) oneByte;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Make a mark of the current position in the stream but the mark method
|
||
|
* does nothing.
|
||
|
*
|
||
|
* @param readlimit
|
||
|
* the maximum number of bytes that are able to be read before
|
||
|
* the mark becomes invalid
|
||
|
* @override the method mark in FilterInputStream
|
||
|
*/
|
||
|
@Override
|
||
|
public void mark(int readlimit) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset current position to the mark made previously int the stream, but
|
||
|
* the reset method will throw IOException and do nothing else if called.
|
||
|
*
|
||
|
* @override the method reset in FilterInputStream
|
||
|
* @throws IOException
|
||
|
* If the method is called
|
||
|
*/
|
||
|
@Override
|
||
|
public void reset() throws IOException {
|
||
|
throw new IOException();
|
||
|
}
|
||
|
}
|