/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2015 Broad Institute
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* Created by IntelliJ IDEA.
* User: martind
* Date: Nov 20, 2009
* Time: 3:49:14 PM
* To change this template use File | Settings | File Templates.
*/
/**
* Container class defines the header information for BigBed and BigWig files
*/
package org.broad.igv.bbfile;
import htsjdk.samtools.seekablestream.SeekableStream;
import org.apache.log4j.Logger;
import org.broad.igv.util.LittleEndianInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
/*
* Container class for holding the BBFile header information, Table C .
**/
public class BBFileHeader {
private static Logger log = Logger.getLogger(BBFileHeader.class);
// defines bigBed/bigwig Header Format types
static public final int BBFILE_HEADER_SIZE = 64;
static public final int BIGWIG_MAGIC_LTH = 0x888FFC26; // BigWig Magic Low to High
static public final int BIGWIG_MAGIC_HTL = 0x26FC8F66; // BigWig Magic High to Low
static public final int BIGBED_MAGIC_LTH = 0x8789F2EB; // BigBed Magic Low to High
static public final int BIGBED_MAGIC_HTL = 0xEBF28987; // BigBed Magic High to Low
// defines the bigBed/bigWig source file access
private String path; // bigBed file/pathname
private SeekableStream fis; // BBFile I/O stream handle
private long fileHeaderOffset; // file offset for file header
private boolean isHeaderOK; // File header read correctly?
private boolean isLowToHigh; // flag indicates values represented low to high bytes
private boolean isBigBed; // flag indicates file is BigBed format
private boolean isBigWig; // flag indicates file is BigWig format;
// BBFile Header items - Table C:
// mMagic number (4 bytes) indicates file type and byte order :
// 0x888FFC26 for bigWig, little endian if swapped
// 0x8789F2EB for bigBed, little endian if swapped
private int magic; // 4 byte mMagic Number
private int version; // 2 byte version ID; currently 3
private int nZoomLevels; // 2 byte count of zoom sumary levels
private long chromTreeOffset; // 8 byte offset to mChromosome B+ Tree index
private long fullDataOffset; // 8 byte offset to unzoomed data dataCount
private long fullIndexOffset; // 8 byte offset to R+ Tree index of items
private int fieldCount; // 2 byte number of fields in bed. (0 for bigWig)
private int definedFieldCount; // 2 byte number of fields that are bed fields
private long autoSqlOffset; // 8 byte offset to 0 terminated string with .as spec
private long totalSummaryOffset; // 8 byte offset to file summary data block
private int uncompressBuffSize; // 4 byte maximum size for decompressed buffer
private long reserved; // 8 bytes reserved for future expansion. Currently 0
// constructor reads BBFile header from an input stream
public BBFileHeader(String path, SeekableStream fis, long fileOffset) {
// save the path and seekable file handle
this.path = path;
this.fis = fis;
fileHeaderOffset = fileOffset;
// read in BBFile header
isHeaderOK = readBBFileHeader(fileHeaderOffset);
}
public String getPath() {
return path;
}
public boolean isHeaderOK() {
return isHeaderOK;
}
public boolean isLowToHigh() {
return isLowToHigh;
}
public boolean isBigBed() {
return isBigBed;
}
public boolean isBigWig() {
return isBigWig;
}
public int getFileHeaderSize() {
return BBFILE_HEADER_SIZE;
}
// ************* return header items ****************
public int getMagic() {
return magic;
}
public int getVersion() {
return version;
}
public int getZoomLevels() {
return nZoomLevels;
}
public long getChromosomeTreeOffset() {
return chromTreeOffset;
}
public long getFullDataOffset() {
return fullDataOffset;
}
public long getFullIndexOffset() {
return fullIndexOffset;
}
public int getFieldCount() {
return fieldCount;
}
public int getDefinedFieldCount() {
return definedFieldCount;
}
public long getAutoSqlOffset() {
return autoSqlOffset;
}
public long getTotalSummaryOffset() {
return totalSummaryOffset;
}
public int getUncompressBuffSize() {
return uncompressBuffSize;
}
public void print() {
if (isHeaderOK) {
if (isBigWig())
System.out.println("BigWig file " + path + ", file header at location " + fileHeaderOffset);
else if (isBigBed())
System.out.println("BigBed file " + path + ", file header at location " + fileHeaderOffset);
} else {
System.out.println("BBFile " + path + " with bad magic = " + magic +
" from file header location " + fileHeaderOffset);
return; // bad read - remaining header items not interpreted
}
// header fields
System.out.println("BBFile header magic = " + magic);
System.out.println("Version = " + version);
System.out.println("Zoom Levels = " + nZoomLevels);
System.out.println("Chromosome Info B+ tree offset = " + chromTreeOffset);
System.out.println("Data Block offset = " + fullDataOffset);
System.out.println("Chromosome Data R+ tree offset = " + fullIndexOffset);
System.out.println("Bed fields count = " + fieldCount);
System.out.println("Bed defined fields count = " + definedFieldCount);
System.out.println("AutoSql Offset = " + autoSqlOffset);
System.out.println("Total Summary offset = " + totalSummaryOffset);
System.out.println("Maximum uncompressed buffer size = " + uncompressBuffSize);
System.out.println("m_reserved = " + reserved);
}
/*
* Reads in BBFile header information.
*
* Returns:
* Success status flag is true for successfully read header,
* or is false for a read error.
**/
private boolean readBBFileHeader(long fileOffset) {
BBFileHeader bbHeader = null;
LittleEndianInputStream lbdis = null;
DataInputStream bdis = null;
byte[] buffer = new byte[BBFILE_HEADER_SIZE];
try {
// Read bigBed header into a buffer
fis.seek(fileOffset);
fis.readFully(buffer);
// decode header - determine byte order from first 4 bytes
// first assume byte order is low to high
isLowToHigh = true;
lbdis = new LittleEndianInputStream(new ByteArrayInputStream(buffer));
magic = lbdis.readInt();
// check for a valid bigBed or bigWig file
if (magic == BIGWIG_MAGIC_LTH) {
isBigWig = true;
} else if (magic == BIGBED_MAGIC_LTH) {
isBigBed = true;
} else {
bdis = new DataInputStream(new ByteArrayInputStream(buffer));
magic = bdis.readInt();
// check for a valid bigBed or bigWig file
if (magic == BIGWIG_MAGIC_HTL)
isBigWig = true;
else if (magic == BIGBED_MAGIC_HTL)
isBigBed = true;
else
return false; // can't identify BBFile type
// success - set order high to low
isLowToHigh = false;
}
// Get header information
if (isLowToHigh) {
version = lbdis.readUShort();
nZoomLevels = lbdis.readUShort();
chromTreeOffset = lbdis.readLong();
fullDataOffset = lbdis.readLong();
fullIndexOffset = lbdis.readLong();
fieldCount = lbdis.readUShort();
definedFieldCount = lbdis.readUShort();
autoSqlOffset = lbdis.readLong();
totalSummaryOffset = lbdis.readLong();
uncompressBuffSize = lbdis.readInt();
reserved = lbdis.readLong();
} else {
version = (short) bdis.readUnsignedShort();
nZoomLevels = (short) bdis.readUnsignedShort();
chromTreeOffset = bdis.readLong();
fullDataOffset = bdis.readLong();
fullIndexOffset = bdis.readLong();
fieldCount = (short) bdis.readUnsignedShort();
definedFieldCount = (short) bdis.readUnsignedShort();
autoSqlOffset = bdis.readLong();
totalSummaryOffset = bdis.readLong();
uncompressBuffSize = bdis.readInt();
reserved = bdis.readLong();
}
} catch (IOException ex) {
throw new RuntimeException("Error reading file header for " + path, ex);
}
// file header was read properly
return true;
}
} // mEndBase of class BBFileHeader