/** * Copyright (C) 2009 Future Invent Informationsmanagement GmbH. All rights * reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) any * later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ package org.fuin.utils4j; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; /** * A random access based file input stream. The readlimit argument * of the mark(int) method is ignored (there is no limit). */ public final class RandomAccessFileInputStream extends InputStream { private final RandomAccessFile file; private long mark = -1; /** * Constructor with file. * * @param file * File the stream operates on - Cannot be null. * @param mode * Access mode, as described in RandomAccessFile - * Cannot be null. * * @throws FileNotFoundException * If the mode is "r" but the given file object does not denote * an existing regular file, or if the mode begins with "rw" but * the given file object does not denote an existing, writable * regular file and a new regular file of that name cannot be * created, or if some other error occurs while opening or * creating the file. */ public RandomAccessFileInputStream(final File file, final String mode) throws FileNotFoundException { super(); Utils4J.checkNotNull("file", file); Utils4J.checkNotNull("mode", mode); this.file = new RandomAccessFile(file, mode); } /** * Constructor with input stream. The new stream shares the * RandomAccessFile with the argument. Be aware that closing * this stream will also close the file used by the argument! * * @param in * The RandomAccessFile instance from this argument * will be used - Cannot be null. */ public RandomAccessFileInputStream(final RandomAccessFileInputStream in) { super(); Utils4J.checkNotNull("in", in); file = in.getRandomAccessFile(); } /** * Constructor with output stream. The new stream shares the * RandomAccessFile with the argument. Be aware that closing * this stream will also close the file used by the argument! * * @param out * The RandomAccessFile instance from this argument * will be used - Cannot be null. */ public RandomAccessFileInputStream(final RandomAccessFileOutputStream out) { super(); Utils4J.checkNotNull("out", out); file = out.getRandomAccessFile(); } /** * {@inheritDoc} */ public final int read() throws IOException { return file.read(); } /** * {@inheritDoc} */ public final int read(final byte[] b) throws IOException { return file.read(b); } /** * {@inheritDoc} */ public final int read(final byte[] b, final int off, final int len) throws IOException { return file.read(b, off, len); } /** * {@inheritDoc} */ public final long skip(final long n) throws IOException { if (n > Integer.MAX_VALUE) { return super.skip(Integer.MAX_VALUE); } return file.skipBytes((int) n); } /** * Returns the size of the underlying file. * * @return Number of bytes. * * @throws IOException * IOException if an I/O error occurs. */ public final long fileSize() throws IOException { return file.length(); } /** * {@inheritDoc} */ public final int available() throws IOException { if (file.getFilePointer() > file.length()) { return 0; } final long avail = file.length() - file.getFilePointer(); if (avail > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } return (int) avail; } /** * {@inheritDoc} */ public final void close() throws IOException { if (file.getChannel().isOpen()) { file.close(); } } /** * {@inheritDoc} */ public synchronized void mark(final int readlimit) { if (file.getChannel().isOpen()) { try { mark = file.getFilePointer(); } catch (final IOException ex) { throw new RuntimeException(ex); } } } /** * {@inheritDoc} */ public final synchronized void reset() throws IOException { if (mark == -1) { throw new IOException("The method 'mark()' has not been called " + "since the stream was created!"); } file.seek(mark); } /** * {@inheritDoc} */ public final boolean markSupported() { return true; } /** * Returns the channel used by the random access file. * * @return Channel. */ public final FileChannel getChannel() { return file.getChannel(); } /** * Returns the underlying file. * * @return Random access file. */ final RandomAccessFile getRandomAccessFile() { return file; } /** * Sets the file-pointer offset, measured from the beginning of this file, * at which the next read or write occurs. The offset may be set beyond the * end of the file. Setting the offset beyond the end of the file does not * change the file length. The file length will change only by writing after * the offset has been set beyond the end of the file. * * @param pos * the offset position, measured in bytes from the beginning of * the file, at which to set the file pointer. * * @exception IOException * if pos is less than 0 or if an * I/O error occurs. */ public final void seek(final long pos) throws IOException { file.seek(pos); } /** * Lock the file. * * @param tryLockMax * Number of tries to lock before throwing an exception. * @param tryWaitMillis * Milliseconds to sleep between retries. * * @return FileLock. * * @throws LockingFailedException * Locking the file failed. */ public final FileLock lock(final int tryLockMax, final long tryWaitMillis) throws LockingFailedException { return Utils4J.lockRandomAccessFile(file, tryLockMax, tryWaitMillis); } }