]> git.donarmstrong.com Git - bamtools.git/blob - src/toolkit/bamtools_convert.cpp
prior commit caused bug with convert
[bamtools.git] / src / toolkit / bamtools_convert.cpp
1 // ***************************************************************************
2 // bamtools_convert.cpp (c) 2010 Derek Barnett, Erik Garrison
3 // Marth Lab, Department of Biology, Boston College
4 // All rights reserved.
5 // ---------------------------------------------------------------------------
6 // Last modified: 23 September 2010
7 // ---------------------------------------------------------------------------
8 // Converts between BAM and a number of other formats
9 // ***************************************************************************
10
11 #include <fstream>
12 #include <iostream>
13 #include <sstream>
14 #include <string>
15 #include <vector>
16 #include "bamtools_convert.h"
17 #include "bamtools_fasta.h"
18 #include "bamtools_options.h"
19 #include "bamtools_pileup_engine.h"
20 #include "bamtools_utilities.h"
21 #include "BGZF.h"
22 #include "BamMultiReader.h"
23 using namespace std;
24 using namespace BamTools;
25   
26 namespace BamTools { 
27   
28     // ---------------------------------------------
29     // ConvertTool constants
30   
31     // supported conversion format command-line names
32     static const string FORMAT_BED      = "bed";
33     static const string FORMAT_BEDGRAPH = "bedgraph";
34     static const string FORMAT_FASTA    = "fasta";
35     static const string FORMAT_FASTQ    = "fastq";
36     static const string FORMAT_JSON     = "json";
37     static const string FORMAT_SAM      = "sam";
38     static const string FORMAT_PILEUP   = "pileup";
39     static const string FORMAT_WIGGLE   = "wig";
40
41     // other constants
42     static const unsigned int FASTA_LINE_MAX = 50;
43     
44     // ---------------------------------------------
45     // ConvertPileupFormatVisitor declaration
46     
47     class ConvertPileupFormatVisitor : public PileupVisitor {
48       
49         // ctor & dtor
50         public:
51             ConvertPileupFormatVisitor(const RefVector& references, 
52                                        const string& fastaFilename,
53                                        const bool isPrintingMapQualities,
54                                        ostream* out);
55             ~ConvertPileupFormatVisitor(void);
56       
57         // PileupVisitor interface implementation
58         public:
59             void Visit(const PileupPosition& pileupData);
60             
61         // data members
62         private:
63             Fasta     m_fasta;
64             bool      m_hasFasta;
65             bool      m_isPrintingMapQualities;
66             ostream*  m_out;
67             RefVector m_references;
68     };
69     
70 } // namespace BamTools
71   
72 // ---------------------------------------------
73 // ConvertSettings implementation
74
75 struct ConvertTool::ConvertSettings {
76
77     // flags
78     bool HasInput;
79     bool HasOutput;
80     bool HasFormat;
81     bool HasRegion;
82
83     // pileup flags
84     bool HasFastaFilename;
85     bool IsOmittingSamHeader;
86     bool IsPrintingPileupMapQualities;
87     
88     // options
89     vector<string> InputFiles;
90     string OutputFilename;
91     string Format;
92     string Region;
93     
94     // pileup options
95     string FastaFilename;
96
97     // constructor
98     ConvertSettings(void)
99         : HasInput(false)
100         , HasOutput(false)
101         , HasFormat(false)
102         , HasRegion(false)
103         , HasFastaFilename(false)
104         , IsOmittingSamHeader(false)
105         , IsPrintingPileupMapQualities(false)
106         , OutputFilename(Options::StandardOut())
107         , FastaFilename("")
108     { } 
109 };    
110
111 // ---------------------------------------------
112 // ConvertToolPrivate implementation  
113   
114 struct ConvertTool::ConvertToolPrivate {
115   
116     // ctor & dtor
117     public:
118         ConvertToolPrivate(ConvertTool::ConvertSettings* settings);
119         ~ConvertToolPrivate(void);
120     
121     // interface
122     public:
123         bool Run(void);
124         
125     // internal methods
126     private:
127         void PrintBed(const BamAlignment& a);
128         void PrintBedGraph(const BamAlignment& a);
129         void PrintFasta(const BamAlignment& a);
130         void PrintFastq(const BamAlignment& a);
131         void PrintJson(const BamAlignment& a);
132         void PrintSam(const BamAlignment& a);
133         void PrintWiggle(const BamAlignment& a);
134         
135         // special case - uses the PileupEngine
136         bool RunPileupConversion(BamMultiReader* reader);
137         
138     // data members
139     private: 
140         ConvertTool::ConvertSettings* m_settings;
141         RefVector m_references;
142         ostream m_out;
143 };
144
145 ConvertTool::ConvertToolPrivate::ConvertToolPrivate(ConvertTool::ConvertSettings* settings)
146     : m_settings(settings)
147     , m_out(cout.rdbuf()) // default output to cout
148 { }
149
150 ConvertTool::ConvertToolPrivate::~ConvertToolPrivate(void) { }
151
152 bool ConvertTool::ConvertToolPrivate::Run(void) {
153  
154     // ------------------------------------
155     // initialize conversion input/output
156         
157     // set to default input if none provided
158     if ( !m_settings->HasInput ) 
159         m_settings->InputFiles.push_back(Options::StandardIn());
160     
161     // open input files
162     BamMultiReader reader;
163     if ( !m_settings->HasInput ) { // don't attempt to open index for stdin
164         if ( !reader.Open(m_settings->InputFiles, false) ) {
165             cerr << "Could not open input files" << endl;
166             return false;
167         }
168     } else {
169         if ( !reader.Open(m_settings->InputFiles, true) ) {
170             cerr << "Could not open input files" << endl;
171             return false;
172         }
173     }
174     m_references = reader.GetReferenceData();
175
176     // set region if specified
177     BamRegion region;
178     if ( m_settings->HasRegion ) {
179         if ( Utilities::ParseRegionString(m_settings->Region, reader, region) ) {
180             if ( !reader.SetRegion(region) ) {
181                 cerr << "Could not set BamReader region to REGION: " << m_settings->Region << endl;
182                 return false;
183             }
184         } else {
185             cerr << "Could not parse REGION: " << m_settings->Region << endl;
186             return false;
187         }
188     }
189         
190     // if output file given
191     ofstream outFile;
192     if ( m_settings->HasOutput ) {
193       
194         // open output file stream
195         outFile.open(m_settings->OutputFilename.c_str());
196         if ( !outFile ) {
197             cerr << "Could not open " << m_settings->OutputFilename << " for output" << endl; 
198             return false; 
199         }
200         
201         // set m_out to file's streambuf
202         m_out.rdbuf(outFile.rdbuf()); 
203     }
204     
205     // -------------------------------------
206     // do conversion based on format
207     
208      bool convertedOk = true;
209     
210     // pileup is special case
211     // conversion not done per alignment, like the other formats
212     if ( m_settings->Format == FORMAT_PILEUP )
213         convertedOk = RunPileupConversion(&reader);
214     
215     // all other formats
216     else {
217     
218         bool formatError = false;
219         
220         // set function pointer to proper conversion method
221         void (BamTools::ConvertTool::ConvertToolPrivate::*pFunction)(const BamAlignment&) = 0;
222         if      ( m_settings->Format == FORMAT_BED )      pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintBed;
223         else if ( m_settings->Format == FORMAT_BEDGRAPH ) pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintBedGraph;
224         else if ( m_settings->Format == FORMAT_FASTA )    pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintFasta;
225         else if ( m_settings->Format == FORMAT_FASTQ )    pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintFastq;
226         else if ( m_settings->Format == FORMAT_JSON )     pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintJson;
227         else if ( m_settings->Format == FORMAT_SAM )      pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintSam;
228         else if ( m_settings->Format == FORMAT_WIGGLE )   pFunction = &BamTools::ConvertTool::ConvertToolPrivate::PrintWiggle;
229         else { 
230             cerr << "Unrecognized format: " << m_settings->Format << endl;
231             cerr << "Please see help|README (?) for details on supported formats " << endl;
232             formatError = true;
233             convertedOk = false;
234         }
235         
236         // if format selected ok
237         if ( !formatError ) {
238         
239             // if SAM format & not omitting header, print SAM header first
240             if ( (m_settings->Format == FORMAT_SAM) && !m_settings->IsOmittingSamHeader ) 
241                 m_out << reader.GetHeaderText();
242             
243             // iterate through file, doing conversion
244             BamAlignment a;
245             while ( reader.GetNextAlignment(a) ) 
246               (this->*pFunction)(a);
247             
248             // set flag for successful conversion
249             convertedOk = true;
250         }
251     }
252     
253     // ------------------------
254     // clean up & exit
255     
256     reader.Close();
257     if ( m_settings->HasOutput ) outFile.close();
258     return convertedOk;   
259 }
260
261 // ----------------------------------------------------------
262 // Conversion/output methods
263 // ----------------------------------------------------------
264
265 void ConvertTool::ConvertToolPrivate::PrintBed(const BamAlignment& a) { 
266   
267     // tab-delimited, 0-based half-open 
268     // (e.g. a 50-base read aligned to pos 10 could have BED coordinates (10, 60) instead of BAM coordinates (10, 59) )
269     // <chromName> <chromStart> <chromEnd> <readName> <score> <strand>
270
271     m_out << m_references.at(a.RefID).RefName << "\t"
272           << a.Position << "\t"
273           << a.GetEndPosition() + 1 << "\t"
274           << a.Name << "\t"
275           << a.MapQuality << "\t"
276           << (a.IsReverseStrand() ? "-" : "+") << endl;
277 }
278
279 void ConvertTool::ConvertToolPrivate::PrintBedGraph(const BamAlignment& a) { 
280     ; 
281 }
282
283 // print BamAlignment in FASTA format
284 // N.B. - uses QueryBases NOT AlignedBases
285 void ConvertTool::ConvertToolPrivate::PrintFasta(const BamAlignment& a) { 
286     
287     // >BamAlignment.Name
288     // BamAlignment.QueryBases (up to FASTA_LINE_MAX bases per line)
289     // ...
290     //
291     // N.B. - QueryBases are reverse-complemented if aligned to reverse strand
292   
293     // print header
294     m_out << "> " << a.Name << endl;
295     
296     // handle reverse strand alignment - bases 
297     string sequence = a.QueryBases;
298     if ( a.IsReverseStrand() )
299         Utilities::ReverseComplement(sequence);
300     
301     // if sequence fits on single line
302     if ( sequence.length() <= FASTA_LINE_MAX )
303         m_out << sequence << endl;
304     
305     // else split over multiple lines
306     else {
307       
308         size_t position = 0;
309         size_t seqLength = sequence.length(); // handle reverse strand alignment - bases & qualitiesth();
310         
311         // write subsequences to each line
312         while ( position < (seqLength - FASTA_LINE_MAX) ) {
313             m_out << sequence.substr(position, FASTA_LINE_MAX) << endl;
314             position += FASTA_LINE_MAX;
315         }
316         
317         // write final subsequence
318         m_out << sequence.substr(position) << endl;
319     }
320 }
321
322 // print BamAlignment in FASTQ format
323 // N.B. - uses QueryBases NOT AlignedBases
324 void ConvertTool::ConvertToolPrivate::PrintFastq(const BamAlignment& a) { 
325   
326     // @BamAlignment.Name
327     // BamAlignment.QueryBases
328     // +
329     // BamAlignment.Qualities
330     //
331     // N.B. - QueryBases are reverse-complemented (& Qualities reversed) if aligned to reverse strand .
332     //        Name is appended "/1" or "/2" if paired-end, to reflect which mate this entry is.
333   
334     // handle paired-end alignments
335     string name = a.Name;
336     if ( a.IsPaired() )
337         name.append( (a.IsFirstMate() ? "/1" : "/2") );
338   
339     // handle reverse strand alignment - bases & qualities
340     string qualities = a.Qualities;
341     string sequence  = a.QueryBases;
342     if ( a.IsReverseStrand() ) {
343         Utilities::Reverse(qualities);
344         Utilities::ReverseComplement(sequence);
345     }
346   
347     // write to output stream
348     m_out << "@" << name << endl
349           << sequence    << endl
350           << "+"         << endl
351           << qualities   << endl;
352 }
353
354 // print BamAlignment in JSON format
355 void ConvertTool::ConvertToolPrivate::PrintJson(const BamAlignment& a) {
356   
357     // write name & alignment flag
358     m_out << "{\"name\":\"" << a.Name << "\",\"alignmentFlag\":\"" << a.AlignmentFlag << "\",";
359     
360     // write reference name
361     if ( (a.RefID >= 0) && (a.RefID < (int)m_references.size()) ) 
362         m_out << "\"reference\":\"" << m_references[a.RefID].RefName << "\",";
363     
364     // write position & map quality
365     m_out << "\"position\":" << a.Position+1 << ",\"mapQuality\":" << a.MapQuality << ",";
366     
367     // write CIGAR
368     const vector<CigarOp>& cigarData = a.CigarData;
369     if ( !cigarData.empty() ) {
370         m_out << "\"cigar\":[";
371         vector<CigarOp>::const_iterator cigarBegin = cigarData.begin();
372         vector<CigarOp>::const_iterator cigarIter = cigarBegin;
373         vector<CigarOp>::const_iterator cigarEnd  = cigarData.end();
374         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
375             const CigarOp& op = (*cigarIter);
376             if (cigarIter != cigarBegin) m_out << ",";
377             m_out << "\"" << op.Length << op.Type << "\"";
378         }
379         m_out << "],";
380     }
381     
382     // write mate reference name, mate position, & insert size
383     if ( a.IsPaired() && (a.MateRefID >= 0) && (a.MateRefID < (int)m_references.size()) ) {
384         m_out << "\"mate\":{"
385               << "\"reference\":\"" << m_references[a.MateRefID].RefName << "\","
386               << "\"position\":" << a.MatePosition+1
387               << ",\"insertSize\":" << a.InsertSize << "},";
388     }
389     
390     // write sequence
391     if ( !a.QueryBases.empty() ) 
392         m_out << "\"queryBases\":\"" << a.QueryBases << "\",";
393     
394     // write qualities
395     if ( !a.Qualities.empty() ) {
396         string::const_iterator s = a.Qualities.begin();
397         m_out << "\"qualities\":[" << static_cast<short>(*s) - 33;
398         ++s;
399         for (; s != a.Qualities.end(); ++s) {
400             m_out << "," << static_cast<short>(*s) - 33;
401         }
402         m_out << "],";
403     }
404     
405     // write tag data
406     const char* tagData = a.TagData.c_str();
407     const size_t tagDataLength = a.TagData.length();
408     size_t index = 0;
409     if (index < tagDataLength) {
410
411         m_out << "\"tags\":{";
412         
413         while ( index < tagDataLength ) {
414
415             if (index > 0)
416                 m_out << ",";
417             
418             // write tag name
419             m_out << "\"" << a.TagData.substr(index, 2) << "\":";
420             index += 2;
421             
422             // get data type
423             char type = a.TagData.at(index);
424             ++index;
425             
426             switch (type) {
427                 case('A') : 
428                     m_out << "\"" << tagData[index] << "\"";
429                     ++index; 
430                     break;
431                 
432                 case('C') : 
433                     m_out << (int)tagData[index]; 
434                     ++index; 
435                     break;
436                 
437                 case('c') : 
438                     m_out << (int)tagData[index];
439                     ++index; 
440                     break;
441                 
442                 case('S') : 
443                     m_out << BgzfData::UnpackUnsignedShort(&tagData[index]); 
444                     index += 2; 
445                     break;
446                     
447                 case('s') : 
448                     m_out << BgzfData::UnpackSignedShort(&tagData[index]);
449                     index += 2; 
450                     break;
451                 
452                 case('I') : 
453                     m_out << BgzfData::UnpackUnsignedInt(&tagData[index]);
454                     index += 4; 
455                     break;
456                 
457                 case('i') : 
458                     m_out << BgzfData::UnpackSignedInt(&tagData[index]);
459                     index += 4; 
460                     break;
461                 
462                 case('f') : 
463                     m_out << BgzfData::UnpackFloat(&tagData[index]);
464                     index += 4; 
465                     break;
466                 
467                 case('d') : 
468                     m_out << BgzfData::UnpackDouble(&tagData[index]);
469                     index += 8; 
470                     break;
471                 
472                 case('Z') :
473                 case('H') : 
474                     m_out << "\""; 
475                     while (tagData[index]) {
476                         if (tagData[index] == '\"')
477                             m_out << "\\\""; // escape for json
478                         else
479                             m_out << tagData[index];
480                         ++index;
481                     }
482                     m_out << "\""; 
483                     ++index; 
484                     break;      
485             }
486             
487             if ( tagData[index] == '\0') 
488                 break;
489         }
490
491         m_out << "}";
492     }
493
494     m_out << "}" << endl;
495 }
496
497 // print BamAlignment in SAM format
498 void ConvertTool::ConvertToolPrivate::PrintSam(const BamAlignment& a) {
499   
500     // tab-delimited
501     // <QNAME> <FLAG> <RNAME> <POS> <MAPQ> <CIGAR> <MRNM> <MPOS> <ISIZE> <SEQ> <QUAL> [ <TAG>:<VTYPE>:<VALUE> [...] ]
502   
503     // write name & alignment flag
504     m_out << a.Name << "\t" << a.AlignmentFlag << "\t";
505     
506     // write reference name
507     if ( (a.RefID >= 0) && (a.RefID < (int)m_references.size()) ) 
508         m_out << m_references[a.RefID].RefName << "\t";
509     else 
510         m_out << "*\t";
511     
512     // write position & map quality
513     m_out << a.Position+1 << "\t" << a.MapQuality << "\t";
514     
515     // write CIGAR
516     const vector<CigarOp>& cigarData = a.CigarData;
517     if ( cigarData.empty() ) m_out << "*\t";
518     else {
519         vector<CigarOp>::const_iterator cigarIter = cigarData.begin();
520         vector<CigarOp>::const_iterator cigarEnd  = cigarData.end();
521         for ( ; cigarIter != cigarEnd; ++cigarIter ) {
522             const CigarOp& op = (*cigarIter);
523             m_out << op.Length << op.Type;
524         }
525         m_out << "\t";
526     }
527     
528     // write mate reference name, mate position, & insert size
529     if ( a.IsPaired() && (a.MateRefID >= 0) && (a.MateRefID < (int)m_references.size()) ) {
530         if ( a.MateRefID == a.RefID ) m_out << "=\t";
531         else m_out << m_references[a.MateRefID].RefName << "\t";
532         m_out << a.MatePosition+1 << "\t" << a.InsertSize << "\t";
533     } 
534     else m_out << "*\t0\t0\t";
535     
536     // write sequence
537     if ( a.QueryBases.empty() ) m_out << "*\t";
538     else m_out << a.QueryBases << "\t";
539     
540     // write qualities
541     if ( a.Qualities.empty() ) m_out << "*";
542     else m_out << a.Qualities;
543     
544     // write tag data
545     const char* tagData = a.TagData.c_str();
546     const size_t tagDataLength = a.TagData.length();
547     
548     size_t index = 0;
549     while ( index < tagDataLength ) {
550
551         // write tag name   
552         string tagName = a.TagData.substr(index, 2);
553         m_out << "\t" << tagName << ":";
554         index += 2;
555         
556         // get data type
557         char type = a.TagData.at(index);
558         ++index;
559         switch (type) {
560             case('A') : 
561                 m_out << "A:" << tagData[index]; 
562                 ++index; 
563                 break;
564             
565             case('C') : 
566                 m_out << "i:" << (int)tagData[index]; 
567                 ++index; 
568                 break;
569             
570             case('c') : 
571                 m_out << "i:" << (int)tagData[index];
572                 ++index; 
573                 break;
574             
575             case('S') : 
576                 m_out << "i:" << BgzfData::UnpackUnsignedShort(&tagData[index]);
577                 index += 2; 
578                 break;
579                 
580             case('s') : 
581                 m_out << "i:" << BgzfData::UnpackSignedShort(&tagData[index]);
582                 index += 2; 
583                 break;
584             
585             case('I') :
586                 m_out << "i:" << BgzfData::UnpackUnsignedInt(&tagData[index]);
587                 index += 4; 
588                 break;
589             
590             case('i') : 
591                 m_out << "i:" << BgzfData::UnpackSignedInt(&tagData[index]);
592                 index += 4; 
593                 break;
594             
595             case('f') : 
596                 m_out << "f:" << BgzfData::UnpackFloat(&tagData[index]);
597                 index += 4; 
598                 break;
599             
600             case('d') : 
601                 m_out << "d:" << BgzfData::UnpackDouble(&tagData[index]);
602                 index += 8; 
603                 break;
604             
605             case('Z') :
606             case('H') : 
607                 m_out << type << ":";
608                 while (tagData[index]) {
609                     m_out << tagData[index];
610                     ++index;
611                 }
612                 ++index; 
613                 break;      
614         }
615
616         if ( tagData[index] == '\0') 
617             break;
618     }
619
620     m_out << endl;
621 }
622
623 void ConvertTool::ConvertToolPrivate::PrintWiggle(const BamAlignment& a) { 
624     ; 
625 }
626
627 bool ConvertTool::ConvertToolPrivate::RunPileupConversion(BamMultiReader* reader) {
628   
629     // check for valid BamMultiReader
630     if ( reader == 0 ) return false;
631   
632     // set up our pileup format 'visitor'
633     ConvertPileupFormatVisitor* v = new ConvertPileupFormatVisitor(m_references, 
634                                                                    m_settings->FastaFilename,
635                                                                    m_settings->IsPrintingPileupMapQualities, 
636                                                                    &m_out);
637
638     // set up PileupEngine
639     PileupEngine pileup;
640     pileup.AddVisitor(v);
641     
642     // iterate through data
643     BamAlignment al;
644     while ( reader->GetNextAlignment(al) )
645         pileup.AddAlignment(al);
646     pileup.Flush();
647     
648     // clean up
649     delete v;
650     v = 0;
651     
652     // return success
653     return true;
654 }       
655
656 // ---------------------------------------------
657 // ConvertTool implementation
658
659 ConvertTool::ConvertTool(void)
660     : AbstractTool()
661     , m_settings(new ConvertSettings)
662     , m_impl(0)
663 {
664     // set program details
665     Options::SetProgramInfo("bamtools convert", "converts BAM to a number of other formats", "-format <FORMAT> [-in <filename> -in <filename> ...] [-out <filename>] [other options]");
666     
667     // set up options 
668     OptionGroup* IO_Opts = Options::CreateOptionGroup("Input & Output");
669     Options::AddValueOption("-in",     "BAM filename", "the input BAM file(s)", "", m_settings->HasInput,   m_settings->InputFiles,     IO_Opts, Options::StandardIn());
670     Options::AddValueOption("-out",    "BAM filename", "the output BAM file",   "", m_settings->HasOutput,  m_settings->OutputFilename, IO_Opts, Options::StandardOut());
671     Options::AddValueOption("-format", "FORMAT", "the output file format - see README for recognized formats", "", m_settings->HasFormat, m_settings->Format, IO_Opts);
672    
673     OptionGroup* FilterOpts = Options::CreateOptionGroup("Filters");
674     Options::AddValueOption("-region", "REGION", "genomic region. Index file is recommended for better performance, and is read automatically if it exists as <filename>.bai. See \'bamtools help index\' for more  details on creating one", "", m_settings->HasRegion, m_settings->Region, FilterOpts);
675     
676     OptionGroup* PileupOpts = Options::CreateOptionGroup("Pileup Options");
677     Options::AddValueOption("-fasta", "FASTA filename", "FASTA reference file", "", m_settings->HasFastaFilename, m_settings->FastaFilename, PileupOpts, "");
678     Options::AddOption("-mapqual", "print the mapping qualities", m_settings->IsPrintingPileupMapQualities, PileupOpts);
679     
680     OptionGroup* SamOpts = Options::CreateOptionGroup("SAM Options");
681     Options::AddOption("-noheader", "omit the SAM header from output", m_settings->IsOmittingSamHeader, SamOpts);
682 }
683
684 ConvertTool::~ConvertTool(void) {
685     delete m_settings;
686     m_settings = 0;
687     
688     delete m_impl;
689     m_impl = 0;
690 }
691
692 int ConvertTool::Help(void) {
693     Options::DisplayHelp();
694     return 0;
695 }
696
697 int ConvertTool::Run(int argc, char* argv[]) {
698   
699     // parse command line arguments
700     Options::Parse(argc, argv, 1);
701     
702     // run internal ConvertTool implementation, return success/fail
703     m_impl = new ConvertToolPrivate(m_settings);
704     
705     if ( m_impl->Run() ) 
706         return 0;
707     else 
708         return 1;
709 }
710
711 // ---------------------------------------------
712 // ConvertPileupFormatVisitor implementation
713
714 ConvertPileupFormatVisitor::ConvertPileupFormatVisitor(const RefVector& references, 
715                                                        const string& fastaFilename,
716                                                        const bool isPrintingMapQualities,
717                                                        ostream* out)
718     : PileupVisitor()
719     , m_hasFasta(false)
720     , m_isPrintingMapQualities(isPrintingMapQualities)
721     , m_out(out)
722     , m_references(references)
723
724     // set up Fasta reader if file is provided
725     if ( !fastaFilename.empty() ) {
726       
727         // check for FASTA index
728         string indexFilename = "";
729         if ( Utilities::FileExists(fastaFilename + ".fai") ) 
730             indexFilename = fastaFilename + ".fai";
731       
732         // open FASTA file
733         if ( m_fasta.Open(fastaFilename, indexFilename) ) 
734             m_hasFasta = true;
735     }
736 }
737
738 ConvertPileupFormatVisitor::~ConvertPileupFormatVisitor(void) { 
739     // be sure to close Fasta reader
740     if ( m_hasFasta ) {
741         m_fasta.Close();
742         m_hasFasta = false;
743     }
744 }
745
746 void ConvertPileupFormatVisitor::Visit(const PileupPosition& pileupData ) {
747   
748     // skip if no alignments at this position
749     if ( pileupData.PileupAlignments.empty() ) return;
750   
751     // retrieve reference name
752     const string& referenceName = m_references[pileupData.RefId].RefName;
753     const int& position   = pileupData.Position;
754     
755     // retrieve reference base from FASTA file, if one provided; otherwise default to 'N'
756     char referenceBase('N');
757     if ( m_hasFasta && (pileupData.Position < m_references[pileupData.RefId].RefLength) ) {
758         if ( !m_fasta.GetBase(pileupData.RefId, pileupData.Position, referenceBase ) ) {
759             cerr << "Pileup error : Could not read reference base from FASTA file" << endl;
760             return;
761         }
762     }
763     
764     // get count of alleles at this position
765     const int numberAlleles = pileupData.PileupAlignments.size();
766     
767     // -----------------------------------------------------------
768     // build strings based on alleles at this positionInAlignment
769     
770     stringstream bases("");
771     stringstream baseQualities("");
772     stringstream mapQualities("");
773     
774     // iterate over alignments at this pileup position
775     vector<PileupAlignment>::const_iterator pileupIter = pileupData.PileupAlignments.begin();
776     vector<PileupAlignment>::const_iterator pileupEnd  = pileupData.PileupAlignments.end();
777     for ( ; pileupIter != pileupEnd; ++pileupIter ) {
778         const PileupAlignment pa = (*pileupIter);
779         const BamAlignment& ba = pa.Alignment;
780         
781         // if beginning of read segment
782         if ( pa.IsSegmentBegin )
783             bases << '^' << ( ((int)ba.MapQuality > 93) ? (char)126 : (char)((int)ba.MapQuality+33) );
784         
785         // if current base is not a DELETION
786         if ( !pa.IsCurrentDeletion ) {
787           
788             // get base at current position
789             char base = ba.QueryBases.at(pa.PositionInAlignment);
790             
791             // if base matches reference
792             if ( base == '=' || 
793                  toupper(base) == toupper(referenceBase) ||
794                  tolower(base) == tolower(referenceBase) ) 
795             {
796                 base = (ba.IsReverseStrand() ? ',' : '.' );
797             }
798             
799             // mismatches reference
800             else base = (ba.IsReverseStrand() ? tolower(base) : toupper(base) );
801             
802             // store base
803             bases << base;
804           
805             // if next position contains insertion
806             if ( pa.IsNextInsertion ) {
807                 bases << '+' << pa.InsertionLength;
808                 for (int i = 1; i <= pa.InsertionLength; ++i) {
809                     char insertedBase = (char)ba.QueryBases.at(pa.PositionInAlignment + i);
810                     bases << (ba.IsReverseStrand() ? (char)tolower(insertedBase) : (char)toupper(insertedBase) );
811                 }
812             }
813             
814             // if next position contains DELETION
815             else if ( pa.IsNextDeletion ) {
816                 bases << '-' << pa.DeletionLength;
817                 for (int i = 1; i <= pa.DeletionLength; ++i) {
818                     char deletedBase('N');
819                     if ( m_hasFasta && (pileupData.Position+i < m_references[pileupData.RefId].RefLength) ) {
820                         if ( !m_fasta.GetBase(pileupData.RefId, pileupData.Position+i, deletedBase ) ) {
821                             cerr << "Pileup error : Could not read reference base from FASTA file" << endl;
822                             return;
823                         }
824                     }
825                     bases << (ba.IsReverseStrand() ? (char)tolower(deletedBase) : (char)toupper(deletedBase) );
826                 }
827             }
828         }
829         
830         // otherwise, DELETION
831         else bases << '*';
832         
833         // if end of read segment
834         if ( pa.IsSegmentEnd ) bases << '$';
835         
836         // store current base quality
837         baseQualities << ba.Qualities.at(pa.PositionInAlignment);
838         
839         // save alignment map quality if desired
840         if ( m_isPrintingMapQualities )
841             mapQualities << ( ((int)ba.MapQuality > 93) ? (char)126 : (char)((int)ba.MapQuality+33) );
842     }
843     
844     // ----------------------
845     // print results 
846     
847     // tab-delimited
848     // <refName> <1-based pos> <refBase> <numberAlleles> <bases> <qualities> [mapQuals]
849     
850     const string TAB = "\t";
851     *m_out << referenceName       << TAB 
852            << position + 1        << TAB 
853            << referenceBase       << TAB 
854            << numberAlleles       << TAB 
855            << bases.str()         << TAB 
856            << baseQualities.str() << TAB
857            << mapQualities.str()  << endl;
858 }