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