package com.illumina.basespace.igv.io;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import net.sf.samtools.seekablestream.SeekableStream;
import com.illumina.basespace.entity.FileCompact;
import com.illumina.basespace.igv.BaseSpaceMain;
import com.illumina.basespace.igv.BaseSpaceResourceLocator;
import com.illumina.basespace.igv.BaseSpaceUtil;
import com.illumina.basespace.infrastructure.BaseSpaceException;
public class BaseSpaceSeekableFileStream extends SeekableStream
{
private static final Logger log = Logger.getLogger(BaseSpaceSeekableFileStream.class.getPackage().getName());
private long position = 0;
private long contentLength = -1;
private BaseSpaceResourceLocator locator;
private FileCompact file;
public BaseSpaceSeekableFileStream(BaseSpaceResourceLocator locator,FileCompact file)
{
contentLength = file.getSize();
this.locator = locator;
this.file = file;
}
public long length()
{
return contentLength;
}
public boolean eof() throws IOException
{
return position >= contentLength;
}
public void seek(final long position)
{
this.position = position;
}
public int read(byte[] buffer, int offset, int len) throws IOException
{
int n = 0;
InputStream is = null;
try
{
if (offset < 0 || len < 0 || (offset + len) > buffer.length)
{
throw new IndexOutOfBoundsException("Offset=" + offset + ",len=" + len + ",buflen=" + buffer.length);
}
if (len == 0)
{
return 0;
}
long endRange = position + len - 1;
// IF we know the total content length, limit the end range to that.
if (contentLength > 0)
{
endRange = Math.min(endRange, contentLength);
}
log.fine(this.hashCode() + "->Read from BaseSpace (file id#" + file.getId() + ") stream position " + position + " to " + endRange + " (content-length=" + contentLength + ")");
is = BaseSpaceMain.instance().getApiClient(locator.getClientId()).getFileInputStream(file, position, endRange);
while (n < len)
{
int count = is.read(buffer, offset + n, len - n);
if (count < 0)
{
if (n == 0)
{
return -1;
}
else
{
break;
}
}
n += count;
}
position += n;
return n;
}
catch(BaseSpaceException fre)
{
throw fre;
}
catch (IOException e)
{
e.printStackTrace();
// THis is a bit of a hack, but its not clear how else to handle
// this. If a byte range is specified
// that goes past the end of the file the response code will be 416.
// The MAC os translates this to
// an IOException with the 416 code in the message. Windows
// translates the error to an EOFException.
//
// The BAM file iterator uses the return value to detect end of file
// (specifically looks for n == 0).
if (e.getMessage().contains("416") || (e instanceof EOFException))
{
if (n < 0)
{
return -1;
}
else
{
position += n;
// As we are at EOF, the contentLength and position are by
// definition =
contentLength = position;
return n;
}
}
else
{
throw e;
}
}
catch (Throwable t)
{
t.printStackTrace();
throw new RuntimeException(t);
}
finally
{
BaseSpaceUtil.dispose(is);
}
}
public void close() throws IOException
{
// Nothing to do
}
public int read() throws IOException
{
byte[] tmp = new byte[1];
read(tmp, 0, 1);
return (int) tmp[0] & 0xFF;
}
@Override
public String getSource()
{
return file.getName();
}
@Override
public long position() throws IOException
{
return position;
}
}