1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.io;
import java.nio.channels.FileChannel;
import libcore.io.IoUtils;
import org.apache.harmony.luni.platform.IFileSystem;
import org.apache.harmony.luni.platform.Platform;
import org.apache.harmony.nio.FileChannelFactory;
/**
* A specialized {@link OutputStream} that writes to a file in the file system.
* All write requests made by calling methods in this class are directly
* forwarded to the equivalent function of the underlying operating system.
* Since this may induce some performance penalty, in particular if many small
* write requests are made, a FileOutputStream is often wrapped by a
* BufferedOutputStream.
*
* @see BufferedOutputStream
* @see FileInputStream
*/
public class FileOutputStream extends OutputStream implements Closeable {
/**
* The FileDescriptor representing this FileOutputStream.
*/
FileDescriptor fd;
boolean innerFD;
// The unique file channel associated with this FileInputStream (lazily
// initialized).
private FileChannel channel;
private IFileSystem fileSystem = Platform.getFileSystem();
/**
* Constructs a new FileOutputStream on the File {@code file}. If the file
* exists, it is overwritten.
*
* @param file
* the file to which this stream writes.
* @throws FileNotFoundException
* if {@code file} cannot be opened for writing.
* @throws SecurityException
* if a {@code SecurityManager} is installed and it denies the
* write request.
* @see java.lang.SecurityManager#checkWrite(FileDescriptor)
*/
public FileOutputStream(File file) throws FileNotFoundException {
this(file, false);
}
/**
* Constructs a new FileOutputStream on the File {@code file}. The
* parameter {@code append} determines whether or not the file is opened and
* appended to or just opened and overwritten.
*
* @param file
* the file to which this stream writes.
* @param append
* indicates whether or not to append to an existing file.
* @throws FileNotFoundException
* if the {@code file} cannot be opened for writing.
* @throws SecurityException
* if a {@code SecurityManager} is installed and it denies the
* write request.
* @see java.lang.SecurityManager#checkWrite(FileDescriptor)
* @see java.lang.SecurityManager#checkWrite(String)
*/
public FileOutputStream(File file, boolean append)
throws FileNotFoundException {
super();
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(file.getPath());
}
fd = new FileDescriptor();
fd.descriptor = fileSystem.open(file.getAbsolutePath(),
append ? IFileSystem.O_APPEND : IFileSystem.O_WRONLY);
innerFD = true;
channel = FileChannelFactory.getFileChannel(this, fd.descriptor,
append ? IFileSystem.O_APPEND : IFileSystem.O_WRONLY);
}
/**
* Constructs a new FileOutputStream on the FileDescriptor {@code fd}. The
* file must already be open, therefore no {@code FileNotFoundException}
* will be thrown.
*
* @param fd
* the FileDescriptor to which this stream writes.
* @throws NullPointerException
* if {@code fd} is {@code null}.
* @throws SecurityException
* if a {@code SecurityManager} is installed and it denies the
* write request.
* @see java.lang.SecurityManager#checkWrite(FileDescriptor)
*/
public FileOutputStream(FileDescriptor fd) {
super();
if (fd == null) {
throw new NullPointerException();
}
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(fd);
}
this.fd = fd;
innerFD = false;
channel = FileChannelFactory.getFileChannel(this, fd.descriptor,
IFileSystem.O_WRONLY);
}
/**
* Constructs a new FileOutputStream on the file named {@code filename}. If
* the file exists, it is overwritten. The {@code filename} may be absolute
* or relative to the system property {@code "user.dir"}.
*
* @param filename
* the name of the file to which this stream writes.
* @throws FileNotFoundException
* if the file cannot be opened for writing.
* @throws SecurityException
* if a {@code SecurityManager} is installed and it denies the
* write request.
*/
public FileOutputStream(String filename) throws FileNotFoundException {
this(filename, false);
}
/**
* Constructs a new FileOutputStream on the file named {@code filename}.
* The parameter {@code append} determines whether or not the file is opened
* and appended to or just opened and overwritten. The {@code filename} may
* be absolute or relative to the system property {@code "user.dir"}.
*
* @param filename
* the name of the file to which this stream writes.
* @param append
* indicates whether or not to append to an existing file.
* @throws FileNotFoundException
* if the file cannot be opened for writing.
* @throws SecurityException
* if a {@code SecurityManager} is installed and it denies the
* write request.
*/
public FileOutputStream(String filename, boolean append)
throws FileNotFoundException {
this(new File(filename), append);
}
/**
* Closes this stream. This implementation closes the underlying operating
* system resources allocated to represent this stream.
*
* @throws IOException
* if an error occurs attempting to close this stream.
*/
@Override
public void close() throws IOException {
if (fd == null) {
// if fd is null, then the underlying file is not opened, so nothing
// to close
return;
}
if (channel != null) {
synchronized (channel) {
if (channel.isOpen() && fd.descriptor >= 0) {
channel.close();
}
}
}
synchronized (this) {
if (fd.valid() && innerFD) {
IoUtils.close(fd);
}
}
}
/**
* Frees any resources allocated for this stream before it is garbage
* collected. This method is called from the Java Virtual Machine.
*
* @throws IOException
* if an error occurs attempting to finalize this stream.
*/
@Override protected void finalize() throws IOException {
try {
close();
} finally {
try {
super.finalize();
} catch (Throwable t) {
throw new AssertionError(t);
}
}
}
/**
* Returns the FileChannel equivalent to this output stream.
* <p>
* The file channel is write-only and has an initial position within the
* file that is the same as the current position of this stream within the
* file. All changes made to the underlying file descriptor state via the
* channel are visible by the output stream and vice versa.
*
* @return the file channel representation for this stream.
*/
public FileChannel getChannel() {
return channel;
}
/**
* Returns a FileDescriptor which represents the lowest level representation
* of an operating system stream resource.
*
* @return a FileDescriptor representing this stream.
* @throws IOException
* if an error occurs attempting to get the FileDescriptor of
* this stream.
*/
public final FileDescriptor getFD() throws IOException {
return fd;
}
/**
* Writes the entire contents of the byte array {@code buffer} to this
* stream.
*
* @param buffer
* the buffer to be written to the file.
* @throws IOException
* if this stream is closed or an error occurs attempting to
* write to this stream.
*/
@Override
public void write(byte[] buffer) throws IOException {
write(buffer, 0, buffer.length);
}
/**
* Writes {@code count} bytes from the byte array {@code buffer} starting at
* {@code offset} to this stream.
*
* @param buffer
* the buffer to write to this stream.
* @param offset
* the index of the first byte in {@code buffer} to write.
* @param count
* the number of bytes from {@code buffer} to write.
* @throws IndexOutOfBoundsException
* if {@code count < 0} or {@code offset < 0}, or if
* {@code count + offset} is greater than the length of
* {@code buffer}.
* @throws IOException
* if this stream is closed or an error occurs attempting to
* write to this stream.
* @throws NullPointerException
* if {@code buffer} is {@code null}.
*/
@Override
public void write(byte[] buffer, int offset, int count) throws IOException {
// BEGIN android-changed
// Exception priorities (in case of multiple errors) differ from
// RI, but are spec-compliant.
// removed redundant check, made implicit null check explicit,
// used (offset | count) < 0 instead of (offset < 0) || (count < 0)
// to safe one operation
if (buffer == null) {
throw new NullPointerException("buffer == null");
}
if ((count | offset) < 0 || count > buffer.length - offset) {
throw new IndexOutOfBoundsException();
}
// END android-changed
if (count == 0) {
return;
}
openCheck();
fileSystem.write(fd.descriptor, buffer, offset, count);
}
/**
* Writes the specified byte {@code oneByte} to this stream. Only the low
* order byte of the integer {@code oneByte} is written.
*
* @param oneByte
* the byte to be written.
* @throws IOException
* if this stream is closed an error occurs attempting to write
* to this stream.
*/
@Override
public void write(int oneByte) throws IOException {
openCheck();
byte[] byteArray = new byte[1];
byteArray[0] = (byte) oneByte;
fileSystem.write(fd.descriptor, byteArray, 0, 1);
}
private synchronized void openCheck() throws IOException {
if (fd.descriptor < 0) {
throw new IOException();
}
}
}
|