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: * FileInputStream is a class for reading bytes from a file. This class may also
029: * be used with other InputStreams, ie: BufferedInputStream, to read data from a
030: * file with buffering.
031: *
032: * @see FileOutputStream
033: */
034: public class FileInputStream extends InputStream implements Closeable {
035: /**
036: * The FileDescriptor representing this FileInputStream.
037: */
038: FileDescriptor fd;
039:
040: // The unique file channel associated with this FileInputStream (lazily
041: // initialized).
042: private FileChannel channel;
043:
044: boolean innerFD;
045:
046: private IFileSystem fileSystem = Platform.getFileSystem();
047:
048: private static class RepositioningLock {
049: }
050:
051: private Object repositioningLock = new RepositioningLock();
052:
053: /**
054: * Constructs a new FileInputStream on the File <code>file</code>. If the
055: * file does not exist, the <code>FileNotFoundException</code> is thrown.
056: *
057: * @param file
058: * the File on which to stream reads.
059: *
060: * @throws FileNotFoundException
061: * If the <code>file</code> is not found.
062: *
063: * @see java.lang.SecurityManager#checkRead(FileDescriptor)
064: * @see java.lang.SecurityManager#checkRead(String)
065: * @see java.lang.SecurityManager#checkRead(String, Object)
066: */
067: public FileInputStream(File file) throws FileNotFoundException {
068: super ();
069: SecurityManager security = System.getSecurityManager();
070: if (security != null) {
071: String filePath = (null == file ? null : file.getPath());
072: security.checkRead(filePath);
073: }
074: fd = new FileDescriptor();
075: fd.readOnly = true;
076: fd.descriptor = fileSystem.open(file.properPath(true),
077: IFileSystem.O_RDONLY);
078: innerFD = true;
079: channel = FileChannelFactory.getFileChannel(this ,
080: fd.descriptor, IFileSystem.O_RDONLY);
081: }
082:
083: /**
084: * Constructs a new FileInputStream on the FileDescriptor <code>fd</code>.
085: * The file must already be open, therefore no
086: * <code>FileNotFoundException</code> will be thrown.
087: *
088: * @param fd
089: * the FileDescriptor on which to stream reads.
090: *
091: * @see java.lang.SecurityManager#checkRead(FileDescriptor)
092: * @see java.lang.SecurityManager#checkRead(String)
093: * @see java.lang.SecurityManager#checkRead(String, Object)
094: */
095: public FileInputStream(FileDescriptor fd) {
096: super ();
097: if (fd == null) {
098: throw new NullPointerException();
099: }
100: SecurityManager security = System.getSecurityManager();
101: if (security != null) {
102: security.checkRead(fd);
103: }
104: this .fd = fd;
105: innerFD = false;
106: channel = FileChannelFactory.getFileChannel(this ,
107: fd.descriptor, IFileSystem.O_RDONLY);
108: }
109:
110: /**
111: * Constructs a new FileInputStream on the file named <code>fileName</code>.
112: * If the file does not exist, the <code>FileNotFoundException</code> is
113: * thrown. The <code>fileName</code> may be absolute or relative to the
114: * System property <code>"user.dir"</code>.
115: *
116: * @param fileName
117: * the file on which to stream reads.
118: *
119: * @throws FileNotFoundException
120: * If the <code>fileName</code> is not found.
121: */
122: public FileInputStream(String fileName)
123: throws FileNotFoundException {
124: this (null == fileName ? (File) null : new File(fileName));
125: }
126:
127: /**
128: * Answers a int representing then number of bytes that are available before
129: * this InputStream will block. This method always returns the size of the
130: * file minus the current position.
131: *
132: * @return the number of bytes available before blocking.
133: *
134: * @throws IOException
135: * If an error occurs in this stream.
136: */
137: @Override
138: public int available() throws IOException {
139: openCheck();
140: synchronized (repositioningLock) {
141: // stdin requires special handling
142: if (fd == FileDescriptor.in) {
143: return (int) fileSystem.ttyAvailable();
144: }
145:
146: long currentPosition = fileSystem.seek(fd.descriptor, 0L,
147: IFileSystem.SEEK_CUR);
148: long endOfFilePosition = fileSystem.seek(fd.descriptor, 0L,
149: IFileSystem.SEEK_END);
150: fileSystem.seek(fd.descriptor, currentPosition,
151: IFileSystem.SEEK_SET);
152: return (int) (endOfFilePosition - currentPosition);
153: }
154: }
155:
156: /**
157: * Close the FileInputStream.
158: *
159: * @throws IOException
160: * If an error occurs attempting to close this FileInputStream.
161: */
162: @Override
163: public void close() throws IOException {
164: if (fd == null) {
165: // if fd is null, then the underlying file is not opened, so nothing
166: // to close
167: return;
168: }
169: if (channel != null) {
170: synchronized (channel) {
171: if (channel.isOpen()) {
172: channel.close();
173: }
174: }
175: }
176: synchronized (this ) {
177: if (fd.descriptor >= 0 && innerFD) {
178: fileSystem.close(fd.descriptor);
179: fd.descriptor = -1;
180: }
181: }
182: }
183:
184: /**
185: * This method ensures that all resources for this file are released when it
186: * is about to be garbage collected.
187: *
188: * @throws IOException
189: * If an error occurs attempting to finalize this
190: * FileInputStream.
191: */
192: @Override
193: protected void finalize() throws IOException {
194: close();
195: }
196:
197: /**
198: * Answers the FileChannel equivalent to this input stream.
199: * <p>
200: * The file channel is read-only and has an initial position within the file
201: * that is the same as the current position of the FileInputStream within
202: * the file. All changes made to the underlying file descriptor state via
203: * the channel are visible by the input stream and vice versa.
204: * </p>
205: *
206: * @return the file channel representation for this FileInputStream.
207: */
208: public FileChannel getChannel() {
209: return channel;
210: }
211:
212: /**
213: * Answers the FileDescriptor representing the operating system resource for
214: * this FileInputStream.
215: *
216: * @return the FileDescriptor for this FileInputStream.
217: *
218: * @throws IOException
219: * If an error occurs attempting to get the FileDescriptor of
220: * this FileInputStream.
221: */
222: public final FileDescriptor getFD() throws IOException {
223: return fd;
224: }
225:
226: /**
227: * Reads a single byte from this FileInputStream and returns the result as
228: * an int. The low-order byte is returned or -1 of the end of stream was
229: * encountered.
230: *
231: * @return the byte read or -1 if end of stream.
232: *
233: * @throws IOException
234: * If the stream is already closed or another IOException
235: * occurs.
236: */
237: @Override
238: public int read() throws IOException {
239: byte[] readed = new byte[1];
240: int result = read(readed, 0, 1);
241: return result == -1 ? -1 : readed[0] & 0xff;
242: }
243:
244: /**
245: * Reads bytes from the FileInputStream and stores them in byte array
246: * <code>buffer</code>. Answer the number of bytes actually read or -1 if
247: * no bytes were read and end of stream was encountered.
248: *
249: * @param buffer
250: * the byte array in which to store the read bytes.
251: * @return the number of bytes actually read or -1 if end of stream.
252: *
253: * @throws IOException
254: * If the stream is already closed or another IOException
255: * occurs.
256: */
257: @Override
258: public int read(byte[] buffer) throws IOException {
259: return read(buffer, 0, buffer.length);
260: }
261:
262: /**
263: * Reads at most <code>count</code> bytes from the FileInputStream and
264: * stores them in byte array <code>buffer</code> starting at
265: * <code>offset</code>. Answer the number of bytes actually read or -1 if
266: * no bytes were read and end of stream was encountered.
267: *
268: * @param buffer
269: * the byte array in which to store the read bytes.
270: * @param offset
271: * the offset in <code>buffer</code> to store the read bytes.
272: * @param count
273: * the maximum number of bytes to store in <code>buffer</code>.
274: * @return the number of bytes actually read or -1 if end of stream.
275: *
276: * @throws IOException
277: * If the stream is already closed or another IOException
278: * occurs.
279: */
280: @Override
281: public int read(byte[] buffer, int offset, int count)
282: throws IOException {
283: if (count > buffer.length - offset || count < 0 || offset < 0) {
284: throw new IndexOutOfBoundsException();
285: }
286: if (0 == count) {
287: return 0;
288: }
289: openCheck();
290: synchronized (repositioningLock) {
291: // stdin requires special handling
292: if (fd == FileDescriptor.in) {
293: return (int) fileSystem.ttyRead(buffer, offset, count);
294: }
295: return (int) fileSystem.read(fd.descriptor, buffer, offset,
296: count);
297: }
298: }
299:
300: /**
301: * Skips <code>count</code> number of bytes in this FileInputStream.
302: * Subsequent <code>read()</code>'s will not return these bytes unless
303: * <code>reset()</code> is used. This method may perform multiple reads to
304: * read <code>count</code> bytes.
305: *
306: * @param count
307: * the number of bytes to skip.
308: * @return the number of bytes actually skipped.
309: *
310: * @throws IOException
311: * If the stream is already closed or another IOException
312: * occurs.
313: */
314: @Override
315: public long skip(long count) throws IOException {
316: openCheck();
317:
318: if (count == 0) {
319: return 0;
320: }
321: if (count < 0) {
322: // KA013=Number of bytes to skip cannot be negative
323: throw new IOException(Msg.getString("KA013")); //$NON-NLS-1$
324: }
325:
326: // stdin requires special handling
327: if (fd == FileDescriptor.in) {
328: // Read and discard count bytes in 8k chunks
329: long skipped = 0, numRead;
330: int chunk = count < 8192 ? (int) count : 8192;
331: byte[] buffer = new byte[chunk];
332: for (long i = count / chunk; i >= 0; i--) {
333: numRead = fileSystem.ttyRead(buffer, 0, chunk);
334: skipped += numRead;
335: if (numRead < chunk) {
336: return skipped;
337: }
338: }
339: return skipped;
340: }
341:
342: synchronized (repositioningLock) {
343: final long currentPosition = fileSystem.seek(fd.descriptor,
344: 0L, IFileSystem.SEEK_CUR);
345: final long newPosition = fileSystem.seek(fd.descriptor,
346: currentPosition + count, IFileSystem.SEEK_SET);
347: return newPosition - currentPosition;
348: }
349: }
350:
351: private synchronized void openCheck() throws IOException {
352: if (fd.descriptor < 0) {
353: throw new IOException();
354: }
355: }
356: }
|