--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package javax.imageio.stream;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import com.sun.imageio.stream.StreamCloser;
+
+/**
+ * An implementation of <code>ImageOutputStream</code> that writes its
+ * output to a regular <code>OutputStream</code>. A file is used to
+ * cache data until it is flushed to the output stream.
+ *
+ */
+public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
+
+ private OutputStream stream;
+
+ private File cacheFile;
+
+ private RandomAccessFile cache;
+
+ // Pos after last (rightmost) byte written
+ private long maxStreamPos = 0L;
+
+ /**
+ * Constructs a <code>FileCacheImageOutputStream</code> that will write
+ * to a given <code>outputStream</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>OutputStream</code> to write to.
+ * @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 FileCacheImageOutputStream(OutputStream 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);
+ this.cache = new RandomAccessFile(cacheFile, "rw");
+ StreamCloser.addToQueue(this);
+ }
+
+ public int read() throws IOException {
+ checkClosed();
+ bitOffset = 0;
+ int val = cache.read();
+ if (val != -1) {
+ ++streamPos;
+ }
+ return val;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ checkClosed();
+
+ if (b == null) {
+ throw new NullPointerException("b == null!");
+ }
+ if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
+ throw new IndexOutOfBoundsException
+ ("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
+ }
+
+ bitOffset = 0;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ int nbytes = cache.read(b, off, len);
+ if (nbytes != -1) {
+ streamPos += nbytes;
+ }
+ return nbytes;
+ }
+
+ public void write(int b) throws IOException {
+ flushBits(); // this will call checkClosed() for us
+ cache.write(b);
+ ++streamPos;
+ maxStreamPos = Math.max(maxStreamPos, streamPos);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ flushBits(); // this will call checkClosed() for us
+ cache.write(b, off, len);
+ streamPos += len;
+ maxStreamPos = Math.max(maxStreamPos, streamPos);
+ }
+
+ public long length() {
+ try {
+ checkClosed();
+ return cache.length();
+ } catch (IOException e) {
+ return -1L;
+ }
+ }
+
+ /**
+ * Sets the current stream position and resets the bit offset to
+ * 0. It is legal to seek past the end of the file; an
+ * <code>EOFException</code> will be thrown only if a read is
+ * performed. The file length will not be increased until a write
+ * is performed.
+ *
+ * @exception IndexOutOfBoundsException if <code>pos</code> is smaller
+ * than the flushed position.
+ * @exception IOException if any other I/O error occurs.
+ */
+ public void seek(long pos) throws IOException {
+ checkClosed();
+
+ if (pos < flushedPos) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ cache.seek(pos);
+ this.streamPos = cache.getFilePointer();
+ maxStreamPos = Math.max(maxStreamPos, streamPos);
+ this.bitOffset = 0;
+ }
+
+ /**
+ * Returns <code>true</code> since this
+ * <code>ImageOutputStream</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>ImageOutputStream</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>ImageOutputStream</code> does not maintain a main memory
+ * cache.
+ *
+ * @return <code>false</code>.
+ *
+ * @see #isCached
+ * @see #isCachedFile
+ */
+ public boolean isCachedMemory() {
+ return false;
+ }
+
+ /**
+ * Closes this <code>FileCacheImageOututStream</code>. All
+ * pending data is flushed to the output, and the cache file
+ * is closed and removed. The destination <code>OutputStream</code>
+ * is not closed.
+ *
+ * @exception IOException if an error occurs.
+ */
+ public void close() throws IOException {
+ maxStreamPos = cache.length();
+
+ seek(maxStreamPos);
+ flushBefore(maxStreamPos);
+ super.close();
+ cache.close();
+ cache = null;
+ cacheFile.delete();
+ cacheFile = null;
+ stream.flush();
+ stream = null;
+ StreamCloser.removeFromQueue(this);
+ }
+
+ public void flushBefore(long pos) throws IOException {
+ long oFlushedPos = flushedPos;
+ super.flushBefore(pos); // this will call checkClosed() for us
+
+ long flushBytes = flushedPos - oFlushedPos;
+ if (flushBytes > 0) {
+ int bufLen = 512;
+ byte[] buf = new byte[bufLen];
+ cache.seek(oFlushedPos);
+ while (flushBytes > 0) {
+ int len = (int)Math.min(flushBytes, bufLen);
+ cache.readFully(buf, 0, len);
+ stream.write(buf, 0, len);
+ flushBytes -= len;
+ }
+ stream.flush();
+ }
+ }
+}