001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import java.nio.channels.FileChannel;
021:
022: import org.apache.harmony.luni.platform.IFileSystem;
023: import org.apache.harmony.luni.platform.Platform;
024: import org.apache.harmony.luni.util.Msg;
025: import org.apache.harmony.nio.FileChannelFactory;
026:
027: /**
028: * FileOutputStream is a class whose underlying stream is represented by a file
029: * in the operating system. The bytes that are written to this stream are passed
030: * directly to the underlying operating system equivalent function. Since
031: * overhead may be high in writing to the OS, FileOutputStreams are usually
032: * wrapped with a BufferedOutputStream to reduce the number of times the OS is
033: * called.
034: * <p>
035: * <code>BufferedOutputStream buf = new BufferedOutputStream(new FileOutputStream("aFile.txt"));</code>
036: *
037: * @see FileInputStream
038: */
039: public class FileOutputStream extends OutputStream implements Closeable {
040:
041: /**
042: * The FileDescriptor representing this FileOutputStream.
043: */
044: FileDescriptor fd;
045:
046: boolean innerFD;
047:
048: // The unique file channel associated with this FileInputStream (lazily
049: // initialized).
050: private FileChannel channel;
051:
052: private IFileSystem fileSystem = Platform.getFileSystem();
053:
054: /**
055: * Constructs a new FileOutputStream on the File <code>file</code>. If
056: * the file exists, it is written over. See the constructor which can append
057: * to the file if so desired.
058: *
059: * @param file
060: * the File on which to stream reads.
061: *
062: * @throws FileNotFoundException
063: * If the <code>file</code> cannot be opened for writing.
064: *
065: * @see java.lang.SecurityManager#checkWrite(FileDescriptor)
066: */
067: public FileOutputStream(File file) throws FileNotFoundException {
068: this (file, false);
069: }
070:
071: /**
072: * Constructs a new FileOutputStream on the File <code>file</code>. If
073: * the file exists, it is written over. The parameter <code>append</code>
074: * determines whether or not the file is opened and appended to or just
075: * opened empty.
076: *
077: * @param file
078: * the File on which to stream reads.
079: * @param append
080: * a boolean indicating whether or not to append to an existing
081: * file.
082: *
083: * @throws FileNotFoundException
084: * If the <code>file</code> cannot be opened for writing.
085: *
086: * @see java.lang.SecurityManager#checkWrite(FileDescriptor)
087: * @see java.lang.SecurityManager#checkWrite(String)
088: */
089: public FileOutputStream(File file, boolean append)
090: throws FileNotFoundException {
091: super ();
092: SecurityManager security = System.getSecurityManager();
093: if (security != null) {
094: security.checkWrite(file.getPath());
095: }
096: fd = new FileDescriptor();
097: fd.descriptor = fileSystem.open(file.properPath(true),
098: append ? IFileSystem.O_APPEND : IFileSystem.O_WRONLY);
099: innerFD = true;
100: channel = FileChannelFactory.getFileChannel(this ,
101: fd.descriptor, append ? IFileSystem.O_APPEND
102: : IFileSystem.O_WRONLY);
103: }
104:
105: /**
106: * Constructs a new FileOutputStream on the FileDescriptor <code>fd</code>.
107: * The file must already be open, therefore no <code>FileIOException</code>
108: * will be thrown.
109: *
110: * @param fd
111: * the FileDescriptor on which to stream writes.
112: *
113: * @see java.lang.SecurityManager#checkWrite(FileDescriptor)
114: */
115: public FileOutputStream(FileDescriptor fd) {
116: super ();
117: if (fd == null) {
118: throw new NullPointerException(Msg.getString("K006c")); //$NON-NLS-1$
119: }
120: SecurityManager security = System.getSecurityManager();
121: if (security != null) {
122: security.checkWrite(fd);
123: }
124: this .fd = fd;
125: innerFD = false;
126: channel = FileChannelFactory.getFileChannel(this ,
127: fd.descriptor, IFileSystem.O_WRONLY);
128: }
129:
130: /**
131: * Constructs a new FileOutputStream on the file named <code>fileName</code>.
132: * If the file exists, it is written over. See the constructor which can
133: * append to the file if so desired. The <code>fileName</code> may be
134: * absolute or relative to the System property <code>"user.dir"</code>.
135: *
136: * @param filename
137: * the file on which to stream writes.
138: *
139: * @throws FileNotFoundException
140: * If the <code>filename</code> cannot be opened for writing.
141: */
142: public FileOutputStream(String filename)
143: throws FileNotFoundException {
144: this (filename, false);
145: }
146:
147: /**
148: * Constructs a new FileOutputStream on the file named <code>filename</code>.
149: * If the file exists, it is written over. The parameter <code>append</code>
150: * determines whether or not the file is opened and appended to or just
151: * opened empty. The <code>filename</code> may be absolute or relative to
152: * the System property <code>"user.dir"</code>.
153: *
154: * @param filename
155: * the file on which to stream writes.
156: * @param append
157: * a boolean indicating whether or not to append to an existing
158: * file.
159: *
160: * @throws FileNotFoundException
161: * If the <code>filename</code> cannot be opened for writing.
162: */
163: public FileOutputStream(String filename, boolean append)
164: throws FileNotFoundException {
165: this (new File(filename), append);
166: }
167:
168: /**
169: * Close the FileOutputStream. This implementation closes the underlying OS
170: * resources allocated to represent this stream.
171: *
172: * @throws IOException
173: * If an error occurs attempting to close this FileOutputStream.
174: */
175: @Override
176: public void close() throws IOException {
177: if (fd == null) {
178: // if fd is null, then the underlying file is not opened, so nothing
179: // to close
180: return;
181: }
182:
183: if (channel != null) {
184: synchronized (channel) {
185: if (channel.isOpen() && fd.descriptor >= 0) {
186: channel.close();
187: }
188: }
189: }
190:
191: synchronized (this ) {
192: if (fd.descriptor >= 0 && innerFD) {
193: fileSystem.close(fd.descriptor);
194: fd.descriptor = -1;
195: }
196: }
197: }
198:
199: /**
200: * Frees any resources allocated to represent this FileOutputStream before
201: * it is garbage collected. This method is called from the Java Virtual
202: * Machine.
203: *
204: * @throws IOException
205: * If an error occurs attempting to finalize this
206: * FileOutputStream.
207: */
208: @Override
209: protected void finalize() throws IOException {
210: close();
211: }
212:
213: /**
214: * Answers the FileChannel equivalent to this output stream.
215: * <p>
216: * The file channel is write-only and has an initial position within the
217: * file that is the same as the current position of this FileOutputStream
218: * within the file. All changes made to the underlying file descriptor state
219: * via the channel are visible by the output stream and vice versa.
220: * </p>
221: *
222: * @return the file channel representation for this FileOutputStream.
223: */
224: public FileChannel getChannel() {
225: return channel;
226: }
227:
228: /**
229: * Answers a FileDescriptor which represents the lowest level representation
230: * of a OS stream resource.
231: *
232: * @return a FileDescriptor representing this FileOutputStream.
233: *
234: * @throws IOException
235: * If the Stream is already closed and there is no
236: * FileDescriptor.
237: */
238: public final FileDescriptor getFD() throws IOException {
239: return fd;
240: }
241:
242: /**
243: * Writes the entire contents of the byte array <code>buffer</code> to
244: * this FileOutputStream.
245: *
246: * @param buffer
247: * the buffer to be written
248: *
249: * @throws IOException
250: * If an error occurs attempting to write to this
251: * FileOutputStream.
252: */
253: @Override
254: public void write(byte[] buffer) throws IOException {
255: write(buffer, 0, buffer.length);
256: }
257:
258: /**
259: * Writes <code>count</code> <code>bytes</code> from the byte array
260: * <code>buffer</code> starting at <code>offset</code> to this
261: * FileOutputStream.
262: *
263: * @param buffer
264: * the buffer to be written
265: * @param offset
266: * offset in buffer to get bytes
267: * @param count
268: * number of bytes in buffer to write
269: *
270: * @throws IOException
271: * If an error occurs attempting to write to this
272: * FileOutputStream.
273: * @throws IndexOutOfBoundsException
274: * If offset or count are outside of bounds.
275: * @throws NullPointerException
276: * If buffer is <code>null</code>.
277: */
278: @Override
279: public void write(byte[] buffer, int offset, int count)
280: throws IOException {
281: if (buffer == null) {
282: throw new NullPointerException();
283: }
284: if (count < 0 || offset < 0 || offset > buffer.length
285: || count > buffer.length - offset) {
286: throw new IndexOutOfBoundsException();
287: }
288:
289: if (count == 0) {
290: return;
291: }
292:
293: openCheck();
294: fileSystem.write(fd.descriptor, buffer, offset, count);
295: }
296:
297: /**
298: * Writes the specified byte <code>oneByte</code> to this
299: * FileOutputStream. Only the low order byte of <code>oneByte</code> is
300: * written.
301: *
302: * @param oneByte
303: * the byte to be written
304: *
305: * @throws IOException
306: * If an error occurs attempting to write to this
307: * FileOutputStream.
308: */
309: @Override
310: public void write(int oneByte) throws IOException {
311: openCheck();
312: byte[] byteArray = new byte[1];
313: byteArray[0] = (byte) oneByte;
314: fileSystem.write(fd.descriptor, byteArray, 0, 1);
315: }
316:
317: private synchronized void openCheck() throws IOException {
318: if (fd.descriptor < 0) {
319: throw new IOException();
320: }
321: }
322: }
|