blob: 7c73c2eb5620f652a829a1a9c181a5b6c117175a [file] [log] [blame]
/*
* 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();
}
}
}