/*
* Copyright (c) 2007-2012 The Broad Institute, Inc.
* SOFTWARE COPYRIGHT NOTICE
* This software and its documentation are the copyright of the Broad Institute, Inc. All rights are reserved.
*
* This software is supplied without any warranty or guaranteed support whatsoever. The Broad Institute is not responsible for its use, misuse, or functionality.
*
* This software is licensed under the terms of the GNU Lesser General Public License (LGPL),
* Version 2.1 which is available at http://www.opensource.org/licenses/lgpl-2.1.php.
*/
package org.broad.igv.bbfile;
import htsjdk.samtools.seekablestream.SeekableStream;
import org.apache.log4j.Logger;
import htsjdk.tribble.util.LittleEndianInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
/**
* Created by IntelliJ IDEA.
* User: martind
* Date: Jan 18, 2010
* Time: 8:44:33 PM
* To change this template use File | Settings | File Templates.
*/
/*
* Container class for holding zoom level format information, BBFile Table O.
*
* Note: The header section corresponds to Table O and the file location for
* zoomCount is identified by the dataOffset entry in BBFile zoom level headers
* Table D.
*
* The zoomCount field in Table O is uncompressed, but the zoomData section is compressed.
*
* Note:
* Table P describes the zoom data record format. It contain statistics for
* mChromosome regions in the zoom data. There are Table O zoomCount data records
* read into the ZoomDataRecords array.
*
* */
public class BBZoomLevelFormat {
private static Logger log = Logger.getLogger(BBZoomLevelFormat.class);
// size of zoom level format header is 4 byte; only accounts for zoomCount of Table O
public static final int ZOOM_FORMAT_HEADER_SIZE = 4;
// Perhaps there is a better way to know the top permitted record count
public static final int MAX_ZOOM_DATA_RECORDS = 100000000;
// defines the zoom data file access
private int zoomLevel; // zoom level for data
private SeekableStream fis; // BBFile handle
private long zoomFormatOffset; // BBFile zoom level data format offset
private long zoomDataOffset; // BBFile zoom level data offset
private long zoomIndexOffset; // BBFile zoom level R+ tree offset
// data reader members
private boolean isLowToHigh; // zoom data low to high byte order if true; else high to low
// zoom level data - BBFile Table O
private int zoomRecordCount; // number of data records; zoomCount in Table O
private long zoomDataSize; // number of (compressed) zoom level data bytes
/*
* constructor - reads zoom level format (but not the data) for a zoom level.
*
* Parameters:
* zoomLevel - zoom level; from 1 to zoomLevels in Table C File Header
* fis - input stream reader handle
* fileOffset - file location for the zoom format table
* ` datasize - byte size of (compressed/uncompressed) zoom data block
* isLowToHigh - boolean flag indicates if buffer data is arranged low to high byte
* uncompressBufSize - buffer size for decompressed data; or 0 for uncompressed
*
* Note: Zoom level data Table O is arranged as:
* zoomCount - 4 bytes
* zoomData - dataSize bytes
* R+ zoom index starts at fileOffset + dataSize
* */
public BBZoomLevelFormat(int zoomLevel, SeekableStream fis, long fileOffset,
long dataSize, boolean isLowToHigh, int uncompressBufSize) {
// store file access info
this.zoomLevel = zoomLevel;
this.fis = fis;
zoomFormatOffset = fileOffset;
zoomDataSize = dataSize;
this.isLowToHigh = isLowToHigh;
// Note: a bad zoom data header will result in a 0 count returned
// or an IOException
// size of buffer is ZOOM_FORMAT_HEADER_SIZE to get the record count
byte[] buffer = new byte[ZOOM_FORMAT_HEADER_SIZE];
try {
// Read zoom level data format into a buffer
fis.seek(zoomFormatOffset);
fis.readFully(buffer);
// decode header - or fail
if(this.isLowToHigh) {
LittleEndianInputStream lbdis = new LittleEndianInputStream(new ByteArrayInputStream(buffer));
zoomRecordCount = lbdis.readInt();
}
else {
DataInputStream bdis = new DataInputStream(new ByteArrayInputStream(buffer));
zoomRecordCount = bdis.readInt();
}
} catch (IOException ex) {
log.error("Error reading zoom level data records (Table O) ", ex);
throw new RuntimeException("Error reading zoom level data records (Table O)", ex);
}
// integrity check - should be > 0 or less than a max like 100M records?
// Note: if trouble reading zoom data records, readAllZoomLevelRecords returns 0
if(zoomRecordCount < 0 || zoomRecordCount > MAX_ZOOM_DATA_RECORDS)
return; // terminate if bad zoom level data encountered
// Position file offset past the current zoom level header to pick up
// the zoom data records which immediately follow.
zoomDataOffset = zoomFormatOffset + ZOOM_FORMAT_HEADER_SIZE;
// calculate the position of the R+ zoom index tree
zoomIndexOffset = zoomDataOffset + zoomDataSize;
}
/*
* Method returns the zoom level for zoom data.
*
* Returns:
* zoom level for zoom data
* */
public int getZoomLevel() {
return zoomLevel;
}
/*
* Method returns the file input stream handle.
*
* Returns:
* file input stream handle
* */
public SeekableStream getBBFis() {
return fis;
}
/*
* Method returns the file location for zoom data format.
*
* Returns:
* file location for zoom data format
* */
public long getZoomFormatLocation() {
return zoomFormatOffset;
}
/*
* Method returns the zoom count found in the zoom level format header .
*
* Note: Zoom count is zoomCount in Table O.
*
* Returns:
* file location for zoom level R+ data records
* */
public int getZoomRecordCount() {
return zoomRecordCount;
}
/*
* Method returns the file location for zoom level data.
*
* Note: ZoomFormatOffset (Table D dataOffset) + zoom data header size
*
* Returns:
* file location for zoom level R+ data records
* */
public long getZoomDataOffset() {
return zoomDataOffset;
}
/*
* Method returns the calculated compressed zoom level data byte size.
*
* Note: Compressed data size is calculated from Table D
* (indexOffset - dataOffset) and is provided by the constructor.
*
* Returns:
* calculated compressed zoom level data byte size
* */
public long getZoomDataSize() {
return zoomDataSize;
}
/*
* Method returns the calculated file location for zoom level R+ tree.
*
* Note: should be equal to indexOffset in zoom level header - Table D
*
* Returns:
* file location for zoom level R+ tree
* */
public long getZoomIndexOffset() {
return zoomIndexOffset;
}
public void print(){
// identify the zoom level and record count
log.debug("Zoom level " + zoomLevel + " format Table O found at "
+ zoomFormatOffset);
log.debug("Zoom record count is " + zoomRecordCount);
log.debug("Zoom data location is " + zoomDataOffset);
log.debug("Zoom data size is " + zoomDataOffset);
log.debug("Zoom index tree location is " + zoomIndexOffset);
}
}