- /*
- * @(#)FileCacheImageInputStream.java 1.28 03/12/19
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
-
- package javax.imageio.stream;
-
- import java.io.DataInput;
- import java.io.File;
- import java.io.InputStream;
- import java.io.IOException;
- import java.io.RandomAccessFile;
-
- /**
- * An implementation of <code>ImageInputStream</code> that gets its
- * input from a regular <code>InputStream</code>. A file is used to
- * cache previously read data.
- *
- * @version 0.5
- */
- public class FileCacheImageInputStream extends ImageInputStreamImpl {
-
- private InputStream stream;
-
- private File cacheFile;
-
- private RandomAccessFile cache;
-
- private static final int BUFFER_LENGTH = 1024;
-
- private byte[] buf = new byte[BUFFER_LENGTH];
-
- private long length = 0L;
-
- private boolean foundEOF = false;
-
- /**
- * Constructs a <code>FileCacheImageInputStream</code> that will read
- * from a given <code>InputStream</code>.
- *
- * <p> A temporary file is used as a cache. If
- * <code>cacheDir</code>is non-<code>null</code> and is a
- * directory, the file will be created there. If it is
- * <code>null</code>, the system-dependent default temporary-file
- * directory will be used (see the documentation for
- * <code>File.createTempFile</code> for details).
- *
- * @param stream an <code>InputStream</code> to read from.
- * @param cacheDir a <code>File</code> indicating where the
- * cache file should be created, or <code>null</code> to use the
- * system directory.
- *
- * @exception IllegalArgumentException if <code>stream</code> is
- * <code>null</code>.
- * @exception IllegalArgumentException if <code>cacheDir</code> is
- * non-<code>null</code> but is not a directory.
- * @exception IOException if a cache file cannot be created.
- */
- public FileCacheImageInputStream(InputStream stream, File cacheDir)
- throws IOException {
- if (stream == null) {
- throw new IllegalArgumentException("stream == null!");
- }
- if ((cacheDir != null) && !(cacheDir.isDirectory())) {
- throw new IllegalArgumentException("Not a directory!");
- }
- this.stream = stream;
- this.cacheFile =
- File.createTempFile("imageio", ".tmp", cacheDir);
- cacheFile.deleteOnExit();
- this.cache = new RandomAccessFile(cacheFile, "rw");
- }
-
- /**
- * Ensures that at least <code>pos</code> bytes are cached,
- * or the end of the source is reached. The return value
- * is equal to the smaller of <code>pos</code> and the
- * length of the source file.
- */
- private long readUntil(long pos) throws IOException {
- // We've already got enough data cached
- if (pos < length) {
- return pos;
- }
- // pos >= length but length isn't getting any bigger, so return it
- if (foundEOF) {
- return length;
- }
-
- long len = pos - length;
- cache.seek(length);
- while (len > 0) {
- // Copy a buffer's worth of data from the source to the cache
- // BUFFER_LENGTH will always fit into an int so this is safe
- int nbytes =
- stream.read(buf, 0, (int)Math.min(len, (long)BUFFER_LENGTH));
- if (nbytes == -1) {
- foundEOF = true;
- return length;
- }
-
- cache.write(buf, 0, nbytes);
- len -= nbytes;
- length += nbytes;
- }
-
- return pos;
- }
-
- public int read() throws IOException {
- bitOffset = 0;
- long next = streamPos + 1;
- long pos = readUntil(next);
- if (pos >= next) {
- cache.seek(streamPos++);
- return cache.read();
- } else {
- return -1;
- }
- }
-
- public int read(byte[] b, int off, int len) throws IOException {
- if (b == null) {
- throw new NullPointerException();
- }
- // Fix 4430357 - if off + len < 0, overflow occurred
- if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (len == 0) {
- return 0;
- }
-
- checkClosed();
-
- bitOffset = 0;
-
- long pos = readUntil(streamPos + len);
-
- // len will always fit into an int so this is safe
- len = (int)Math.min((long)len, pos - streamPos);
- if (len > 0) {
- cache.seek(streamPos);
- cache.readFully(b, off, len);
- streamPos += len;
- return len;
- } else {
- return -1;
- }
- }
-
- /**
- * Returns <code>true</code> since this
- * <code>ImageInputStream</code> caches data in order to allow
- * seeking backwards.
- *
- * @return <code>true</code>.
- *
- * @see #isCachedMemory
- * @see #isCachedFile
- */
- public boolean isCached() {
- return true;
- }
-
- /**
- * Returns <code>true</code> since this
- * <code>ImageInputStream</code> maintains a file cache.
- *
- * @return <code>true</code>.
- *
- * @see #isCached
- * @see #isCachedMemory
- */
- public boolean isCachedFile() {
- return true;
- }
-
- /**
- * Returns <code>false</code> since this
- * <code>ImageInputStream</code> does not maintain a main memory
- * cache.
- *
- * @return <code>false</code>.
- *
- * @see #isCached
- * @see #isCachedFile
- */
- public boolean isCachedMemory() {
- return false;
- }
-
- /**
- * Closes this <code>FileCacheImageInputStream</code>, closing
- * and removing the cache file. The source <code>InputStream</code>
- * is not closed.
- *
- * @exception IOException if an error occurs.
- */
- public void close() throws IOException {
- super.close();
- cache.close();
- cacheFile.delete();
- stream = null;
- }
- }