]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/BamRandomAccessController_p.cpp
a44563f5489952fb13a1f0e7d8fc0cac35db1b8c
[bamtools.git] / src / api / internal / BamRandomAccessController_p.cpp
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 // **************************************************************************
9
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;
16
17 #include <iostream>
18 using namespace std;
19
20 BamRandomAccessController::BamRandomAccessController(void)
21     : m_index(0)
22     , m_indexCacheMode(BamIndex::LimitedIndexCaching)
23     , m_hasAlignmentsInRegion(true)
24 { }
25
26 BamRandomAccessController::~BamRandomAccessController(void) {
27     Close();
28 }
29
30 void BamRandomAccessController::AdjustRegion(const int& referenceCount) {
31
32     // skip if no index available
33     if ( m_index == 0 )
34         return;
35
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;
43         ++currentId;
44     }
45
46     // if no data found on any reference in region
47     if ( !m_hasAlignmentsInRegion )
48         return;
49
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;
55     }
56 }
57
58 // returns alignments' "RegionState": { Before|Overlaps|After } current region
59 BamRandomAccessController::RegionState
60 BamRandomAccessController::AlignmentState(const BamAlignment& alignment) const {
61
62     // if region has no left bound at all
63     if ( !m_region.isLeftBoundSpecified() )
64         return OverlapsRegion;
65
66     // handle unmapped reads - return AFTER region to halt processing
67     if ( alignment.RefID == -1 )
68         return AfterRegion;
69
70     // if alignment is on any reference before left bound reference
71     if ( alignment.RefID < m_region.LeftRefID )
72         return BeforeRegion;
73
74     // if alignment is on left bound reference
75     else if ( alignment.RefID == m_region.LeftRefID ) {
76
77         // if alignment starts at or after left bound position
78         if ( alignment.Position >= m_region.LeftPosition) {
79
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
83                 return AfterRegion;
84
85             // otherwise, alignment overlaps region
86             else return OverlapsRegion;
87         }
88
89         // alignment starts before left bound position
90         else {
91
92             // if alignment overlaps left bound position
93             if ( alignment.GetEndPosition() >= m_region.LeftPosition )
94                 return OverlapsRegion;
95             else
96                 return BeforeRegion;
97         }
98     }
99
100     // otherwise alignment is on a reference after left bound reference
101     else {
102
103         // if region has a right bound
104         if ( m_region.isRightBoundSpecified() ) {
105
106             // alignment is on any reference between boundaries
107             if ( alignment.RefID < m_region.RightRefID )
108                 return OverlapsRegion;
109
110             // alignment is on any reference after right boundary
111             else if ( alignment.RefID > m_region.RightRefID )
112                 return AfterRegion;
113
114             // alignment is on right bound reference
115             else {
116
117                 // if alignment starts on or before right bound position
118                 if ( alignment.Position <= m_region.RightPosition )
119                     return OverlapsRegion;
120                 else
121                     return AfterRegion;
122             }
123         }
124
125         // otherwise, alignment starts after left bound and there is no right bound
126         else return OverlapsRegion;
127     }
128 }
129
130 void BamRandomAccessController::Close(void) {
131     ClearIndex();
132     ClearRegion();
133 }
134
135 void BamRandomAccessController::ClearIndex(void) {
136     if ( m_index == 0 )
137         return;
138     delete m_index;
139     m_index = 0;
140 }
141
142 void BamRandomAccessController::ClearRegion(void) {
143     m_region.clear();
144     m_hasAlignmentsInRegion = true;
145 }
146
147 bool BamRandomAccessController::CreateIndex(BamReaderPrivate* reader,
148                                             const BamIndex::IndexType& type) {
149
150     // skip if reader is invalid
151     if ( reader == 0 )
152         return false;
153
154     // create new index of requested type
155     BamIndex* newIndex = BamIndexFactory::CreateIndexOfType(type, reader);
156     if ( newIndex == 0 ) {
157         cerr << "BamRandomAccessController ERROR: could not create index of type " << type << endl;
158         return false;
159     }
160
161     // attempt to build index from current BamReader file
162     if ( !newIndex->Create() ) {
163         cerr << "BamRandomAccessController ERROR: could not create index for BAM file: "
164              << reader->Filename() << endl;
165         return false;
166     }
167
168     // save new index
169     SetIndex(newIndex);
170
171     // set new index's cache mode & return success
172     newIndex->SetCacheMode(m_indexCacheMode);
173     return true;
174 }
175
176 bool BamRandomAccessController::HasIndex(void) const {
177     return ( m_index != 0 );
178 }
179
180 bool BamRandomAccessController::HasRegion(void) const  {
181     return ( !m_region.isNull() );
182 }
183
184 bool BamRandomAccessController::IndexHasAlignmentsForReference(const int& refId) {
185     return m_index->HasAlignments(refId);
186 }
187
188 bool BamRandomAccessController::LocateIndex(BamReaderPrivate* reader,
189                                             const BamIndex::IndexType& preferredType)
190 {
191     // look up index filename, deferring to preferredType if possible
192     const string& indexFilename = BamIndexFactory::FindIndexFilename(reader->Filename(), preferredType);
193
194     // if no index file found (of any type)
195     if ( indexFilename.empty() ) {
196         cerr << "BamRandomAccessController WARNING: "
197              << "could not find index file for BAM: "
198              << reader->Filename() << endl;
199         return false;
200     }
201
202     // otherwise open & use index file that was found
203     return OpenIndex(indexFilename, reader);
204 }
205
206 bool BamRandomAccessController::OpenIndex(const string& indexFilename, BamReaderPrivate* reader) {
207
208     // attempt create new index of type based on filename
209     BamIndex* index = BamIndexFactory::CreateIndexFromFilename(indexFilename, reader);
210     if ( index == 0 ) {
211         cerr << "BamRandomAccessController ERROR: could not create index for file: " << indexFilename << endl;
212         return false;
213     }
214
215     // set cache mode
216     index->SetCacheMode(m_indexCacheMode);
217
218     // attempt to load data from index file
219     if ( !index->Load(indexFilename) ) {
220         cerr << "BamRandomAccessController ERROR: could not load index data from file: " << indexFilename << endl;
221         return false;
222     }
223
224     // save new index & return success
225     SetIndex(index);
226     return true;
227 }
228
229 bool BamRandomAccessController::RegionHasAlignments(void) const {
230     return m_hasAlignmentsInRegion;
231 }
232
233 void BamRandomAccessController::SetIndex(BamIndex* index) {
234     if ( m_index )
235         ClearIndex();
236     m_index = index;
237 }
238
239 void BamRandomAccessController::SetIndexCacheMode(const BamIndex::IndexCacheMode& mode) {
240     m_indexCacheMode = mode;
241     if ( m_index )
242         m_index->SetCacheMode(mode);
243 }
244
245 bool BamRandomAccessController::SetRegion(BamReaderPrivate* reader,
246                                           const BamRegion& region,
247                                           const int& referenceCount)
248 {
249     // store region
250     m_region = region;
251
252     // cannot jump when no index is available
253     if ( !HasIndex() )
254         return false;
255
256     // adjust region as necessary to reflect where data actually begins
257     AdjustRegion(referenceCount);
258
259     // if no data present, return true
260     //   * Not an error, but future attempts to access alignments in this region will not return data
261     //     Returning true is useful in a BamMultiReader setting where some BAM files may
262     //     lack alignments in regions where other BAMs do have data.
263     if ( !m_hasAlignmentsInRegion )
264         return true;
265
266     // return success/failure of jump to specified region,
267     //
268     //  * Index::Jump() is allowed to modify the m_hasAlignmentsInRegion flag
269     //    This covers 'corner case' where a region is requested that lies beyond the last
270     //    alignment on a reference. If this occurs, any subsequent calls to GetNextAlignment[Core]
271     //    will not return data. BamMultiReader will still be able to successfully pull alignments
272     //    from a region from multiple files even if one or more have no data.
273     return m_index->Jump(m_region, &m_hasAlignmentsInRegion);
274 }