Home | History | Annotate | Download | only in io
      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 java.nio.channels.FileChannel;
     21 
     22 import org.apache.harmony.luni.platform.IFileSystem;
     23 import org.apache.harmony.luni.platform.Platform;
     24 import org.apache.harmony.luni.util.Msg;
     25 import org.apache.harmony.nio.FileChannelFactory;
     26 
     27 /**
     28  * A specialized {@link InputStream} that reads from a file in the file system.
     29  * All read requests made by calling methods in this class are directly
     30  * forwarded to the equivalent function of the underlying operating system.
     31  * Since this may induce some performance penalty, in particular if many small
     32  * read requests are made, a FileInputStream is often wrapped by a
     33  * BufferedInputStream.
     34  *
     35  * @see BufferedInputStream
     36  * @see FileOutputStream
     37  */
     38 public class FileInputStream extends InputStream implements Closeable {
     39     /**
     40      * The {@link FileDescriptor} representing this {@code FileInputStream}.
     41      */
     42     FileDescriptor fd;
     43 
     44     // The unique file channel associated with this FileInputStream (lazily
     45     // initialized).
     46     private FileChannel channel;
     47 
     48     boolean innerFD;
     49 
     50     private IFileSystem fileSystem = Platform.getFileSystem();
     51 
     52     private static class RepositioningLock {
     53     }
     54 
     55     private Object repositioningLock = new RepositioningLock();
     56 
     57     /**
     58      * Constructs a new {@code FileInputStream} based on {@code file}.
     59      *
     60      * @param file
     61      *            the file from which this stream reads.
     62      * @throws FileNotFoundException
     63      *             if {@code file} does not exist.
     64      * @throws SecurityException
     65      *             if a {@code SecurityManager} is installed and it denies the
     66      *             read request.
     67      */
     68     public FileInputStream(File file) throws FileNotFoundException {
     69         super();
     70         SecurityManager security = System.getSecurityManager();
     71         if (security != null) {
     72             // For compatibility, nulls are passed to the manager.
     73             String filePath = (null == file ? null : file.getPath());
     74             security.checkRead(filePath);
     75         }
     76         if (file == null) {
     77             // KA001=Argument must not be null
     78             throw new NullPointerException(Msg.getString("KA001")); //$NON-NLS-1$
     79         }
     80         fd = new FileDescriptor();
     81         fd.readOnly = true;
     82         fd.descriptor = fileSystem.open(file.pathBytes, IFileSystem.O_RDONLY);
     83         innerFD = true;
     84         // BEGIN android-removed
     85         // channel = FileChannelFactory.getFileChannel(this, fd.descriptor,
     86         //         IFileSystem.O_RDONLY);
     87         // END android-removed
     88     }
     89 
     90     /**
     91      * Constructs a new {@code FileInputStream} on the {@link FileDescriptor}
     92      * {@code fd}. The file must already be open, therefore no
     93      * {@code FileNotFoundException} will be thrown.
     94      *
     95      * @param fd
     96      *            the FileDescriptor from which this stream reads.
     97      * @throws NullPointerException
     98      *             if {@code fd} is {@code null}.
     99      * @throws SecurityException
    100      *             if a {@code SecurityManager} is installed and it denies the
    101      *             read request.
    102      */
    103     public FileInputStream(FileDescriptor fd) {
    104         super();
    105         if (fd == null) {
    106             throw new NullPointerException();
    107         }
    108         SecurityManager security = System.getSecurityManager();
    109         if (security != null) {
    110             security.checkRead(fd);
    111         }
    112         this.fd = fd;
    113         innerFD = false;
    114         // BEGIN android-removed
    115         // channel = FileChannelFactory.getFileChannel(this, fd.descriptor,
    116         //         IFileSystem.O_RDONLY);
    117         // END android-removed
    118     }
    119 
    120     /**
    121      * Constructs a new {@code FileInputStream} on the file named
    122      * {@code fileName}. The path of {@code fileName} may be absolute or
    123      * relative to the system property {@code "user.dir"}.
    124      *
    125      * @param fileName
    126      *            the path and name of the file from which this stream reads.
    127      * @throws FileNotFoundException
    128      *             if there is no file named {@code fileName}.
    129      * @throws SecurityException
    130      *             if a {@code SecurityManager} is installed and it denies the
    131      *             read request.
    132      */
    133     public FileInputStream(String fileName) throws FileNotFoundException {
    134         this(null == fileName ? (File) null : new File(fileName));
    135     }
    136 
    137     @Override
    138     public int available() throws IOException {
    139         openCheck();
    140 
    141         // BEGIN android-added
    142 
    143         // Android always uses the ioctl() method of determining bytes
    144         // available. See the long discussion in
    145         // org_apache_harmony_luni_platform_OSFileSystem.cpp about its
    146         // use.
    147 
    148         return fileSystem.ioctlAvailable(fd.descriptor);
    149         // END android-added
    150 
    151         // BEGIN android-deleted
    152         // synchronized (repositioningLock) {
    153         //     // stdin requires special handling
    154         //     if (fd == FileDescriptor.in) {
    155         //         return (int) fileSystem.ttyAvailable();
    156         //     }
    157         //
    158         //     long currentPosition = fileSystem.seek(fd.descriptor, 0L,
    159         //             IFileSystem.SEEK_CUR);
    160         //     long endOfFilePosition = fileSystem.seek(fd.descriptor, 0L,
    161         //             IFileSystem.SEEK_END);
    162         //     fileSystem.seek(fd.descriptor, currentPosition,
    163         //             IFileSystem.SEEK_SET);
    164         //     return (int) (endOfFilePosition - currentPosition);
    165         // }
    166         // END android-deleted
    167     }
    168 
    169     /**
    170      * Closes this stream.
    171      *
    172      * @throws IOException
    173      *             if an error occurs attempting to close this stream.
    174      */
    175     @Override
    176     public void close() throws IOException {
    177         // BEGIN android-changed
    178         synchronized (this) {
    179             if (channel != null && channel.isOpen()) {
    180                 channel.close();
    181                 channel = null;
    182             }
    183             if (fd != null && fd.descriptor >= 0 && innerFD) {
    184                 fileSystem.close(fd.descriptor);
    185                 fd.descriptor = -1;
    186             }
    187         }
    188         // END android-changed
    189     }
    190 
    191     /**
    192      * Ensures that all resources for this stream are released when it is about
    193      * to be garbage collected.
    194      *
    195      * @throws IOException
    196      *             if an error occurs attempting to finalize this stream.
    197      */
    198     @Override
    199     protected void finalize() throws IOException {
    200         close();
    201     }
    202 
    203     /**
    204      * Returns the {@link FileChannel} equivalent to this input stream.
    205      * <p>
    206      * The file channel is read-only and has an initial position within the file
    207      * that is the same as the current position of this stream within the file.
    208      * All changes made to the underlying file descriptor state via the channel
    209      * are visible by the input stream and vice versa.
    210      *
    211      * @return the file channel for this stream.
    212      */
    213     public FileChannel getChannel() {
    214         // BEGIN android-changed
    215         synchronized(this) {
    216             if (channel == null) {
    217                 channel = FileChannelFactory.getFileChannel(this, fd.descriptor,
    218                         IFileSystem.O_RDONLY);
    219             }
    220             return channel;
    221         }
    222         // END android-changed
    223     }
    224 
    225     /**
    226      * Returns the {@link FileDescriptor} representing the operating system
    227      * resource for this stream.
    228      *
    229      * @return the {@code FileDescriptor} for this stream.
    230      * @throws IOException
    231      *             if an error occurs while getting this stream's
    232      *             {@code FileDescriptor}.
    233      */
    234     public final FileDescriptor getFD() throws IOException {
    235         return fd;
    236     }
    237 
    238     /**
    239      * Reads a single byte from this stream and returns it as an integer in the
    240      * range from 0 to 255. Returns -1 if the end of this stream has been
    241      * reached.
    242      *
    243      * @return the byte read or -1 if the end of this stream has been reached.
    244      * @throws IOException
    245      *             if this stream is closed or another I/O error occurs.
    246      */
    247     @Override
    248     public int read() throws IOException {
    249         byte[] readed = new byte[1];
    250         int result = read(readed, 0, 1);
    251         return result == -1 ? -1 : readed[0] & 0xff;
    252     }
    253 
    254     /**
    255      * Reads bytes from this stream and stores them in the byte array
    256      * {@code buffer}.
    257      *
    258      * @param buffer
    259      *            the byte array in which to store the bytes read.
    260      * @return the number of bytes actually read or -1 if the end of the stream
    261      *         has been reached.
    262      * @throws IOException
    263      *             if this stream is closed or another I/O error occurs.
    264      */
    265     @Override
    266     public int read(byte[] buffer) throws IOException {
    267         return read(buffer, 0, buffer.length);
    268     }
    269 
    270     /**
    271      * Reads at most {@code count} bytes from this stream and stores them in the
    272      * byte array {@code buffer} starting at {@code offset}.
    273      *
    274      * @param buffer
    275      *            the byte array in which to store the bytes read.
    276      * @param offset
    277      *            the initial position in {@code buffer} to store the bytes read
    278      *            from this stream.
    279      * @param count
    280      *            the maximum number of bytes to store in {@code buffer}.
    281      * @return the number of bytes actually read or -1 if the end of the stream
    282      *         has been reached.
    283      * @throws IndexOutOfBoundsException
    284      *             if {@code offset < 0} or {@code count < 0}, or if
    285      *             {@code offset + count} is greater than the size of
    286      *             {@code buffer}.
    287      * @throws IOException
    288      *             if the stream is closed or another IOException occurs.
    289      */
    290     @Override
    291     public int read(byte[] buffer, int offset, int count) throws IOException {
    292         // BEGIN android-changed
    293         // Exception priorities (in case of multiple errors) differ from
    294         // RI, but are spec-compliant.
    295         // made implicit null check explicit,
    296         // used (offset | count) < 0 instead of (offset < 0) || (count < 0)
    297         // to safe one operation
    298         if (buffer == null) {
    299             throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$
    300         }
    301         if ((count | offset) < 0 || count > buffer.length - offset) {
    302             throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$
    303         }
    304         // END android-changed
    305         if (0 == count) {
    306             return 0;
    307         }
    308         openCheck();
    309         synchronized (repositioningLock) {
    310             // BEGIN android-changed
    311             // If you only support Linux, there's nothing special about stdin.
    312             return (int) fileSystem.read(fd.descriptor, buffer, offset, count);
    313             // END android-changed
    314         }
    315     }
    316 
    317     /**
    318      * Skips {@code count} number of bytes in this stream. Subsequent
    319      * {@code read()}s will not return these bytes unless {@code reset()} is
    320      * used. If the underlying stream is unseekable, an IOException is thrown.
    321      *
    322      * @param count
    323      *            the number of bytes to skip.
    324      * @return the number of bytes actually skipped.
    325      * @throws IOException
    326      *             if {@code count < 0}, this stream is closed or unseekable,
    327      *             or another IOException occurs.
    328      */
    329     @Override
    330     public long skip(long count) throws IOException {
    331         openCheck();
    332 
    333         if (count == 0) {
    334             return 0;
    335         }
    336         if (count < 0) {
    337             // KA013=Number of bytes to skip cannot be negative
    338             throw new IOException(Msg.getString("KA013")); //$NON-NLS-1$
    339         }
    340 
    341         // BEGIN android-changed
    342         // The RI doesn't treat stdin as special. It throws IOException for
    343         // all non-seekable streams, so we do too. If you did want to support
    344         // non-seekable streams, the best way to do it would be to recognize
    345         // when lseek(2) fails with ESPIPE and call super.skip(count).
    346         synchronized (repositioningLock) {
    347             // Our seek returns the new offset, but we know it will throw an
    348             // exception if it couldn't perform exactly the seek we asked for.
    349             fileSystem.seek(fd.descriptor, count, IFileSystem.SEEK_CUR);
    350             return count;
    351         }
    352         // END android-changed
    353     }
    354 
    355     private synchronized void openCheck() throws IOException {
    356         if (fd.descriptor < 0) {
    357             throw new IOException();
    358         }
    359     }
    360 }
    361