View Javadoc
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 org.apache.commons.io.input;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.RandomAccessFile;
23  import java.util.Objects;
24  
25  /**
26   * Streams data from a {@link RandomAccessFile} starting at its current position.
27   *
28   * @since 2.8.0
29   */
30  public class RandomAccessFileInputStream extends InputStream {
31  
32      private final boolean closeOnClose;
33      private final RandomAccessFile randomAccessFile;
34  
35      /**
36       * Constructs a new instance configured to leave the underlying file open when this stream is closed.
37       *
38       * @param file The file to stream.
39       */
40      public RandomAccessFileInputStream(final RandomAccessFile file) {
41          this(file, false);
42      }
43  
44      /**
45       * Constructs a new instance.
46       *
47       * @param file The file to stream.
48       * @param closeOnClose Whether to close the underlying file when this stream is closed.
49       */
50      public RandomAccessFileInputStream(final RandomAccessFile file, final boolean closeOnClose) {
51          this.randomAccessFile = Objects.requireNonNull(file, "file");
52          this.closeOnClose = closeOnClose;
53      }
54  
55      /**
56       * Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream.
57       *
58       * If there are more than {@link Integer#MAX_VALUE} bytes available, return {@link Integer#MAX_VALUE}.
59       *
60       * @return An estimate of the number of bytes that can be read.
61       * @throws IOException If an I/O error occurs.
62       */
63      @Override
64      public int available() throws IOException {
65          final long avail = availableLong();
66          if (avail > Integer.MAX_VALUE) {
67              return Integer.MAX_VALUE;
68          }
69          return (int) avail;
70      }
71  
72      /**
73       * Returns the number of bytes that can be read (or skipped over) from this input stream.
74       *
75       * @return The number of bytes that can be read.
76       * @throws IOException If an I/O error occurs.
77       */
78      public long availableLong() throws IOException {
79          return randomAccessFile.length() - randomAccessFile.getFilePointer();
80      }
81  
82      @Override
83      public void close() throws IOException {
84          super.close();
85          if (closeOnClose) {
86              randomAccessFile.close();
87          }
88      }
89  
90      /**
91       * Gets the underlying file.
92       *
93       * @return the underlying file.
94       */
95      public RandomAccessFile getRandomAccessFile() {
96          return randomAccessFile;
97      }
98  
99      /**
100      * Returns whether to close the underlying file when this stream is closed.
101      *
102      * @return Whether to close the underlying file when this stream is closed.
103      */
104     public boolean isCloseOnClose() {
105         return closeOnClose;
106     }
107 
108     @Override
109     public int read() throws IOException {
110         return randomAccessFile.read();
111     }
112 
113     @Override
114     public int read(final byte[] bytes) throws IOException {
115         return randomAccessFile.read(bytes);
116     }
117 
118     @Override
119     public int read(final byte[] bytes, final int offset, final int length) throws IOException {
120         return randomAccessFile.read(bytes, offset, length);
121     }
122 
123     /**
124      * Delegates to the underlying file.
125      *
126      * @param position See {@link RandomAccessFile#seek(long)}.
127      * @throws IOException See {@link RandomAccessFile#seek(long)}.
128      * @see RandomAccessFile#seek(long)
129      */
130     private void seek(final long position) throws IOException {
131         randomAccessFile.seek(position);
132     }
133 
134     @Override
135     public long skip(final long skipCount) throws IOException {
136         if (skipCount <= 0) {
137             return 0;
138         }
139         final long filePointer = randomAccessFile.getFilePointer();
140         final long fileLength = randomAccessFile.length();
141         if (filePointer >= fileLength) {
142             return 0;
143         }
144         final long targetPos = filePointer + skipCount;
145         final long newPos = targetPos > fileLength ? fileLength - 1 : targetPos;
146         if (newPos > 0) {
147             seek(newPos);
148         }
149         return randomAccessFile.getFilePointer() - filePointer;
150     }
151 }