jdk/src/share/classes/javax/imageio/stream/FileCacheImageOutputStream.java
changeset 2 90ce3da70b43
child 3448 1ccef37a150f
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 2000-2007 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package javax.imageio.stream;
       
    27 
       
    28 import java.io.File;
       
    29 import java.io.IOException;
       
    30 import java.io.OutputStream;
       
    31 import java.io.RandomAccessFile;
       
    32 import com.sun.imageio.stream.StreamCloser;
       
    33 
       
    34 /**
       
    35  * An implementation of <code>ImageOutputStream</code> that writes its
       
    36  * output to a regular <code>OutputStream</code>.  A file is used to
       
    37  * cache data until it is flushed to the output stream.
       
    38  *
       
    39  */
       
    40 public class FileCacheImageOutputStream extends ImageOutputStreamImpl {
       
    41 
       
    42     private OutputStream stream;
       
    43 
       
    44     private File cacheFile;
       
    45 
       
    46     private RandomAccessFile cache;
       
    47 
       
    48     // Pos after last (rightmost) byte written
       
    49     private long maxStreamPos = 0L;
       
    50 
       
    51     /**
       
    52      * Constructs a <code>FileCacheImageOutputStream</code> that will write
       
    53      * to a given <code>outputStream</code>.
       
    54      *
       
    55      * <p> A temporary file is used as a cache.  If
       
    56      * <code>cacheDir</code>is non-<code>null</code> and is a
       
    57      * directory, the file will be created there.  If it is
       
    58      * <code>null</code>, the system-dependent default temporary-file
       
    59      * directory will be used (see the documentation for
       
    60      * <code>File.createTempFile</code> for details).
       
    61      *
       
    62      * @param stream an <code>OutputStream</code> to write to.
       
    63      * @param cacheDir a <code>File</code> indicating where the
       
    64      * cache file should be created, or <code>null</code> to use the
       
    65      * system directory.
       
    66      *
       
    67      * @exception IllegalArgumentException if <code>stream</code>
       
    68      * is <code>null</code>.
       
    69      * @exception IllegalArgumentException if <code>cacheDir</code> is
       
    70      * non-<code>null</code> but is not a directory.
       
    71      * @exception IOException if a cache file cannot be created.
       
    72      */
       
    73     public FileCacheImageOutputStream(OutputStream stream, File cacheDir)
       
    74         throws IOException {
       
    75         if (stream == null) {
       
    76             throw new IllegalArgumentException("stream == null!");
       
    77         }
       
    78         if ((cacheDir != null) && !(cacheDir.isDirectory())) {
       
    79             throw new IllegalArgumentException("Not a directory!");
       
    80         }
       
    81         this.stream = stream;
       
    82         this.cacheFile =
       
    83             File.createTempFile("imageio", ".tmp", cacheDir);
       
    84         this.cache = new RandomAccessFile(cacheFile, "rw");
       
    85         StreamCloser.addToQueue(this);
       
    86     }
       
    87 
       
    88     public int read() throws IOException {
       
    89         checkClosed();
       
    90         bitOffset = 0;
       
    91         int val =  cache.read();
       
    92         if (val != -1) {
       
    93             ++streamPos;
       
    94         }
       
    95         return val;
       
    96     }
       
    97 
       
    98     public int read(byte[] b, int off, int len) throws IOException {
       
    99         checkClosed();
       
   100 
       
   101         if (b == null) {
       
   102             throw new NullPointerException("b == null!");
       
   103         }
       
   104         if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
       
   105             throw new IndexOutOfBoundsException
       
   106                 ("off < 0 || len < 0 || off+len > b.length || off+len < 0!");
       
   107         }
       
   108 
       
   109         bitOffset = 0;
       
   110 
       
   111         if (len == 0) {
       
   112             return 0;
       
   113         }
       
   114 
       
   115         int nbytes = cache.read(b, off, len);
       
   116         if (nbytes != -1) {
       
   117             streamPos += nbytes;
       
   118         }
       
   119         return nbytes;
       
   120     }
       
   121 
       
   122     public void write(int b) throws IOException {
       
   123         flushBits(); // this will call checkClosed() for us
       
   124         cache.write(b);
       
   125         ++streamPos;
       
   126         maxStreamPos = Math.max(maxStreamPos, streamPos);
       
   127     }
       
   128 
       
   129     public void write(byte[] b, int off, int len) throws IOException {
       
   130         flushBits(); // this will call checkClosed() for us
       
   131         cache.write(b, off, len);
       
   132         streamPos += len;
       
   133         maxStreamPos = Math.max(maxStreamPos, streamPos);
       
   134     }
       
   135 
       
   136     public long length() {
       
   137         try {
       
   138             checkClosed();
       
   139             return cache.length();
       
   140         } catch (IOException e) {
       
   141             return -1L;
       
   142         }
       
   143     }
       
   144 
       
   145     /**
       
   146      * Sets the current stream position and resets the bit offset to
       
   147      * 0.  It is legal to seek past the end of the file; an
       
   148      * <code>EOFException</code> will be thrown only if a read is
       
   149      * performed.  The file length will not be increased until a write
       
   150      * is performed.
       
   151      *
       
   152      * @exception IndexOutOfBoundsException if <code>pos</code> is smaller
       
   153      * than the flushed position.
       
   154      * @exception IOException if any other I/O error occurs.
       
   155      */
       
   156     public void seek(long pos) throws IOException {
       
   157         checkClosed();
       
   158 
       
   159         if (pos < flushedPos) {
       
   160             throw new IndexOutOfBoundsException();
       
   161         }
       
   162 
       
   163         cache.seek(pos);
       
   164         this.streamPos = cache.getFilePointer();
       
   165         maxStreamPos = Math.max(maxStreamPos, streamPos);
       
   166         this.bitOffset = 0;
       
   167     }
       
   168 
       
   169     /**
       
   170      * Returns <code>true</code> since this
       
   171      * <code>ImageOutputStream</code> caches data in order to allow
       
   172      * seeking backwards.
       
   173      *
       
   174      * @return <code>true</code>.
       
   175      *
       
   176      * @see #isCachedMemory
       
   177      * @see #isCachedFile
       
   178      */
       
   179     public boolean isCached() {
       
   180         return true;
       
   181     }
       
   182 
       
   183     /**
       
   184      * Returns <code>true</code> since this
       
   185      * <code>ImageOutputStream</code> maintains a file cache.
       
   186      *
       
   187      * @return <code>true</code>.
       
   188      *
       
   189      * @see #isCached
       
   190      * @see #isCachedMemory
       
   191      */
       
   192     public boolean isCachedFile() {
       
   193         return true;
       
   194     }
       
   195 
       
   196     /**
       
   197      * Returns <code>false</code> since this
       
   198      * <code>ImageOutputStream</code> does not maintain a main memory
       
   199      * cache.
       
   200      *
       
   201      * @return <code>false</code>.
       
   202      *
       
   203      * @see #isCached
       
   204      * @see #isCachedFile
       
   205      */
       
   206     public boolean isCachedMemory() {
       
   207         return false;
       
   208     }
       
   209 
       
   210     /**
       
   211      * Closes this <code>FileCacheImageOututStream</code>.  All
       
   212      * pending data is flushed to the output, and the cache file
       
   213      * is closed and removed.  The destination <code>OutputStream</code>
       
   214      * is not closed.
       
   215      *
       
   216      * @exception IOException if an error occurs.
       
   217      */
       
   218     public void close() throws IOException {
       
   219         maxStreamPos = cache.length();
       
   220 
       
   221         seek(maxStreamPos);
       
   222         flushBefore(maxStreamPos);
       
   223         super.close();
       
   224         cache.close();
       
   225         cache = null;
       
   226         cacheFile.delete();
       
   227         cacheFile = null;
       
   228         stream.flush();
       
   229         stream = null;
       
   230         StreamCloser.removeFromQueue(this);
       
   231     }
       
   232 
       
   233     public void flushBefore(long pos) throws IOException {
       
   234         long oFlushedPos = flushedPos;
       
   235         super.flushBefore(pos); // this will call checkClosed() for us
       
   236 
       
   237         long flushBytes = flushedPos - oFlushedPos;
       
   238         if (flushBytes > 0) {
       
   239             int bufLen = 512;
       
   240             byte[] buf = new byte[bufLen];
       
   241             cache.seek(oFlushedPos);
       
   242             while (flushBytes > 0) {
       
   243                 int len = (int)Math.min(flushBytes, bufLen);
       
   244                 cache.readFully(buf, 0, len);
       
   245                 stream.write(buf, 0, len);
       
   246                 flushBytes -= len;
       
   247             }
       
   248             stream.flush();
       
   249         }
       
   250     }
       
   251 }