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 018package org.apache.commons.io.input; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.RandomAccessFile; 023import java.util.Objects; 024 025/** 026 * Streams data from a {@link RandomAccessFile} starting at its current position. 027 * 028 * @since 2.8.0 029 */ 030public class RandomAccessFileInputStream extends InputStream { 031 032 private final boolean closeOnClose; 033 private final RandomAccessFile randomAccessFile; 034 035 /** 036 * Constructs a new instance configured to leave the underlying file open when this stream is closed. 037 * 038 * @param file The file to stream. 039 */ 040 public RandomAccessFileInputStream(final RandomAccessFile file) { 041 this(file, false); 042 } 043 044 /** 045 * Constructs a new instance. 046 * 047 * @param file The file to stream. 048 * @param closeOnClose Whether to close the underlying file when this stream is closed. 049 */ 050 public RandomAccessFileInputStream(final RandomAccessFile file, final boolean closeOnClose) { 051 this.randomAccessFile = Objects.requireNonNull(file, "file"); 052 this.closeOnClose = closeOnClose; 053 } 054 055 /** 056 * Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream. 057 * 058 * If there are more than {@link Integer#MAX_VALUE} bytes available, return {@link Integer#MAX_VALUE}. 059 * 060 * @return An estimate of the number of bytes that can be read. 061 * @throws IOException If an I/O error occurs. 062 */ 063 @Override 064 public int available() throws IOException { 065 final long avail = availableLong(); 066 if (avail > Integer.MAX_VALUE) { 067 return Integer.MAX_VALUE; 068 } 069 return (int) avail; 070 } 071 072 /** 073 * Returns the number of bytes that can be read (or skipped over) from this input stream. 074 * 075 * @return The number of bytes that can be read. 076 * @throws IOException If an I/O error occurs. 077 */ 078 public long availableLong() throws IOException { 079 return randomAccessFile.length() - randomAccessFile.getFilePointer(); 080 } 081 082 @Override 083 public void close() throws IOException { 084 super.close(); 085 if (closeOnClose) { 086 randomAccessFile.close(); 087 } 088 } 089 090 /** 091 * Gets the underlying file. 092 * 093 * @return the underlying file. 094 */ 095 public RandomAccessFile getRandomAccessFile() { 096 return randomAccessFile; 097 } 098 099 /** 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}