]> git.donarmstrong.com Git - bamtools.git/blob - src/api/BamAlignment.cpp
Modified BamAlignment in attempt to resolve symbol issues on 64-bit systems.
[bamtools.git] / src / api / BamAlignment.cpp
1 // ***************************************************************************
2 // BamAlignment.cpp (c) 2009 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // All rights reserved.
5 // ---------------------------------------------------------------------------
6 // Last modified: 22 April 2011 (DB)
7 // ---------------------------------------------------------------------------
8 // Provides the BamAlignment data structure
9 // ***************************************************************************
10
11 #include <api/BamAlignment.h>
12 #include <api/BamConstants.h>
13 using namespace BamTools;
14
15 #include <cctype>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <exception>
20 #include <iostream>
21 #include <map>
22 #include <utility>
23 using namespace std;
24
25 /*! \class BamTools::BamAlignment
26     \brief The main BAM alignment data structure.
27
28     Provides methods to query/modify BAM alignment data fields.
29 */
30 /*! \var BamAlignment::Name
31     \brief read name
32 */
33 /*! \var BamAlignment::Length
34     \brief length of query sequence
35 */
36 /*! \var BamAlignment::QueryBases
37     \brief 'original' sequence (as reported from sequencing machine)
38 */
39 /*! \var BamAlignment::AlignedBases
40     \brief 'aligned' sequence (includes any indels, padding, clipping)
41 */
42 /*! \var BamAlignment::Qualities
43     \brief FASTQ qualities (ASCII characters, not numeric values)
44 */
45 /*! \var BamAlignment::TagData
46     \brief tag data (use the provided methods to query/modify)
47 */
48 /*! \var BamAlignment::RefID
49     \brief ID number for reference sequence
50 */
51 /*! \var BamAlignment::Position
52     \brief position (0-based) where alignment starts
53 */
54 /*! \var BamAlignment::Bin
55     \brief BAM (standard) index bin number for this alignment
56 */
57 /*! \var BamAlignment::MapQuality
58     \brief mapping quality score
59 */
60 /*! \var BamAlignment::AlignmentFlag
61     \brief alignment bit-flag (use the provided methods to query/modify)
62 */
63 /*! \var BamAlignment::CigarData
64     \brief CIGAR operations for this alignment
65 */
66 /*! \var BamAlignment::MateRefID
67     \brief ID number for reference sequence where alignment's mate was aligned
68 */
69 /*! \var BamAlignment::MatePosition
70     \brief position (0-based) where alignment's mate starts
71 */
72 /*! \var BamAlignment::InsertSize
73     \brief mate-pair insert size
74 */
75 /*! \var BamAlignment::Filename
76     \brief name of BAM file which this alignment comes from
77 */
78
79 /*! \fn BamAlignment::BamAlignment(void)
80     \brief constructor
81 */
82 BamAlignment::BamAlignment(void)
83     : RefID(-1)
84     , Position(-1)
85     , MateRefID(-1)
86     , MatePosition(-1)
87     , InsertSize(0)
88 { }
89
90 /*! \fn BamAlignment::BamAlignment(const BamAlignment& other)
91     \brief copy constructor
92 */
93 BamAlignment::BamAlignment(const BamAlignment& other)
94     : Name(other.Name)
95     , Length(other.Length)
96     , QueryBases(other.QueryBases)
97     , AlignedBases(other.AlignedBases)
98     , Qualities(other.Qualities)
99     , TagData(other.TagData)
100     , RefID(other.RefID)
101     , Position(other.Position)
102     , Bin(other.Bin)
103     , MapQuality(other.MapQuality)
104     , AlignmentFlag(other.AlignmentFlag)
105     , CigarData(other.CigarData)
106     , MateRefID(other.MateRefID)
107     , MatePosition(other.MatePosition)
108     , InsertSize(other.InsertSize)
109     , Filename(other.Filename)
110     , SupportData(other.SupportData)
111 { }
112
113 /*! \fn BamAlignment::~BamAlignment(void)
114     \brief destructor
115 */
116 BamAlignment::~BamAlignment(void) { }
117
118 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const std::string& value)
119     \brief Adds a field with string data to the BAM tags.
120
121     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
122
123     \param tag   2-character tag name
124     \param type  1-character tag type (must be "Z" or "H")
125     \param value string data to store
126
127     \return \c true if the \b new tag was added successfully
128     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
129 */
130 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const std::string& value) {
131   
132     // skip if core data not parsed
133     if ( SupportData.HasCoreOnly ) return false;
134
135     // validate tag/type size & that type is OK for string value
136     if ( !IsValidSize(tag, type) ) return false;
137     if ( type.at(0) != Constants::BAM_TAG_TYPE_STRING &&
138          type.at(0) != Constants::BAM_TAG_TYPE_HEX
139        )
140     {
141         return false;
142     }
143   
144     // localize the tag data
145     char* pTagData = (char*)TagData.data();
146     const unsigned int tagDataLength = TagData.size();
147     unsigned int numBytesParsed = 0;
148     
149     // if tag already exists, return false
150     // use EditTag explicitly instead
151     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
152         return false;
153   
154     // otherwise, copy tag data to temp buffer
155     string newTag = tag + type + value;
156     const int newTagDataLength = tagDataLength + newTag.size() + 1; // leave room for null-term
157     char originalTagData[newTagDataLength];
158     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
159     
160     // append newTag
161     strcat(originalTagData + tagDataLength, newTag.data());  // removes original null-term, appends newTag + null-term
162     
163     // store temp buffer back in TagData
164     const char* newTagData = (const char*)originalTagData;
165     TagData.assign(newTagData, newTagDataLength);
166     
167     // return success
168     return true;
169 }
170
171 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const uint32_t& value)
172     \brief Adds a field with unsigned integer data to the BAM tags.
173
174     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
175
176     \param tag   2-character tag name
177     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
178     \param value unsigned int data to store
179
180     \return \c true if the \b new tag was added successfully
181     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
182 */
183 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const uint32_t& value) {
184   
185     // skip if core data not parsed
186     if ( SupportData.HasCoreOnly ) return false;
187
188     // validate tag/type size & that type is OK for uint32_t value
189     if ( !IsValidSize(tag, type) ) return false;
190     if ( type.at(0) == Constants::BAM_TAG_TYPE_FLOAT  ||
191          type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
192          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
193          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
194        )
195     {
196         return false;
197     }
198   
199     // localize the tag data
200     char* pTagData = (char*)TagData.data();
201     const unsigned int tagDataLength = TagData.size();
202     unsigned int numBytesParsed = 0;
203     
204     // if tag already exists, return false
205     // use EditTag explicitly instead
206     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
207         return false;
208   
209     // otherwise, convert value to string
210     union { uint32_t value; char valueBuffer[sizeof(uint32_t)]; } un;
211     un.value = value;
212
213     // copy original tag data to temp buffer
214     string newTag = tag + type;
215     const int newTagDataLength = tagDataLength + newTag.size() + 4; // leave room for new integer
216     char originalTagData[newTagDataLength];
217     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
218     
219     // append newTag
220     strcat(originalTagData + tagDataLength, newTag.data());
221     memcpy(originalTagData + tagDataLength + newTag.size(), un.valueBuffer, sizeof(uint32_t));
222     
223     // store temp buffer back in TagData
224     const char* newTagData = (const char*)originalTagData;
225     TagData.assign(newTagData, newTagDataLength);
226     
227     // return success
228     return true;
229 }
230
231 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const int32_t& value)
232     \brief Adds a field with signed integer data to the BAM tags.
233
234     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
235
236     \param tag   2-character tag name
237     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
238     \param value signed int data to store
239
240     \return \c true if the \b new tag was added successfully
241     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
242 */
243 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const int32_t& value) {
244     return AddTag(tag, type, (const uint32_t&)value);
245 }
246
247 /*! \fn bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const float& value)
248     \brief Adds a field with floating-point data to the BAM tags.
249
250     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
251
252     \param tag  2-character tag name
253     \param type  1-character tag type (must NOT be "Z", "H", or "B")
254     \param value float data to store
255
256     \return \c true if the \b new tag was added successfully
257     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
258 */
259 bool BamAlignment::AddTag(const std::string& tag, const std::string& type, const float& value) {
260   
261     // skip if core data not parsed
262     if ( SupportData.HasCoreOnly ) return false;
263
264     // validate tag/type size & that type is OK for float value
265     if ( !IsValidSize(tag, type) ) return false;
266     if ( type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
267          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
268          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
269        )
270     {
271         return false;
272     }
273
274     // localize the tag data
275     char* pTagData = (char*)TagData.data();
276     const unsigned int tagDataLength = TagData.size();
277     unsigned int numBytesParsed = 0;
278     
279     // if tag already exists, return false
280     // use EditTag explicitly instead
281     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
282         return false;
283   
284     // otherwise, convert value to string
285     union { float value; char valueBuffer[sizeof(float)]; } un;
286     un.value = value;
287
288     // copy original tag data to temp buffer
289     string newTag = tag + type;
290     const int newTagDataLength = tagDataLength + newTag.size() + 4; // leave room for new float
291     char originalTagData[newTagDataLength];
292     memcpy(originalTagData, TagData.c_str(), tagDataLength + 1);    // '+1' for TagData null-term
293     
294     // append newTag
295     strcat(originalTagData + tagDataLength, newTag.data());
296     memcpy(originalTagData + tagDataLength + newTag.size(), un.valueBuffer, sizeof(float));
297     
298     // store temp buffer back in TagData
299     const char* newTagData = (const char*)originalTagData;
300     TagData.assign(newTagData, newTagDataLength);
301     
302     // return success
303     return true;
304 }
305
306 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint8_t>& values);
307     \brief Adds a numeric array field to the BAM tags.
308
309     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
310
311     \param tag    2-character tag name
312     \param values vector of uint8_t values to store
313
314     \return \c true if the \b new tag was added successfully
315     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
316 */
317 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint8_t>& values) {
318
319     // skip if core data not parsed
320     if ( SupportData.HasCoreOnly ) return false;
321
322     // check for valid tag length
323     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
324
325     // localize the tag data
326     char* pTagData = (char*)TagData.data();
327     const unsigned int tagDataLength = TagData.size();
328     unsigned int numBytesParsed = 0;
329
330     // if tag already exists, return false
331     // use EditTag explicitly instead
332     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
333         return false;
334
335     // build new tag's base information
336     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
337     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
338     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
339     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT8;
340
341     // add number of array elements to newTagBase
342     const int32_t numElements  = values.size();
343     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
344
345     // copy current TagData string to temp buffer, leaving room for new tag's contents
346     const int newTagDataLength = tagDataLength +
347                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
348                                  numElements*sizeof(uint8_t);
349     char originalTagData[newTagDataLength];
350     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
351
352     // write newTagBase (removes old null term)
353     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
354
355     // add vector elements to tag
356     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
357     for ( int i = 0 ; i < numElements; ++i ) {
358         const uint8_t value = values.at(i);
359         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint8_t),
360                &value, sizeof(uint8_t));
361     }
362
363     // store temp buffer back in TagData
364     const char* newTagData = (const char*)originalTagData;
365     TagData.assign(newTagData, newTagDataLength);
366
367     // return success
368     return true;
369 }
370
371 /*! \fn bool AddTag(const std::string& tag, const std::vector<int8_t>& values);
372     \brief Adds a numeric array field to the BAM tags.
373
374     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
375
376     \param tag    2-character tag name
377     \param values vector of int8_t values to store
378
379     \return \c true if the \b new tag was added successfully
380     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
381 */
382 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int8_t>& values) {
383
384     // skip if core data not parsed
385     if ( SupportData.HasCoreOnly ) return false;
386
387     // check for valid tag length
388     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
389
390     // localize the tag data
391     char* pTagData = (char*)TagData.data();
392     const unsigned int tagDataLength = TagData.size();
393     unsigned int numBytesParsed = 0;
394
395     // if tag already exists, return false
396     // use EditTag explicitly instead
397     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
398         return false;
399
400     // build new tag's base information
401     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
402     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
403     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
404     newTagBase[3] = Constants::BAM_TAG_TYPE_INT8;
405
406     // add number of array elements to newTagBase
407     const int32_t numElements  = values.size();
408     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
409
410     // copy current TagData string to temp buffer, leaving room for new tag's contents
411     const int newTagDataLength = tagDataLength +
412                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
413                                  numElements*sizeof(int8_t);
414     char originalTagData[newTagDataLength];
415     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
416
417     // write newTagBase (removes old null term)
418     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
419
420     // add vector elements to tag
421     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
422     for ( int i = 0 ; i < numElements; ++i ) {
423         const int8_t value = values.at(i);
424         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int8_t),
425                &value, sizeof(int8_t));
426     }
427
428     // store temp buffer back in TagData
429     const char* newTagData = (const char*)originalTagData;
430     TagData.assign(newTagData, newTagDataLength);
431
432     // return success
433     return true;
434 }
435
436 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint16_t>& values);
437     \brief Adds a numeric array field to the BAM tags.
438
439     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
440
441     \param tag    2-character tag name
442     \param values vector of uint16_t values to store
443
444     \return \c true if the \b new tag was added successfully
445     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
446 */
447 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint16_t>& values) {
448
449     // skip if core data not parsed
450     if ( SupportData.HasCoreOnly ) return false;
451
452     // check for valid tag length
453     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
454
455     // localize the tag data
456     char* pTagData = (char*)TagData.data();
457     const unsigned int tagDataLength = TagData.size();
458     unsigned int numBytesParsed = 0;
459
460     // if tag already exists, return false
461     // use EditTag explicitly instead
462     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
463         return false;
464
465     // build new tag's base information
466     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
467     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
468     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
469     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT16;
470
471     // add number of array elements to newTagBase
472     const int32_t numElements  = values.size();
473     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
474
475     // copy current TagData string to temp buffer, leaving room for new tag's contents
476     const int newTagDataLength = tagDataLength +
477                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
478                                  numElements*sizeof(uint16_t);
479     char originalTagData[newTagDataLength];
480     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
481
482     // write newTagBase (removes old null term)
483     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
484
485     // add vector elements to tag
486     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
487     for ( int i = 0 ; i < numElements; ++i ) {
488         const uint16_t value = values.at(i);
489         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint16_t),
490                &value, sizeof(uint16_t));
491     }
492
493     // store temp buffer back in TagData
494     const char* newTagData = (const char*)originalTagData;
495     TagData.assign(newTagData, newTagDataLength);
496
497     // return success
498     return true;
499 }
500
501 /*! \fn bool AddTag(const std::string& tag, const std::vector<int16_t>& values);
502     \brief Adds a numeric array field to the BAM tags.
503
504     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
505
506     \param tag    2-character tag name
507     \param values vector of int16_t values to store
508
509     \return \c true if the \b new tag was added successfully
510     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
511 */
512 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int16_t>& values) {
513
514     // skip if core data not parsed
515     if ( SupportData.HasCoreOnly ) return false;
516
517     // check for valid tag length
518     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
519
520     // localize the tag data
521     char* pTagData = (char*)TagData.data();
522     const unsigned int tagDataLength = TagData.size();
523     unsigned int numBytesParsed = 0;
524
525     // if tag already exists, return false
526     // use EditTag explicitly instead
527     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
528         return false;
529
530     // build new tag's base information
531     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
532     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
533     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
534     newTagBase[3] = Constants::BAM_TAG_TYPE_INT16;
535
536     // add number of array elements to newTagBase
537     const int32_t numElements  = values.size();
538     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
539
540     // copy current TagData string to temp buffer, leaving room for new tag's contents
541     const int newTagDataLength = tagDataLength +
542                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
543                                  numElements*sizeof(int16_t);
544     char originalTagData[newTagDataLength];
545     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
546
547     // write newTagBase (removes old null term)
548     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
549
550     // add vector elements to tag
551     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
552     for ( int i = 0 ; i < numElements; ++i ) {
553         const int16_t value = values.at(i);
554         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int16_t),
555                &value, sizeof(int16_t));
556     }
557
558     // store temp buffer back in TagData
559     const char* newTagData = (const char*)originalTagData;
560     TagData.assign(newTagData, newTagDataLength);
561
562     // return success
563     return true;
564 }
565
566 /*! \fn bool AddTag(const std::string& tag, const std::vector<uint32_t>& values);
567     \brief Adds a numeric array field to the BAM tags.
568
569     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
570
571     \param tag    2-character tag name
572     \param values vector of uint32_t values to store
573
574     \return \c true if the \b new tag was added successfully
575     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
576 */
577 bool BamAlignment::AddTag(const std::string& tag, const std::vector<uint32_t>& values) {
578
579     // skip if core data not parsed
580     if ( SupportData.HasCoreOnly ) return false;
581
582     // check for valid tag length
583     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
584
585     // localize the tag data
586     char* pTagData = (char*)TagData.data();
587     const unsigned int tagDataLength = TagData.size();
588     unsigned int numBytesParsed = 0;
589
590     // if tag already exists, return false
591     // use EditTag explicitly instead
592     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
593         return false;
594
595     // build new tag's base information
596     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
597     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
598     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
599     newTagBase[3] = Constants::BAM_TAG_TYPE_UINT32;
600
601     // add number of array elements to newTagBase
602     const int32_t numElements  = values.size();
603     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
604
605     // copy current TagData string to temp buffer, leaving room for new tag's contents
606     const int newTagDataLength = tagDataLength +
607                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
608                                  numElements*sizeof(uint32_t);
609     char originalTagData[newTagDataLength];
610     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
611
612     // write newTagBase (removes old null term)
613     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
614
615     // add vector elements to tag
616     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
617     for ( int i = 0 ; i < numElements; ++i ) {
618         const uint32_t value = values.at(i);
619         memcpy(originalTagData + elementsBeginOffset + i*sizeof(uint32_t),
620                &value, sizeof(uint32_t));
621     }
622
623     // store temp buffer back in TagData
624     const char* newTagData = (const char*)originalTagData;
625     TagData.assign(newTagData, newTagDataLength);
626
627     // return success
628     return true;
629 }
630
631 /*! \fn bool AddTag(const std::string& tag, const std::vector<int32_t>& values);
632     \brief Adds a numeric array field to the BAM tags.
633
634     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
635
636     \param tag    2-character tag name
637     \param values vector of int32_t values to store
638
639     \return \c true if the \b new tag was added successfully
640     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
641 */
642 bool BamAlignment::AddTag(const std::string& tag, const std::vector<int32_t>& values) {
643
644     // skip if core data not parsed
645     if ( SupportData.HasCoreOnly ) return false;
646
647     // check for valid tag length
648     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
649
650     // localize the tag data
651     char* pTagData = (char*)TagData.data();
652     const unsigned int tagDataLength = TagData.size();
653     unsigned int numBytesParsed = 0;
654
655     // if tag already exists, return false
656     // use EditTag explicitly instead
657     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
658         return false;
659
660     // build new tag's base information
661     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
662     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
663     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
664     newTagBase[3] = Constants::BAM_TAG_TYPE_INT32;
665
666     // add number of array elements to newTagBase
667     const int32_t numElements  = values.size();
668     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
669
670     // copy current TagData string to temp buffer, leaving room for new tag's contents
671     const int newTagDataLength = tagDataLength +
672                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
673                                  numElements*sizeof(int32_t);
674     char originalTagData[newTagDataLength];
675     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
676
677     // write newTagBase (removes old null term)
678     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
679
680     // add vector elements to tag
681     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
682     for ( int i = 0 ; i < numElements; ++i ) {
683         const int32_t value = values.at(i);
684         memcpy(originalTagData + elementsBeginOffset + i*sizeof(int32_t),
685                &value, sizeof(int32_t));
686     }
687
688     // store temp buffer back in TagData
689     const char* newTagData = (const char*)originalTagData;
690     TagData.assign(newTagData, newTagDataLength);
691
692     // return success
693     return true;
694 }
695
696 /*! \fn bool AddTag(const std::string& tag, const std::vector<float>& values);
697     \brief Adds a numeric array field to the BAM tags.
698
699     Does NOT modify an existing tag - use \link BamAlignment::EditTag() \endlink instead.
700
701     \param tag    2-character tag name
702     \param values vector of float values to store
703
704     \return \c true if the \b new tag was added successfully
705     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
706 */
707 bool BamAlignment::AddTag(const std::string& tag, const std::vector<float>& values) {
708
709     // skip if core data not parsed
710     if ( SupportData.HasCoreOnly ) return false;
711
712     // check for valid tag length
713     if ( tag.size() != Constants::BAM_TAG_TAGSIZE ) return false;
714
715     // localize the tag data
716     char* pTagData = (char*)TagData.data();
717     const unsigned int tagDataLength = TagData.size();
718     unsigned int numBytesParsed = 0;
719
720     // if tag already exists, return false
721     // use EditTag explicitly instead
722     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
723         return false;
724
725     // build new tag's base information
726     char newTagBase[Constants::BAM_TAG_ARRAYBASE_SIZE];
727     memcpy( newTagBase, tag.c_str(), Constants::BAM_TAG_TAGSIZE );
728     newTagBase[2] = Constants::BAM_TAG_TYPE_ARRAY;
729     newTagBase[3] = Constants::BAM_TAG_TYPE_FLOAT;
730
731     // add number of array elements to newTagBase
732     const int32_t numElements  = values.size();
733     memcpy(newTagBase + 4, &numElements, sizeof(int32_t));
734
735     // copy current TagData string to temp buffer, leaving room for new tag's contents
736     const int newTagDataLength = tagDataLength +
737                                  Constants::BAM_TAG_ARRAYBASE_SIZE +
738                                  numElements*sizeof(float);
739     char originalTagData[newTagDataLength];
740     memcpy(originalTagData, TagData.c_str(), tagDataLength+1); // '+1' for TagData's null-term
741
742     // write newTagBase (removes old null term)
743     strcat(originalTagData + tagDataLength, (const char*)newTagBase);
744
745     // add vector elements to tag
746     int elementsBeginOffset = tagDataLength + Constants::BAM_TAG_ARRAYBASE_SIZE;
747     for ( int i = 0 ; i < numElements; ++i ) {
748         const float value = values.at(i);
749         memcpy(originalTagData + elementsBeginOffset + i*sizeof(float),
750                &value, sizeof(float));
751     }
752
753     // store temp buffer back in TagData
754     const char* newTagData = (const char*)originalTagData;
755     TagData.assign(newTagData, newTagDataLength);
756
757     // return success
758     return true;
759 }
760
761 /*! \fn bool BamAlignment::BuildCharData(void)
762     \brief Populates alignment string fields (read name, bases, qualities, tag data).
763
764     An alignment retrieved using BamReader::GetNextAlignmentCore() lacks this data.
765     Using that method makes parsing much quicker when only positional data is required.
766
767     However, if you later want to access the character data fields from such an alignment,
768     use this method to populate those fields. Provides ability to do 'lazy evaluation' of
769     alignment parsing.
770
771     \return \c true if character data populated successfully (or was already available to begin with)
772 */
773 bool BamAlignment::BuildCharData(void) {
774
775     // skip if char data already parsed
776     if ( !SupportData.HasCoreOnly )
777         return true;
778
779     // check system endianness
780     bool IsBigEndian = BamTools::SystemIsBigEndian();
781
782     // calculate character lengths/offsets
783     const unsigned int dataLength     = SupportData.BlockLength - Constants::BAM_CORE_SIZE;
784     const unsigned int seqDataOffset  = SupportData.QueryNameLength + (SupportData.NumCigarOperations * 4);
785     const unsigned int qualDataOffset = seqDataOffset + (SupportData.QuerySequenceLength+1)/2;
786     const unsigned int tagDataOffset  = qualDataOffset + SupportData.QuerySequenceLength;
787     const unsigned int tagDataLength  = dataLength - tagDataOffset;
788
789     // check offsets to see what char data exists
790     const bool hasSeqData  = ( seqDataOffset  < dataLength );
791     const bool hasQualData = ( qualDataOffset < dataLength );
792     const bool hasTagData  = ( tagDataOffset  < dataLength );
793
794     // set up char buffers
795     const char* allCharData = SupportData.AllCharData.data();
796     const char* seqData     = ( hasSeqData  ? (((const char*)allCharData) + seqDataOffset)  : (const char*)0 );
797     const char* qualData    = ( hasQualData ? (((const char*)allCharData) + qualDataOffset) : (const char*)0 );
798           char* tagData     = ( hasTagData  ? (((char*)allCharData) + tagDataOffset)        : (char*)0 );
799
800     // store alignment name (relies on null char in name as terminator)
801     Name.assign((const char*)(allCharData));
802
803     // save query sequence
804     QueryBases.clear();
805     if ( hasSeqData ) {
806         QueryBases.reserve(SupportData.QuerySequenceLength);
807         for (unsigned int i = 0; i < SupportData.QuerySequenceLength; ++i) {
808             char singleBase = Constants::BAM_DNA_LOOKUP[ ( (seqData[(i/2)] >> (4*(1-(i%2)))) & 0xf ) ];
809             QueryBases.append(1, singleBase);
810         }
811     }
812
813     // save qualities, converting from numeric QV to 'FASTQ-style' ASCII character
814     Qualities.clear();
815     if ( hasQualData ) {
816         Qualities.reserve(SupportData.QuerySequenceLength);
817         for (unsigned int i = 0; i < SupportData.QuerySequenceLength; ++i) {
818             char singleQuality = (char)(qualData[i]+33);
819             Qualities.append(1, singleQuality);
820         }
821     }
822
823     // clear previous AlignedBases
824     AlignedBases.clear();
825
826     // if QueryBases has data, build AlignedBases using CIGAR data
827     // otherwise, AlignedBases will remain empty (this case IS allowed)
828     if ( !QueryBases.empty() ) {
829
830         // resize AlignedBases
831         AlignedBases.reserve(SupportData.QuerySequenceLength);
832
833         // iterate over CigarOps
834         int k = 0;
835         vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
836         vector<CigarOp>::const_iterator cigarEnd  = CigarData.end();
837         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
838             const CigarOp& op = (*cigarIter);
839
840             switch (op.Type) {
841
842                 // for 'M', 'I', '=', 'X' - write bases
843                 case (Constants::BAM_CIGAR_MATCH_CHAR)    :
844                 case (Constants::BAM_CIGAR_INS_CHAR)      :
845                 case (Constants::BAM_CIGAR_SEQMATCH_CHAR) :
846                 case (Constants::BAM_CIGAR_MISMATCH_CHAR) :
847                     AlignedBases.append(QueryBases.substr(k, op.Length));
848                     // fall through
849
850                 // for 'S' - soft clip, do not write bases
851                 // but increment placeholder 'k'
852                 case (Constants::BAM_CIGAR_SOFTCLIP_CHAR) :
853                     k += op.Length;
854                     break;
855
856                 // for 'D' - write gap character
857                 case (Constants::BAM_CIGAR_DEL_CHAR) :
858                     AlignedBases.append(op.Length, Constants::BAM_DNA_DEL);
859                     break;
860
861                 // for 'P' - write padding character
862                 case (Constants::BAM_CIGAR_PAD_CHAR) :
863                     AlignedBases.append( op.Length, Constants::BAM_DNA_PAD );
864                     break;
865
866                 // for 'N' - write N's, skip bases in original query sequence
867                 case (Constants::BAM_CIGAR_REFSKIP_CHAR) :
868                     AlignedBases.append( op.Length, Constants::BAM_DNA_N );
869                     break;
870
871                 // for 'H' - hard clip, do nothing to AlignedBases, move to next op
872                 case (Constants::BAM_CIGAR_HARDCLIP_CHAR) :
873                     break;
874
875                 // shouldn't get here
876                 default:
877                     cerr << "BamAlignment ERROR: invalid CIGAR operation type: "
878                          << op.Type << endl;
879                     exit(1);
880             }
881         }
882     }
883
884     // save tag data
885     TagData.clear();
886     if ( hasTagData ) {
887         if ( IsBigEndian ) {
888             int i = 0;
889             while ( (unsigned int)i < tagDataLength ) {
890
891                 i += Constants::BAM_TAG_TAGSIZE;  // skip tag chars (e.g. "RG", "NM", etc.)
892                 const char type = tagData[i];     // get tag type at position i
893                 ++i;                              // move i past tag type
894
895                 switch (type) {
896
897                     case(Constants::BAM_TAG_TYPE_ASCII) :
898                     case(Constants::BAM_TAG_TYPE_INT8)  :
899                     case(Constants::BAM_TAG_TYPE_UINT8) :
900                         // no endian swapping necessary for single-byte data
901                         ++i;
902                         break;
903
904                     case(Constants::BAM_TAG_TYPE_INT16)  :
905                     case(Constants::BAM_TAG_TYPE_UINT16) :
906                         BamTools::SwapEndian_16p(&tagData[i]);
907                         i += sizeof(uint16_t);
908                         break;
909
910                     case(Constants::BAM_TAG_TYPE_FLOAT)  :
911                     case(Constants::BAM_TAG_TYPE_INT32)  :
912                     case(Constants::BAM_TAG_TYPE_UINT32) :
913                         BamTools::SwapEndian_32p(&tagData[i]);
914                         i += sizeof(uint32_t);
915                         break;
916
917                     case(Constants::BAM_TAG_TYPE_HEX) :
918                     case(Constants::BAM_TAG_TYPE_STRING) :
919                         // no endian swapping necessary for hex-string/string data
920                         while ( tagData[i] )
921                             ++i;
922                         // increment one more for null terminator
923                         ++i;
924                         break;
925
926                     case(Constants::BAM_TAG_TYPE_ARRAY) :
927
928                     {
929                         // read array type
930                         const char arrayType = tagData[i];
931                         ++i;
932
933                         // swap endian-ness of number of elements in place, then retrieve for loop
934                         BamTools::SwapEndian_32p(&tagData[i]);
935                         int32_t numElements;
936                         memcpy(&numElements, &tagData[i], sizeof(uint32_t));
937                         i += sizeof(uint32_t);
938
939                         // swap endian-ness of array elements
940                         for ( int j = 0; j < numElements; ++j ) {
941                             switch (arrayType) {
942                                 case (Constants::BAM_TAG_TYPE_INT8)  :
943                                 case (Constants::BAM_TAG_TYPE_UINT8) :
944                                     // no endian-swapping necessary
945                                     ++i;
946                                     break;
947                                 case (Constants::BAM_TAG_TYPE_INT16)  :
948                                 case (Constants::BAM_TAG_TYPE_UINT16) :
949                                     BamTools::SwapEndian_16p(&tagData[i]);
950                                     i += sizeof(uint16_t);
951                                     break;
952                                 case (Constants::BAM_TAG_TYPE_FLOAT)  :
953                                 case (Constants::BAM_TAG_TYPE_INT32)  :
954                                 case (Constants::BAM_TAG_TYPE_UINT32) :
955                                     BamTools::SwapEndian_32p(&tagData[i]);
956                                     i += sizeof(uint32_t);
957                                     break;
958                                 default:
959                                     // error case
960                                     cerr << "BamAlignment ERROR: unknown binary array type encountered: "
961                                          << arrayType << endl;
962                                     return false;
963                             }
964                         }
965
966                         break;
967                     }
968
969                     // shouldn't get here
970                     default :
971                         cerr << "BamAlignment ERROR: invalid tag value type: "
972                              << type << endl;
973                         exit(1);
974                 }
975             }
976         }
977
978         // store tagData in alignment
979         TagData.resize(tagDataLength);
980         memcpy((char*)TagData.data(), tagData, tagDataLength);
981     }
982
983     // clear the core-only flag
984     SupportData.HasCoreOnly = false;
985
986     // return success
987     return true;
988 }
989
990 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const std::string& value)
991     \brief Edits a BAM tag field containing string data.
992
993     If \a tag does not exist, a new entry is created.
994
995     \param tag   2-character tag name
996     \param type  1-character tag type (must be "Z" or "H")
997     \param value string data to store
998
999     \return \c true if the tag was modified/created successfully
1000
1001     \sa BamAlignment::RemoveTag()
1002     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1003 */
1004 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const std::string& value) {
1005   
1006     // skip if core data not parsed
1007     if ( SupportData.HasCoreOnly ) return false;
1008
1009     // validate tag/type size & that type is OK for string value
1010     if ( !IsValidSize(tag, type) ) return false;
1011     if ( type.at(0) != Constants::BAM_TAG_TYPE_STRING &&
1012          type.at(0) != Constants::BAM_TAG_TYPE_HEX )
1013         return false;
1014   
1015     // localize the tag data
1016     char* pOriginalTagData = (char*)TagData.data();
1017     char* pTagData = pOriginalTagData;
1018     const unsigned int originalTagDataLength = TagData.size();
1019     
1020     unsigned int newTagDataLength = 0;
1021     unsigned int numBytesParsed = 0;
1022     
1023     // if tag found
1024     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1025         
1026         // make sure array is more than big enough
1027         char newTagData[originalTagDataLength + value.size()];  
1028
1029         // copy original tag data up til desired tag
1030         const unsigned int beginningTagDataLength = numBytesParsed;
1031         newTagDataLength += beginningTagDataLength;
1032         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1033       
1034         // copy new @value in place of current tag data
1035         const unsigned int dataLength = strlen(value.c_str());
1036         memcpy(newTagData + beginningTagDataLength, (char*)value.c_str(), dataLength+1 );
1037         
1038         // skip to next tag (if tag for removal is last, return true) 
1039         const char* pTagStorageType = pTagData - 1;
1040         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1041             return true;
1042          
1043         // copy everything from current tag (the next one after tag for removal) to end
1044         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1045         const unsigned int endTagOffset      = beginningTagDataLength + dataLength + 1;
1046         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1047         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1048         
1049         // ensure null-terminator
1050         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1051         
1052         // save new tag data
1053         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1054         return true;
1055     }
1056     
1057     // tag not found, attempt AddTag
1058     else return AddTag(tag, type, value);
1059 }
1060
1061 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const uint32_t& value)
1062     \brief Edits a BAM tag field containing unsigned integer data.
1063
1064     If \a tag does not exist, a new entry is created.
1065
1066     \param tag   2-character tag name
1067     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
1068     \param value unsigned integer data to store
1069
1070     \return \c true if the tag was modified/created successfully
1071
1072     \sa BamAlignment::RemoveTag()
1073     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1074 */
1075 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const uint32_t& value) {
1076   
1077     // skip if core data not parsed
1078     if ( SupportData.HasCoreOnly ) return false;
1079
1080     // validate tag/type size & that type is OK for uint32_t value
1081     if ( !IsValidSize(tag, type) ) return false;
1082     if ( type.at(0) == Constants::BAM_TAG_TYPE_FLOAT  ||
1083          type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
1084          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
1085          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
1086        )
1087     {
1088         return false;
1089     }
1090
1091     // localize the tag data
1092     char* pOriginalTagData = (char*)TagData.data();
1093     char* pTagData = pOriginalTagData;
1094     const unsigned int originalTagDataLength = TagData.size();
1095     
1096     unsigned int newTagDataLength = 0;
1097     unsigned int numBytesParsed = 0;
1098     
1099     // if tag found
1100     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1101         
1102         // make sure array is more than big enough
1103         char newTagData[originalTagDataLength + sizeof(value)];  
1104
1105         // copy original tag data up til desired tag
1106         const unsigned int beginningTagDataLength = numBytesParsed;
1107         newTagDataLength += beginningTagDataLength;
1108         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1109       
1110         // copy new @value in place of current tag data
1111         union { uint32_t value; char valueBuffer[sizeof(uint32_t)]; } un;
1112         un.value = value;
1113         memcpy(newTagData + beginningTagDataLength, un.valueBuffer, sizeof(uint32_t));
1114         
1115         // skip to next tag (if tag for removal is last, return true) 
1116         const char* pTagStorageType = pTagData - 1;
1117         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1118             return true;
1119          
1120         // copy everything from current tag (the next one after tag for removal) to end
1121         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1122         const unsigned int endTagOffset      = beginningTagDataLength + sizeof(uint32_t);
1123         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1124         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1125         
1126         // ensure null-terminator
1127         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1128         
1129         // save new tag data
1130         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1131         return true;
1132     }
1133     
1134     // tag not found, attempt AddTag
1135     else return AddTag(tag, type, value);
1136 }
1137
1138 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const int32_t& value)
1139     \brief Edits a BAM tag field containing signed integer data.
1140
1141     If \a tag does not exist, a new entry is created.
1142
1143     \param tag   2-character tag name
1144     \param type  1-character tag type (must NOT be "f", "Z", "H", or "B")
1145     \param value signed integer data to store
1146
1147     \return \c true if the tag was modified/created successfully
1148
1149     \sa BamAlignment::RemoveTag()
1150     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1151 */
1152 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const int32_t& value) {
1153     return EditTag(tag, type, (const uint32_t&)value);
1154 }
1155
1156 /*! \fn bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const float& value)
1157     \brief Edits a BAM tag field containing floating-point data.
1158
1159     If \a tag does not exist, a new entry is created.
1160
1161     \param tag   2-character tag name
1162     \param type  1-character tag type (must NOT be "Z", "H", or "B")
1163     \param value float data to store
1164
1165     \return \c true if the tag was modified/created successfully
1166
1167     \sa BamAlignment::RemoveTag()
1168     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1169 */
1170 bool BamAlignment::EditTag(const std::string& tag, const std::string& type, const float& value) {
1171   
1172     // skip if core data not parsed
1173     if ( SupportData.HasCoreOnly ) return false;
1174
1175     // validate tag/type size & that type is OK for float value
1176     if ( !IsValidSize(tag, type) ) return false;
1177     if ( type.at(0) == Constants::BAM_TAG_TYPE_STRING ||
1178          type.at(0) == Constants::BAM_TAG_TYPE_HEX    ||
1179          type.at(0) == Constants::BAM_TAG_TYPE_ARRAY
1180        )
1181     {
1182         return false;
1183     }
1184
1185      // localize the tag data
1186     char* pOriginalTagData = (char*)TagData.data();
1187     char* pTagData = pOriginalTagData;
1188     const unsigned int originalTagDataLength = TagData.size();
1189     
1190     unsigned int newTagDataLength = 0;
1191     unsigned int numBytesParsed = 0;
1192     
1193     // if tag found
1194     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
1195         
1196         // make sure array is more than big enough
1197         char newTagData[originalTagDataLength + sizeof(value)];  
1198
1199         // copy original tag data up til desired tag
1200         const unsigned int beginningTagDataLength = numBytesParsed;
1201         newTagDataLength += beginningTagDataLength;
1202         memcpy(newTagData, pOriginalTagData, numBytesParsed);
1203       
1204         // copy new @value in place of current tag data
1205         union { float value; char valueBuffer[sizeof(float)]; } un;
1206         un.value = value;
1207         memcpy(newTagData + beginningTagDataLength, un.valueBuffer, sizeof(float));
1208         
1209         // skip to next tag (if tag for removal is last, return true) 
1210         const char* pTagStorageType = pTagData - 1;
1211         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
1212             return true;
1213          
1214         // copy everything from current tag (the next one after tag for removal) to end
1215         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
1216         const unsigned int endTagOffset      = beginningTagDataLength + sizeof(float);
1217         const unsigned int endTagDataLength  = originalTagDataLength - beginningTagDataLength - skippedDataLength;
1218         memcpy(newTagData + endTagOffset, pTagData, endTagDataLength);
1219         
1220         // ensure null-terminator
1221         newTagData[ endTagOffset + endTagDataLength + 1 ] = 0;
1222         
1223         // save new tag data
1224         TagData.assign(newTagData, endTagOffset + endTagDataLength);
1225         return true;
1226     }
1227     
1228     // tag not found, attempt AddTag
1229     else return AddTag(tag, type, value);
1230 }
1231
1232 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint8_t>& values);
1233     \brief Edits a BAM tag field containing a numeric array.
1234
1235     If \a tag does not exist, a new entry is created.
1236
1237     \param tag   2-character tag name
1238     \param value vector of uint8_t values to store
1239
1240     \return \c true if the tag was modified/created successfully
1241     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1242 */
1243 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint8_t>& values) {
1244
1245     // can't do anything if TagData not parsed
1246     if ( SupportData.HasCoreOnly )
1247         return false;
1248
1249     // remove existing tag if present
1250     if ( HasTag(tag) )
1251         RemoveTag(tag);
1252
1253     // add tag record with new values
1254     return AddTag(tag, values);
1255 }
1256
1257 /*! \fn bool EditTag(const std::string& tag, const std::vector<int8_t>& values);
1258     \brief Edits a BAM tag field containing a numeric array.
1259
1260     If \a tag does not exist, a new entry is created.
1261
1262     \param tag   2-character tag name
1263     \param value vector of int8_t values to store
1264
1265     \return \c true if the tag was modified/created successfully
1266     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1267 */
1268 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int8_t>& values) {
1269
1270     // can't do anything if TagData not parsed
1271     if ( SupportData.HasCoreOnly )
1272         return false;
1273
1274     // remove existing tag if present
1275     if ( HasTag(tag) )
1276         RemoveTag(tag);
1277
1278     // add tag record with new values
1279     return AddTag(tag, values);
1280 }
1281
1282 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint16_t>& values);
1283     \brief Edits a BAM tag field containing a numeric array.
1284
1285     If \a tag does not exist, a new entry is created.
1286
1287     \param tag   2-character tag name
1288     \param value vector of uint16_t values to store
1289
1290     \return \c true if the tag was modified/created successfully
1291     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1292 */
1293 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint16_t>& values) {
1294
1295     // can't do anything if TagData not parsed
1296     if ( SupportData.HasCoreOnly )
1297         return false;
1298
1299     // remove existing tag if present
1300     if ( HasTag(tag) )
1301         RemoveTag(tag);
1302
1303     // add tag record with new values
1304     return AddTag(tag, values);
1305 }
1306
1307 /*! \fn bool EditTag(const std::string& tag, const std::vector<int16_t>& values);
1308     \brief Edits a BAM tag field containing a numeric array.
1309
1310     If \a tag does not exist, a new entry is created.
1311
1312     \param tag   2-character tag name
1313     \param value vector of int16_t values to store
1314
1315     \return \c true if the tag was modified/created successfully
1316     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1317 */
1318 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int16_t>& values) {
1319
1320     // can't do anything if TagData not parsed
1321     if ( SupportData.HasCoreOnly )
1322         return false;
1323
1324     // remove existing tag if present
1325     if ( HasTag(tag) )
1326         RemoveTag(tag);
1327
1328     // add tag record with new values
1329     return AddTag(tag, values);
1330 }
1331
1332 /*! \fn bool EditTag(const std::string& tag, const std::vector<uint32_t>& values);
1333     \brief Edits a BAM tag field containing a numeric array.
1334
1335     If \a tag does not exist, a new entry is created.
1336
1337     \param tag   2-character tag name
1338     \param value vector of uint32_t values to store
1339
1340     \return \c true if the tag was modified/created successfully
1341     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1342 */
1343 bool BamAlignment::EditTag(const std::string& tag, const std::vector<uint32_t>& values) {
1344
1345     // can't do anything if TagData not parsed
1346     if ( SupportData.HasCoreOnly )
1347         return false;
1348
1349     // remove existing tag if present
1350     if ( HasTag(tag) )
1351         RemoveTag(tag);
1352
1353     // add tag record with new values
1354     return AddTag(tag, values);
1355 }
1356
1357 /*! \fn bool EditTag(const std::string& tag, const std::vector<int32_t>& values);
1358     \brief Edits a BAM tag field containing a numeric array.
1359
1360     If \a tag does not exist, a new entry is created.
1361
1362     \param tag   2-character tag name
1363     \param value vector of int32_t values to store
1364
1365     \return \c true if the tag was modified/created successfully
1366     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1367 */
1368 bool BamAlignment::EditTag(const std::string& tag, const std::vector<int32_t>& values) {
1369
1370     // can't do anything if TagData not parsed
1371     if ( SupportData.HasCoreOnly )
1372         return false;
1373
1374     // remove existing tag if present
1375     if ( HasTag(tag) )
1376         RemoveTag(tag);
1377
1378     // add tag record with new values
1379     return AddTag(tag, values);
1380 }
1381
1382 /*! \fn bool EditTag(const std::string& tag, const std::vector<float>& values);
1383     \brief Edits a BAM tag field containing a numeric array.
1384
1385     If \a tag does not exist, a new entry is created.
1386
1387     \param tag   2-character tag name
1388     \param value vector of float values to store
1389
1390     \return \c true if the tag was modified/created successfully
1391     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1392 */
1393 bool BamAlignment::EditTag(const std::string& tag, const std::vector<float>& values) {
1394
1395     // can't do anything if TagData not parsed
1396     if ( SupportData.HasCoreOnly )
1397         return false;
1398
1399     // remove existing tag if present
1400     if ( HasTag(tag) )
1401         RemoveTag(tag);
1402
1403     // add tag record with new values
1404     return AddTag(tag, values);
1405 }
1406
1407 /*! \fn bool BamAlignment::FindTag(const std::string& tag, char*& pTagData, const unsigned int& tagDataLength, unsigned int& numBytesParsed)
1408     \internal
1409
1410     Searches for requested tag in BAM tag data.
1411
1412     \param tag            requested 2-character tag name
1413     \param pTagData       pointer to current position in BamAlignment::TagData
1414     \param tagDataLength  length of BamAlignment::TagData
1415     \param numBytesParsed number of bytes parsed so far
1416
1417     \return \c true if found
1418
1419     \post If \a tag is found, \a pTagData will point to the byte where the tag data begins.
1420           \a numBytesParsed will correspond to the position in the full TagData string.
1421
1422 */
1423 bool BamAlignment::FindTag(const std::string& tag,
1424                            char*& pTagData,
1425                            const unsigned int& tagDataLength,
1426                            unsigned int& numBytesParsed) const
1427 {
1428
1429     while ( numBytesParsed < tagDataLength ) {
1430
1431         const char* pTagType        = pTagData;
1432         const char* pTagStorageType = pTagData + 2;
1433         pTagData       += 3;
1434         numBytesParsed += 3;
1435
1436         // check the current tag, return true on match
1437         if ( strncmp(pTagType, tag.c_str(), 2) == 0 )
1438             return true;
1439
1440         // get the storage class and find the next tag
1441         if ( *pTagStorageType == '\0' ) return false;
1442         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) ) return false;
1443         if ( *pTagData == '\0' ) return false;
1444     }
1445
1446     // checked all tags, none match
1447     return false;
1448 }
1449
1450 /*! \fn bool BamAlignment::GetEditDistance(uint32_t& editDistance) const
1451     \brief Retrieves value of edit distance tag ("NM").
1452
1453     \deprecated Instead use BamAlignment::GetTag()
1454         \code
1455             BamAlignment::GetTag("NM", editDistance);
1456         \endcode
1457
1458     \param editDistance destination for retrieved value
1459
1460     \return \c true if found
1461 */
1462 bool BamAlignment::GetEditDistance(uint32_t& editDistance) const {
1463     return GetTag("NM", (uint32_t&)editDistance);
1464 }
1465
1466 /*! \fn int BamAlignment::GetEndPosition(bool usePadded = false, bool zeroBased = true) const
1467     \brief Calculates alignment end position, based on starting position and CIGAR data.
1468
1469     \param usePadded Inserted bases affect reported position. Default is false, so that reported
1470                      position stays 'sync-ed' with reference coordinates.
1471     \param zeroBased Return (BAM standard) 0-based coordinate. Setting this to false can be useful
1472                      when using BAM data with half-open formats (e.g. BED).
1473
1474     \return alignment end position
1475 */
1476 int BamAlignment::GetEndPosition(bool usePadded, bool zeroBased) const {
1477
1478     // initialize alignment end to starting position
1479     int alignEnd = Position;
1480
1481     // iterate over cigar operations
1482     vector<CigarOp>::const_iterator cigarIter = CigarData.begin();
1483     vector<CigarOp>::const_iterator cigarEnd  = CigarData.end();
1484     for ( ; cigarIter != cigarEnd; ++cigarIter) {
1485         const char cigarType = (*cigarIter).Type;
1486         const uint32_t& cigarLength = (*cigarIter).Length;
1487
1488         if ( cigarType == Constants::BAM_CIGAR_MATCH_CHAR ||
1489              cigarType == Constants::BAM_CIGAR_DEL_CHAR ||
1490              cigarType == Constants::BAM_CIGAR_REFSKIP_CHAR )
1491             alignEnd += cigarLength;
1492         else if ( usePadded && cigarType == Constants::BAM_CIGAR_INS_CHAR )
1493             alignEnd += cigarLength;
1494     }
1495
1496     // adjust for zero-based coordinates, if requested
1497     if ( zeroBased ) alignEnd -= 1;
1498
1499     // return result
1500     return alignEnd;
1501 }
1502
1503 /*! \fn bool BamAlignment::GetReadGroup(std::string& readGroup) const
1504     \brief Retrieves value of read group tag ("RG").
1505
1506     \deprecated Instead use BamAlignment::GetTag()
1507         \code
1508             BamAlignment::GetTag("RG", readGroup);
1509         \endcode
1510
1511     \param readGroup destination for retrieved value
1512
1513     \return \c true if found
1514 */
1515 bool BamAlignment::GetReadGroup(std::string& readGroup) const {
1516     return GetTag("RG", readGroup);
1517 }
1518
1519 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::string& destination) const
1520     \brief Retrieves the string value associated with a BAM tag.
1521
1522     \param tag         2-character tag name
1523     \param destination destination for retrieved value
1524
1525     \return \c true if found
1526 */
1527 bool BamAlignment::GetTag(const std::string& tag, std::string& destination) const {
1528
1529     // make sure tag data exists
1530     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1531         return false;
1532
1533     // localize the tag data
1534     char* pTagData = (char*)TagData.data();
1535     const unsigned int tagDataLength = TagData.size();
1536     unsigned int numBytesParsed = 0;
1537     
1538     // if tag found
1539     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1540         const unsigned int dataLength = strlen(pTagData);
1541         destination.clear();
1542         destination.resize(dataLength);
1543         memcpy( (char*)destination.data(), pTagData, dataLength );
1544         return true;
1545     }
1546     
1547     // tag not found, return failure
1548     return false;
1549 }
1550
1551 /*! \fn bool BamAlignment::GetTag(const std::string& tag, uint32_t& destination) const
1552     \brief Retrieves the unsigned integer value associated with a BAM tag.
1553
1554     \param tag         2-character tag name
1555     \param destination destination for retrieved value
1556
1557     \return \c true if found
1558 */
1559 bool BamAlignment::GetTag(const std::string& tag, uint32_t& destination) const {
1560   
1561     // make sure tag data exists
1562     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1563         return false;
1564
1565     // localize the tag data
1566     char* pTagData = (char*)TagData.data();
1567     const unsigned int tagDataLength = TagData.size();
1568     unsigned int numBytesParsed = 0;
1569     
1570     // if tag found
1571     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1572         
1573         // determine data byte-length
1574         const char type = *(pTagData - 1);
1575         int destinationLength = 0;
1576         switch (type) {
1577
1578             // 1 byte data
1579             case (Constants::BAM_TAG_TYPE_ASCII) :
1580             case (Constants::BAM_TAG_TYPE_INT8)  :
1581             case (Constants::BAM_TAG_TYPE_UINT8) :
1582                 destinationLength = 1;
1583                 break;
1584
1585             // 2 byte data
1586             case (Constants::BAM_TAG_TYPE_INT16)  :
1587             case (Constants::BAM_TAG_TYPE_UINT16) :
1588                 destinationLength = 2;
1589                 break;
1590
1591             // 4 byte data
1592             case (Constants::BAM_TAG_TYPE_INT32)  :
1593             case (Constants::BAM_TAG_TYPE_UINT32) :
1594                 destinationLength = 4;
1595                 break;
1596
1597             // unsupported type for integer destination (float or var-length strings)
1598             case (Constants::BAM_TAG_TYPE_FLOAT)  :
1599             case (Constants::BAM_TAG_TYPE_STRING) :
1600             case (Constants::BAM_TAG_TYPE_HEX)    :
1601             case (Constants::BAM_TAG_TYPE_ARRAY)  :
1602                 cerr << "BamAlignment ERROR: cannot store tag of type " << type
1603                      << " in integer destination" << endl;
1604                 return false;
1605
1606             // unknown tag type
1607             default:
1608                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
1609                      << type << endl;
1610                 return false;
1611         }
1612           
1613         // store in destination
1614         destination = 0;
1615         memcpy(&destination, pTagData, destinationLength);
1616         return true;
1617     }
1618     
1619     // tag not found, return failure
1620     return false;
1621 }
1622
1623 /*! \fn bool BamAlignment::GetTag(const std::string& tag, int32_t& destination) const
1624     \brief Retrieves the signed integer value associated with a BAM tag.
1625
1626     \param tag         2-character tag name
1627     \param destination destination for retrieved value
1628
1629     \return \c true if found
1630 */
1631 bool BamAlignment::GetTag(const std::string& tag, int32_t& destination) const {
1632     return GetTag(tag, (uint32_t&)destination);
1633 }
1634
1635 /*! \fn bool BamAlignment::GetTag(const std::string& tag, float& destination) const
1636     \brief Retrieves the floating-point value associated with a BAM tag.
1637
1638     \param tag         2-character tag name
1639     \param destination destination for retrieved value
1640
1641     \return \c true if found
1642 */
1643 bool BamAlignment::GetTag(const std::string& tag, float& destination) const {
1644   
1645     // make sure tag data exists
1646     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1647         return false;
1648
1649     // localize the tag data
1650     char* pTagData = (char*)TagData.data();
1651     const unsigned int tagDataLength = TagData.size();
1652     unsigned int numBytesParsed = 0;
1653     
1654     // if tag found
1655     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1656         
1657         // determine data byte-length
1658         const char type = *(pTagData - 1);
1659         int destinationLength = 0;
1660         switch (type) {
1661
1662             // 1 byte data
1663             case (Constants::BAM_TAG_TYPE_ASCII) :
1664             case (Constants::BAM_TAG_TYPE_INT8)  :
1665             case (Constants::BAM_TAG_TYPE_UINT8) :
1666                 destinationLength = 1;
1667                 break;
1668
1669             // 2 byte data
1670             case (Constants::BAM_TAG_TYPE_INT16)  :
1671             case (Constants::BAM_TAG_TYPE_UINT16) :
1672                 destinationLength = 2;
1673                 break;
1674
1675             // 4 byte data
1676             case (Constants::BAM_TAG_TYPE_FLOAT)  :
1677             case (Constants::BAM_TAG_TYPE_INT32)  :
1678             case (Constants::BAM_TAG_TYPE_UINT32) :
1679                 destinationLength = 4;
1680                 break;
1681             
1682             // unsupported type (var-length strings)
1683             case (Constants::BAM_TAG_TYPE_STRING) :
1684             case (Constants::BAM_TAG_TYPE_HEX)    :
1685             case (Constants::BAM_TAG_TYPE_ARRAY)  :
1686                 cerr << "BamAlignment ERROR: cannot store tag of type " << type
1687                      << " in float destination" << endl;
1688                 return false;
1689
1690             // unknown tag type
1691             default:
1692                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
1693                      << type << endl;
1694                 return false;
1695         }
1696           
1697         // store in destination
1698         destination = 0.0;
1699         memcpy(&destination, pTagData, destinationLength);
1700         return true;
1701     }
1702     
1703     // tag not found, return failure
1704     return false;
1705 }
1706
1707 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<uint32_t>& destination) const
1708     \brief Retrieves the numeric array data associated with a BAM tag
1709
1710     \param tag         2-character tag name
1711     \param destination destination for retrieved data
1712
1713     \return \c true if found
1714 */
1715 bool BamAlignment::GetTag(const std::string& tag, std::vector<uint32_t>& destination) const {
1716
1717     // make sure tag data exists
1718     if ( SupportData.HasCoreOnly || TagData.empty() )
1719         return false;
1720
1721     // localize the tag data
1722     char* pTagData = (char*)TagData.data();
1723     const unsigned int tagDataLength = TagData.size();
1724     unsigned int numBytesParsed = 0;
1725
1726     // return false if tag not found
1727     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1728         return false;
1729
1730     // check that tag is array type
1731     const char tagType = *(pTagData - 1);
1732     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1733         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1734              << tag << " in array destination" << endl;
1735         return false;
1736     }
1737
1738     // calculate length of each element in tag's array
1739     const char elementType = *pTagData;
1740     ++pTagData;
1741     int elementLength = 0;
1742     switch ( elementType ) {
1743         case (Constants::BAM_TAG_TYPE_ASCII) :
1744         case (Constants::BAM_TAG_TYPE_INT8)  :
1745         case (Constants::BAM_TAG_TYPE_UINT8) :
1746             elementLength = sizeof(uint8_t);
1747             break;
1748
1749         case (Constants::BAM_TAG_TYPE_INT16)  :
1750         case (Constants::BAM_TAG_TYPE_UINT16) :
1751             elementLength = sizeof(uint16_t);
1752             break;
1753
1754         case (Constants::BAM_TAG_TYPE_INT32)  :
1755         case (Constants::BAM_TAG_TYPE_UINT32) :
1756             elementLength = sizeof(uint32_t);
1757             break;
1758
1759         // unsupported type for integer destination (float or var-length data)
1760         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1761         case (Constants::BAM_TAG_TYPE_STRING) :
1762         case (Constants::BAM_TAG_TYPE_HEX)    :
1763         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1764             cerr << "BamAlignment ERROR: array element type: " << elementType
1765                  << " cannot be stored in integer value" << endl;
1766             return false;
1767
1768         // unknown tag type
1769         default:
1770             cerr << "BamAlignment ERROR: unknown element type encountered: "
1771                  << elementType << endl;
1772             return false;
1773     }
1774
1775     // get number of elements
1776     int32_t numElements;
1777     memcpy(&numElements, pTagData, sizeof(int32_t));
1778     pTagData += 4;
1779     destination.clear();
1780     destination.reserve(numElements);
1781
1782     // read in elements
1783     uint32_t value;
1784     for ( int i = 0 ; i < numElements; ++i ) {
1785         memcpy(&value, pTagData, sizeof(uint32_t));
1786         pTagData += sizeof(uint32_t);
1787         destination.push_back(value);
1788     }
1789
1790     // return success
1791     return false;
1792 }
1793
1794 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<int32_t>& destination) const
1795     \brief Retrieves the numeric array data associated with a BAM tag
1796
1797     \param tag         2-character tag name
1798     \param destination destination for retrieved data
1799
1800     \return \c true if found
1801 */
1802 bool BamAlignment::GetTag(const std::string& tag, std::vector<int32_t>& destination) const {
1803
1804     // make sure tag data exists
1805     if ( SupportData.HasCoreOnly || TagData.empty() )
1806         return false;
1807
1808     // localize the tag data
1809     char* pTagData = (char*)TagData.data();
1810     const unsigned int tagDataLength = TagData.size();
1811     unsigned int numBytesParsed = 0;
1812
1813     // return false if tag not found
1814     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1815         return false;
1816
1817     // check that tag is array type
1818     const char tagType = *(pTagData - 1);
1819     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1820         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1821              << tag << " in array destination" << endl;
1822         return false;
1823     }
1824
1825     // calculate length of each element in tag's array
1826     const char elementType = *pTagData;
1827     ++pTagData;
1828     int elementLength = 0;
1829     switch ( elementType ) {
1830         case (Constants::BAM_TAG_TYPE_ASCII) :
1831         case (Constants::BAM_TAG_TYPE_INT8)  :
1832         case (Constants::BAM_TAG_TYPE_UINT8) :
1833             elementLength = sizeof(uint8_t);
1834             break;
1835
1836         case (Constants::BAM_TAG_TYPE_INT16)  :
1837         case (Constants::BAM_TAG_TYPE_UINT16) :
1838             elementLength = sizeof(uint16_t);
1839             break;
1840
1841         case (Constants::BAM_TAG_TYPE_INT32)  :
1842         case (Constants::BAM_TAG_TYPE_UINT32) :
1843             elementLength = sizeof(uint32_t);
1844             break;
1845
1846         // unsupported type for integer destination (float or var-length data)
1847         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1848         case (Constants::BAM_TAG_TYPE_STRING) :
1849         case (Constants::BAM_TAG_TYPE_HEX)    :
1850         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1851             cerr << "BamAlignment ERROR: array element type: " << elementType
1852                  << " cannot be stored in integer value" << endl;
1853             return false;
1854
1855         // unknown tag type
1856         default:
1857             cerr << "BamAlignment ERROR: unknown element type encountered: "
1858                  << elementType << endl;
1859             return false;
1860     }
1861
1862     // get number of elements
1863     int32_t numElements;
1864     memcpy(&numElements, pTagData, sizeof(int32_t));
1865     pTagData += 4;
1866     destination.clear();
1867     destination.reserve(numElements);
1868
1869     // read in elements
1870     int32_t value;
1871     for ( int i = 0 ; i < numElements; ++i ) {
1872         memcpy(&value, pTagData, sizeof(int32_t));
1873         pTagData += sizeof(int32_t);
1874         destination.push_back(value);
1875     }
1876
1877     // return success
1878     return false;
1879
1880 }
1881
1882 /*! \fn bool BamAlignment::GetTag(const std::string& tag, std::vector<float>& destination) const
1883     \brief Retrieves the numeric array data associated with a BAM tag
1884
1885     \param tag         2-character tag name
1886     \param destination destination for retrieved data
1887
1888     \return \c true if found
1889 */
1890 bool BamAlignment::GetTag(const std::string& tag, std::vector<float>& destination) const {
1891
1892     // make sure tag data exists
1893     if ( SupportData.HasCoreOnly || TagData.empty() )
1894         return false;
1895
1896     // localize the tag data
1897     char* pTagData = (char*)TagData.data();
1898     const unsigned int tagDataLength = TagData.size();
1899     unsigned int numBytesParsed = 0;
1900
1901     // return false if tag not found
1902     if ( !FindTag(tag, pTagData, tagDataLength, numBytesParsed) )
1903         return false;
1904
1905     // check that tag is array type
1906     const char tagType = *(pTagData - 1);
1907     if ( tagType != Constants::BAM_TAG_TYPE_ARRAY ) {
1908         cerr << "BamAlignment ERROR: Cannot store non-array data from tag: "
1909              << tag << " in array destination" << endl;
1910         return false;
1911     }
1912
1913     // calculate length of each element in tag's array
1914     const char elementType = *pTagData;
1915     ++pTagData;
1916     int elementLength = 0;
1917     switch ( elementType ) {
1918         case (Constants::BAM_TAG_TYPE_ASCII) :
1919         case (Constants::BAM_TAG_TYPE_INT8)  :
1920         case (Constants::BAM_TAG_TYPE_UINT8) :
1921             elementLength = sizeof(uint8_t);
1922             break;
1923
1924         case (Constants::BAM_TAG_TYPE_INT16)  :
1925         case (Constants::BAM_TAG_TYPE_UINT16) :
1926             elementLength = sizeof(uint16_t);
1927             break;
1928
1929         case (Constants::BAM_TAG_TYPE_INT32)  :
1930         case (Constants::BAM_TAG_TYPE_UINT32) :
1931         case (Constants::BAM_TAG_TYPE_FLOAT)  :
1932             elementLength = sizeof(uint32_t);
1933             break;
1934
1935         // unsupported type for float destination (var-length data)
1936         case (Constants::BAM_TAG_TYPE_STRING) :
1937         case (Constants::BAM_TAG_TYPE_HEX)    :
1938         case (Constants::BAM_TAG_TYPE_ARRAY)  :
1939             cerr << "BamAlignment ERROR: array element type: " << elementType
1940                  << " cannot be stored in float value" << endl;
1941             return false;
1942
1943         // unknown tag type
1944         default:
1945             cerr << "BamAlignment ERROR: unknown element type encountered: "
1946                  << elementType << endl;
1947             return false;
1948     }
1949
1950     // get number of elements
1951     int32_t numElements;
1952     memcpy(&numElements, pTagData, sizeof(int32_t));
1953     pTagData += 4;
1954     destination.clear();
1955     destination.reserve(numElements);
1956
1957     // read in elements
1958     float value;
1959     for ( int i = 0 ; i < numElements; ++i ) {
1960         memcpy(&value, pTagData, sizeof(float));
1961         pTagData += sizeof(float);
1962         destination.push_back(value);
1963     }
1964
1965     // return success
1966     return false;
1967 }
1968
1969 /*! \fn bool BamAlignment::GetTagType(const std::string& tag, char& type) const
1970     \brief Retrieves the BAM tag type-code associated with requested tag name.
1971
1972     \param tag  2-character tag name
1973     \param type destination for the retrieved (1-character) tag type
1974
1975     \return \c true if found
1976     \sa \samSpecURL for more details on reserved tag names, supported tag types, etc.
1977 */
1978 bool BamAlignment::GetTagType(const std::string& tag, char& type) const {
1979   
1980     // make sure tag data exists
1981     if ( SupportData.HasCoreOnly || TagData.empty() ) 
1982         return false;
1983
1984     // localize the tag data
1985     char* pTagData = (char*)TagData.data();
1986     const unsigned int tagDataLength = TagData.size();
1987     unsigned int numBytesParsed = 0;
1988     
1989     // lookup tag
1990     if ( FindTag(tag, pTagData, tagDataLength, numBytesParsed) ) {
1991         
1992         // retrieve tag type code
1993         type = *(pTagData - 1);
1994         
1995         // validate that type is a proper BAM tag type
1996         switch (type) {
1997             case (Constants::BAM_TAG_TYPE_ASCII)  :
1998             case (Constants::BAM_TAG_TYPE_INT8)   :
1999             case (Constants::BAM_TAG_TYPE_UINT8)  :
2000             case (Constants::BAM_TAG_TYPE_INT16)  :
2001             case (Constants::BAM_TAG_TYPE_UINT16) :
2002             case (Constants::BAM_TAG_TYPE_INT32)  :
2003             case (Constants::BAM_TAG_TYPE_UINT32) :
2004             case (Constants::BAM_TAG_TYPE_FLOAT)  :
2005             case (Constants::BAM_TAG_TYPE_STRING) :
2006             case (Constants::BAM_TAG_TYPE_HEX)    :
2007             case (Constants::BAM_TAG_TYPE_ARRAY)  :
2008                 return true;
2009
2010             // unknown tag type
2011             default:
2012                 cerr << "BamAlignment ERROR: unknown tag type encountered: "
2013                      << type << endl;
2014                 return false;
2015         }
2016     }
2017     
2018     // tag not found, return failure
2019     return false;
2020 }
2021
2022 /*! \fn bool BamAlignment::HasTag(const std::string& tag) const
2023     \brief Returns true if alignment has a record for requested tag.
2024     \param tag 2-character tag name
2025     \return \c true if alignment has a record for tag
2026 */
2027 bool BamAlignment::HasTag(const std::string& tag) const {
2028
2029     // return false if no tag data present
2030     if ( SupportData.HasCoreOnly || TagData.empty() )
2031         return false;
2032
2033     // localize the tag data for lookup
2034     char* pTagData = (char*)TagData.data();
2035     const unsigned int tagDataLength = TagData.size();
2036     unsigned int numBytesParsed = 0;
2037
2038     // if result of tag lookup
2039     return FindTag(tag, pTagData, tagDataLength, numBytesParsed);
2040 }
2041
2042 /*! \fn bool BamAlignment::IsDuplicate(void) const
2043     \return \c true if this read is a PCR duplicate
2044 */
2045 bool BamAlignment::IsDuplicate(void) const {
2046     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_DUPLICATE) != 0 );
2047 }
2048
2049 /*! \fn bool BamAlignment::IsFailedQC(void) const
2050     \return \c true if this read failed quality control
2051 */
2052 bool BamAlignment::IsFailedQC(void) const {
2053     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_QC_FAILED) != 0 );
2054 }
2055
2056 /*! \fn bool BamAlignment::IsFirstMate(void) const
2057     \return \c true if alignment is first mate on paired-end read
2058 */
2059 bool BamAlignment::IsFirstMate(void) const {
2060     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_1) != 0 );
2061 }
2062
2063 /*! \fn bool BamAlignment::IsMapped(void) const
2064     \return \c true if alignment is mapped
2065 */
2066 bool BamAlignment::IsMapped(void) const {
2067     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_UNMAPPED) == 0 );
2068 }
2069
2070 /*! \fn bool BamAlignment::IsMateMapped(void) const
2071     \return \c true if alignment's mate is mapped
2072 */
2073 bool BamAlignment::IsMateMapped(void) const {
2074     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_UNMAPPED) == 0 );
2075 }
2076
2077 /*! \fn bool BamAlignment::IsMateReverseStrand(void) const
2078     \return \c true if alignment's mate mapped to reverse strand
2079 */
2080 bool BamAlignment::IsMateReverseStrand(void) const {
2081     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND) != 0 );
2082 }
2083
2084 /*! \fn bool BamAlignment::IsPaired(void) const
2085     \return \c true if alignment part of paired-end read
2086 */
2087 bool BamAlignment::IsPaired(void) const {
2088     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PAIRED) != 0 );
2089 }
2090
2091 /*! \fn bool BamAlignment::IsPrimaryAlignment(void) const
2092     \return \c true if reported position is primary alignment
2093 */
2094 bool BamAlignment::IsPrimaryAlignment(void) const  {
2095     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_SECONDARY) == 0 );
2096 }
2097
2098 /*! \fn bool BamAlignment::IsProperPair(void) const
2099     \return \c true if alignment is part of read that satisfied paired-end resolution
2100 */
2101 bool BamAlignment::IsProperPair(void) const {
2102     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_PROPER_PAIR) != 0 );
2103 }
2104
2105 /*! \fn bool BamAlignment::IsReverseStrand(void) const
2106     \return \c true if alignment mapped to reverse strand
2107 */
2108 bool BamAlignment::IsReverseStrand(void) const {
2109     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_REVERSE_STRAND) != 0 );
2110 }
2111
2112 /*! \fn bool BamAlignment::IsSecondMate(void) const
2113     \return \c true if alignment is second mate on read
2114 */
2115 bool BamAlignment::IsSecondMate(void) const {
2116     return ( (AlignmentFlag & Constants::BAM_ALIGNMENT_READ_2) != 0 );
2117 }
2118
2119 /*! \fn bool BamAlignment::IsValidSize(const string& tag, const string& type) const
2120     \internal
2121
2122     Checks that tag name & type strings are expected sizes.
2123     \a tag  should have length
2124     \a type should have length 1
2125
2126     \param tag  BAM tag name
2127     \param type BAM tag type-code
2128
2129     \return \c true if both \a tag and \a type are correct sizes
2130 */
2131 bool BamAlignment::IsValidSize(const string& tag, const string& type) const {
2132     return (tag.size()  == Constants::BAM_TAG_TAGSIZE) &&
2133            (type.size() == Constants::BAM_TAG_TYPESIZE);
2134 }
2135
2136 /*! \fn bool BamAlignment::RemoveTag(const std::string& tag)
2137     \brief Removes field from BAM tags.
2138
2139     \return \c true if tag was removed successfully (or didn't exist before)
2140 */
2141 bool BamAlignment::RemoveTag(const std::string& tag) {
2142   
2143     // skip if no tag data available
2144     if ( SupportData.HasCoreOnly || TagData.empty() )
2145         return false;
2146   
2147     // localize the tag data
2148     char* pOriginalTagData = (char*)TagData.data();
2149     char* pTagData = pOriginalTagData;
2150     const unsigned int originalTagDataLength = TagData.size();
2151     unsigned int newTagDataLength = 0;
2152     unsigned int numBytesParsed = 0;
2153     
2154     // if tag found
2155     if ( FindTag(tag, pTagData, originalTagDataLength, numBytesParsed) ) {
2156         
2157         char newTagData[originalTagDataLength];
2158
2159         // copy original tag data up til desired tag
2160         pTagData       -= 3;
2161         numBytesParsed -= 3;
2162         const unsigned int beginningTagDataLength = numBytesParsed;
2163         newTagDataLength += beginningTagDataLength;
2164         memcpy(newTagData, pOriginalTagData, numBytesParsed);
2165         
2166         // skip to next tag (if tag for removal is last, return true) 
2167         const char* pTagStorageType = pTagData + 2;
2168         pTagData       += 3;
2169         numBytesParsed += 3;
2170         if ( !SkipToNextTag(*pTagStorageType, pTagData, numBytesParsed) )
2171             return true;
2172          
2173         // copy everything from current tag (the next one after tag for removal) to end
2174         const unsigned int skippedDataLength = (numBytesParsed - beginningTagDataLength);
2175         const unsigned int endTagDataLength = originalTagDataLength - beginningTagDataLength - skippedDataLength;
2176         memcpy(newTagData + beginningTagDataLength, pTagData, endTagDataLength );
2177         
2178         // save new tag data
2179         TagData.assign(newTagData, beginningTagDataLength + endTagDataLength);
2180         return true;
2181     }
2182     
2183     // tag not found, no removal - return failure
2184     return false;
2185 }
2186
2187 /*! \fn void BamAlignment::SetIsDuplicate(bool ok)
2188     \brief Sets value of "PCR duplicate" flag to \a ok.
2189 */
2190 void BamAlignment::SetIsDuplicate(bool ok) {
2191     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_DUPLICATE;
2192     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_DUPLICATE;
2193 }
2194
2195 /*! \fn void BamAlignment::SetIsFailedQC(bool ok)
2196     \brief Sets "failed quality control" flag to \a ok.
2197 */
2198 void BamAlignment::SetIsFailedQC(bool ok) {
2199     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_QC_FAILED;
2200     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_QC_FAILED;
2201 }
2202
2203 /*! \fn void BamAlignment::SetIsFirstMate(bool ok)
2204     \brief Sets "alignment is first mate" flag to \a ok.
2205 */
2206 void BamAlignment::SetIsFirstMate(bool ok) {
2207     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_READ_1;
2208     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_1;
2209 }
2210
2211 /*! \fn void BamAlignment::SetIsMapped(bool ok)
2212     \brief Sets "alignment is mapped" flag to \a ok.
2213 */
2214 void BamAlignment::SetIsMapped(bool ok) {
2215     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_UNMAPPED;
2216     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_UNMAPPED;
2217 }
2218
2219 /*! \fn void BamAlignment::SetIsMateMapped(bool ok)
2220     \brief Sets "alignment's mate is mapped" flag to \a ok.
2221 */
2222 void BamAlignment::SetIsMateMapped(bool ok) {
2223     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
2224     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_MATE_UNMAPPED;
2225 }
2226
2227 /*! \fn void BamAlignment::SetIsMateUnmapped(bool ok)
2228     \brief Complement of using SetIsMateMapped().
2229     \deprecated For sake of symmetry with the query methods
2230     \sa IsMateMapped(), SetIsMateMapped()
2231 */
2232 void BamAlignment::SetIsMateUnmapped(bool ok) {
2233     SetIsMateMapped(!ok);
2234 }
2235
2236 /*! \fn void BamAlignment::SetIsMateReverseStrand(bool ok)
2237     \brief Sets "alignment's mate mapped to reverse strand" flag to \a ok.
2238 */
2239 void BamAlignment::SetIsMateReverseStrand(bool ok) {
2240     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
2241     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_MATE_REVERSE_STRAND;
2242 }
2243
2244 /*! \fn void BamAlignment::SetIsPaired(bool ok)
2245     \brief Sets "alignment part of paired-end read" flag to \a ok.
2246 */
2247 void BamAlignment::SetIsPaired(bool ok) {
2248     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_PAIRED;
2249     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PAIRED;
2250 }
2251
2252 /*! \fn void BamAlignment::SetIsPrimaryAlignment(bool ok)
2253     \brief Sets "position is primary alignment" flag to \a ok.
2254 */
2255 void BamAlignment::SetIsPrimaryAlignment(bool ok) {
2256     if (ok) AlignmentFlag &= ~Constants::BAM_ALIGNMENT_SECONDARY;
2257     else    AlignmentFlag |=  Constants::BAM_ALIGNMENT_SECONDARY;
2258 }
2259
2260 /*! \fn void BamAlignment::SetIsProperPair(bool ok)
2261     \brief Sets "alignment is part of read that satisfied paired-end resolution" flag to \a ok.
2262 */
2263 void BamAlignment::SetIsProperPair(bool ok) {
2264     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_PROPER_PAIR;
2265     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_PROPER_PAIR;
2266 }
2267
2268 /*! \fn void BamAlignment::SetIsReverseStrand(bool ok)
2269     \brief Sets "alignment mapped to reverse strand" flag to \a ok.
2270 */
2271 void BamAlignment::SetIsReverseStrand(bool ok) {
2272     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_REVERSE_STRAND;
2273     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_REVERSE_STRAND;
2274 }
2275
2276 /*! \fn void BamAlignment::SetIsSecondaryAlignment(bool ok)
2277     \brief Complement of using SetIsPrimaryAlignment().
2278     \deprecated For sake of symmetry with the query methods
2279     \sa IsPrimaryAlignment(), SetIsPrimaryAlignment()
2280 */
2281 void BamAlignment::SetIsSecondaryAlignment(bool ok) {
2282     SetIsPrimaryAlignment(!ok);
2283 }
2284
2285 /*! \fn void BamAlignment::SetIsSecondMate(bool ok)
2286     \brief Sets "alignment is second mate on read" flag to \a ok.
2287 */
2288 void BamAlignment::SetIsSecondMate(bool ok) {
2289     if (ok) AlignmentFlag |=  Constants::BAM_ALIGNMENT_READ_2;
2290     else    AlignmentFlag &= ~Constants::BAM_ALIGNMENT_READ_2;
2291 }
2292
2293 /*! \fn void BamAlignment::SetIsUnmapped(bool ok)
2294     \brief Complement of using SetIsMapped().
2295     \deprecated For sake of symmetry with the query methods
2296     \sa IsMapped(), SetIsMapped()
2297 */
2298 void BamAlignment::SetIsUnmapped(bool ok) {
2299     SetIsMapped(!ok);
2300 }
2301
2302 /*! \fn bool BamAlignment::SkipToNextTag(const char storageType, char*& pTagData, unsigned int& numBytesParsed)
2303     \internal
2304
2305     Moves to next available tag in tag data string
2306
2307     \param storageType    BAM tag type-code that determines how far to move cursor
2308     \param pTagData       pointer to current position (cursor) in tag string
2309     \param numBytesParsed report of how many bytes were parsed (cumulatively)
2310
2311     \return \c if storageType was a recognized BAM tag type
2312     \post \a pTagData will point to the byte where the next tag data begins.
2313           \a numBytesParsed will correspond to the cursor's position in the full TagData string.
2314 */
2315 bool BamAlignment::SkipToNextTag(const char storageType,
2316                                  char*& pTagData,
2317                                  unsigned int& numBytesParsed) const
2318 {
2319     switch (storageType) {
2320
2321         case (Constants::BAM_TAG_TYPE_ASCII) :
2322         case (Constants::BAM_TAG_TYPE_INT8)  :
2323         case (Constants::BAM_TAG_TYPE_UINT8) :
2324             ++numBytesParsed;
2325             ++pTagData;
2326             break;
2327
2328         case (Constants::BAM_TAG_TYPE_INT16)  :
2329         case (Constants::BAM_TAG_TYPE_UINT16) :
2330             numBytesParsed += sizeof(uint16_t);
2331             pTagData       += sizeof(uint16_t);
2332             break;
2333
2334         case (Constants::BAM_TAG_TYPE_FLOAT)  :
2335         case (Constants::BAM_TAG_TYPE_INT32)  :
2336         case (Constants::BAM_TAG_TYPE_UINT32) :
2337             numBytesParsed += sizeof(uint32_t);
2338             pTagData       += sizeof(uint32_t);
2339             break;
2340
2341         case (Constants::BAM_TAG_TYPE_STRING) :
2342         case (Constants::BAM_TAG_TYPE_HEX)    :
2343             while( *pTagData ) {
2344                 ++numBytesParsed;
2345                 ++pTagData;
2346             }
2347             // increment for null-terminator
2348             ++numBytesParsed;
2349             ++pTagData;
2350             break;
2351
2352         case (Constants::BAM_TAG_TYPE_ARRAY) :
2353
2354         {
2355             // read array type
2356             const char arrayType = *pTagData;
2357             ++numBytesParsed;
2358             ++pTagData;
2359
2360             // read number of elements
2361             int32_t numElements;
2362             memcpy(&numElements, pTagData, sizeof(uint32_t)); // already endian-swapped if necessary
2363             numBytesParsed += sizeof(uint32_t);
2364             pTagData       += sizeof(uint32_t);
2365
2366             // calculate number of bytes to skip
2367             int bytesToSkip = 0;
2368             switch (arrayType) {
2369                 case (Constants::BAM_TAG_TYPE_INT8)  :
2370                 case (Constants::BAM_TAG_TYPE_UINT8) :
2371                     bytesToSkip = numElements;
2372                     break;
2373                 case (Constants::BAM_TAG_TYPE_INT16)  :
2374                 case (Constants::BAM_TAG_TYPE_UINT16) :
2375                     bytesToSkip = numElements*sizeof(uint16_t);
2376                     break;
2377                 case (Constants::BAM_TAG_TYPE_FLOAT)  :
2378                 case (Constants::BAM_TAG_TYPE_INT32)  :
2379                 case (Constants::BAM_TAG_TYPE_UINT32) :
2380                     bytesToSkip = numElements*sizeof(uint32_t);
2381                     break;
2382                 default:
2383                     cerr << "BamAlignment ERROR: unknown binary array type encountered: "
2384                          << arrayType << endl;
2385                     return false;
2386             }
2387
2388             // skip binary array contents
2389             numBytesParsed += bytesToSkip;
2390             pTagData       += bytesToSkip;
2391             break;
2392         }
2393
2394         default:
2395             cerr << "BamAlignment ERROR: unknown tag type encountered"
2396                  << storageType << endl;
2397             return false;
2398     }
2399
2400     // return success
2401     return true;
2402 }