+// --------------------------------------------------------------------------
+// ReadNamesFileReader implementation
+
+struct ResolveTool::ReadNamesFileReader {
+
+ // ctor & dtor
+ ReadNamesFileReader(void) { }
+ ~ReadNamesFileReader(void) { Close(); }
+
+ // main reader interface
+ public:
+ void Close(void);
+ bool Open(const string& filename);
+ bool Read(map<string, ReadGroupResolver>& readGroups);
+
+ // data members
+ private:
+ ifstream m_stream;
+};
+
+void ResolveTool::ReadNamesFileReader::Close(void) {
+ if ( m_stream.is_open() )
+ m_stream.close();
+}
+
+bool ResolveTool::ReadNamesFileReader::Open(const string& filename) {
+
+ // make sure stream is fresh
+ Close();
+
+ // attempt to open filename, return status
+ m_stream.open(filename.c_str(), ifstream::in);
+ return m_stream.good();
+}
+
+bool ResolveTool::ReadNamesFileReader::Read(map<string, ReadGroupResolver>& readGroups) {
+
+ // up-front sanity check
+ if ( !m_stream.is_open() ) return false;
+
+ // parse read names file
+ string line;
+ vector<string> fields;
+ map<string, ReadGroupResolver>::iterator rgIter;
+ map<string, ReadGroupResolver>::iterator rgEnd = readGroups.end();
+ while ( getline(m_stream, line) ) {
+
+ // skip if empty line
+ if ( line.empty() ) continue;
+
+ // split line on '\t'
+ fields = Utilities::Split(line, TAB_CHAR);
+ if ( fields.size() != 2 ) continue;
+
+ // look up resolver for read group
+ rgIter = readGroups.find( fields[0] );
+ if ( rgIter == rgEnd ) return false;
+ ReadGroupResolver& resolver = (*rgIter).second;
+
+ // store read name with resolver
+ resolver.ReadNames.insert( make_pair<string,bool>(fields[1], true) ) ;
+ }
+
+ // if here, return success
+ return true;
+}
+
+// --------------------------------------------------------------------------
+// ReadNamesFileWriter implementation
+
+struct ResolveTool::ReadNamesFileWriter {
+
+ // ctor & dtor
+ ReadNamesFileWriter(void) { }
+ ~ReadNamesFileWriter(void) { Close(); }
+
+ // main reader interface
+ public:
+ void Close(void);
+ bool Open(const string& filename);
+ void Write(const string& readGroupName, const string& readName);
+
+ // data members
+ private:
+ ofstream m_stream;
+};
+
+void ResolveTool::ReadNamesFileWriter::Close(void) {
+ if ( m_stream.is_open() )
+ m_stream.close();
+}
+
+bool ResolveTool::ReadNamesFileWriter::Open(const string& filename) {
+
+ // make sure stream is fresh
+ Close();
+
+ // attempt to open filename, return status
+ m_stream.open(filename.c_str(), ofstream::out);
+ return m_stream.good();
+}
+
+void ResolveTool::ReadNamesFileWriter::Write(const string& readGroupName,
+ const string& readName)
+{
+ m_stream << readGroupName << TAB_CHAR << readName << endl;
+}
+
+// --------------------------------------------------------------------------
+// StatsFileReader implementation
+
+struct ResolveTool::StatsFileReader {
+
+ // ctor & dtor
+ public:
+ StatsFileReader(void) { }
+ ~StatsFileReader(void) { Close(); }
+
+ // main reader interface
+ public:
+ void Close(void);
+ bool Open(const string& filename);
+ bool Read(ResolveTool::ResolveSettings* settings,
+ map<string, ReadGroupResolver>& readGroups);
+
+ // internal methods
+ private:
+ bool IsComment(const string& line) const;
+ bool IsWhitespace(const string& line) const;
+ bool ParseInputLine(const string& line);
+ bool ParseOptionLine(const string& line, ResolveTool::ResolveSettings* settings);
+ bool ParseReadGroupLine(const string& line, map<string, ReadGroupResolver>& readGroups);
+ string SkipCommentsAndWhitespace(void);
+
+ // data members
+ private:
+ ifstream m_stream;
+
+ enum State { None = 0
+ , InInput
+ , InOptions
+ , InReadGroups };
+};
+
+void ResolveTool::StatsFileReader::Close(void) {
+ if ( m_stream.is_open() )
+ m_stream.close();
+}
+
+bool ResolveTool::StatsFileReader::IsComment(const string& line) const {
+ assert( !line.empty() );
+ return ( line.at(0) == COMMENT_CHAR );
+}
+
+bool ResolveTool::StatsFileReader::IsWhitespace(const string& line) const {
+ if ( line.empty() )
+ return true;
+ return ( isspace(line.at(0)) );
+}
+
+bool ResolveTool::StatsFileReader::Open(const string& filename) {
+
+ // make sure stream is fresh
+ Close();
+
+ // attempt to open filename, return status
+ m_stream.open(filename.c_str(), ifstream::in);
+ return m_stream.good();
+}
+
+bool ResolveTool::StatsFileReader::ParseInputLine(const string& /*line*/) {
+ // input lines are ignored (for now at least), tool will use input from command line
+ return true;
+}
+
+bool ResolveTool::StatsFileReader::ParseOptionLine(const string& line,
+ ResolveTool::ResolveSettings* settings)
+{
+ // split line into option, value
+ vector<string> fields = Utilities::Split(line, EQUAL_CHAR);
+ if ( fields.size() != NUM_OPTIONS_FIELDS )
+ return false;
+ const string& option = fields.at(0);
+ stringstream value(fields.at(1));
+
+ // -----------------------------------
+ // handle option based on keyword
+
+ // ConfidenceInterval
+ if ( option == OPTION_CONFIDENCEINTERVAL ) {
+ value >> settings->ConfidenceInterval;
+ settings->HasConfidenceInterval = true;
+ return true;
+ }
+
+ // ForceMarkReadGroups
+ if ( option == OPTION_FORCEMARKREADGROUPS ) {
+ value >> settings->HasForceMarkReadGroups;
+ return true;
+ }
+
+ // MinimumMapQuality
+ if ( option == OPTION_MINIMUMMAPQUALITY ) {
+ value >> settings->MinimumMapQuality;
+ settings->HasMinimumMapQuality = true;
+ return true;
+ }
+
+ // UnusedModelThreshold
+ if ( option == OPTION_UNUSEDMODELTHRESHOLD ) {
+ value >> settings->UnusedModelThreshold;
+ settings->HasUnusedModelThreshold = true;
+ return true;
+ }
+
+ // otherwise unknown option
+ cerr << "bamtools resolve ERROR - unrecognized option: " << option << " in stats file" << endl;
+ return false;
+}
+
+bool ResolveTool::StatsFileReader::ParseReadGroupLine(const string& line,
+ map<string, ReadGroupResolver>& readGroups)
+{
+ // split read group data in to fields
+ vector<string> fields = Utilities::Split(line, WHITESPACE_CHARS);
+ if ( fields.size() != NUM_READGROUPS_FIELDS ) return false;
+
+ // retrieve RG name
+ const string& name = fields.at(0);
+
+ // populate RG's 'resolver' data
+ ReadGroupResolver resolver;
+
+ stringstream dataStream;
+ dataStream.str(fields.at(1));
+ dataStream >> resolver.MedianFragmentLength;
+ dataStream.clear();
+
+ dataStream.str(fields.at(2));
+ dataStream >> resolver.MinFragmentLength;
+ dataStream.clear();
+
+ dataStream.str(fields.at(3));
+ dataStream >> resolver.MaxFragmentLength;
+ dataStream.clear();
+
+ dataStream.str(fields.at(4));
+ dataStream >> resolver.TopModelId;
+ dataStream.clear();
+
+ dataStream.str(fields.at(5));
+ dataStream >> resolver.NextTopModelId;
+ dataStream.clear();
+
+ resolver.IsAmbiguous = ( fields.at(6) == TRUE_KEYWORD );
+
+ // store RG entry and return success
+ readGroups.insert( make_pair<string, ReadGroupResolver>(name, resolver) );
+ return true;
+}
+
+bool ResolveTool::StatsFileReader::Read(ResolveTool::ResolveSettings* settings,
+ map<string, ReadGroupResolver>& readGroups)
+{
+ // up-front sanity checks
+ if ( !m_stream.is_open() || settings == 0 )
+ return false;
+
+ // clear out read group data
+ readGroups.clear();
+
+ // initialize state
+ State currentState = StatsFileReader::None;
+
+ // read stats file
+ string line = SkipCommentsAndWhitespace();
+ while ( !line.empty() ) {
+
+ bool foundError = false;
+
+ // switch state on keyword found
+ if ( Utilities::StartsWith(line, INPUT_TOKEN) )
+ currentState = StatsFileReader::InInput;
+ else if ( Utilities::StartsWith(line, OPTIONS_TOKEN) )
+ currentState = StatsFileReader::InOptions;
+ else if ( Utilities::StartsWith(line, READGROUPS_TOKEN) )
+ currentState = StatsFileReader::InReadGroups;
+
+ // otherwise parse data line, depending on state
+ else {
+ if ( currentState == StatsFileReader::InInput )
+ foundError = !ParseInputLine(line);
+ else if ( currentState == StatsFileReader::InOptions )
+ foundError = !ParseOptionLine(line, settings);
+ else if ( currentState == StatsFileReader::InReadGroups )
+ foundError = !ParseReadGroupLine(line, readGroups);
+ else
+ foundError = true;
+ }
+
+ // break out if error found
+ if ( foundError )
+ return false;
+
+ // get next line
+ line = SkipCommentsAndWhitespace();
+ }
+
+ // if here, return success
+ return true;
+}
+
+string ResolveTool::StatsFileReader::SkipCommentsAndWhitespace(void) {
+ string line;
+ do {
+ if ( m_stream.eof() )
+ return string();
+ getline(m_stream, line);
+ } while ( IsWhitespace(line) || IsComment(line) );
+ return line;
+}
+
+// --------------------------------------------------------------------------
+// StatsFileReader implementation
+
+struct ResolveTool::StatsFileWriter {
+
+ // ctor & dtor
+ public:
+ StatsFileWriter(void) { }
+ ~StatsFileWriter(void) { Close(); }
+
+ // main reader interface
+ public:
+ void Close(void);
+ bool Open(const string& filename);
+ bool Write(ResolveTool::ResolveSettings* settings,
+ const map<string, ReadGroupResolver>& readGroups);
+
+ // internal methods
+ private:
+ void WriteHeader(void);
+ void WriteInput(ResolveTool::ResolveSettings* settings);
+ void WriteOptions(ResolveTool::ResolveSettings* settings);
+ void WriteReadGroups(const map<string, ReadGroupResolver>& readGroups);
+
+ // data members
+ private:
+ ofstream m_stream;
+};
+
+void ResolveTool::StatsFileWriter::Close(void) {
+ if ( m_stream.is_open() )
+ m_stream.close();
+}
+
+bool ResolveTool::StatsFileWriter::Open(const string& filename) {
+
+ // make sure stream is fresh
+ Close();
+
+ // attempt to open filename, return status
+ m_stream.open(filename.c_str(), ofstream::out);
+ return m_stream.good();
+}
+
+bool ResolveTool::StatsFileWriter::Write(ResolveTool::ResolveSettings* settings,
+ const map<string, ReadGroupResolver>& readGroups)
+{
+ // return failure if file not open
+ if ( !m_stream.is_open() )
+ return false;
+
+ // write stats file elements
+ WriteHeader();
+ WriteInput(settings);
+ WriteOptions(settings);
+ WriteReadGroups(readGroups);
+
+ // return success
+ return true;
+}
+
+void ResolveTool::StatsFileWriter::WriteHeader(void) {
+
+ // stringify current bamtools version
+ stringstream versionStream("");
+ versionStream << "v"
+ << BAMTOOLS_VERSION_MAJOR << "."
+ << BAMTOOLS_VERSION_MINOR << "."
+ << BAMTOOLS_VERSION_BUILD;
+
+ // # bamtools resolve (vX.Y.Z)
+ // #
+ // # MODEL DESCRIPTION - see above for actual text
+ // \n
+
+ m_stream << COMMENT_CHAR << " bamtools resolve (" << versionStream.str() << ")" << endl
+ << COMMENT_CHAR << endl
+ << MODEL_DESCRIPTION
+ << endl;
+}
+
+void ResolveTool::StatsFileWriter::WriteInput(ResolveTool::ResolveSettings* settings) {
+
+ // [Input]
+ // filename
+ // \n
+
+ m_stream << INPUT_TOKEN << endl
+ << settings->InputBamFilename << endl
+ << endl;
+}
+
+void ResolveTool::StatsFileWriter::WriteOptions(ResolveTool::ResolveSettings* settings) {
+
+ // [Options]
+ // ConfidenceInterval=<double>
+ // ForceMarkReadGroups=<true|false>
+ // MinimumMapQuality=<uint16_t>
+ // UnusedModelThreshold=<double>
+ // \n
+
+ m_stream << OPTIONS_TOKEN << endl
+ << OPTION_CONFIDENCEINTERVAL << EQUAL_CHAR << settings->ConfidenceInterval << endl
+ << OPTION_FORCEMARKREADGROUPS << EQUAL_CHAR << boolalpha << settings->HasForceMarkReadGroups << endl
+ << OPTION_MINIMUMMAPQUALITY << EQUAL_CHAR << settings->MinimumMapQuality << endl
+ << OPTION_UNUSEDMODELTHRESHOLD << EQUAL_CHAR << settings->UnusedModelThreshold << endl
+ << endl;
+}
+
+void ResolveTool::StatsFileWriter::WriteReadGroups(const map<string, ReadGroupResolver>& readGroups) {
+
+ // [ReadGroups]
+ // #<name> <medianFL> <minFL> <maxFL> <topModelID> <nextTopModelID> <isAmbiguous?>
+ m_stream << READGROUPS_TOKEN << endl
+ << RG_FIELD_DESCRIPTION << endl;
+
+ // iterate over read groups
+ map<string, ReadGroupResolver>::const_iterator rgIter = readGroups.begin();
+ map<string, ReadGroupResolver>::const_iterator rgEnd = readGroups.end();
+ for ( ; rgIter != rgEnd; ++rgIter ) {
+ const string& name = (*rgIter).first;
+ const ReadGroupResolver& resolver = (*rgIter).second;
+
+ // skip if read group has no data
+ if ( !resolver.HasData )
+ continue;
+
+ // write read group data
+ m_stream << name << TAB_CHAR
+ << resolver.MedianFragmentLength << TAB_CHAR
+ << resolver.MinFragmentLength << TAB_CHAR
+ << resolver.MaxFragmentLength << TAB_CHAR
+ << resolver.TopModelId << TAB_CHAR
+ << resolver.NextTopModelId << TAB_CHAR
+ << boolalpha << resolver.IsAmbiguous
+ << endl;
+ }
+
+ // extra newline at end
+ m_stream << endl;
+}
+
+// --------------------------------------------------------------------------