]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/toolkit/bamtools_random.cpp
Minor cleanup
[bamtools.git] / src / toolkit / bamtools_random.cpp
index ae6d63add0a08edcb8fd74664806c98c80e95d05..e28ea70962088578ddb27989c25f996925185f11 100644 (file)
@@ -1,25 +1,26 @@
 // ***************************************************************************
 // bamtools_random.cpp (c) 2010 Derek Barnett, Erik Garrison
 // Marth Lab, Department of Biology, Boston College
-// All rights reserved.
 // ---------------------------------------------------------------------------
-// Last modified: 3 September 2010 (DB)
+// Last modified: 7 April 2011 (DB)
 // ---------------------------------------------------------------------------
-// Grab a random subset of alignments.
+// Grab a random subset of alignments (testing tool)
 // ***************************************************************************
 
+#include "bamtools_random.h"
+
+#include <api/BamMultiReader.h>
+#include <api/BamWriter.h>
+#include <utils/bamtools_options.h>
+#include <utils/bamtools_utilities.h>
+using namespace BamTools;
+
 #include <ctime>
 #include <cstdlib>
 #include <iostream>
 #include <string>
 #include <vector>
-#include "bamtools_random.h"
-#include "bamtools_options.h"
-#include "bamtools_utilities.h"
-#include "BamMultiReader.h"
-#include "BamWriter.h"
 using namespace std;
