/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.imageio.stream;
import java.io.
DataInputStream;
import java.io.
EOFException;
import java.io.
IOException;
import java.nio.
ByteOrder;
import java.util.
Stack;
import javax.imageio.
IIOException;
/**
* An abstract class implementing the <code>ImageInputStream</code> interface.
* This class is designed to reduce the number of methods that must
* be implemented by subclasses.
*
* <p> In particular, this class handles most or all of the details of
* byte order interpretation, buffering, mark/reset, discarding,
* closing, and disposing.
*/
public abstract class
ImageInputStreamImpl implements
ImageInputStream {
private
Stack markByteStack = new
Stack();
private
Stack markBitStack = new
Stack();
private boolean
isClosed = false;
// Length of the buffer used for readFully(type[], int, int)
private static final int
BYTE_BUF_LENGTH = 8192;
/**
* Byte buffer used for readFully(type[], int, int). Note that this
* array is also used for bulk reads in readShort(), readInt(), etc, so
* it should be large enough to hold a primitive value (i.e. >= 8 bytes).
* Also note that this array is package protected, so that it can be
* used by ImageOutputStreamImpl in a similar manner.
*/
byte[]
byteBuf = new byte[
BYTE_BUF_LENGTH];
/**
* The byte order of the stream as an instance of the enumeration
* class <code>java.nio.ByteOrder</code>, where
* <code>ByteOrder.BIG_ENDIAN</code> indicates network byte order
* and <code>ByteOrder.LITTLE_ENDIAN</code> indicates the reverse
* order. By default, the value is
* <code>ByteOrder.BIG_ENDIAN</code>.
*/
protected
ByteOrder byteOrder =
ByteOrder.
BIG_ENDIAN;
/**
* The current read position within the stream. Subclasses are
* responsible for keeping this value current from any method they
* override that alters the read position.
*/
protected long
streamPos;
/**
* The current bit offset within the stream. Subclasses are
* responsible for keeping this value current from any method they
* override that alters the bit offset.
*/
protected int
bitOffset;
/**
* The position prior to which data may be discarded. Seeking
* to a smaller position is not allowed. <code>flushedPos</code>
* will always be {@literal >= 0}.
*/
protected long
flushedPos = 0;
/**
* Constructs an <code>ImageInputStreamImpl</code>.
*/
public
ImageInputStreamImpl() {
}
/**
* Throws an <code>IOException</code> if the stream has been closed.
* Subclasses may call this method from any of their methods that
* require the stream not to be closed.
*
* @exception IOException if the stream is closed.
*/
protected final void
checkClosed() throws
IOException {
if (
isClosed) {
throw new
IOException("closed");
}
}
public void
setByteOrder(
ByteOrder byteOrder) {
this.
byteOrder =
byteOrder;
}
public
ByteOrder getByteOrder() {
return
byteOrder;
}
/**
* Reads a single byte from the stream and returns it as an
* <code>int</code> between 0 and 255. If EOF is reached,
* <code>-1</code> is returned.
*
* <p> Subclasses must provide an implementation for this method.
* The subclass implementation should update the stream position
* before exiting.
*
* <p> The bit offset within the stream must be reset to zero before
* the read occurs.
*
* @return the value of the next byte in the stream, or <code>-1</code>
* if EOF is reached.
*
* @exception IOException if the stream has been closed.
*/
public abstract int
read() throws
IOException;
/**
* A convenience method that calls <code>read(b, 0, b.length)</code>.
*
* <p> The bit offset within the stream is reset to zero before
* the read occurs.
*
* @return the number of bytes actually read, or <code>-1</code>
* to indicate EOF.
*
* @exception NullPointerException if <code>b</code> is
* <code>null</code>.
* @exception IOException if an I/O error occurs.
*/
public int
read(byte[]
b) throws
IOException {
return
read(
b, 0,
b.length);
}
/**
* Reads up to <code>len</code> bytes from the stream, and stores
* them into <code>b</code> starting at index <code>off</code>.
* If no bytes can be read because the end of the stream has been
* reached, <code>-1</code> is returned.
*
* <p> The bit offset within the stream must be reset to zero before
* the read occurs.
*
* <p> Subclasses must provide an implementation for this method.
* The subclass implementation should update the stream position
* before exiting.
*
* @param b an array of bytes to be written to.
* @param off the starting position within <code>b</code> to write to.
* @param len the maximum number of bytes to read.
*
* @return the number of bytes actually read, or <code>-1</code>
* to indicate EOF.
*
* @exception IndexOutOfBoundsException if <code>off</code> is
* negative, <code>len</code> is negative, or <code>off +
* len</code> is greater than <code>b.length</code>.
* @exception NullPointerException if <code>b</code> is
* <code>null</code>.
* @exception IOException if an I/O error occurs.
*/
public abstract int
read(byte[]
b, int
off, int
len) throws
IOException;
public void
readBytes(
IIOByteBuffer buf, int
len) throws
IOException {
if (
len < 0) {
throw new
IndexOutOfBoundsException("len < 0!");
}
if (
buf == null) {
throw new
NullPointerException("buf == null!");
}
byte[]
data = new byte[
len];
len =
read(
data, 0,
len);
buf.
setData(
data);
buf.
setOffset(0);
buf.
setLength(
len);
}
public boolean
readBoolean() throws
IOException {
int
ch = this.
read();
if (
ch < 0) {
throw new
EOFException();
}
return (
ch != 0);
}
public byte
readByte() throws
IOException {
int
ch = this.
read();
if (
ch < 0) {
throw new
EOFException();
}
return (byte)
ch;
}
public int
readUnsignedByte() throws
IOException {
int
ch = this.
read();
if (
ch < 0) {
throw new
EOFException();
}
return
ch;
}
public short
readShort() throws
IOException {
if (
read(
byteBuf, 0, 2) != 2) {
throw new
EOFException();
}
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
return (short)
(((
byteBuf[0] & 0xff) << 8) | ((
byteBuf[1] & 0xff) << 0));
} else {
return (short)
(((
byteBuf[1] & 0xff) << 8) | ((
byteBuf[0] & 0xff) << 0));
}
}
public int
readUnsignedShort() throws
IOException {
return ((int)
readShort()) & 0xffff;
}
public char
readChar() throws
IOException {
return (char)
readShort();
}
public int
readInt() throws
IOException {
if (
read(
byteBuf, 0, 4) != 4) {
throw new
EOFException();
}
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
return
(((
byteBuf[0] & 0xff) << 24) | ((
byteBuf[1] & 0xff) << 16) |
((
byteBuf[2] & 0xff) << 8) | ((
byteBuf[3] & 0xff) << 0));
} else {
return
(((
byteBuf[3] & 0xff) << 24) | ((
byteBuf[2] & 0xff) << 16) |
((
byteBuf[1] & 0xff) << 8) | ((
byteBuf[0] & 0xff) << 0));
}
}
public long
readUnsignedInt() throws
IOException {
return ((long)
readInt()) & 0xffffffffL;
}
public long
readLong() throws
IOException {
// REMIND: Once 6277756 is fixed, we should do a bulk read of all 8
// bytes here as we do in readShort() and readInt() for even better
// performance (see 6347575 for details).
int
i1 =
readInt();
int
i2 =
readInt();
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
return ((long)
i1 << 32) + (
i2 & 0xFFFFFFFFL);
} else {
return ((long)
i2 << 32) + (
i1 & 0xFFFFFFFFL);
}
}
public float
readFloat() throws
IOException {
return
Float.
intBitsToFloat(
readInt());
}
public double
readDouble() throws
IOException {
return
Double.
longBitsToDouble(
readLong());
}
public
String readLine() throws
IOException {
StringBuffer input = new
StringBuffer();
int
c = -1;
boolean
eol = false;
while (!
eol) {
switch (
c =
read()) {
case -1:
case '\n':
eol = true;
break;
case '\r':
eol = true;
long
cur =
getStreamPosition();
if ((
read()) != '\n') {
seek(
cur);
}
break;
default:
input.
append((char)
c);
break;
}
}
if ((
c == -1) && (
input.
length() == 0)) {
return null;
}
return
input.
toString();
}
public
String readUTF() throws
IOException {
this.
bitOffset = 0;
// Fix 4494369: method ImageInputStreamImpl.readUTF()
// does not work as specified (it should always assume
// network byte order).
ByteOrder oldByteOrder =
getByteOrder();
setByteOrder(
ByteOrder.
BIG_ENDIAN);
String ret;
try {
ret =
DataInputStream.
readUTF(this);
} catch (
IOException e) {
// Restore the old byte order even if an exception occurs
setByteOrder(
oldByteOrder);
throw
e;
}
setByteOrder(
oldByteOrder);
return
ret;
}
public void
readFully(byte[]
b, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
b.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > b.length!");
}
while (
len > 0) {
int
nbytes =
read(
b,
off,
len);
if (
nbytes == -1) {
throw new
EOFException();
}
off +=
nbytes;
len -=
nbytes;
}
}
public void
readFully(byte[]
b) throws
IOException {
readFully(
b, 0,
b.length);
}
public void
readFully(short[]
s, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
s.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > s.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/2);
readFully(
byteBuf, 0,
nelts*2);
toShorts(
byteBuf,
s,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
public void
readFully(char[]
c, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
c.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > c.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/2);
readFully(
byteBuf, 0,
nelts*2);
toChars(
byteBuf,
c,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
public void
readFully(int[]
i, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
i.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > i.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/4);
readFully(
byteBuf, 0,
nelts*4);
toInts(
byteBuf,
i,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
public void
readFully(long[]
l, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
l.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > l.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/8);
readFully(
byteBuf, 0,
nelts*8);
toLongs(
byteBuf,
l,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
public void
readFully(float[]
f, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
f.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > f.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/4);
readFully(
byteBuf, 0,
nelts*4);
toFloats(
byteBuf,
f,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
public void
readFully(double[]
d, int
off, int
len) throws
IOException {
// Fix 4430357 - if off + len < 0, overflow occurred
if (
off < 0 ||
len < 0 ||
off +
len >
d.length ||
off +
len < 0) {
throw new
IndexOutOfBoundsException
("off < 0 || len < 0 || off + len > d.length!");
}
while (
len > 0) {
int
nelts =
Math.
min(
len,
byteBuf.length/8);
readFully(
byteBuf, 0,
nelts*8);
toDoubles(
byteBuf,
d,
off,
nelts);
off +=
nelts;
len -=
nelts;
}
}
private void
toShorts(byte[]
b, short[]
s, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
s[
off +
j] = (short)((
b0 << 8) |
b1);
boff += 2;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 1];
int
b1 =
b[
boff] & 0xff;
s[
off +
j] = (short)((
b0 << 8) |
b1);
boff += 2;
}
}
}
private void
toChars(byte[]
b, char[]
c, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
c[
off +
j] = (char)((
b0 << 8) |
b1);
boff += 2;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 1];
int
b1 =
b[
boff] & 0xff;
c[
off +
j] = (char)((
b0 << 8) |
b1);
boff += 2;
}
}
}
private void
toInts(byte[]
b, int[]
i, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
int
b2 =
b[
boff + 2] & 0xff;
int
b3 =
b[
boff + 3] & 0xff;
i[
off +
j] = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
boff += 4;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 3];
int
b1 =
b[
boff + 2] & 0xff;
int
b2 =
b[
boff + 1] & 0xff;
int
b3 =
b[
boff] & 0xff;
i[
off +
j] = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
boff += 4;
}
}
}
private void
toLongs(byte[]
b, long[]
l, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
int
b2 =
b[
boff + 2] & 0xff;
int
b3 =
b[
boff + 3] & 0xff;
int
b4 =
b[
boff + 4];
int
b5 =
b[
boff + 5] & 0xff;
int
b6 =
b[
boff + 6] & 0xff;
int
b7 =
b[
boff + 7] & 0xff;
int
i0 = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
int
i1 = (
b4 << 24) | (
b5 << 16) | (
b6 << 8) |
b7;
l[
off +
j] = ((long)
i0 << 32) | (
i1 & 0xffffffffL);
boff += 8;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 7];
int
b1 =
b[
boff + 6] & 0xff;
int
b2 =
b[
boff + 5] & 0xff;
int
b3 =
b[
boff + 4] & 0xff;
int
b4 =
b[
boff + 3];
int
b5 =
b[
boff + 2] & 0xff;
int
b6 =
b[
boff + 1] & 0xff;
int
b7 =
b[
boff] & 0xff;
int
i0 = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
int
i1 = (
b4 << 24) | (
b5 << 16) | (
b6 << 8) |
b7;
l[
off +
j] = ((long)
i0 << 32) | (
i1 & 0xffffffffL);
boff += 8;
}
}
}
private void
toFloats(byte[]
b, float[]
f, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
int
b2 =
b[
boff + 2] & 0xff;
int
b3 =
b[
boff + 3] & 0xff;
int
i = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
f[
off +
j] =
Float.
intBitsToFloat(
i);
boff += 4;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 3];
int
b1 =
b[
boff + 2] & 0xff;
int
b2 =
b[
boff + 1] & 0xff;
int
b3 =
b[
boff + 0] & 0xff;
int
i = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
f[
off +
j] =
Float.
intBitsToFloat(
i);
boff += 4;
}
}
}
private void
toDoubles(byte[]
b, double[]
d, int
off, int
len) {
int
boff = 0;
if (
byteOrder ==
ByteOrder.
BIG_ENDIAN) {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff];
int
b1 =
b[
boff + 1] & 0xff;
int
b2 =
b[
boff + 2] & 0xff;
int
b3 =
b[
boff + 3] & 0xff;
int
b4 =
b[
boff + 4];
int
b5 =
b[
boff + 5] & 0xff;
int
b6 =
b[
boff + 6] & 0xff;
int
b7 =
b[
boff + 7] & 0xff;
int
i0 = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
int
i1 = (
b4 << 24) | (
b5 << 16) | (
b6 << 8) |
b7;
long
l = ((long)
i0 << 32) | (
i1 & 0xffffffffL);
d[
off +
j] =
Double.
longBitsToDouble(
l);
boff += 8;
}
} else {
for (int
j = 0;
j <
len;
j++) {
int
b0 =
b[
boff + 7];
int
b1 =
b[
boff + 6] & 0xff;
int
b2 =
b[
boff + 5] & 0xff;
int
b3 =
b[
boff + 4] & 0xff;
int
b4 =
b[
boff + 3];
int
b5 =
b[
boff + 2] & 0xff;
int
b6 =
b[
boff + 1] & 0xff;
int
b7 =
b[
boff] & 0xff;
int
i0 = (
b0 << 24) | (
b1 << 16) | (
b2 << 8) |
b3;
int
i1 = (
b4 << 24) | (
b5 << 16) | (
b6 << 8) |
b7;
long
l = ((long)
i0 << 32) | (
i1 & 0xffffffffL);
d[
off +
j] =
Double.
longBitsToDouble(
l);
boff += 8;
}
}
}
public long
getStreamPosition() throws
IOException {
checkClosed();
return
streamPos;
}
public int
getBitOffset() throws
IOException {
checkClosed();
return
bitOffset;
}
public void
setBitOffset(int
bitOffset) throws
IOException {
checkClosed();
if (
bitOffset < 0 ||
bitOffset > 7) {
throw new
IllegalArgumentException("bitOffset must be betwwen 0 and 7!");
}
this.
bitOffset =
bitOffset;
}
public int
readBit() throws
IOException {
checkClosed();
// Compute final bit offset before we call read() and seek()
int
newBitOffset = (this.
bitOffset + 1) & 0x7;
int
val =
read();
if (
val == -1) {
throw new
EOFException();
}
if (
newBitOffset != 0) {
// Move byte position back if in the middle of a byte
seek(
getStreamPosition() - 1);
// Shift the bit to be read to the rightmost position
val >>= 8 -
newBitOffset;
}
this.
bitOffset =
newBitOffset;
return
val & 0x1;
}
public long
readBits(int
numBits) throws
IOException {
checkClosed();
if (
numBits < 0 ||
numBits > 64) {
throw new
IllegalArgumentException();
}
if (
numBits == 0) {
return 0L;
}
// Have to read additional bits on the left equal to the bit offset
int
bitsToRead =
numBits +
bitOffset;
// Compute final bit offset before we call read() and seek()
int
newBitOffset = (this.
bitOffset +
numBits) & 0x7;
// Read a byte at a time, accumulate
long
accum = 0L;
while (
bitsToRead > 0) {
int
val =
read();
if (
val == -1) {
throw new
EOFException();
}
accum <<= 8;
accum |=
val;
bitsToRead -= 8;
}
// Move byte position back if in the middle of a byte
if (
newBitOffset != 0) {
seek(
getStreamPosition() - 1);
}
this.
bitOffset =
newBitOffset;
// Shift away unwanted bits on the right.
accum >>>= (-
bitsToRead); // Negative of bitsToRead == extra bits read
// Mask out unwanted bits on the left
accum &= (-1L >>> (64 -
numBits));
return
accum;
}
/**
* Returns <code>-1L</code> to indicate that the stream has unknown
* length. Subclasses must override this method to provide actual
* length information.
*
* @return -1L to indicate unknown length.
*/
public long
length() {
return -1L;
}
/**
* Advances the current stream position by calling
* <code>seek(getStreamPosition() + n)</code>.
*
* <p> The bit offset is reset to zero.
*
* @param n the number of bytes to seek forward.
*
* @return an <code>int</code> representing the number of bytes
* skipped.
*
* @exception IOException if <code>getStreamPosition</code>
* throws an <code>IOException</code> when computing either
* the starting or ending position.
*/
public int
skipBytes(int
n) throws
IOException {
long
pos =
getStreamPosition();
seek(
pos +
n);
return (int)(
getStreamPosition() -
pos);
}
/**
* Advances the current stream position by calling
* <code>seek(getStreamPosition() + n)</code>.
*
* <p> The bit offset is reset to zero.
*
* @param n the number of bytes to seek forward.
*
* @return a <code>long</code> representing the number of bytes
* skipped.
*
* @exception IOException if <code>getStreamPosition</code>
* throws an <code>IOException</code> when computing either
* the starting or ending position.
*/
public long
skipBytes(long
n) throws
IOException {
long
pos =
getStreamPosition();
seek(
pos +
n);
return
getStreamPosition() -
pos;
}
public void
seek(long
pos) throws
IOException {
checkClosed();
// This test also covers pos < 0
if (
pos <
flushedPos) {
throw new
IndexOutOfBoundsException("pos < flushedPos!");
}
this.
streamPos =
pos;
this.
bitOffset = 0;
}
/**
* Pushes the current stream position onto a stack of marked
* positions.
*/
public void
mark() {
try {
markByteStack.
push(
Long.
valueOf(
getStreamPosition()));
markBitStack.
push(
Integer.
valueOf(
getBitOffset()));
} catch (
IOException e) {
}
}
/**
* Resets the current stream byte and bit positions from the stack
* of marked positions.
*
* <p> An <code>IOException</code> will be thrown if the previous
* marked position lies in the discarded portion of the stream.
*
* @exception IOException if an I/O error occurs.
*/
public void
reset() throws
IOException {
if (
markByteStack.
empty()) {
return;
}
long
pos = ((
Long)
markByteStack.
pop()).
longValue();
if (
pos <
flushedPos) {
throw new
IIOException
("Previous marked position has been discarded!");
}
seek(
pos);
int
offset = ((
Integer)
markBitStack.
pop()).
intValue();
setBitOffset(
offset);
}
public void
flushBefore(long
pos) throws
IOException {
checkClosed();
if (
pos <
flushedPos) {
throw new
IndexOutOfBoundsException("pos < flushedPos!");
}
if (
pos >
getStreamPosition()) {
throw new
IndexOutOfBoundsException("pos > getStreamPosition()!");
}
// Invariant: flushedPos >= 0
flushedPos =
pos;
}
public void
flush() throws
IOException {
flushBefore(
getStreamPosition());
}
public long
getFlushedPosition() {
return
flushedPos;
}
/**
* Default implementation returns false. Subclasses should
* override this if they cache data.
*/
public boolean
isCached() {
return false;
}
/**
* Default implementation returns false. Subclasses should
* override this if they cache data in main memory.
*/
public boolean
isCachedMemory() {
return false;
}
/**
* Default implementation returns false. Subclasses should
* override this if they cache data in a temporary file.
*/
public boolean
isCachedFile() {
return false;
}
public void
close() throws
IOException {
checkClosed();
isClosed = true;
}
/**
* Finalizes this object prior to garbage collection. The
* <code>close</code> method is called to close any open input
* source. This method should not be called from application
* code.
*
* @exception Throwable if an error occurs during superclass
* finalization.
*/
protected void
finalize() throws
Throwable {
if (!
isClosed) {
try {
close();
} catch (
IOException e) {
}
}
super.finalize();
}
}