Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.io;
     19 
     20 import dalvik.system.CloseGuard;
     21 import java.nio.NioUtils;
     22 import java.nio.channels.FileChannel;
     23 import java.util.Arrays;
     24 import libcore.io.IoBridge;
     25 import libcore.io.IoUtils;
     26 import static libcore.io.OsConstants.*;
     27 
     28 /**
     29  * An output stream that writes bytes to a file. If the output file exists, it
     30  * can be replaced or appended to. If it does not exist, a new file will be
     31  * created.
     32  * <pre>   {@code
     33  *   File file = ...
     34  *   OutputStream out = null;
     35  *   try {
     36  *     out = new BufferedOutputStream(new FileOutputStream(file));
     37  *     ...
     38  *   } finally {
     39  *     if (out != null) {
     40  *       out.close();
     41  *     }
     42  *   }
     43  * }</pre>
     44  *
     45  * <p>This stream is <strong>not buffered</strong>. Most callers should wrap
     46  * this stream with a {@link BufferedOutputStream}.
     47  *
     48  * <p>Use {@link FileWriter} to write characters, as opposed to bytes, to a file.
     49  *
     50  * @see BufferedOutputStream
     51  * @see FileInputStream
     52  */
     53 public class FileOutputStream extends OutputStream {
     54 
     55     private FileDescriptor fd;
     56     private final boolean shouldClose;
     57 
     58     /** The unique file channel. Lazily initialized because it's rarely needed. */
     59     private FileChannel channel;
     60 
     61     /** File access mode */
     62     private final int mode;
     63 
     64     private final CloseGuard guard = CloseGuard.get();
     65 
     66     /**
     67      * Constructs a new {@code FileOutputStream} that writes to {@code file}. The file will be
     68      * truncated if it exists, and created if it doesn't exist.
     69      *
     70      * @throws FileNotFoundException if file cannot be opened for writing.
     71      */
     72     public FileOutputStream(File file) throws FileNotFoundException {
     73         this(file, false);
     74     }
     75 
     76     /**
     77      * Constructs a new {@code FileOutputStream} that writes to {@code file}.
     78      * If {@code append} is true and the file already exists, it will be appended to; otherwise
     79      * it will be truncated. The file will be created if it does not exist.
     80      *
     81      * @throws FileNotFoundException if the file cannot be opened for writing.
     82      */
     83     public FileOutputStream(File file, boolean append) throws FileNotFoundException {
     84         if (file == null) {
     85             throw new NullPointerException("file == null");
     86         }
     87         this.mode = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
     88         this.fd = IoBridge.open(file.getAbsolutePath(), mode);
     89         this.shouldClose = true;
     90         this.guard.open("close");
     91     }
     92 
     93     /**
     94      * Constructs a new {@code FileOutputStream} that writes to {@code fd}.
     95      *
     96      * @throws NullPointerException if {@code fd} is null.
     97      */
     98     public FileOutputStream(FileDescriptor fd) {
     99         if (fd == null) {
    100             throw new NullPointerException("fd == null");
    101         }
    102         this.fd = fd;
    103         this.shouldClose = false;
    104         this.mode = O_WRONLY;
    105         this.channel = NioUtils.newFileChannel(this, fd, mode);
    106         // Note that we do not call guard.open here because the
    107         // FileDescriptor is not owned by the stream.
    108     }
    109 
    110     /**
    111      * Constructs a new {@code FileOutputStream} that writes to {@code path}. The file will be
    112      * truncated if it exists, and created if it doesn't exist.
    113      *
    114      * @throws FileNotFoundException if file cannot be opened for writing.
    115      */
    116     public FileOutputStream(String path) throws FileNotFoundException {
    117         this(path, false);
    118     }
    119 
    120     /**
    121      * Constructs a new {@code FileOutputStream} that writes to {@code path}.
    122      * If {@code append} is true and the file already exists, it will be appended to; otherwise
    123      * it will be truncated. The file will be created if it does not exist.
    124      *
    125      * @throws FileNotFoundException if the file cannot be opened for writing.
    126      */
    127     public FileOutputStream(String path, boolean append) throws FileNotFoundException {
    128         this(new File(path), append);
    129     }
    130 
    131     @Override
    132     public void close() throws IOException {
    133         guard.close();
    134         synchronized (this) {
    135             if (channel != null) {
    136                 channel.close();
    137             }
    138             if (shouldClose) {
    139                 IoUtils.close(fd);
    140             } else {
    141                 // An owned fd has been invalidated by IoUtils.close, but
    142                 // we need to explicitly stop using an unowned fd (http://b/4361076).
    143                 fd = new FileDescriptor();
    144             }
    145         }
    146     }
    147 
    148     @Override protected void finalize() throws IOException {
    149         try {
    150             if (guard != null) {
    151                 guard.warnIfOpen();
    152             }
    153             close();
    154         } finally {
    155             try {
    156                 super.finalize();
    157             } catch (Throwable t) {
    158                 // for consistency with the RI, we must override Object.finalize() to
    159                 // remove the 'throws Throwable' clause.
    160                 throw new AssertionError(t);
    161             }
    162         }
    163     }
    164 
    165     /**
    166      * Returns a write-only {@link FileChannel} that shares its position with
    167      * this stream.
    168      */
    169     public FileChannel getChannel() {
    170         synchronized (this) {
    171             if (channel == null) {
    172                 channel = NioUtils.newFileChannel(this, fd, mode);
    173             }
    174             return channel;
    175         }
    176     }
    177 
    178     /**
    179      * Returns the underlying file descriptor.
    180      */
    181     public final FileDescriptor getFD() throws IOException {
    182         return fd;
    183     }
    184 
    185     @Override
    186     public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
    187         IoBridge.write(fd, buffer, byteOffset, byteCount);
    188     }
    189 
    190     @Override
    191     public void write(int oneByte) throws IOException {
    192         write(new byte[] { (byte) oneByte }, 0, 1);
    193     }
    194 }
    195