-using namespace BamTools; 
   
 namespace BamTools {
   
@@ -27,7 +28,7 @@ namespace BamTools {
 const unsigned int RANDOM_MAX_ALIGNMENT_COUNT = 10000;
 
 // utility methods for RandomTool
-const int getRandomInt(const int& lowerBound, const int& upperBound) {
+int getRandomInt(const int& lowerBound, const int& upperBound) {
     const int range = (upperBound - lowerBound) + 1;
     return ( lowerBound + (int)(range * (double)rand()/((double)RAND_MAX + 1)) );
 }
@@ -65,124 +66,125 @@ struct RandomTool::RandomSettings {
 };  
 
 // ---------------------------------------------
-// RandomTool implementation
+// RandomToolPrivate implementation
 
-RandomTool::RandomTool(void) 
-    : AbstractTool()
-    , m_settings(new RandomSettings)
-{ 
-    // set program details
-    Options::SetProgramInfo("bamtools random", "grab a random subset of alignments", "[-in <filename> -in <filename> ...] [-out <filename>] [-forceCompression] [-n] [-region <REGION>]");
-    
-    // set up options 
-    OptionGroup* IO_Opts = Options::CreateOptionGroup("Input & Output");
-    Options::AddValueOption("-in",  "BAM filename", "the input BAM file",  "", m_settings->HasInput,  m_settings->InputFiles,     IO_Opts, Options::StandardIn());
-    Options::AddValueOption("-out", "BAM filename", "the output BAM file", "", m_settings->HasOutput, m_settings->OutputFilename, IO_Opts, Options::StandardOut());
-    Options::AddOption("-forceCompression", "if results are sent to stdout (like when piping to another tool), default behavior is to leave output uncompressed. Use this flag to override and force compression", m_settings->IsForceCompression, IO_Opts);
-    Options::AddValueOption("-region", "REGION", "only pull random alignments from within this genomic region. Index file is recommended for better performance, and is used automatically if it exists. See \'bamtools help index\' for more details on creating one", "", m_settings->HasRegion, m_settings->Region, IO_Opts);
-    
-    OptionGroup* SettingsOpts = Options::CreateOptionGroup("Settings");
-    Options::AddValueOption("-n", "count", "number of alignments to grab. Note - no duplicate checking is performed", "", m_settings->HasAlignmentCount, m_settings->AlignmentCount, SettingsOpts, RANDOM_MAX_ALIGNMENT_COUNT);
-}
+struct RandomTool::RandomToolPrivate {
 
-RandomTool::~RandomTool(void) { 
-    delete m_settings;
-    m_settings = 0;
-}
+    // ctor & dtor
+    public:
+        RandomToolPrivate(RandomTool::RandomSettings* settings)
+            : m_settings(settings)
+        { }
 
-int RandomTool::Help(void) { 
-    Options::DisplayHelp();
-    return 0;
-} 
+        ~RandomToolPrivate(void) { }
 
-int RandomTool::Run(int argc, char* argv[]) { 
+    // interface
+    public:
+        bool Run(void);
 
-    // parse command line arguments
-    Options::Parse(argc, argv, 1);
+    // data members
+    private:
+        RandomTool::RandomSettings* m_settings;
+};
+
+bool RandomTool::RandomToolPrivate::Run(void) {
 
     // set to default stdin if no input files provided
-    if ( !m_settings->HasInput ) 
+    if ( !m_settings->HasInput )
         m_settings->InputFiles.push_back(Options::StandardIn());
-    
+
     // open our reader
     BamMultiReader reader;
     if ( !reader.Open(m_settings->InputFiles) ) {
-        cerr << "ERROR: Could not open input BAM file(s)." << endl;
-        return 1;
+        cerr << "bamtools random ERROR: could not open input BAM file(s)... Aborting." << endl;
+        return false;
     }
-     
+
+    // look up index files for all BAM files
+    reader.LocateIndexes();
+
     // make sure index data is available
-    if ( !reader.IsIndexLoaded() ) {
-        cerr << "ERROR: Could not load index data for all input BAM file(s)." << endl;
-        cerr << "\'bamtools random\' requires valid index files to provide efficient performance." << endl;
+    if ( !reader.HasIndexes() ) {
+        cerr << "bamtools random ERROR: could not load index data for all input BAM file(s)... Aborting." << endl;
         reader.Close();
-        return 1;
-      
+        return false;
     }
-     
-    // get BamReader metadata  
+
+    // get BamReader metadata
     const string headerText = reader.GetHeaderText();
     const RefVector references = reader.GetReferenceData();
     if ( references.empty() ) {
-        cerr << "ERROR: No reference data available - required to perform random access throughtout input file(s)." << endl;
+        cerr << "bamtools random ERROR: no reference data available... Aborting." << endl;
         reader.Close();
-        return 1;
+        return false;
     }
-    
-    // open our writer
+
+    // determine compression mode for BamWriter
+    bool writeUncompressed = ( m_settings->OutputFilename == Options::StandardOut() &&
+                              !m_settings->IsForceCompression );
+    BamWriter::CompressionMode compressionMode = BamWriter::Compressed;
+    if ( writeUncompressed ) compressionMode = BamWriter::Uncompressed;
+
+    // open BamWriter
     BamWriter writer;
-    bool writeUncompressed = ( m_settings->OutputFilename == Options::StandardOut() && !m_settings->IsForceCompression );
-    if ( !writer.Open(m_settings->OutputFilename, headerText, references, writeUncompressed) ) {
-        cerr << "ERROR: Could not open BamWriter." << endl;
+    writer.SetCompressionMode(compressionMode);
+    if ( !writer.Open(m_settings->OutputFilename, headerText, references) ) {
+        cerr << "bamtools random ERROR: could not open " << m_settings->OutputFilename
+             << " for writing... Aborting." << endl;
         reader.Close();
-        return 1;
+        return false;
     }
 
-    // if user specified a REGION constraint, attempt to parse REGION string 
-    BamRegion region; 
+    // if user specified a REGION constraint, attempt to parse REGION string
+    BamRegion region;
     if ( m_settings->HasRegion && !Utilities::ParseRegionString(m_settings->Region, reader, region) ) {
-        cerr << "ERROR: Could not parse REGION: " << m_settings->Region << endl;
-        cerr << "Be sure REGION is in valid format (see README) and that coordinates are valid for selected references" << endl;
+        cerr << "bamtools random ERROR: could not parse REGION: " << m_settings->Region << endl;
+        cerr << "Check that REGION is in valid format (see documentation) and that the coordinates are valid"
+             << endl;
         reader.Close();
         writer.Close();
-        return 1;
+        return false;
     }
-      
+
     // seed our random number generator
     srand( time(NULL) );
-    
-    // grab random alignments 
+
+    // grab random alignments
     BamAlignment al;
     unsigned int i = 0;
     while ( i < m_settings->AlignmentCount ) {
-      
+
         int randomRefId    = 0;
         int randomPosition = 0;
-      
+
         // use REGION constraints to select random refId & position
         if ( m_settings->HasRegion ) {
-          
+
             // select a random refId
             randomRefId = getRandomInt(region.LeftRefID, region.RightRefID);
-            
+
             // select a random position based on randomRefId
-            const int lowerBoundPosition = ( (randomRefId == region.LeftRefID)  ? region.LeftPosition  : 0 );
-            const int upperBoundPosition = ( (randomRefId == region.RightRefID) ? region.RightPosition : (references.at(randomRefId).RefLength - 1) );
+            const int lowerBoundPosition = ( (randomRefId == region.LeftRefID)
+                                             ? region.LeftPosition
+                                             : 0 );
+            const int upperBoundPosition = ( (randomRefId == region.RightRefID)
+                                             ? region.RightPosition
+                                             : (references.at(randomRefId).RefLength - 1) );
             randomPosition = getRandomInt(lowerBoundPosition, upperBoundPosition);
-        } 
-        
+        }
+
         // otherwise select from all possible random refId & position
         else {
-          
+
             // select random refId
             randomRefId = getRandomInt(0, (int)references.size() - 1);
-            
+
             // select random position based on randomRefId
             const int lowerBoundPosition = 0;
             const int upperBoundPosition = references.at(randomRefId).RefLength - 1;
-            randomPosition = getRandomInt(lowerBoundPosition, upperBoundPosition); 
+            randomPosition = getRandomInt(lowerBoundPosition, upperBoundPosition);
         }
-      
+
         // if jump & read successful, save first alignment that overlaps random refId & position
         if ( reader.Jump(randomRefId, randomPosition) ) {
             while ( reader.GetNextAlignmentCore(al) ) {
@@ -194,9 +196,60 @@ int RandomTool::Run(int argc, char* argv[]) {
             }
         }
     }
-    
+
     // cleanup & exit
     reader.Close();
     writer.Close();
+    return true;
+}
+
+// ---------------------------------------------
+// RandomTool implementation
+
+RandomTool::RandomTool(void) 
+    : AbstractTool()
+    , m_settings(new RandomSettings)
+    , m_impl(0)
+{ 
+    // set program details
+    Options::SetProgramInfo("bamtools random", "grab a random subset of alignments", "[-in <filename> -in <filename> ...] [-out <filename>] [-forceCompression] [-n] [-region <REGION>]");
+    
+    // set up options 
+    OptionGroup* IO_Opts = Options::CreateOptionGroup("Input & Output");
+    Options::AddValueOption("-in",  "BAM filename", "the input BAM file",  "", m_settings->HasInput,  m_settings->InputFiles,     IO_Opts, Options::StandardIn());
+    Options::AddValueOption("-out", "BAM filename", "the output BAM file", "", m_settings->HasOutput, m_settings->OutputFilename, IO_Opts, Options::StandardOut());
+    Options::AddOption("-forceCompression", "if results are sent to stdout (like when piping to another tool), default behavior is to leave output uncompressed. Use this flag to override and force compression", m_settings->IsForceCompression, IO_Opts);
+    Options::AddValueOption("-region", "REGION", "only pull random alignments from within this genomic region. Index file is recommended for better performance, and is used automatically if it exists. See \'bamtools help index\' for more details on creating one", "", m_settings->HasRegion, m_settings->Region, IO_Opts);
+    
+    OptionGroup* SettingsOpts = Options::CreateOptionGroup("Settings");
+    Options::AddValueOption("-n", "count", "number of alignments to grab. Note - no duplicate checking is performed", "", m_settings->HasAlignmentCount, m_settings->AlignmentCount, SettingsOpts, RANDOM_MAX_ALIGNMENT_COUNT);
+}
+
+RandomTool::~RandomTool(void) { 
+
+    delete m_settings;
+    m_settings = 0;
+
+    delete m_impl;
+    m_impl = 0;
+}
+
+int RandomTool::Help(void) { 
+    Options::DisplayHelp();
     return 0;
-}
\ No newline at end of file
+} 
+
+int RandomTool::Run(int argc, char* argv[]) { 
+
+    // parse command line arguments
+    Options::Parse(argc, argv, 1);
+
+    // initialize RandomTool with settings
+    m_impl = new RandomToolPrivate(m_settings);
+
+    // run RandomTool, return success/fail
+    if ( m_impl->Run() )
+        return 0;
+    else
+        return 1;
+}