/*
* Copyright The National Archives 2005-2006. All rights reserved.
* See Licence.txt for full licence details.
*
* Developed by:
* Tessella Support Services plc
* 3 Vineyard Chambers
* Abingdon, OX14 3PX
* United Kingdom
* http://www.tessella.com
*
* Tessella/NPD/4826
* PRONOM 5a
*
* $Id: StreamByteReader.java,v 1.7 2006/03/13 15:15:28 linb Exp $
*
* $Log: StreamByteReader.java,v $
* Revision 1.7 2006/03/13 15:15:28 linb
* Changed copyright holder from Crown Copyright to The National Archives.
* Added reference to licence.txt
* Changed dates to 2005-2006
*
* Revision 1.6 2006/02/09 15:34:10 linb
* Updates to javadoc and code following the code review
*
* Revision 1.5 2006/02/09 15:31:23 linb
* Updates to javadoc and code following the code review
*
* Revision 1.4 2006/02/09 13:17:42 linb
* Changed StreamByteReader to InputStreamByteReader
* Refactored common code from UrlByteReader and InputStreamByteReader into new class StreamByteReader, from which they both inherit
* Updated javadoc
*
* Revision 1.3 2006/02/09 12:14:16 linb
* Changed some javadoc to allow it to be created cleanly
*
* Revision 1.2 2006/02/08 12:03:37 linb
* - add more comments
*
* Revision 1.1 2006/02/08 11:45:48 linb
* - add support for streams
*
*
*/
package uk.gov.nationalarchives.droid.binFileReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import uk.gov.nationalarchives.droid.IdentificationFile;
/**
* Class providing common functionality for reading streams.
* <p/>
* <p>This class cannot be instantiated (there is no constructor).
*
* @author linb
*/
public class StreamByteReader extends AbstractByteReader {
/**
* Creates a new instance of StreamByteReader
*/
protected StreamByteReader(IdentificationFile theIDFile) {
super(theIDFile);
}
/**
* Size of buffer to store the stream in
*/
private static final int BUFFER_SIZE = 131072;
/**
* Buffer to contain the contents of the stream.
*/
protected ByteBuffer buffer = null;
/**
* This will be non-null if the stream has been written to a temporary file.
*/
protected File tempFile = null;
/**
* Read stream into a <code>ByteBuffer</code> or temporary file.
* <p/>
* <p>This method allocates a buffer, and then attempts to read the stream into
* it. If the buffer isn't big enough, the contents of it are transferred to a
* temporary file, and then the rest of the stream is appended to this file.
* <p/>
* <p>After this method has been called, the field <code>tempFile</code> is
* <code>null</code> if the contents of the stream could fit into the buffer,
* and is the created temporary file otherwise.
*
* @param inStream the stream to read in.
* @throws java.io.IOException if there is an error writing to the temporary
* file
*/
protected void readStream(InputStream inStream) throws IOException {
ReadableByteChannel c = Channels.newChannel(inStream);
if (buffer == null) {
buffer = ByteBuffer.allocate(BUFFER_SIZE);
} else {
buffer.clear();
}
/*
* Not all bytes in the channel are available at once.
* Loop until end of file and while the buffer has space
*/
int bytes = 0;
while (bytes >= 0 && buffer.hasRemaining()) {
bytes = c.read(buffer);
}
// Sets limit to current position, then position to start of the buffer.
buffer.flip();
if (buffer.limit() == 0) {
this.setErrorIdent();
this.setIdentificationWarning("Zero-length file");
return;
}
if (bytes != -1) {
// Haven't got the whole file
// Write it to a temporary file
tempFile = writeToTempFile(buffer, c);
}
}
/**
* Write contents of <code>buffer</code> to a temporary file, followed by the remaining bytes
* in <code>channel</code>.
* <p/>
* <p>The bytes are read from <code>buffer</code> from the current position to its limit.
*
* @param buffer contains the contents of the channel read so far
* @param channel the rest of the channel
* @return <code>File</code> object for the temporary file.
* @throws java.io.IOException if there is a problem writing to the file
*/
static File writeToTempFile(ByteBuffer buffer, ReadableByteChannel channel) throws IOException {
File tempFile = java.io.File.createTempFile("droid", null);
FileChannel fc = (new FileOutputStream(tempFile)).getChannel();
ByteBuffer buf = ByteBuffer.allocate(8192);
fc.write(buffer);
buf.clear();
for (; ;) {
if (channel.read(buf) < 0) {
break; // No more bytes to transfer
}
buf.flip();
fc.write(buf);
buf.compact(); // In case of partial write
}
fc.close();
return tempFile;
}
/**
* Get a byte from file
*
* @param fileIndex position of required byte in the file
* @return the byte at position <code>fileIndex</code> in the file
*/
public byte getByte(long fileIndex) {
return buffer.get((int) fileIndex);
}
/**
* Gets the current position of the file marker.
*
* @return the current position of the file marker
*/
public long getFileMarker() {
return buffer.position();
}
/**
* Returns the number of bytes in the file
*/
public long getNumBytes() {
return buffer == null ? 0 : buffer.limit();
}
/**
* Position the file marker at a given byte position.
* <p/>
* <p>The file marker is used to record how far through the file
* the byte sequence matching algorithm has got.
*
* @param markerPosition The byte number in the file at which to position the marker
*/
public void setFileMarker(long markerPosition) {
buffer.position((int) markerPosition);
}
public byte[] getbuffer() {
return buffer.array();
}
/************ Added for JHOVE2 ****/
public void close(){
if (this.tempFile != null){
try {
this.tempFile.delete();
}
catch (Exception e){
;
}
}
}
}