1 /*
   2  * Copyright (c) 2000, 2007, Oracle and/or its affiliates. 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.imageio.stream;
  27 
  28 import java.io.File;
  29 import java.io.FileNotFoundException;
  30 import java.io.IOException;
  31 import java.io.RandomAccessFile;
  32 import com.sun.imageio.stream.CloseableDisposerRecord;
  33 import com.sun.imageio.stream.StreamFinalizer;
  34 import sun.java2d.Disposer;
  35 
  36 /**
  37  * An implementation of {@code ImageOutputStream} that writes its
  38  * output directly to a {@code File} or
  39  * {@code RandomAccessFile}.
  40  *
  41  */
  42 public class FileImageOutputStream extends ImageOutputStreamImpl {
  43 
  44     private RandomAccessFile raf;
  45 
  46     /** The referent to be registered with the Disposer. */
  47     private final Object disposerReferent;
  48 
  49     /** The DisposerRecord that closes the underlying RandomAccessFile. */
  50     private final CloseableDisposerRecord disposerRecord;
  51 
  52     /**
  53      * Constructs a {@code FileImageOutputStream} that will write
  54      * to a given {@code File}.
  55      *
  56      * @param f a {@code File} to write to.
  57      *
  58      * @exception IllegalArgumentException if {@code f} is
  59      * {@code null}.
  60      * @exception SecurityException if a security manager exists
  61      * and does not allow write access to the file.
  62      * @exception FileNotFoundException if {@code f} does not denote
  63      * a regular file or it cannot be opened for reading and writing for any
  64      * other reason.
  65      * @exception IOException if an I/O error occurs.
  66      */
  67     public FileImageOutputStream(File f)
  68         throws FileNotFoundException, IOException {
  69         this(f == null ? null : new RandomAccessFile(f, "rw"));
  70     }
  71 
  72     /**
  73      * Constructs a {@code FileImageOutputStream} that will write
  74      * to a given {@code RandomAccessFile}.
  75      *
  76      * @param raf a {@code RandomAccessFile} to write to.
  77      *
  78      * @exception IllegalArgumentException if {@code raf} is
  79      * {@code null}.
  80      */
  81     public FileImageOutputStream(RandomAccessFile raf) {
  82         if (raf == null) {
  83             throw new IllegalArgumentException("raf == null!");
  84         }
  85         this.raf = raf;
  86 
  87         disposerRecord = new CloseableDisposerRecord(raf);
  88         if (getClass() == FileImageOutputStream.class) {
  89             disposerReferent = new Object();
  90             Disposer.addRecord(disposerReferent, disposerRecord);
  91         } else {
  92             disposerReferent = new StreamFinalizer(this);
  93         }
  94     }
  95 
  96     public int read() throws IOException {
  97         checkClosed();
  98         bitOffset = 0;
  99         int val = raf.read();
 100         if (val != -1) {
 101             ++streamPos;
 102         }
 103         return val;
 104     }
 105 
 106     public int read(byte[] b, int off, int len) throws IOException {
 107         checkClosed();
 108         bitOffset = 0;
 109         int nbytes = raf.read(b, off, len);
 110         if (nbytes != -1) {
 111             streamPos += nbytes;
 112         }
 113         return nbytes;
 114     }
 115 
 116     public void write(int b) throws IOException {
 117         flushBits(); // this will call checkClosed() for us
 118         raf.write(b);
 119         ++streamPos;
 120     }
 121 
 122     public void write(byte[] b, int off, int len) throws IOException {
 123         flushBits(); // this will call checkClosed() for us
 124         raf.write(b, off, len);
 125         streamPos += len;
 126     }
 127 
 128     public long length() {
 129         try {
 130             checkClosed();
 131             return raf.length();
 132         } catch (IOException e) {
 133             return -1L;
 134         }
 135     }
 136 
 137     /**
 138      * Sets the current stream position and resets the bit offset to
 139      * 0.  It is legal to seeking past the end of the file; an
 140      * {@code EOFException} will be thrown only if a read is
 141      * performed.  The file length will not be increased until a write
 142      * is performed.
 143      *
 144      * @exception IndexOutOfBoundsException if {@code pos} is smaller
 145      * than the flushed position.
 146      * @exception IOException if any other I/O error occurs.
 147      */
 148     public void seek(long pos) throws IOException {
 149         checkClosed();
 150         if (pos < flushedPos) {
 151             throw new IndexOutOfBoundsException("pos < flushedPos!");
 152         }
 153         bitOffset = 0;
 154         raf.seek(pos);
 155         streamPos = raf.getFilePointer();
 156     }
 157 
 158     public void close() throws IOException {
 159         super.close();
 160         disposerRecord.dispose(); // this closes the RandomAccessFile
 161         raf = null;
 162     }
 163 
 164     /**
 165      * {@inheritDoc}
 166      */
 167     protected void finalize() throws Throwable {
 168         // Empty finalizer: for performance reasons we instead use the
 169         // Disposer mechanism for ensuring that the underlying
 170         // RandomAccessFile is closed prior to garbage collection
 171     }
 172 }
--- EOF ---