• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 libcore.io.IoBridge;
24 
25 import static android.system.OsConstants.*;
26 
27 /**
28  * An output stream that writes bytes to a file. If the output file exists, it
29  * can be replaced or appended to. If it does not exist, a new file will be
30  * created.
31  * <pre>   {@code
32  *   File file = ...
33  *   OutputStream out = null;
34  *   try {
35  *     out = new BufferedOutputStream(new FileOutputStream(file));
36  *     ...
37  *   } finally {
38  *     if (out != null) {
39  *       out.close();
40  *     }
41  *   }
42  * }</pre>
43  *
44  * <p>This stream is <strong>not buffered</strong>. Most callers should wrap
45  * this stream with a {@link BufferedOutputStream}.
46  *
47  * <p>Use {@link FileWriter} to write characters, as opposed to bytes, to a file.
48  *
49  * @see BufferedOutputStream
50  * @see FileInputStream
51  */
52 public class FileOutputStream extends OutputStream {
53 
54     private FileDescriptor fd;
55     private final boolean shouldClose;
56 
57     /** The unique file channel. Lazily initialized because it's rarely needed. */
58     private FileChannel channel;
59 
60     /** File access mode */
61     private final int mode;
62 
63     private final CloseGuard guard = CloseGuard.get();
64 
65     /**
66      * Constructs a new {@code FileOutputStream} that writes to {@code file}. The file will be
67      * truncated if it exists, and created if it doesn't exist.
68      *
69      * @throws FileNotFoundException if file cannot be opened for writing.
70      */
FileOutputStream(File file)71     public FileOutputStream(File file) throws FileNotFoundException {
72         this(file, false);
73     }
74 
75     /**
76      * Constructs a new {@code FileOutputStream} that writes to {@code file}.
77      * If {@code append} is true and the file already exists, it will be appended to; otherwise
78      * it will be truncated. The file will be created if it does not exist.
79      *
80      * @throws FileNotFoundException if the file cannot be opened for writing.
81      */
FileOutputStream(File file, boolean append)82     public FileOutputStream(File file, boolean append) throws FileNotFoundException {
83         if (file == null) {
84             throw new NullPointerException("file == null");
85         }
86         this.mode = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
87         this.fd = IoBridge.open(file.getPath(), mode);
88         this.shouldClose = true;
89         this.guard.open("close");
90     }
91 
92     /**
93      * Constructs a new {@code FileOutputStream} that writes to {@code fd}.
94      *
95      * @throws NullPointerException if {@code fd} is null.
96      */
FileOutputStream(FileDescriptor fd)97     public FileOutputStream(FileDescriptor fd) {
98         if (fd == null) {
99             throw new NullPointerException("fd == null");
100         }
101         this.fd = fd;
102         this.shouldClose = false;
103         this.mode = O_WRONLY;
104         this.channel = NioUtils.newFileChannel(this, fd, mode);
105         // Note that we do not call guard.open here because the
106         // FileDescriptor is not owned by the stream.
107     }
108 
109     /**
110      * Constructs a new {@code FileOutputStream} that writes to {@code path}. The file will be
111      * truncated if it exists, and created if it doesn't exist.
112      *
113      * @throws FileNotFoundException if file cannot be opened for writing.
114      */
FileOutputStream(String path)115     public FileOutputStream(String path) throws FileNotFoundException {
116         this(path, false);
117     }
118 
119     /**
120      * Constructs a new {@code FileOutputStream} that writes to {@code path}.
121      * If {@code append} is true and the file already exists, it will be appended to; otherwise
122      * it will be truncated. The file will be created if it does not exist.
123      *
124      * @throws FileNotFoundException if the file cannot be opened for writing.
125      */
FileOutputStream(String path, boolean append)126     public FileOutputStream(String path, boolean append) throws FileNotFoundException {
127         this(new File(path), append);
128     }
129 
130     @Override
close()131     public void close() throws IOException {
132         guard.close();
133         synchronized (this) {
134             if (channel != null) {
135                 channel.close();
136             }
137             if (shouldClose) {
138                 IoBridge.closeAndSignalBlockedThreads(fd);
139             } else {
140                 // An owned fd has been invalidated by IoUtils.close, but
141                 // we need to explicitly stop using an unowned fd (http://b/4361076).
142                 fd = new FileDescriptor();
143             }
144         }
145     }
146 
finalize()147     @Override protected void finalize() throws IOException {
148         try {
149             if (guard != null) {
150                 guard.warnIfOpen();
151             }
152             close();
153         } finally {
154             try {
155                 super.finalize();
156             } catch (Throwable t) {
157                 // for consistency with the RI, we must override Object.finalize() to
158                 // remove the 'throws Throwable' clause.
159                 throw new AssertionError(t);
160             }
161         }
162     }
163 
164     /**
165      * Returns a write-only {@link FileChannel} that shares its position with
166      * this stream.
167      */
getChannel()168     public FileChannel getChannel() {
169         synchronized (this) {
170             if (channel == null) {
171                 channel = NioUtils.newFileChannel(this, fd, mode);
172             }
173             return channel;
174         }
175     }
176 
177     /**
178      * Returns the underlying file descriptor.
179      */
getFD()180     public final FileDescriptor getFD() throws IOException {
181         return fd;
182     }
183 
184     @Override
write(byte[] buffer, int byteOffset, int byteCount)185     public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
186         IoBridge.write(fd, buffer, byteOffset, byteCount);
187     }
188 
189     @Override
write(int oneByte)190     public void write(int oneByte) throws IOException {
191         write(new byte[] { (byte) oneByte }, 0, 1);
192     }
193 }
194