- Added SubImageInputStream with test case.

- Changed BufferedImageInputStream to return -1 instead of the hack throwing of unchecked IOException.
This commit is contained in:
Harald Kuhr 2009-11-09 20:17:59 +01:00
parent 2ab9cbadee
commit effd80d42f
3 changed files with 272 additions and 7 deletions

View File

@ -172,14 +172,9 @@ public final class BufferedImageInputStream extends ImageInputStreamImpl impleme
try { try {
return mStream.length(); return mStream.length();
} }
catch (IOException e) { catch (IOException ignore) {
throw unchecked(e, RuntimeException.class);
} }
}
@SuppressWarnings({"unchecked", "UnusedDeclaration"}) return -1;
private <T extends Throwable> T unchecked(IOException pExcption, Class<T> pClass) {
// Ugly hack to fool the compiler..
return (T) pExcption;
} }
} }

View File

@ -0,0 +1,102 @@
package com.twelvemonkeys.imageio.stream;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageInputStreamImpl;
import java.io.IOException;
/**
* A wrapper for {@link ImageInputStream} to limit the number of bytes that can be read.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SubImageInputStream.java,v 1.0 Nov 8, 2009 2:50:58 PM haraldk Exp$
*/
public final class SubImageInputStream extends ImageInputStreamImpl {
// NOTE: This class is based on com.sun.imageio.plugins.common.SubImageInputStream, but fixes some of its bugs.
private final ImageInputStream mStream;
private final long mStartPos;
private final long mLength;
/**
* Creates a {@link ImageInputStream}, reading up to a maximum number of bytes from the underlying stream.
*
* @param pStream the underlying stream
* @param pLength the maximum length to read from the stream.
* Note that {@code pStream} may contain less than this maximum number of bytes.
*
* @throws IOException if {@code pStream}'s position can't be determined.
* @throws IllegalArgumentException if {@code pStream == null} or {@code pLength < 0}
*/
public SubImageInputStream(final ImageInputStream pStream, final long pLength) throws IOException {
Validate.notNull(pStream, "stream");
if (pLength < 0) {
throw new IllegalArgumentException("length < 0");
}
mStream = pStream;
mStartPos = pStream.getStreamPosition();
mLength = pLength;
}
public int read() throws IOException {
if (streamPos >= mLength) { // Local EOF
return -1;
}
else {
int read = mStream.read();
if (read >= 0) {
streamPos++;
}
return read;
}
}
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
if (streamPos >= mLength) { // Local EOF
return -1;
}
// Safe cast, as pLength can never cause int overflow
int length = (int) Math.min(pLength, mLength - streamPos);
int count = mStream.read(pBytes, pOffset, length);
if (count >= 0) {
streamPos += count;
}
return count;
}
@Override
public long length() {
try {
long length = mStream.length();
return length < 0 ? -1 : Math.min(length - mStartPos, mLength);
}
catch (IOException ignore) {
}
return -1;
}
@Override
public void seek(final long pPosition) throws IOException {
if (pPosition < getFlushedPosition()) {
throw new IndexOutOfBoundsException("pos < flushedPosition");
}
mStream.seek(mStartPos + pPosition);
streamPos = pPosition;
}
@SuppressWarnings({"FinalizeDoesntCallSuperFinalize"})
@Override
protected void finalize() throws Throwable {
// Empty finalizer (for improved performance; no need to call super.finalize() in this case)
}
}

View File

@ -0,0 +1,168 @@
package com.twelvemonkeys.imageio.stream;
import junit.framework.TestCase;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageInputStreamImpl;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
/**
* SubImageInputStreamTestCase
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haraldk$
* @version $Id: SubImageInputStreamTestCase.java,v 1.0 Nov 8, 2009 3:03:32 PM haraldk Exp$
*/
public class SubImageInputStreamTestCase extends TestCase {
// TODO: Extract super test case for all stream tests
private final Random mRandom = new Random(837468l);
private ImageInputStream createStream(final int pSize) {
byte[] bytes = new byte[pSize];
mRandom.nextBytes(bytes);
return new MemoryCacheImageInputStream(new ByteArrayInputStream(bytes)) {
@Override
public long length() {
return pSize;
}
};
}
public void testCreateNullStream() throws IOException {
try {
new SubImageInputStream(null, 1);
fail("Expected IllegalArgumentException with null stream");
}
catch (IllegalArgumentException e) {
}
}
public void testCreateNegativeLength() throws IOException {
try {
new SubImageInputStream(createStream(0), -1);
fail("Expected IllegalArgumentException with negative length");
}
catch (IllegalArgumentException e) {
}
}
public void testCreate() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(11), 7);
assertEquals(0, stream.getStreamPosition());
assertEquals(7, stream.length());
}
public void testWraphBeyondWrappedLength() throws IOException {
SubImageInputStream stream = new SubImageInputStream(createStream(5), 6);
assertEquals(5, stream.length());
}
public void testWrapUnknownLength() throws IOException {
SubImageInputStream stream = new SubImageInputStream(new ImageInputStreamImpl() {
@Override
public int read() throws IOException {
throw new UnsupportedOperationException("Method read not implemented");
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
throw new UnsupportedOperationException("Method read not implemented");
}
@Override
public long length() {
return -1;
}
}, 6);
assertEquals(-1, stream.length());
}
public void testRead() throws IOException {
ImageInputStream wrapped = createStream(42);
wrapped.skipBytes(13);
ImageInputStream stream = new SubImageInputStream(wrapped, 27);
assertEquals(0, stream.getStreamPosition());
assertEquals(27, stream.length());
stream.read();
assertEquals(1, stream.getStreamPosition());
assertEquals(27, stream.length());
stream.readFully(new byte[11]);
assertEquals(12, stream.getStreamPosition());
assertEquals(27, stream.length());
assertEquals(25, wrapped.getStreamPosition());
}
public void testReadResetRead() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(32), 16);
stream.mark();
byte[] first = new byte[16];
stream.readFully(first);
stream.reset();
byte[] second = new byte[16];
stream.readFully(second);
assertTrue(Arrays.equals(first, second));
}
public void testSeekNegative() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(7), 5);
try {
stream.seek(-2);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
}
assertEquals(0, stream.getStreamPosition());
}
public void testSeekBeforeFlushedPos() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(7), 5);
stream.seek(3);
stream.flushBefore(3);
assertEquals(3, stream.getStreamPosition());
try {
stream.seek(0);
fail("Expected IndexOutOfBoundsException");
}
catch (IndexOutOfBoundsException expected) {
}
assertEquals(3, stream.getStreamPosition());
}
public void testSeekAfterEOF() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(7), 5);
stream.seek(6);
assertEquals(-1, stream.read());
}
public void testSeek() throws IOException {
ImageInputStream stream = new SubImageInputStream(createStream(7), 5);
stream.seek(5);
assertEquals(5, stream.getStreamPosition());
stream.seek(1);
assertEquals(1, stream.getStreamPosition());
}
}