📄 rewindableinputstream.java
字号:
//
// Borrowed from org.apache.xerces.impl.XMLEntityManager,
// slightly modified and commented out. -AK
//
// This class wraps the byte inputstreams we're presented with.
// We need it because java.io.InputStreams don't provide
// functionality to reread processed bytes, and they have a habit
// of reading more than one character when you call their read()
// methods. This means that, once we discover the true (declared)
// encoding of a document, we can neither backtrack to read the
// whole doc again nor start reading where we are with a new
// reader.
//
// This class allows rewinding an inputStream by allowing a mark
// to be set, and the stream reset to that position. <strong>The
// class assumes that it needs to read one character per
// invocation when it's read() method is inovked, but uses the
// underlying InputStream's read(char[], offset length) method--it
// won't buffer data read this way!</strong>
//
// @task TODO: How about implementing an ability to completely
// disable buffering performed by this stream?
// It is very unlikely that someone will read data from
// the stream byte by byte with <code>read()</code>
// method, but if they do, the whole content will
// be unconditionally stored in internal buffer, resulting
// in additional (and most probably vain) memory impact.
//
// @author Neil Graham, IBM
// @author Glenn Marcy, IBM
//
package org.geoserver.ows.util;
import java.io.IOException;
import java.io.InputStream;
public class RewindableInputStream extends InputStream {
/**
* Default buffer size before we've finished with the XMLDecl:
* I think the name should be left unchanged to give a hint for
* possible use of this class :)
*/
public static final int DEFAULT_XMLDECL_BUFFER_SIZE = 64;
/**
* Tells whether <code>read(byte[], int, int)</code> method
* is allowed to read multiple bytes beyond the internal buffer
* (<code>true</code>) or not (<code>true</code> is the default)
*/
protected boolean fMayReadChunks;
/**
* Source input stream we are wrapping with rewindable one.
*/
protected InputStream fInputStream;
/**
* Internal buffer of bytes already read from source input stream.
* Allows to access the same byte more than once.
*/
protected byte[] fData;
/**
* Position in the stream that to which stream pointer can be reset
* with <code>rewind</code> method invocation.
*/
protected int fStartOffset;
/**
* Position where the end of underlying stream was encountered. Potentially
* in <code>RewindableInputStream</code> instance stream's "end" could be
* reached more than once, so it is a good thing to know where original
* stream ended to avoid <code>IOExceptions</code>.
*/
protected int fEndOffset;
/**
* Offset of the next byte to be read from the stream relative to
* the beginning of the stream (and <code>fData</code> array as well)
*/
protected int fOffset;
/**
* Number of read bytes currently stored in <code>fData</code> buffer.
* Size of the buffer itself can be greater than this value, obviously.
*/
protected int fLength;
/**
* Offset of the "marked" position in the stream relative to its beginning.
*/
protected int fMark;
/**
* Creates new <code>RewindableInputStream</code> object with internal
* buffer of default size and default value of chunked reading flag (which
* is _currently_ <code>true</code>).
*
* @param is InputStream that needs basic reset/rewind functionality.
*/
public RewindableInputStream(InputStream is) {
this(is, true, DEFAULT_XMLDECL_BUFFER_SIZE);
}
/**
* Creates new RewindableInputStream with internal buffer of specified size
* and no chunk reading beyound the buffer limits allowed.
*
* @param is InputStream that needs some reset/rewind functionality.
*
* @param chunkedMode See the <code>RewindableInputStream(InputStream,
* boolean, int)</code> constructor description.
*/
public RewindableInputStream(InputStream is, boolean chunkedMode) {
this(is, chunkedMode, DEFAULT_XMLDECL_BUFFER_SIZE);
}
/**
* Primary constructor that allows to specify all parameters exlicitly
* affecting class work (initial size of the internal buffer and
* chunk read mode).
*
* @param is InputStream that needs some reset/rewind functionality.
*
* @param chunkedMode
*
* Initial value of <code>fMayReadChunks</code> flag which determines
* whether multiple bytes can be read from the underlying stream in
* single reading operation or not. This value can be changed using
* <code>setChunkedMode</code> (or its aliases). For specific
* purpose of inferring encoding/charset of XML document typical
* usage policy is to disable chunked reads while obtaining XML
* declaration and then enable it to speed up reading the rest of
* document.
*
* @param initialSize Initial size of the internal buffer array.
*/
public RewindableInputStream(InputStream is, boolean chunkedMode, int initialSize) {
if (0 >= initialSize) {
initialSize = DEFAULT_XMLDECL_BUFFER_SIZE;
}
fData = new byte[initialSize];
fInputStream = is;
fStartOffset = 0;
fMayReadChunks = chunkedMode;
fEndOffset = -1;
fOffset = 0;
fLength = 0;
fMark = 0;
}
/**
* Sets the position somewhere in the stream to which the stream pointer
* will be reset after <code>rewind</code> invocation. By default this
* position is the beginning of the stream.
*
* @param offset New value for "fStartOffset".
*/
public void setStartOffset(int offset) {
fStartOffset = offset;
}
/**
* Allows to change the behavior of the stream regarding chunked reading
* at runtime. If you allowed chunked reading and then read some data from
* the stream, you better forget about <code>reset</code>ting or
* <code>rewind</code>ing it after that.
*
* @param chunkedMode New value for <code>fMayReadChunks</code>.
*/
public void setChunkedMode(boolean chunkedMode) {
fMayReadChunks = chunkedMode;
}
/**
* More conscious alias for <code>setChunkedMode(true)</code>. While last
* method is a general purpose mutator, code may look a bit more clear if
* you use specialized methods to enable/disable chunk read mode.
*/
public void enableChunkedMode() {
fMayReadChunks = true;
}
/**
* More conscious alias for <code>setChunkedMode(false)</code>. While last
* method is a general purpose mutator, code may look a bit more clear if
* you use specialized methods to enable/disable chunk read mode.
*/
public void disableChunkedMode() {
fMayReadChunks = false;
}
/**
* Quickly reset stream pointer to the beginning of the stream or to
* position which offset was specified during the last
* <code>setStartOffset</code> call.
*/
public void rewind() {
fOffset = fStartOffset;
}
/**
* Reads next byte from this stream. This byte is either being read from
* underlying InputStream or taken from the internal buffer in case it was
* already read at some point before.
*
* @return Next byte of data or <code>-1</code> if end of stream is reached.
*
* @throws IOException in case of any I/O errors.
*/
public int read() throws IOException {
int b = 0;
// Byte to be read is already in out buffer, simply returning it
if (fOffset < fLength) {
return fData[fOffset++] & 0xff;
}
/*
* End of the stream is reached.
* I also believe that in certain cases fOffset can point to the
* position after the end of stream, for example, after invalid
* `setStartOffset()` call followed by `rewind()`.
* This situation is not handled currently.
*/
if (fOffset == fEndOffset) {
return -1;
}
/*
* Ok, we should actually read data from underlying stream, but
* first it will be good to check if buffer array should be
* expanded. Each time buffer size is doubled.
*/
if (fOffset == fData.length) {
byte[] newData = new byte[fOffset << 1];
System.arraycopy(fData, 0, newData, 0, fOffset);
fData = newData;
}
// Reading byte from the underlying stream, storing it in buffer and
// then returning it.
b = fInputStream.read();
if (b == -1) {
fEndOffset = fOffset;
return -1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -