Loading...
Searching...
No Matches
SamFileHeader.cpp
/*
* Copyright (C) 2010 Regents of the University of Michigan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SamFileHeader.h"
#include "SamHeaderSQ.h"
#include "SamHeaderRG.h"
const std::string SamFileHeader::EMPTY_RETURN = "";
SamFileHeader::SamFileHeader()
: myHD(NULL),
myReferenceInfo(),
myErrorMessage("")
{
resetHeader();
mySQs.setCaseSensitive(true);
myRGs.setCaseSensitive(true);
myPGs.setCaseSensitive(true);
}
SamFileHeader::~SamFileHeader()
{
resetHeader();
}
// Copy Constructor
SamFileHeader::SamFileHeader(const SamFileHeader& header)
{
copy(header);
}
// Overload operator = to copy the passed in header into this header.
SamFileHeader & SamFileHeader::operator = (const SamFileHeader& header)
{
copy(header);
return(*this);
}
bool SamFileHeader::copy(const SamFileHeader& header)
{
// Check to see if the passed in value is the same as this.
if(this == &header)
{
return(true);
}
resetHeader();
// Copy the records by getting the other header's header string
// and parsing it.
std::string newString;
bool status = header.getHeaderString(newString);
String newHeaderString = newString.c_str();
status &= parseHeader(newHeaderString);
myCurrentHeaderIndex = header.myCurrentHeaderIndex;
myCurrentCommentIndex = header.myCurrentCommentIndex;
// Clear the reference info and copy it to ensure it is the same.
myReferenceInfo.clear();
// Copy Reference contigs, hash, lengths.
myReferenceInfo = header.myReferenceInfo;
return(status);
}
// Reset the header for a new entry, clearing out previous values.
void SamFileHeader::resetHeader()
{
myReferenceInfo.clear();
// Clear the pointers to the header records. They are deleted when the
// vector is cleaned up.
myHD = NULL;
mySQs.Clear();
myRGs.Clear();
myPGs.Clear();
// Delete the header records and clear the vector.
for(unsigned int headerIndex = 0; headerIndex < myHeaderRecords.size();
headerIndex++)
{
if(myHeaderRecords[headerIndex] != NULL)
{
delete myHeaderRecords[headerIndex];
myHeaderRecords[headerIndex] = NULL;
}
}
myHeaderRecords.clear();
// Reset the iterator for the header lines.
resetHeaderRecordIter();
// Reset the comment iterator.
resetCommentIter();
// Reset the individual type header iterators.
resetSQRecordIter();
resetRGRecordIter();
resetPGRecordIter();
// Clear the comments
myComments.clear();
}
// Set the passed in string to the entire header string. Clearing its
// current contents.
bool SamFileHeader::getHeaderString(std::string& header) const
{
header.clear();
// Keep getting header lines until there are no more - false returned.
unsigned int index = 0;
while(getHeaderLine(index, header) != false)
{
++index;
}
return(true);
}
int SamFileHeader::getReferenceID(const String & referenceName, bool addID)
{
return(myReferenceInfo.getReferenceID(referenceName, addID));
}
int SamFileHeader::getReferenceID(const char* referenceName, bool addID)
{
return(myReferenceInfo.getReferenceID(referenceName, addID));
}
const String & SamFileHeader::getReferenceLabel(int id) const
{
return(myReferenceInfo.getReferenceLabel(id));
}
// Get the Reference Information
const SamReferenceInfo& SamFileHeader::getReferenceInfo() const
{
return(myReferenceInfo);
}
// Get the Reference Information for updating separately when reading
// BAMs...should only be called by BamInterface.
SamReferenceInfo& SamFileHeader::getReferenceInfoForBamInterface()
{
return(myReferenceInfo);
}
// Add a header line that has an const char* value.
bool SamFileHeader::addHeaderLine(const char* type, const char* tag,
const char* value)
{
String headerLine;
headerLine += "@";
headerLine += type;
headerLine += "\t";
headerLine += tag;
headerLine += ":";
headerLine += value;
return(addHeaderLine(headerLine.c_str()));
}
// Add a header line that is already preformatted in a const char*.
bool SamFileHeader::addHeaderLine(const char* headerLine)
{
// Parse the added header line.
String headerString = headerLine;
return(parseHeader(headerString));
}
// Add a header line that is already preformatted in a const char*.
bool SamFileHeader::addHeader(const char* header)
{
// Parse the added header line.
String headerString = header;
return(parseHeader(headerString));
}
// Add a comment.
bool SamFileHeader::addComment(const char* comment)
{
if((comment != NULL) && (strcmp(comment, EMPTY_RETURN.c_str()) != 0))
{
// Valid comment, so add it.
myComments.push_back(comment);
}
return(true);
}
// Add the specified tag and value to the HD header.
bool SamFileHeader::setHDTag(const char* tag, const char* value)
{
if(myHD == NULL)
{
// Need to create the HD line.
myHD = new SamHeaderHD();
if(myHD == NULL)
{
// New failed, return false.
myErrorMessage = "SamFileHeader: Failed to allocate a new HD tag";
return(false);
}
// Succeeded to create the line, add it to the
// list.
myHeaderRecords.push_back(myHD);
}
if(!myHD->setTag(tag, value))
{
myErrorMessage = "SamFileHeader: Failed to set the specified HD tag";
return(false);
}
return(true);
}
// Add the specified tag and value to the SQ header with the specified name.
// If the header does not yet exist, the header is added.
bool SamFileHeader::setSQTag(const char* tag, const char* value,
const char* name)
{
// Get the SQ record for the specified name.
SamHeaderSQ* sq = getSQ(name);
if(sq == NULL)
{
// The SQ does not yet exist.
// Make sure the tag is LN.
if(strcmp(tag, "LN") != 0)
{
// LN is required so must be the first tag added
myErrorMessage =
"SamFileHeader:Failed to add the specified SQ key, LN not specified.";
return(false);
}
// Add it.
sq = new SamHeaderSQ();
if(sq == NULL)
{
// Could not create the header record.
myErrorMessage = "SamFileHeader: Failed to allocate a new SQ tag";
return(false);
}
// Created the header record, so add it to the list of SQ lines.
mySQs.Add(name, sq);
myHeaderRecords.push_back(sq);
// value is the length, so update the reference info.
myReferenceInfo.add(name, atoi(value));
// Add the key tag
if(!sq->addKey(name))
{
// Failed to add the key tag, return false.
myErrorMessage = "SamFileHeader:Failed to add the specified SQ key";
return(false);
}
}
else if(strcmp(tag, "LN") == 0)
{
// Cannot modify/remove the LN tag.
myErrorMessage = "SamFileHeader:Cannot modify/remove the SQ's LN tag";
return(false);
}
if(!sq->setTag(tag, value))
{
myErrorMessage = "Failed to set the specified SQ tag";
return(false);
}
return(true);
}
// Add the specified tag and value to the RG header with the read group
// identifier. If the header does not yet exist, the header is added.
bool SamFileHeader::setRGTag(const char* tag, const char* value, const char* id)
{
// Get the RG record for the specified name.
SamHeaderRG* rg = getRG(id);
if(rg == NULL)
{
// The RG does not yet exist.
// Add it.
rg = new SamHeaderRG();
if(rg == NULL)
{
// Could not create the header record.
myErrorMessage = "Failed to allocate a new RG tag";
return(false);
}
// Created the header record, so add it to the list of RG lines.
myRGs.Add(id, rg);
myHeaderRecords.push_back(rg);
// Add the key tag
if(!rg->addKey(id))
{
// Failed to add the key tag, return false.
myErrorMessage = "Failed to add the specified RG key";
return(false);
}
}
if(!rg->setTag(tag, value))
{
myErrorMessage = "Failed to set the specified RG tag";
return(false);
}
return(true);
}
// Add the specified tag and value to the PG header with the specified id.
// If the header does not yet exist, the header is added.
// Add the specified tag and value to the PG header.
bool SamFileHeader::setPGTag(const char* tag, const char* value, const char* id)
{
// Get the PG record for the specified name.
SamHeaderPG* pg = getPG(id);
if(pg == NULL)
{
// The PG does not yet exist.
// Add it.
pg = new SamHeaderPG();
if(pg == NULL)
{
// Could not create the header record.
myErrorMessage = "Failed to allocate a new PG tag";
return(false);
}
// Created the header record, so add it to the list of PG lines.
myPGs.Add(id, pg);
myHeaderRecords.push_back(pg);
// Add the key tag
if(!pg->addKey(id))
{
// Failed to add the key tag, return false.
myErrorMessage = "Failed to add the specified PG key";
return(false);
}
}
if(!pg->setTag(tag, value))
{
myErrorMessage = "Failed to set the specified PG tag";
return(false);
}
return(true);
}
// Add the HD record to the header.
bool SamFileHeader::addHD(SamHeaderHD* hd)
{
// If there is already an HD header or if null
// was passed in, return false.
if(myHD != NULL)
{
myErrorMessage = "Failed add an HD tag - there is already one";
return(false);
}
if(hd == NULL)
{
myErrorMessage = "Failed add an HD tag - no tag specified";
return(false);
}
myHD = hd;
myHeaderRecords.push_back(myHD);
return(true);
}
// Add the SQ record to the header.
bool SamFileHeader::addSQ(SamHeaderSQ* sq)
{
if(sq == NULL)
{
// null pointer passed in, can't add it.
myErrorMessage = "SAM/BAM Header line failed to allocate SQ.";
return(false);
}
const char* name = sq->getTagValue("SN");
const char* length = sq->getTagValue("LN");
if(strcmp(name, EMPTY_RETURN.c_str()) == 0)
{
// SN is not set, so can't add it.
myErrorMessage =
"SAM/BAM Header line failure: Skipping SQ line that is missing the SN field.";
return(false);
}
if(strcmp(length, EMPTY_RETURN.c_str()) == 0)
{
// LN is not set, so can't add it.
myErrorMessage =
"SAM/BAM Header line failure: Skipping SQ line that is missing the LN field.";
return(false);
}
// Determine whether or not a record with this
// key is already in the hash.
if(mySQs.Find(name) < 0)
{
// It is not already in the hash so add it.
mySQs.Add(name, sq);
myHeaderRecords.push_back(sq);
myReferenceInfo.add(name, atoi(length));
return(true);
}
// It is already in the hash, so cannot be added.
myErrorMessage = "SAM/BAM Header line failure: Skipping SQ line that has a repeated SN field.";
return(false);
}
// Add the RG record to the header.
bool SamFileHeader::addRG(SamHeaderRG* rg)
{
if(rg == NULL)
{
// null pointer passed in, can't add it.
myErrorMessage = "SAM/BAM Header line failed to allocate RG.";
return(false);
}
const char* id = rg->getTagValue("ID");
if(strcmp(id, EMPTY_RETURN.c_str()) == 0)
{
// ID is not set, so can't add it.
myErrorMessage = "SAM/BAM Header line failure: Skipping RG line that is missing the ID field.";
return(false);
}
// Determine whether or not a record with this
// key is already in the hash.
if(myRGs.Find(id) < 0)
{
// It is not already in the hash so
// add it.
myRGs.Add(id, rg);
myHeaderRecords.push_back(rg);
return(true);
}
// It is already in the hash, so cannot be added.
myErrorMessage = "SAM/BAM Header line failure: Skipping RG line that has a repeated ID field.";
return(false);
}
// Add the PG record to the header.
bool SamFileHeader::addPG(SamHeaderPG* pg)
{
// If a null pointer was passed in, return false.
if(pg == NULL)
{
myErrorMessage = "SAM/BAM Header line failed to allocate PG.";
return(false);
}
const char* id = pg->getTagValue("ID");
if(strcmp(id, EMPTY_RETURN.c_str()) == 0)
{
// ID is not set, so can't add the header record.
myErrorMessage = "SAM/BAM Header line failure: Skipping PG line that is missing the ID field.";
return(false);
}
// Determine whether or not a record with this
// key is already in the hash.
if(myPGs.Find(id) < 0)
{
// It is not already in the hash so
// add it.
myPGs.Add(id, pg);
myHeaderRecords.push_back(pg);
return(true);
}
// It is already in the hash, so cannot be added.
myErrorMessage = "SAM/BAM Header line failure: Skipping PG line that has a repeated ID field.";
return(false);
}
// Add the RG record to the header.
bool SamFileHeader::addRecordCopy(const SamHeaderRecord& hdrRec)
{
SamHeaderRecord* newRec = hdrRec.createCopy();
bool returnVal = true;
switch(newRec->getType())
{
case SamHeaderRecord::HD:
returnVal = addHD((SamHeaderHD*)newRec);
break;
case SamHeaderRecord::PG:
returnVal = addPG((SamHeaderPG*)newRec);
break;
case SamHeaderRecord::RG:
returnVal = addRG((SamHeaderRG*)newRec);
break;
case SamHeaderRecord::SQ:
returnVal = addSQ((SamHeaderSQ*)newRec);
break;
default:
myErrorMessage = "Failed to copy a header record, unknown type.";
returnVal = false;
break;
}
return(returnVal);
}
// Remove the HD record.
bool SamFileHeader::removeHD()
{
if(myHD != NULL)
{
// Reset the record. Do not delete it since it is in the headerRecords
// vector and it is not worth the time to remove it from the middle of
// that vector since this is the header and the space does not need
// to be conserved.
myHD->reset();
// Set myHD to null so a new HD could be added.
myHD = NULL;
}
return(true);
}
// Remove the SQ record associated with the specified name.
bool SamFileHeader::removeSQ(const char* name)
{
// Look up the name in the hash.
int hashIndex = mySQs.Find(name);
if(hashIndex < 0)
{
// Not found in the hash, so nothing to
// delete, return true it does not exist
// in the hash.
return(true);
}
// Get the SQ.
SamHeaderSQ* sq = (SamHeaderSQ*)(mySQs.Object(hashIndex));
if(sq == NULL)
{
// sq is null, this is an error since hashIndex was greater than 0,
// so it should have been found.
myErrorMessage = "SAM/BAM Header line failed to get SQ object.";
return(false);
}
// Reset the record. Do not delete it since it is in the headerRecords
// vector and it is not worth the time to remove it from the middle of
// that vector since this is the header and the space does not need
// to be conserved.
sq->reset();
// Delete the entry from the hash.
mySQs.Delete(hashIndex);
return(true);
}
// Remove the RG record associated with the specified id.
bool SamFileHeader::removeRG(const char* id)
{
// Look up the id in the hash.
int hashIndex = myRGs.Find(id);
if(hashIndex < 0)
{
// Not found in the hash, so nothing to
// delete, return true it does not exist
// in the hash.
return(true);
}
// Get the RG.
SamHeaderRG* rg = (SamHeaderRG*)(myRGs.Object(hashIndex));
if(rg == NULL)
{
// rg is null, this is an error since hashIndex was greater than 0,
// so it should have been found.
myErrorMessage = "SAM/BAM Header line failed to get RG object.";
return(false);
}
// Reset the record. Do not delete it since it is in the headerRecords
// vector and it is not worth the time to remove it from the middle of
// that vector since this is the header and the space does not need
// to be conserved.
rg->reset();
// Delete the entry from the hash.
myRGs.Delete(hashIndex);
return(true);
}
// Remove the PG record associated with the specified id.
bool SamFileHeader::removePG(const char* id)
{
// Look up the id in the hash.
int hashIndex = myPGs.Find(id);
if(hashIndex < 0)
{
// Not found in the hash, so nothing to
// delete, return true it does not exist
// in the hash.
return(true);
}
// Get the PG.
SamHeaderPG* pg = (SamHeaderPG*)(myPGs.Object(hashIndex));
if(pg == NULL)
{
// pg is null, this is an error since hashIndex was greater than 0,
// so it should have been found.
myErrorMessage = "SAM/BAM Header line failed to get PG object.";
return(false);
}
// Reset the record. Do not delete it since it is in the headerRecords
// vector and it is not worth the time to remove it from the middle of
// that vector since this is the header and the space does not need
// to be conserved.
pg->reset();
// Delete the entry from the hash.
myPGs.Delete(hashIndex);
return(true);
}
const char* SamFileHeader::getHDTagValue(const char* tag)
{
if(myHD == NULL)
{
// return blank since there is no HD type.
return(EMPTY_RETURN.c_str());
}
return(myHD->getTagValue(tag));
}
// Get the value associated with the specified tag on the SQ line with
// the specified sequence name.
const char* SamFileHeader::getSQTagValue(const char* tag, const char* name)
{
// Look up the name in the hash to get the associated SQ object.
SamHeaderSQ* sq = (SamHeaderSQ*)(mySQs.Object(name));
// If it is NULL - the tag was not found, so return
if(sq == NULL)
{
return(EMPTY_RETURN.c_str());
}
// Found the object, so return the SQ Tag.
return(sq->getTagValue(tag));
}
// Get the value associated with the specified tag on the RG line with
// the specified read group identifier.
const char* SamFileHeader::getRGTagValue(const char* tag, const char* id)
{
// Look up the id in the hash to get the associated RG object.
SamHeaderRG* rg = (SamHeaderRG*)(myRGs.Object(id));
// If it is NULL - the tag was not found, so return
if(rg == NULL)
{
return(EMPTY_RETURN.c_str());
}
// Found the object, so return the RG Tag.
return(rg->getTagValue(tag));
}
const char* SamFileHeader::getPGTagValue(const char* tag, const char* id)
{
// Look up the id in the hash to get the associated PG object.
SamHeaderPG* pg = (SamHeaderPG*)(myPGs.Object(id));
// If it is NULL - the tag was not found, so return
if(pg == NULL)
{
return(EMPTY_RETURN.c_str());
}
// Found the object, so return the PG Tag.
return(pg->getTagValue(tag));
}
// Get the number of SQ objects.
int SamFileHeader::getNumSQs()
{
return(mySQs.Entries());
}
// Get the number of RG objects.
int SamFileHeader::getNumRGs()
{
return(myRGs.Entries());
}
// Get the number of PG objects.
int SamFileHeader::getNumPGs()
{
return(myPGs.Entries());
}
// Get the HD object.
SamHeaderHD* SamFileHeader::getHD()
{
return(myHD);
}
// Get the SQ object with the specified sequence name.
SamHeaderSQ* SamFileHeader::getSQ(const char* name)
{
return((SamHeaderSQ*)(mySQs.Object(name)));
}
// Get the RG object with the specified read group identifier.
SamHeaderRG* SamFileHeader::getRG(const char* id)
{
return((SamHeaderRG*)(myRGs.Object(id)));
}
// Get the PG object.
SamHeaderPG* SamFileHeader::getPG(const char* id)
{
return((SamHeaderPG*)(myPGs.Object(id)));
}
// Return the value of the SO tag.
// If this field does not exist, EMPTY_RETURN.c_str() is returned.
const char* SamFileHeader::getSortOrder()
{
if(myHD == NULL)
{
// No HD, so return blank EMPTY_RETURN.c_str()
return(EMPTY_RETURN.c_str());
}
return(myHD->getSortOrder());
}
// Deprecated way of getting the sort order from the file.
const char* SamFileHeader::getTagSO()
{
return(getSortOrder());
}
// Get the next SQ header record. After all SQ headers have been retrieved,
// NULL is returned until a reset is called.
SamHeaderRecord* SamFileHeader::getNextSQRecord()
{
return(getNextHeaderRecord(myCurrentSQIndex,
SamHeaderRecord::SQ));
}
// Get the next RG header record. After all RG headers have been retrieved,
// NULL is returned until a reset is called.
SamHeaderRecord* SamFileHeader::getNextRGRecord()
{
return(getNextHeaderRecord(myCurrentRGIndex,
SamHeaderRecord::RG));
}
// Get the next PG header record. After all PG headers have been retrieved,
// NULL is returned until a reset is called.
SamHeaderRecord* SamFileHeader::getNextPGRecord()
{
return(getNextHeaderRecord(myCurrentPGIndex,
SamHeaderRecord::PG));
}
// Reset to the beginning of the header records so the next call
// to getNextSQRecord returns the first SQ header record.
void SamFileHeader::resetSQRecordIter()
{
myCurrentSQIndex = 0;
}
// Reset to the beginning of the header records so the next call
// to getNextRGRecord returns the first RG header record.
void SamFileHeader::resetRGRecordIter()
{
myCurrentRGIndex = 0;
}
// Reset to the beginning of the header records so the next call
// to getNextPGRecord returns the first PG header record.
void SamFileHeader::resetPGRecordIter()
{
myCurrentPGIndex = 0;
}
// Get the next header record of the specified type.
// Pass in the index to start looking at and the type to look for.
// Update the index.
// After all headers of that type have been retrieved,
// NULL is returned until a reset is called for that type.
SamHeaderRecord* SamFileHeader::getNextHeaderRecord(uint32_t& index,
SamHeaderRecord::SamHeaderRecordType headerType)
{
SamHeaderRecord* foundRecord = NULL;
// Loop until a record is found or until out of range of the
// headerRecord vector.
while((index < myHeaderRecords.size())
&& (foundRecord == NULL))
{
// Get the next record.
foundRecord = myHeaderRecords[index];
// Either way, increment the index.
++index;
// Check to see if the next record is active.
if(!foundRecord->isActiveHeaderRecord())
{
// Not active, so clear the pointer.
foundRecord = NULL;
}
// Check to see if the record is the right type.
else if(foundRecord->getType() != headerType)
{
// Not the right type, so clear the pointer.
foundRecord = NULL;
}
}
// Return the record if it was found. Will be null if none were found.
return(foundRecord);
}
// Get the next header record. After all headers have been retrieved,
// NULL is returned until a reset is called. Does not return the
// Comment lines.
// NOTE: both getNextHeaderRecord and getNextHeaderLine increment the
// same iterator.
SamHeaderRecord* SamFileHeader::getNextHeaderRecord()
{
// Get the next header record
SamHeaderRecord* foundRecord = NULL;
// Loop until a record is found or until out of range of the
// headerRecord vector.
while((myCurrentHeaderIndex < myHeaderRecords.size())
&& (foundRecord == NULL))
{
// Get the next record.
foundRecord = myHeaderRecords[myCurrentHeaderIndex];
// Either way, increment the index.
++myCurrentHeaderIndex;
// Check to see if the next record is active.
if(!foundRecord->isActiveHeaderRecord())
{
// Not active, so clear the pointer.
foundRecord = NULL;
}
}
// Return the record if it was found. Will be null if none were found.
return(foundRecord);
}
// Set the passed in string to the next header line. The passed in
// string will be overwritten. If there are no more header lines or there
// is an error, false is returned and the passed in string is set to EMPTY_RETURN.c_str()
// until a rest is called.
// Will also return the comment lines.
// NOTE: both getNextHeaderRecord and getNextHeaderLine increment the
// same iterator.
bool SamFileHeader::getNextHeaderLine(std::string &headerLine)
{
headerLine = EMPTY_RETURN.c_str();
// Until the header is set, keep reading.
// Header could return EMPTY_RETURN.c_str() if the header line is blank.
while(headerLine == EMPTY_RETURN.c_str())
{
if(getHeaderLine(myCurrentHeaderIndex, headerLine) == false)
{
// getHeaderLine failed, so stop processing, and return false.
return(false);
}
else
{
// In range, increment the index.
++myCurrentHeaderIndex;
}
}
return(true);
}
// Reset to the beginning of the header records so the next call
// to getNextHeaderRecord returns the first header line.
void SamFileHeader::resetHeaderRecordIter()
{
myCurrentHeaderIndex = 0;
}
void SamFileHeader::appendCommentLines(std::string &commentLines)
{
for(unsigned int i = 0; i < myComments.size(); i++)
{
commentLines += "@CO\t";;
commentLines += myComments[i];
commentLines += "\n";
}
}
// Returns the comment on the next comment line. Returns EMPTY_RETURN.c_str() if all comment
// lines have been returned, until resetCommentIter is called.
const char* SamFileHeader::getNextComment()
{
if(myCurrentCommentIndex < myComments.size())
{
return(myComments[myCurrentCommentIndex++].c_str());
}
// Already gone through all the comments, return EMPTY_RETURN.c_str().
return(EMPTY_RETURN.c_str());
}
// Resets to the beginning of the comments so getNextComment returns
// the first comment.
void SamFileHeader::resetCommentIter()
{
myCurrentCommentIndex = 0;
}
// Parse the header.
bool SamFileHeader::parseHeader(String& header)
{
std::string errorMessage = "";
int numErrors = 0;
int numValid = 0;
// Split the header into lines.
std::vector<String>* types = header.Split('\n');
// Loop through each header line, parsing that line.
for(uint32_t index = 0; index < types->size(); index++)
{
// Parse the header line.
if(!parseHeaderLine(types->at(index)))
{
errorMessage += myErrorMessage;
errorMessage += "\n";
++numErrors;
}
else
{
// valid header line
++numValid;
}
}
// Delete the types vector.
delete types;
types = NULL;
myErrorMessage = errorMessage;
if((numErrors > 0) && (numValid == 0))
{
// Only errors.
std::cerr << numErrors
<< " invalid SAM/BAM Header lines were skipped due to:\n"
<< errorMessage << std::endl;
return(false);
}
else if(numErrors > 0)
{
// Some valid & some invalid.
// Going to return true, but add note about the invalid lines.
std::cerr << numErrors
<< " invalid SAM/BAM Header lines were skipped due to:\n"
<< errorMessage << std::endl;
}
return(true);
}
// Parse one line of the header.
bool SamFileHeader::parseHeaderLine(const String& headerLine)
{
// Check if the line starts with @CO.
if((headerLine.Length() >= 4) && (headerLine[0] == '@') &&
(headerLine[1] == 'C') && (headerLine[2] == 'O') &&
(headerLine[3] == '\t'))
{
// Comment line.
String comment = headerLine.SubStr(4);
return(addComment(comment));
}
StringArray tokens;
// Split the line by tabs.
tokens.ReplaceColumns(headerLine, '\t');
if(tokens.Length() < 1)
{
// Nothing on this line, just return true.
return(true);
}
// Get the header type, the first column.
if((tokens[0].Length() != 3) || (tokens[0][0] != '@'))
{
// The header type string is incorrect. Should be 3 characters
// with the first one @.
myErrorMessage = "SAM/BAM Header line does not start with @ & at least 2 chars.";
return(false);
}
bool status = true;
if(tokens[0] == "@HD")
{
if(myHD == NULL)
{
// Create a new hd.
myHD = new SamHeaderHD();
if(myHD == NULL)
{
// Failed to allocate HD, so return false.
myErrorMessage = "SAM/BAM Header line failed to allocate HD.";
return(false);
}
myHeaderRecords.push_back(myHD);
if(!myHD->setFields(tokens))
{
myErrorMessage = "SAM/BAM Header line failed to store HD record.";
status = false;
}
}
else
{
// HD already set, so return false.
myErrorMessage = "SAM/BAM Header line failure: multiple HD records.";
status = false;
}
}
else if(tokens[0] == "@SQ")
{
// Create a new SQ record.
SamHeaderSQ* sq = new SamHeaderSQ();
if(sq->setFields(tokens))
{
// sq fields were properly set, so add it to the list of
// SQ lines.
// myStatus set in the method.
status &= addSQ(sq);
}
else
{
myErrorMessage = "SAM/BAM Header line failed to store SQ record.";
status = false;
}
}
else if(tokens[0] == "@RG")
{
// Create a new RG record.
SamHeaderRG* rg = new SamHeaderRG();
if(rg->setFields(tokens))
{
// rg fields were properly set, so add it to the list of
// RG lines.
// myStatus set in the method.
status &= addRG(rg);
}
else
{
myErrorMessage = "SAM/BAM Header line failed to store RG record.";
status = false;
}
}
else if(tokens[0] == "@PG")
{
// Create a new PG record.
SamHeaderPG* pg = new SamHeaderPG();
if(pg->setFields(tokens))
{
// pg fields were properly set, so add it to the list of
// PG lines.
// myStatus set in the method.
status &= addPG(pg);
}
else
{
myErrorMessage = "SAM/BAM Header line failed to store PG record.";
status = false;
}
}
else
{
// Unknown header type.
myErrorMessage =
"SAM/BAM Header line failure: Skipping unknown header type, ";
myErrorMessage += (const char*)(tokens[0]);
status = false;
}
return(status);
}
// Set the passed in string to the header line at the specified index.
// It does NOT clear the current contents of header.
// NOTE: some indexes will return blank if the entry was deleted.
bool SamFileHeader::getHeaderLine(unsigned int index, std::string& header) const
{
// Check to see if the index is in range of the header records vector.
if(index < myHeaderRecords.size())
{
// In range of the header records vector, so get the string for
// that record.
SamHeaderRecord* hdrRec = myHeaderRecords[index];
hdrRec->appendString(header);
return(true);
}
else
{
unsigned int commentIndex = index - myHeaderRecords.size();
// Check to see if it is in range of the comments.
if(commentIndex < myComments.size())
{
// It is in range of the comments, so add the type.
header += "@CO\t";
// Add the comment.
header += myComments[commentIndex];
// Add the new line.
header += "\n";
return(true);
}
}
// Invalid index.
return(false);
}
SamFileHeader::getPG
SamHeaderPG * getPG(const char *id)
Get the PG object with the specified id, returning NULL if there is no PG object with that key.
Definition SamFileHeader.cpp:789
SamFileHeader::addPG
bool addPG(SamHeaderPG *pg)
Add the PG record to the header.
Definition SamFileHeader.cpp:492
SamFileHeader::getSortOrder
const char * getSortOrder()
Return the Sort Order value that is set in the Header, returning "" if this field does not exist.
Definition SamFileHeader.cpp:797
SamFileHeader::getSQTagValue
const char * getSQTagValue(const char *tag, const char *name)
Get the value associated with the specified tag on the SQ line with the specified sequence name,...
Definition SamFileHeader.cpp:696
SamFileHeader::getSQ
SamHeaderSQ * getSQ(const char *name)
Get the SQ object with the specified sequence name, returning NULL if there is no SQ object with that...
Definition SamFileHeader.cpp:775
SamFileHeader::getHD
SamHeaderHD * getHD()
Get the HD object, returning NULL if there is no HD record.
Definition SamFileHeader.cpp:768
SamFileHeader::resetRGRecordIter
void resetRGRecordIter()
Reset to the beginning of the header records so the next call to getNextRGRecord returns the first RG...
Definition SamFileHeader.cpp:852
SamFileHeader::setPGTag
bool setPGTag(const char *tag, const char *value, const char *id)
Set the specified tag to the specified value in the PG header with the specified id,...
Definition SamFileHeader.cpp:353
SamFileHeader::getHDTagValue
const char * getHDTagValue(const char *tag)
Returns the value associated with the specified HD tag, returning "" if the tag does not exist in the...
Definition SamFileHeader.cpp:683
SamFileHeader::addRG
bool addRG(SamHeaderRG *rg)
Add the RG record to the header.
Definition SamFileHeader.cpp:458
SamFileHeader::getNextHeaderLine
bool getNextHeaderLine(std::string &headerLine)
Set the passed in string to the next header line, overwritting the passed in string.
Definition SamFileHeader.cpp:941
SamFileHeader::getNextPGRecord
SamHeaderRecord * getNextPGRecord()
Get the next PG header record.
Definition SamFileHeader.cpp:835
SamFileHeader::getReferenceID
int getReferenceID(const String &referenceName, bool addID=false)
Get the reference ID for the specified reference name (chromosome).
Definition SamFileHeader.cpp:146
SamFileHeader::removePG
bool removePG(const char *id)
Remove PG record with the specified key.
Definition SamFileHeader.cpp:647
SamFileHeader::addSQ
bool addSQ(SamHeaderSQ *sq)
Add the SQ record to the header.
Definition SamFileHeader.cpp:415
SamFileHeader::getNumSQs
int getNumSQs()
Get the number of SQ objects.
Definition SamFileHeader.cpp:747
SamFileHeader::removeRG
bool removeRG(const char *id)
Remove RG record with the specified key.
Definition SamFileHeader.cpp:610
SamFileHeader::setSQTag
bool setSQTag(const char *tag, const char *value, const char *name)
Set the specified tag to the specified value in the SQ header with the specified name,...
Definition SamFileHeader.cpp:252
SamFileHeader::addComment
bool addComment(const char *comment)
Add the specified comment to the header (do not include "@CO" or "\n").
Definition SamFileHeader.cpp:213
SamFileHeader::getNumRGs
int getNumRGs()
Get the number of RG objects.
Definition SamFileHeader.cpp:754
SamFileHeader::getTagSO
const char * getTagSO()
DEPRECATED.
Definition SamFileHeader.cpp:809
SamFileHeader::getNextHeaderRecord
SamHeaderRecord * getNextHeaderRecord()
Get the next header record, but not comment line.
Definition SamFileHeader.cpp:908
SamFileHeader::addHD
bool addHD(SamHeaderHD *hd)
Add the HD record to the header.
Definition SamFileHeader.cpp:393
SamFileHeader::getNextComment
const char * getNextComment()
Returns the comment on the next comment line.
Definition SamFileHeader.cpp:985
SamFileHeader::getRGTagValue
const char * getRGTagValue(const char *tag, const char *id)
Get the value associated with the specified tag on the RG line with the specified read group identifi...
Definition SamFileHeader.cpp:714
SamFileHeader::getReferenceLabel
const String & getReferenceLabel(int id) const
Return the reference name (chromosome) for the specified reference id.
Definition SamFileHeader.cpp:158
SamFileHeader::addHeaderLine
bool addHeaderLine(const char *type, const char *tag, const char *value)
Add a header line that is just one tag with a const char* value.
Definition SamFileHeader.cpp:180
SamFileHeader::getReferenceInfo
const SamReferenceInfo & getReferenceInfo() const
Get the Reference Information.
Definition SamFileHeader.cpp:165
SamFileHeader::removeHD
bool removeHD()
Remove the HD record.
Definition SamFileHeader.cpp:554
SamFileHeader::resetSQRecordIter
void resetSQRecordIter()
Reset to the beginning of the header records so the next call to getNextSQRecord returns the first SQ...
Definition SamFileHeader.cpp:844
SamFileHeader::setRGTag
bool setRGTag(const char *tag, const char *value, const char *id)
Set the specified tag to the specified value in the RG header with the specified id,...
Definition SamFileHeader.cpp:311
SamFileHeader::getHeaderString
bool getHeaderString(std::string &header) const
Set the passed in string to the entire header string, clearing its current contents.
Definition SamFileHeader.cpp:131
SamFileHeader::copy
bool copy(const SamFileHeader &header)
Copy method copies the passed in header into this header.
Definition SamFileHeader.cpp:59
SamFileHeader::appendCommentLines
void appendCommentLines(std::string &commentLines)
Append all of the comment lines to the specified string.
Definition SamFileHeader.cpp:972
SamFileHeader::resetHeaderRecordIter
void resetHeaderRecordIter()
Reset to the beginning of the header records so the next call to getNextHeaderRecord returns the firs...
Definition SamFileHeader.cpp:966
SamFileHeader::getNumPGs
int getNumPGs()
Get the number of PG objects.
Definition SamFileHeader.cpp:761
SamFileHeader::getNextRGRecord
SamHeaderRecord * getNextRGRecord()
Get the next RG header record.
Definition SamFileHeader.cpp:826
SamFileHeader::getNextSQRecord
SamHeaderRecord * getNextSQRecord()
Get the next SQ header record.
Definition SamFileHeader.cpp:817
SamFileHeader::resetHeader
void resetHeader()
Initialize the header.
Definition SamFileHeader.cpp:90
SamFileHeader::removeSQ
bool removeSQ(const char *name)
Remove SQ record with the specified key.
Definition SamFileHeader.cpp:573
SamFileHeader::getPGTagValue
const char * getPGTagValue(const char *tag, const char *id)
Get the value associated with the specified tag on the RG line with the specified id,...
Definition SamFileHeader.cpp:730
SamFileHeader::addHeader
bool addHeader(const char *header)
Add a header that is already preformatted in a const char*.
Definition SamFileHeader.cpp:204
SamFileHeader::resetPGRecordIter
void resetPGRecordIter()
Reset to the beginning of the header records so the next call to getNextPGRecord returns the first PG...
Definition SamFileHeader.cpp:860
SamFileHeader::getRG
SamHeaderRG * getRG(const char *id)
Get the RG object with the specified read group identifier, returning NULL if there is no RG object w...
Definition SamFileHeader.cpp:782
SamFileHeader::addRecordCopy
bool addRecordCopy(const SamHeaderRecord &hdrRec)
Add a copy of the specified header record to the header.
Definition SamFileHeader.cpp:526
SamFileHeader::setHDTag
bool setHDTag(const char *tag, const char *value)
Set the specified tag to the specified value in the HD header, remove the tag by specifying value="".
Definition SamFileHeader.cpp:225
SamFileHeader::operator=
SamFileHeader & operator=(const SamFileHeader &header)
Overload operator = to copy the passed in header into this header.
Definition SamFileHeader.cpp:52
SamFileHeader::resetCommentIter
void resetCommentIter()
Resets to the beginning of the comments so getNextComment returns the first comment.
Definition SamFileHeader.cpp:998
SamHeaderHD
Definition SamHeaderHD.h:24
SamHeaderPG
Definition SamHeaderPG.h:25
SamHeaderRG
Definition SamHeaderRG.h:24
SamHeaderRecord
This class encapsulates the tag value pairs contained with a SAM Header line with accessors for getti...
Definition SamHeaderRecord.h:28
SamHeaderRecord::getTagValue
const char * getTagValue(const char *tag) const
Return the value associated with the specified tag.
Definition SamHeaderRecord.cpp:100
SamHeaderRecord::reset
void reset()
Reset this header record to an empty state with no tags.
Definition SamHeaderRecord.cpp:212
SamHeaderRecord::getType
SamHeaderRecordType getType()
Return the type of this header record (HD, SQ, RG, or PG) as an enum.
Definition SamHeaderRecord.cpp:315
SamHeaderRecord::setFields
bool setFields(const StringArray &tokens)
Set the fields from the passed in line.
Definition SamHeaderRecord.cpp:38
SamHeaderRecord::isActiveHeaderRecord
bool isActiveHeaderRecord()
This record is active (true) if there is at least one tag set.
Definition SamHeaderRecord.cpp:301
SamHeaderRecord::createCopy
virtual SamHeaderRecord * createCopy() const =0
Return a pointer to a newly created header record of the appropriate type that is a copy of this reco...
SamHeaderRecord::appendString
bool appendString(std::string &header)
Appends the string representation of this header record to the passed in string.
Definition SamHeaderRecord.cpp:234
SamHeaderRecord::addKey
bool addKey(const char *value)
Add the key tag with the specified value (not for HD headers).
Definition SamHeaderRecord.cpp:273
SamHeaderRecord::SamHeaderRecordType
SamHeaderRecordType
Specifies the Type for the sam header record (line).
Definition SamHeaderRecord.h:31
SamHeaderRecord::HD
@ HD
Header.
Definition SamHeaderRecord.h:32
SamHeaderRecord::SQ
@ SQ
Sequence Dictionary.
Definition SamHeaderRecord.h:33
SamHeaderRecord::PG
@ PG
Program.
Definition SamHeaderRecord.h:35
SamHeaderRecord::RG
@ RG
Read Group.
Definition SamHeaderRecord.h:34
SamHeaderRecord::setTag
bool setTag(const char *tag, const char *value)
Set the value of the specified tag to the specified value, deletes the tag when value is NULL.
Definition SamHeaderRecord.cpp:119
SamHeaderSQ
Definition SamHeaderSQ.h:24
SamReferenceInfo
Class for tracking the reference information mapping between the reference ids and the reference name...
Definition SamReferenceInfo.h:28
SamReferenceInfo::clear
void clear()
Reset this reference info.
Definition SamReferenceInfo.cpp:123
String
Definition StringBasics.h:39