1 // ***************************************************************************
2 // BamRandomAccessController_p.cpp (c) 2011 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // ---------------------------------------------------------------------------
5 // Last modified: 5 April 2011(DB)
6 // ---------------------------------------------------------------------------
7 // Manages random access operations in a BAM file
8 // **************************************************************************
10 #include <api/BamIndex.h>
11 #include <api/internal/BamRandomAccessController_p.h>
12 #include <api/internal/BamReader_p.h>
13 #include <api/internal/BamIndexFactory_p.h>
14 using namespace BamTools;
15 using namespace BamTools::Internal;
20 BamRandomAccessController::BamRandomAccessController(void)
22 , m_indexCacheMode(BamIndex::LimitedIndexCaching)
23 , m_hasAlignmentsInRegion(true)
26 BamRandomAccessController::~BamRandomAccessController(void) {
30 void BamRandomAccessController::AdjustRegion(const int& referenceCount) {
32 // skip if no index available
36 // see if any references in region have alignments
37 m_hasAlignmentsInRegion = false;
38 int currentId = m_region.LeftRefID;
39 const int rightBoundRefId = ( m_region.isRightBoundSpecified() ? m_region.RightRefID : referenceCount - 1 );
40 while ( currentId <= rightBoundRefId ) {
41 m_hasAlignmentsInRegion = m_index->HasAlignments(currentId);
42 if ( m_hasAlignmentsInRegion ) break;
46 // if no data found on any reference in region
47 if ( !m_hasAlignmentsInRegion )
50 // if left bound of desired region had no data, use first reference that had data
51 // otherwise, leave requested region as-is
52 if ( currentId != m_region.LeftRefID ) {
53 m_region.LeftRefID = currentId;
54 m_region.LeftPosition = 0;
58 // returns alignments' "RegionState": { Before|Overlaps|After } current region
59 BamRandomAccessController::RegionState
60 BamRandomAccessController::AlignmentState(const BamAlignment& alignment) const {
62 // if region has no left bound at all
63 if ( !m_region.isLeftBoundSpecified() )
64 return OverlapsRegion;
66 // handle unmapped reads - return AFTER region to halt processing
67 if ( alignment.RefID == -1 )
70 // if alignment is on any reference before left bound reference
71 if ( alignment.RefID < m_region.LeftRefID )
74 // if alignment is on left bound reference
75 else if ( alignment.RefID == m_region.LeftRefID ) {
77 // if alignment starts at or after left bound position
78 if ( alignment.Position >= m_region.LeftPosition) {
80 if ( m_region.isRightBoundSpecified() && // right bound is specified AND
81 m_region.LeftRefID == m_region.RightRefID && // left & right bounds on same reference AND
82 alignment.Position > m_region.RightPosition ) // alignment starts after right bound position
85 // otherwise, alignment overlaps region
86 else return OverlapsRegion;
89 // alignment starts before left bound position
92 // if alignment overlaps left bound position
93 if ( alignment.GetEndPosition() >= m_region.LeftPosition )
94 return OverlapsRegion;
100 // otherwise alignment is on a reference after left bound reference
103 // if region has a right bound
104 if ( m_region.isRightBoundSpecified() ) {
106 // alignment is on any reference between boundaries
107 if ( alignment.RefID < m_region.RightRefID )
108 return OverlapsRegion;
110 // alignment is on any reference after right boundary
111 else if ( alignment.RefID > m_region.RightRefID )
114 // alignment is on right bound reference
117 // if alignment starts on or before right bound position
118 if ( alignment.Position <= m_region.RightPosition )
119 return OverlapsRegion;
125 // otherwise, alignment starts after left bound and there is no right bound
126 else return OverlapsRegion;
130 void BamRandomAccessController::Close(void) {
135 void BamRandomAccessController::ClearIndex(void) {
140 void BamRandomAccessController::ClearRegion(void) {
142 m_hasAlignmentsInRegion = true;
145 bool BamRandomAccessController::CreateIndex(BamReaderPrivate* reader,
146 const BamIndex::IndexType& type) {
148 // skip if reader is invalid
152 // create new index of requested type
153 BamIndex* newIndex = BamIndexFactory::CreateIndexOfType(type, reader);
154 if ( newIndex == 0 ) {
155 cerr << "BamRandomAccessController ERROR: could not create index of type " << type << endl;
159 // attempt to build index from current BamReader file
160 if ( !newIndex->Create() ) {
161 cerr << "BamRandomAccessController ERROR: could not create index for BAM file: "
162 << reader->Filename() << endl;
169 // set new index's cache mode & return success
170 newIndex->SetCacheMode(m_indexCacheMode);
174 bool BamRandomAccessController::HasIndex(void) const {
175 return ( m_index != 0 );
178 bool BamRandomAccessController::HasRegion(void) const {
179 return ( !m_region.isNull() );
182 bool BamRandomAccessController::IndexHasAlignmentsForReference(const int& refId) {
183 return m_index->HasAlignments(refId);
186 bool BamRandomAccessController::LocateIndex(BamReaderPrivate* reader,
187 const BamIndex::IndexType& preferredType)
189 // look up index filename, deferring to preferredType if possible
190 const string& indexFilename = BamIndexFactory::FindIndexFilename(reader->Filename(), preferredType);
192 // if no index file found (of any type)
193 if ( indexFilename.empty() ) {
194 cerr << "BamRandomAccessController WARNING: "
195 << "could not find index file for BAM: "
196 << reader->Filename() << endl;
200 // otherwise open & use index file that was found
201 return OpenIndex(indexFilename, reader);
204 bool BamRandomAccessController::OpenIndex(const string& indexFilename, BamReaderPrivate* reader) {
206 // attempt create new index of type based on filename
207 BamIndex* index = BamIndexFactory::CreateIndexFromFilename(indexFilename, reader);
209 cerr << "BamRandomAccessController ERROR: could not create index for file: " << indexFilename << endl;
214 index->SetCacheMode(m_indexCacheMode);
216 // attempt to load data from index file
217 if ( !index->Load(indexFilename) ) {
218 cerr << "BamRandomAccessController ERROR: could not load index data from file: " << indexFilename << endl;
222 // save new index & return success
227 bool BamRandomAccessController::RegionHasAlignments(void) const {
228 return m_hasAlignmentsInRegion;
231 void BamRandomAccessController::SetIndex(BamIndex* index) {
237 void BamRandomAccessController::SetIndexCacheMode(const BamIndex::IndexCacheMode& mode) {
238 m_indexCacheMode = mode;
240 m_index->SetCacheMode(mode);
243 bool BamRandomAccessController::SetRegion(BamReaderPrivate* reader,
244 const BamRegion& region,
245 const int& referenceCount)
250 // cannot jump when no index is available
254 // adjust region as necessary to reflect where data actually begins
255 AdjustRegion(referenceCount);
257 // if no data present, return true
258 // * Not an error, but future attempts to access alignments in this region will not return data
259 // Returning true is useful in a BamMultiReader setting where some BAM files may
260 // lack alignments in regions where other BAMs do have data.
261 if ( !m_hasAlignmentsInRegion )
264 // return success/failure of jump to specified region,
266 // * Index::Jump() is allowed to modify the m_hasAlignmentsInRegion flag
267 // This covers 'corner case' where a region is requested that lies beyond the last
268 // alignment on a reference. If this occurs, any subsequent calls to GetNextAlignment[Core]
269 // will not return data. BamMultiReader will still be able to successfully pull alignments
270 // from a region from multiple files even if one or more have no data.
271 return m_index->Jump(m_region, &m_hasAlignmentsInRegion);