FileInputStream.java revision 461d0d860814c68154d8dd06d24f94118f33d28a
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
18package java.io;
19
20import dalvik.system.CloseGuard;
21
22import java.nio.NioUtils;
23import java.nio.channels.FileChannel;
24import java.util.Arrays;
25import libcore.io.ErrnoException;
26import libcore.io.IoUtils;
27import libcore.io.Libcore;
28import libcore.io.Streams;
29import org.apache.harmony.luni.platform.IFileSystem;
30import org.apache.harmony.luni.platform.Platform;
31import static libcore.io.OsConstants.*;
32
33/**
34 * An input stream that reads bytes from a file.
35 * <pre>   {@code
36 *   File file = ...
37 *   InputStream in = null;
38 *   try {
39 *     in = new BufferedInputStream(new FileInputStream(file));
40 *     ...
41 *   } finally {
42 *     if (in != null) {
43 *       in.close();
44 *     }
45 *   }
46 * }</pre>
47 *
48 * <p>This stream is <strong>not buffered</strong>. Most callers should wrap
49 * this stream with a {@link BufferedInputStream}.
50 *
51 * <p>Use {@link FileReader} to read characters, as opposed to bytes, from a
52 * file.
53 *
54 * @see BufferedInputStream
55 * @see FileOutputStream
56 */
57public class FileInputStream extends InputStream implements Closeable {
58
59    private final FileDescriptor fd;
60
61    /** The unique file channel. Lazily initialized because it's rarely needed. */
62    private FileChannel channel;
63
64    private final boolean shouldCloseFd;
65
66    private final CloseGuard guard = CloseGuard.get();
67
68    /**
69     * Constructs a new {@code FileInputStream} that reads from {@code file}.
70     *
71     * @param file
72     *            the file from which this stream reads.
73     * @throws FileNotFoundException
74     *             if {@code file} does not exist.
75     */
76    public FileInputStream(File file) throws FileNotFoundException {
77        if (file == null) {
78            throw new NullPointerException("file == null");
79        }
80        fd = IoUtils.open(file.getAbsolutePath(), O_RDONLY);
81        shouldCloseFd = true;
82        guard.open("close");
83    }
84
85    /**
86     * Constructs a new {@code FileInputStream} that reads from {@code fd}.
87     *
88     * @param fd
89     *            the FileDescriptor from which this stream reads.
90     * @throws NullPointerException
91     *             if {@code fd} is {@code null}.
92     */
93    public FileInputStream(FileDescriptor fd) {
94        if (fd == null) {
95            throw new NullPointerException("fd == null");
96        }
97        this.fd = fd;
98        this.shouldCloseFd = false;
99        // Note that we do not call guard.open here because the
100        // FileDescriptor is not owned by the stream.
101    }
102
103    /**
104     * Equivalent to {@code new FileInputStream(new File(path))}.
105     */
106    public FileInputStream(String path) throws FileNotFoundException {
107        this(new File(path));
108    }
109
110    @Override
111    public int available() throws IOException {
112        return IoUtils.available(fd);
113    }
114
115    @Override
116    public void close() throws IOException {
117        guard.close();
118        synchronized (this) {
119            if (channel != null) {
120                channel.close();
121            }
122            if (shouldCloseFd && fd.valid()) {
123                IoUtils.close(fd);
124            }
125        }
126    }
127
128    /**
129     * Ensures that all resources for this stream are released when it is about
130     * to be garbage collected.
131     *
132     * @throws IOException
133     *             if an error occurs attempting to finalize this stream.
134     */
135    @Override protected void finalize() throws IOException {
136        try {
137            if (guard != null) {
138                guard.warnIfOpen();
139            }
140            close();
141        } finally {
142            try {
143                super.finalize();
144            } catch (Throwable t) {
145                // for consistency with the RI, we must override Object.finalize() to
146                // remove the 'throws Throwable' clause.
147                throw new AssertionError(t);
148            }
149        }
150    }
151
152    /**
153     * Returns a read-only {@link FileChannel} that shares its position with
154     * this stream.
155     */
156    public FileChannel getChannel() {
157        synchronized (this) {
158            if (channel == null) {
159                channel = NioUtils.newFileChannel(this, fd, O_RDONLY);
160            }
161            return channel;
162        }
163    }
164
165    /**
166     * Returns the underlying file descriptor.
167     */
168    public final FileDescriptor getFD() throws IOException {
169        return fd;
170    }
171
172    @Override public int read() throws IOException {
173        return Streams.readSingleByte(this);
174    }
175
176    @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
177        return IoUtils.read(fd, buffer, byteOffset, byteCount);
178    }
179
180    @Override
181    public long skip(long byteCount) throws IOException {
182        if (byteCount == 0) {
183            return 0;
184        }
185        if (byteCount < 0) {
186            throw new IOException("byteCount < 0: " + byteCount);
187        }
188        try {
189            // Try lseek(2). That returns the new offset, but we'll throw an
190            // exception if it couldn't perform exactly the seek we asked for.
191            Libcore.os.lseek(fd, byteCount, SEEK_CUR);
192            return byteCount;
193        } catch (ErrnoException errnoException) {
194            if (errnoException.errno == ESPIPE) {
195                // You can't seek on a pipe, so fall back to the superclass' implementation.
196                return super.skip(byteCount);
197            }
198            throw errnoException.rethrowAsIOException();
199        }
200    }
201}
202