// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/io/StreamInputReader.java,v $
// $RCSfile: StreamInputReader.java,v $
// $Revision: 1.4 $
// $Date: 2007/06/21 21:39:03 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.io;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import com.bbn.openmap.util.Debug;
/**
* An Abstract InputReader to handle reading data from streams, where seeking to
* a place in the file in front of the current pointer involves closing the
* stream, and re-seeking from the beginning.
*
* @see com.bbn.openmap.io.InputReader
* @see com.bbn.openmap.io.BinaryFile
*/
public abstract class StreamInputReader
implements InputReader {
/**
* The underlying data input stream, for resource files. Is used if the
* inputFile is null.
*/
protected InputStream inputStream = null;
/**
* Keep track of how many bytes have been read when using the DataInputStream
* to read the file.
*/
protected long inputStreamCount = 0;
/**
* The source name.
*/
protected String name = null;
public String getName() {
return name;
}
/**
* Add the number of bytes to the inputStreamCount.
*
* @return number of bytes added in this call, to pass along to anything else
* that will be interested.
*/
protected int count(int add) {
inputStreamCount += add;
return (int) add;
}
/**
* Reset the DataInputStream to the beginning, by closing the current
* connection and reopening it. The StreamInputReader method simply closes
* the input stream and resets the input stream count, so the implementation
* of this class needs to reopen the stream at the beginning of the source
* file.
*/
protected void reopen()
throws IOException {
if (inputStream != null) {
Debug.message("binaryfile", "StreamInputReader: Closing inputStream");
inputStream.close();
}
inputStreamCount = 0;
}
/**
* Skip over n bytes in the input file
*
* @param n the number of bytes to skip
* @return the actual number of bytes skipped. annoying, isn't it?
* @exception IOException Any IO errors that occur in skipping bytes in the
* underlying file
*/
public long skipBytes(long n)
throws IOException {
long count = 0;
long gotsofar = 0;
if (inputStream == null) {
reopen();
if (inputStream == null) {
reopen();
throw new IOException("Stream closed, can't be reopened");
}
}
while (count < n) {
gotsofar = inputStream.skip(n - count);
if (gotsofar == 0) { // added from david marklund
Debug.error("StreamInputReader can't skip " + n + " bytes as instructed");
break;
}
count += gotsofar;
}
count((int) count);
return count;
}
/**
* Get the index of the next character to be read
*
* @return the index
* @exception IOException Any IO errors that occur in accessing the
* underlying file
*/
public long getFilePointer()
throws IOException {
return inputStreamCount;
}
/**
* Set the index of the next character to be read.
*
* @param pos the position to seek to.
* @exception IOException Any IO Errors that occur in seeking the underlying
* file.
*/
public void seek(long pos)
throws IOException {
boolean seekComments = false;
long skipped;
if (Debug.debugging("binaryfileseek")) {
seekComments = true;
}
long curPosition = inputStreamCount;
if (pos >= curPosition) {
if (seekComments) {
Debug.output("StreamInputReader - seeking to " + pos + " from " + curPosition);
}
skipped = skipBytes(pos - curPosition);
if (seekComments) {
Debug.output(" now at: " + inputStreamCount + ", having skipped " + skipped);
}
} else {
if (seekComments) {
Debug.output("StreamInputReader - having to start over for seek - " + pos + " from " + curPosition);
}
reopen();
if (seekComments)
Debug.output(" skipping to: " + pos);
skipped = skipBytes(pos);
if (seekComments) {
Debug.output(" now at: " + inputStreamCount + ", having skipped " + skipped);
}
}
}
/**
* Return how many bytes the input stream thinks make up the file. This is
* calculated by adding the number of bytes read to the number of bytes
* available. May not be reliable.
*
* @return the number of bytes remaining to be read (counted in bytes)
* @exception IOException Any IO errors encountered in accessing the file
*/
public long length()
throws IOException {
if (inputStream == null) {
throw new IOException("Stream closed");
}
return inputStreamCount + inputStream.available();
}
/**
* Return how many bytes the input stream thinks are available.
*
* @return the number of bytes remaining to be read (counted in bytes)
* @exception IOException Any IO errors encountered in accessing the file
*/
public long available()
throws IOException {
if (inputStream == null) {
throw new IOException("Stream closed");
}
return inputStream.available();
}
/**
* Closes the underlying file
*
* @exception IOException Any IO errors encountered in accessing the file
*/
public void close()
throws IOException {
try {
Debug.message("binaryfile", "StreamInputReader.close()");
// From the Sun Network Programming Guide for 1.4, if
// there are
// problems with Connection reset by peer, then you should
// do this before closing the stream, giving all the data
// a chance to be read. Haven't decided to do this by
// default, but put it in here for easy access if people
// decided they need it.
if (Debug.debugging("connection_problems")) {
Thread.sleep(1000);
}
if (inputStream != null)
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
inputStream = null;
}
/**
* Read from the file.
*
* @return one byte from the file. -1 for EOF
* @exception IOException Any IO errors encountered in reading from the file
*/
public int read()
throws IOException {
if (inputStream == null) {
reopen();
}
count(1);
return inputStream.read();
}
/**
* Read from the file
*
* @param b The byte array to read into
* @param off the first array position to read into
* @param len the number of bytes to read
* @return the number of bytes read
* @exception IOException Any IO errors encountered in reading from the file
*/
public int read(byte b[], int off, int len)
throws IOException {
if (inputStream == null) {
throw new IOException("Stream closed");
}
int gotsofar = 0;
while (gotsofar < len) {
int read = inputStream.read(b, off + gotsofar, len - gotsofar);
if (read == -1) {
if (gotsofar > 0) {
// Hit the EOF in the middle of the loop.
count(gotsofar); // bug fix from Carsten
return gotsofar;
} else {
return read;
}
} else {
gotsofar += read;
}
}
count(gotsofar);
return gotsofar;
}
/**
* Read from the file.
*
* @param b the byte array to read into. Equivalent to
* <code>read(b, 0, b.length)</code>
* @return the number of bytes read
* @exception IOException Any IO errors encountered in reading from the file
* @see java.io.RandomAccessFile#read(byte[])
*/
public int read(byte b[])
throws IOException {
if (inputStream == null) {
throw new IOException("Stream closed");
}
return inputStream.read(b, 0, b.length);
}
/**
* Read from the file.
*
* @param howmany the number of bytes to read
* @param allowless if we can return fewer bytes than requested
* @return the array of bytes read.
* @exception FormatException Any IO Exceptions, plus an end-of-file
* encountered after reading some, but now enough, bytes when
* allowless was <code>false</code>
* @exception EOFException Encountered an end-of-file while allowless was
* <code>false</code>, but NO bytes had been read.
*/
public byte[] readBytes(int howmany, boolean allowless)
throws EOFException, FormatException {
byte foo[] = new byte[howmany];
int gotsofar = 0;
int err = 0;
try {
while (gotsofar < howmany) {
err = inputStream.read(foo, gotsofar, howmany - gotsofar);
if (err == -1) {
if (allowless) {
// return a smaller array, so the caller can
// tell how much
// they really got
byte retval[] = new byte[gotsofar];
System.arraycopy(foo, 0, retval, 0, gotsofar);
count(gotsofar);
return retval;
} else { // some kind of failure...
if (gotsofar > 0) {
throw new FormatException("StreamInputReader: EOF while reading data");
} else {
throw new EOFException();
}
}
}
gotsofar += err;
}
} catch (IOException i) {
throw new FormatException("StreamInputReader: readBytes IOException: " + i.getMessage());
}
count(howmany);
return foo;
}
}