Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* $URL$
* $Id$
*
* Copyright 1997-2006 Day Management AG
* Barfuesserplatz 6, 4001 Basel, Switzerland
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Day Management AG, ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Day.
*/
package com.day.io;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.FileNotFoundException;
import java.util.zip.Inflater;
import java.util.zip.DataFormatException;
import java.util.zip.ZipException;
/**
* RegionFileInputStream implements an input stream that streams
* the contents of a region of a file. The reading is buffered. The file is
* openend upon the first access. this helps minimizing the total number of
* open files.
*
* @author tripod
* @version $Rev$, $Date$
*/
public class RegionFileInputStream extends InputStream {
/**
* The CVS/SVN id
*/
static final String CVS_ID = "$URL$ $Rev$ $Date$";
/** the maximum size of a buffer */
static final int MAX_BUFFER = 8192;
/** the buffer for the data */
private byte[] buffer;
/** one byte buffer for 'read' */
private final byte[] singleByteBuf = new byte[1];
/** the read offset within the buffer */
private int pos = 0;
/** the end of valid data available in the buffer */
private int end = 0;
/** the absolute position of the start of this region */
private final long regionStart;
/** the absolute position of the end of this region */
private final long regionEnd;
/** last sync point */
private long lastSyncPoint = -1;
/** the position at the last sync point */
private long lastSyncPos = 0;
/** the relative length of this region */
private long length;
/** the relative position (actual bytes returned, if inflating) */
private long position;
/** the position when 'mark' was invoked. */
private long markedPosition = 0;
/** the file this stream was created from */
private File file;
/** the random access file this stream operates on */
private RandomAccessFile raf;
/** the inflater to use for compressed data */
private Inflater inflater;
/** flag indicating if inflater is needed */
private boolean isInflating;
/** true when eof is reached */
private boolean reachEOF = false;
/** true when stream is closed */
private IOException closedBy = null;
/** defines that the region can have multiple zip streams */
private boolean hasMultiStreams = true;
/**
* Same as {@link #RegionFileInputStream(File, boolean)} with inflate set to
* false.
*
* @param file the file to read from.
*
* @throws FileNotFoundException if the file was not found
* @throws IOException if an I/O error occurs
*/
public RegionFileInputStream(File file)
throws IOException, FileNotFoundException {
this(file, false);
}
/**
* Same as {@link #RegionFileInputStream(File, long, long, boolean)} where
* offset is 0 and len the length of the file.
*
* @param file the file to read from.
* @param inflate indicates if the file is compressed
*
* @throws FileNotFoundException if the file was not found
* @throws IOException if an I/O error occurs
*/
public RegionFileInputStream(File file, boolean inflate)
throws IOException, FileNotFoundException {
this(file, 0, file.length(), inflate);
}
/**
* Creates a new input stream that reads for the region [off, off+len] of
* the given file. If the region is greater than the length of the file an
* IOException os thrown.
*
* @param file the file to read from.
* @param off the absolute offset in the file to read from
* @param len the total length of the region
*
* @throws FileNotFoundException if the file was not found
* @throws IOException if an I/O error occurs
*/
public RegionFileInputStream(File file, long off, long len)
throws IOException, FileNotFoundException {
this(file, off, len, false);
}
/**
* Creates a new input stream that reads for the region [off, off+len] of
* the given file. If the region is greater than the length of the file an
* IOException os thrown.
*
* @param file the file to read from.
* @param off the absolute offset in the file to read from
* @param len the total length of the region
* @param inflate indicates if the file is compressed
*
* @throws FileNotFoundException if the file was not found
* @throws IOException if an I/O error occurs
*/
public RegionFileInputStream(File file, long off, long len, boolean inflate)
throws FileNotFoundException, IOException {
this.file = file;
regionStart = off;
regionEnd = off + len;
length = Integer.MAX_VALUE;
if (regionEnd > file.length()) {
throw new EOFException("Region overlaps.");
}
// check if we can read from the file
if (!file.canRead()) {
throw new FileNotFoundException(file.getPath());
}
isInflating = inflate;
if (!isInflating) {
length = regionEnd - regionStart;
}
}
/**
* internally creates a new region input stream that operates on a physical
* region but seeks to the correct logical position in the decompressed
* stream.
*
* @param file the file to read from.
* @param start the (physical) start of the new region
* @param end the (physical) end of the new region
* @param pos the (logical) position of the region
* @param len the (logical) length of the region
*
* @throws FileNotFoundException if the file was not found
* @throws IOException if an I/O error occurs
* @throws EOFException if invalid ranges are specified.
*/
private RegionFileInputStream(File file, long start, long end, long pos, long len)
throws IOException, FileNotFoundException, EOFException {
this(file, start, end-start, true);
if (skip(pos) < 0) {
throw new EOFException("error while seeking to " + pos);
}
length = position + len;
}
/**
* checks if the stream is propery initialized and opens the underlying
* file if needed.
*
* @throws IOException if an I/O error occurs.
*/
private void ensureOpen() throws IOException {
if (closedBy != null) {
throw closedBy;
}
if (raf == null) {
// open raf
raf = new RandomAccessFile(file, "r");
if (isInflating) {
inflater = new Inflater();
}
raf.seek(regionStart);
// create buffer
buffer = new byte[(int) Math.min(regionEnd - regionStart, MAX_BUFFER)];
}
}
/**
* {@inheritDoc}
*/
public int available() throws IOException {
if (reachEOF) {
return 0;
} else {
return 1;
}
}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
if (raf != null) {
raf.close();
raf = null;
}
try {
throw new IOException("RegionFileInputStream already closed. Stack trace is the one of the closer. (file=" + file.getPath() + ", s=" + regionStart + ", e=" + regionEnd);
} catch (IOException e) {
closedBy = e;
}
}
/**
* {@inheritDoc}
*/
public synchronized void reset() throws IOException {
if (!markSupported()) {
throw new IOException("mark not supported or not called.");
}
pos = 0;
end = 0;
position = markedPosition;
raf.seek(regionStart + position);
}
/**
* {@inheritDoc}
*/
public boolean markSupported() {
return !isInflating;
}
/**
* {@inheritDoc}
*/
public synchronized void mark(int readlimit) {
if (!isInflating) {
try {
ensureOpen();
markedPosition = position;
} catch (IOException e) {
// ignore
}
}
}
/**
* {@inheritDoc}
*/
public long skip(long n) throws IOException {
ensureOpen();
// todo: improve
final byte[] b = new byte[8192];
int max = (int)Math.min(n, Integer.MAX_VALUE);
int total = 0;
while (total < max) {
int len = max - total;
if (len > b.length) {
len = b.length;
}
len = read(b, 0, len);
if (len == -1) {
reachEOF = true;
break;
}
total += len;
}
return total;
}
/**
* {@inheritDoc}
*/
public int read() throws IOException {
return read(singleByteBuf, 0, 1) == -1 ? -1 : singleByteBuf[0] & 0xff;
}
/**
* {@inheritDoc}
*/
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
/**
* {@inheritDoc}
*/
public int read(byte b[], int off, int len) throws IOException {
ensureOpen();
// limit length to max
len = Math.min(len, (int) (length - position));
if (len == 0) {
return -1;
}
int read = inflater == null
? readBuffered(b, off, len)
: readCompressed(b, off, len);
if (read > 0) {
position += read;
}
return read;
}
private int readBuffered(byte b[], int off, int len) throws IOException {
if (fill() < 0) {
return -1;
}
int read = 0;
while (len > 0) {
if (fill() < 0) {
break;
}
int d = Math.min((end-pos), len);
System.arraycopy(buffer, pos, b, off, d);
pos += d;
off += d;
len -= d;
read += d;
}
return read;
}
private int readCompressed(byte[] b, int off, int len) throws IOException {
try {
int n;
while ((n = inflater.inflate(b, off, len)) == 0) {
if (inflater.finished() || inflater.needsDictionary()) {
if (hasMultiStreams && getRemaining()>0) {
int r = inflater.getRemaining();
inflater.reset();
inflater.setInput(buffer, end - r, r);
lastSyncPoint = raf.getFilePointer() - r;
lastSyncPos = position;
} else {
reachEOF = true;
return -1;
}
}
if (inflater.needsInput()) {
feed();
}
}
return n;
} catch (DataFormatException e) {
String s = e.getMessage();
throw new ZipException(s != null ? s : "Invalid ZLIB data format");
}
}
/**
* fills the buffer by at least 1 byte.
* @return the number of bytes read.
* @throws IOException if an I/O error occurs.
*/
private int fill() throws IOException {
if (end > pos) {
return end - pos;
}
int d = (int) Math.min((long) buffer.length, regionEnd - raf.getFilePointer());
pos = 0;
end = 0;
if (d == 0) {
reachEOF = true;
return -1;
}
while (d > 0) {
int read = raf.read(buffer, end, d);
if (read < 0) {
throw new EOFException("error while reading past region boundaries");
}
end += read;
d -= read;
}
return end - pos;
}
/**
* fills the buffer by at least 1 byte.
* @return the number of bytes fed into the buffer
* @throws IOException if an I/O error occurs.
*/
private int feed() throws IOException {
int d = (int) Math.min((long) buffer.length, regionEnd - raf.getFilePointer());
pos = 0;
end = 0;
if (d == 0) {
reachEOF = true;
return -1;
}
while (d > 0) {
int read = raf.read(buffer, end, d);
if (read < 0) {
throw new EOFException("error while reading past region boundaries");
}
end += read;
d -= read;
}
inflater.setInput(buffer, 0, end);
return end;
}
/**
* Retruns the underlying file.
*
* @return the underlying file.
*/
public File getFile() {
return file;
}
/**
* Returns the read pointer position relative to this file.
* @return the read pointer position relative to this fille.
* @throws IOException if an I/O error occurs.
*/
public long getAbsolutePosition() throws IOException {
ensureOpen();
return regionStart + position;
}
/**
* Returns the read pointer position relative to this region.
* @return the read pointer position relative to this region.
* @throws IOException if an I/O error occurs.
*/
public long getPosition() throws IOException {
ensureOpen();
return position;
}
/**
* Returns the remaining bytes available in this region
* @return the remaining bytes available in this region
* @throws IOException if an I/O error occurs.
*/
public long getRemaining() throws IOException {
ensureOpen();
// check if length is set
if (length == Integer.MAX_VALUE) {
return regionEnd - raf.getFilePointer() + inflater.getRemaining();
} else {
return length - position;
}
}
/**
* Creates a new RegionFileInputStream that is based on this one.
*
* @param off relative offset to the current read pointer of this region
* @param len total length of this new stream.
* @return the new region streem
*
* @throws IOException if an I/O error occurs.
*/
public RegionFileInputStream substream(long off, long len)
throws IOException {
ensureOpen();
if (inflater == null) {
return new RegionFileInputStream(file, getAbsolutePosition() + off, len);
} else {
// if synced
if (lastSyncPoint < 0) {
return new RegionFileInputStream(file, regionStart, regionEnd, position + off, len);
} else {
return new RegionFileInputStream(file, lastSyncPoint, regionEnd, position + off - lastSyncPos, len);
}
}
}
/**
* Duplicates this stream. If the stream is already consumed or compressed,
* null is returned.
*
* @return a duplicate of this stream or null
* @throws IOException if an I/O error occurs.
*/
public RegionFileInputStream duplicate() throws IOException {
if (raf != null || inflater != null) {
return null;
} else {
return new RegionFileInputStream(file, regionStart, regionEnd - regionStart, false);
}
}
}