]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/api/BamReader.cpp
Reorganizing index/jumping calls. Now all BamReader cares about is sending a Jump...
[bamtools.git] / src / api / BamReader.cpp
index 93a991bd77f942becb85fa505dba8b6334a15be1..06232afd13e56fbae2b98bb7c8cf0c5951601adf 100644 (file)
@@ -3,7 +3,7 @@
 // Marth Lab, Department of Biology, Boston College\r
 // All rights reserved.\r
 // ---------------------------------------------------------------------------\r
-// Last modified: 7 September 2010 (DB)\r
+// Last modified: 10 September 2010 (DB)\r
 // ---------------------------------------------------------------------------\r
 // Uses BGZF routines were adapted from the bgzf.c code developed at the Broad\r
 // Institute.\r
@@ -42,8 +42,7 @@ struct BamReader::BamReaderPrivate {
     // general file data\r
     BgzfData  mBGZF;\r
     string    HeaderText;\r
-    //BamIndex  Index;\r
-    BamIndex* NewIndex;\r
+    BamIndex* Index;\r
     RefVector References;\r
     bool      IsIndexLoaded;\r
     int64_t   AlignmentsBeginOffset;\r
@@ -54,11 +53,7 @@ struct BamReader::BamReaderPrivate {
     bool IsBigEndian;\r
 \r
     // user-specified region values\r
-    BamRegion Region;\r
-    bool IsLeftBoundSpecified;\r
-    bool IsRightBoundSpecified;\r
-    \r
-    bool IsRegionSpecified;\r
+    BamRegion Region;    \r
     int  CurrentRefID;\r
     int  CurrentLeft;\r
 \r
@@ -81,7 +76,6 @@ struct BamReader::BamReaderPrivate {
 \r
     // file operations\r
     void Close(void);\r
-    bool Jump(int refID, int position);\r
     bool Open(const std::string& filename, \r
               const std::string& indexFilename, \r
               const bool lookForIndex, \r
@@ -142,14 +136,7 @@ BamReader::~BamReader(void) {
 void BamReader::Close(void) { d->Close(); }\r
 bool BamReader::IsIndexLoaded(void) const { return d->IsIndexLoaded; }\r
 bool BamReader::IsOpen(void) const { return d->mBGZF.IsOpen; }\r
-bool BamReader::Jump(int refID, int position) \r
-{ \r
-    d->Region.LeftRefID = refID;\r
-    d->Region.LeftPosition = position;\r
-    d->IsLeftBoundSpecified = true;\r
-    d->IsRightBoundSpecified = false;\r
-    return d->Jump(refID, position); \r
-}\r
+bool BamReader::Jump(int refID, int position)  { return d->SetRegion( BamRegion(refID, position) ); }\r
 bool BamReader::Open(const std::string& filename, \r
                      const std::string& indexFilename, \r
                      const bool lookForIndex, \r
@@ -183,12 +170,9 @@ bool BamReader::CreateIndex(bool useStandardIndex) { return d->CreateIndex(useSt
 \r
 // constructor\r
 BamReader::BamReaderPrivate::BamReaderPrivate(BamReader* parent)\r
-    : NewIndex(0)\r
+    : Index(0)\r
     , IsIndexLoaded(false)\r
     , AlignmentsBeginOffset(0)\r
-    , IsLeftBoundSpecified(false)\r
-    , IsRightBoundSpecified(false)\r
-    , IsRegionSpecified(false)\r
     , CurrentRefID(0)\r
     , CurrentLeft(0)\r
     , Parent(parent)\r
@@ -350,8 +334,8 @@ bool BamReader::BamReaderPrivate::BuildCharData(BamAlignment& bAlignment) {
 \r
 // clear index data structure\r
 void BamReader::BamReaderPrivate::ClearIndex(void) {\r
-    delete NewIndex;\r
-    NewIndex = 0;\r
+    delete Index;\r
+    Index = 0;\r
     IsIndexLoaded = false;\r
 }\r
 \r
@@ -368,9 +352,7 @@ void BamReader::BamReaderPrivate::Close(void) {
     HeaderText.clear();\r
     \r
     // clear out region flags\r
-    IsLeftBoundSpecified  = false;\r
-    IsRightBoundSpecified = false;\r
-    IsRegionSpecified     = false;\r
+    Region.clear();\r
 }\r
 \r
 // creates index for BAM file, saves to file\r
@@ -383,18 +365,18 @@ bool BamReader::BamReaderPrivate::CreateIndex(bool useStandardIndex) {
     \r
     // create index based on type requested\r
     if ( useStandardIndex ) \r
-        NewIndex = new BamStandardIndex(&mBGZF, Parent, IsBigEndian);\r
+        Index = new BamStandardIndex(&mBGZF, Parent, IsBigEndian);\r
     // create BamTools 'custom' index\r
     else\r
-        NewIndex = new BamToolsIndex(&mBGZF, Parent, IsBigEndian);\r
+        Index = new BamToolsIndex(&mBGZF, Parent, IsBigEndian);\r
     \r
     // build new index\r
     bool ok = true;\r
-    ok &= NewIndex->Build();\r
+    ok &= Index->Build();\r
     IsIndexLoaded = ok;\r
     \r
     // attempt to save index data to file\r
-    ok &= NewIndex->Write(Filename); \r
+    ok &= Index->Write(Filename); \r
     \r
     // return success/fail of both building & writing index\r
     return ok;\r
@@ -425,7 +407,7 @@ bool BamReader::BamReaderPrivate::GetNextAlignmentCore(BamAlignment& bAlignment)
         bAlignment.SupportData.HasCoreOnly = true;\r
       \r
         // if region not specified, return success\r
-        if ( !IsLeftBoundSpecified ) return true;\r
+        if ( !Region.isLeftBoundSpecified() ) return true;\r
 \r
         // determine region state (before, within, after)\r
         BamReader::BamReaderPrivate::RegionState state = IsOverlap(bAlignment);\r
@@ -472,7 +454,7 @@ BamReader::BamReaderPrivate::RegionState BamReader::BamReaderPrivate::IsOverlap(
     // check alignment start against right bound cutoff\r
     \r
     // if full region of interest was given\r
-    if ( IsRightBoundSpecified ) {\r
+    if ( Region.isRightBoundSpecified() ) {\r
       \r
         // read starts on right bound reference, but AFTER right bound position\r
         if ( bAlignment.RefID == Region.RightRefID && bAlignment.Position > Region.RightPosition )\r
@@ -506,44 +488,6 @@ BamReader::BamReaderPrivate::RegionState BamReader::BamReaderPrivate::IsOverlap(
         return BEFORE_REGION;\r
 }\r
 \r
-// jumps to specified region(refID, leftBound) in BAM file, returns success/fail\r
-bool BamReader::BamReaderPrivate::Jump(int refID, int position) {\r
-\r
-    // -----------------------------------------------------------------------\r
-    // check for existing index \r
-    if ( !IsIndexLoaded || NewIndex == 0 ) return false; \r
-    // see if reference has alignments\r
-    if ( !NewIndex->HasAlignments(refID) ) return false; \r
-    // make sure position is valid\r
-    if ( position > References.at(refID).RefLength ) return false;\r
-    \r
-    // determine possible offsets\r
-    vector<int64_t> offsets;\r
-    if ( !NewIndex->GetOffsets(Region, IsRightBoundSpecified, offsets) ) {\r
-        fprintf(stderr, "ERROR: Could not jump: unable to calculate offset for specified region.\n");\r
-        return false;\r
-    }\r
-      \r
-    // iterate through offsets\r
-    BamAlignment bAlignment;\r
-    bool result = true;\r
-    for ( vector<int64_t>::const_iterator o = offsets.begin(); o != offsets.end(); ++o) {\r
-        \r
-        // attempt seek & load first available alignment\r
-        result &= mBGZF.Seek(*o);\r
-        LoadNextAlignment(bAlignment);\r
-        \r
-        // if this alignment corresponds to desired position\r
-        // return success of seeking back to 'current offset'\r
-        if ( (bAlignment.RefID == refID && bAlignment.Position + bAlignment.Length > position) || (bAlignment.RefID > refID) ) {\r
-            if ( o != offsets.begin() ) --o;\r
-            return mBGZF.Seek(*o);\r
-        }\r
-    }\r
-    \r
-    return result;\r
-}\r
-\r
 // load BAM header data\r
 void BamReader::BamReaderPrivate::LoadHeaderData(void) {\r
 \r
@@ -584,26 +528,26 @@ bool BamReader::BamReaderPrivate::LoadIndex(const bool lookForIndex, const bool
       \r
         // attempt to load BamIndex based on current Filename provided & preferStandardIndex flag\r
         const BamIndex::PreferredIndexType type = (preferStandardIndex ? BamIndex::STANDARD : BamIndex::BAMTOOLS);\r
-        NewIndex = BamIndex::FromBamFilename(Filename, &mBGZF, Parent, IsBigEndian, type);\r
+        Index = BamIndex::FromBamFilename(Filename, &mBGZF, Parent, IsBigEndian, type);\r
         \r
         // if null, return failure\r
-        if ( NewIndex == 0 ) return false;\r
+        if ( Index == 0 ) return false;\r
         \r
         // generate proper IndexFilename based on type of index created\r
-        IndexFilename = Filename + NewIndex->Extension();\r
+        IndexFilename = Filename + Index->Extension();\r
     }\r
     \r
     else {\r
         // attempt to load BamIndex based on IndexFilename provided by client\r
-        NewIndex = BamIndex::FromIndexFilename(IndexFilename, &mBGZF, Parent, IsBigEndian);\r
+        Index = BamIndex::FromIndexFilename(IndexFilename, &mBGZF, Parent, IsBigEndian);\r
         \r
         // if null, return failure\r
-        if ( NewIndex == 0 ) return false; \r
+        if ( Index == 0 ) return false; \r
     }\r
     \r
     // an index file was found\r
     // return success of loading the index data from file\r
-    IsIndexLoaded = NewIndex->Load(IndexFilename);\r
+    IsIndexLoaded = Index->Load(IndexFilename);\r
     return IsIndexLoaded;\r
 }\r
 \r
@@ -759,38 +703,44 @@ bool BamReader::BamReaderPrivate::Open(const string& filename, const string& ind
 // returns BAM file pointer to beginning of alignment data\r
 bool BamReader::BamReaderPrivate::Rewind(void) {\r
    \r
-    // rewind to first alignment\r
+    // rewind to first alignment, return false if unable to seek\r
     if ( !mBGZF.Seek(AlignmentsBeginOffset) ) return false;\r
   \r
-    // retrieve first alignment data\r
+    // retrieve first alignment data, return false if unable to read\r
     BamAlignment al;\r
     if ( !LoadNextAlignment(al) ) return false;\r
       \r
     // reset default region info using first alignment in file\r
-    Region.LeftRefID      = al.RefID;\r
-    Region.LeftPosition   = al.Position;\r
-    Region.RightRefID     = -1;\r
-    Region.RightPosition  = -1;\r
-    IsLeftBoundSpecified  = false;\r
-    IsRightBoundSpecified = false; \r
-\r
-    // rewind back to before first alignment\r
+    Region.clear();\r
+    Region.LeftRefID    = al.RefID;\r
+    Region.LeftPosition = al.Position;\r
+\r
+    // rewind back to beginning of first alignment\r
     // return success/fail of seek\r
     return mBGZF.Seek(AlignmentsBeginOffset);\r
 }\r
 \r
-// sets a region of interest (with left & right bound reference/position)\r
-// attempts a Jump() to left bound as well\r
-// returns success/failure of Jump()\r
+// asks Index to attempt a Jump() to specified region\r
+// returns success/failure\r
 bool BamReader::BamReaderPrivate::SetRegion(const BamRegion& region) {\r
+      \r
+    // clear out any prior BamReader region data\r
+    //\r
+    // N.B. - this is cleared so that BamIndex now has free reign to call\r
+    // GetNextAlignmentCore() and do overlap checking without worrying about BamReader \r
+    // performing any overlap checking of its own and moving on to the next read... Calls \r
+    // to GetNextAlignmentCore() with no Region set, simply return the next alignment.\r
+    // This ensures that the Index is able to do just that. (All without exposing \r
+    // LoadNextAlignment() to the public API, and potentially confusing clients with the nomenclature)\r
+    Region.clear();\r
+  \r
+    // check for existing index \r
+    if ( !IsIndexLoaded || Index == 0 ) return false; \r
     \r
-    // save region of interest\r
+    // attempt jump to user-specified region, return false if failed\r
+    if ( !Index->Jump(region) ) return false;\r
+      \r
+    // if successful, save region data locally, return success\r
     Region = region;\r
-    \r
-    // set flags\r
-    if ( region.LeftRefID  >= 0 && region.LeftPosition  >= 0 ) IsLeftBoundSpecified  = true;\r
-    if ( region.RightRefID >= 0 && region.RightPosition >= 0 ) IsRightBoundSpecified = true;\r
-    \r
-    // attempt jump to beginning of region, return success/fail of Jump()\r
-    return Jump( Region.LeftRefID, Region.LeftPosition );\r
+    return true;\r
 }\r