1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
|
/*
* 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 "SamHeaderRecord.h"
// Constructor
SamHeaderRecord::SamHeaderRecord()
: myTagHash(),
myTags(),
myNumActiveTags(0)
{
}
// Destructor
SamHeaderRecord::~SamHeaderRecord()
{
reset();
}
// Set the fields from the passed in line.
// Return true if successfully set.
bool SamHeaderRecord::setFields(const StringArray& tokens)
{
bool status = true;
// Loop through the tags for this type.
// The tags start in column 1 since column 0 contains the type.
for(int columnIndex = 1; columnIndex < tokens.Length(); columnIndex++)
{
// Validate that the tag is at least 3 characters. Two for the token,
// one for the ':'.
if((tokens[columnIndex].Length() < 3) ||
(tokens[columnIndex][2] != ':'))
{
// Continue to the next tag, this one is too small/invalid.
status = false;
std::cerr << "ERROR: Poorly formatted tag in header: "
<< tokens[columnIndex] << std::endl;
continue;
}
// Get the tag from the token.
char tag[3];
tag[0] = tokens[columnIndex][0];
tag[1] = tokens[columnIndex][1];
tag[2] = 0;
// The tag value is the rest of the substring.
String tagValue = (tokens[columnIndex]).SubStr(3);
// Set the tag.
status &= setTag(tag, tagValue.c_str());
}
status &= isValid();
return(status);
}
// Check to see if the record is valid.
bool SamHeaderRecord::isValid()
{
bool status = true;
// Check that the required tags are set. If they aren't, return false.
for(unsigned int reqIndex = 0; reqIndex < myRequiredTags.size(); reqIndex++)
{
// Check to see if the required tag at this index exists and has
// a value.
int index = myTagHash.Integer(myRequiredTags[reqIndex].c_str());
if((index < 0) || !(myTags[index]->hasValue()))
{
// Did not find the tag, stet status to false.
std::cerr << "ERROR: Missing required tag: "
<< myRequiredTags[reqIndex] << "." << std::endl;
status = false;
}
}
return(status);
}
// Return the value associated with the specified tag.
const char* SamHeaderRecord::getTagValue(const char* tag) const
{
// Look up the tag in myTags.
int index = myTagHash.Integer(tag);
if(index < 0)
{
// The tag was not found in the hash, so return "".
return("");
}
// The tag was found in the hash, so return the tag value found at the
// index associated with the tag.
return(myTags[index]->getValue());
}
// Set the value of the specified tag to the specified value.
// Set value to NULL in order to delete the tag.
// Returns whether or not it was successful.
bool SamHeaderRecord::setTag(const char* tag, const char* value)
{
// Lookup the tag in the hash.
int vectorIndex = myTagHash.Integer(tag);
if(vectorIndex < 0)
{
// The tag was not found in the hash, so create a new one.
SamHeaderTag* tagPtr = new SamHeaderTag(tag, value);
if(tagPtr == NULL)
{
// Failed to allocate the tag, return false.
std::cerr << "Failed to allocate space (new) for a SamHeaderTag.\n";
return(false);
}
// Add the new tag to the back of the tag values.
vectorIndex = myTags.size();
myTags.push_back(tagPtr);
// If the value is not null, increment the number of active tags.
if(value[0] != 0)
{
++myNumActiveTags;
}
// Add the tag to the hash.
int hashIndex = myTagHash.Add(tag, vectorIndex);
if((myTagHash.Integer(hashIndex) != vectorIndex) ||
(myTagHash[hashIndex] != tag))
{
// Failed to add the tag, so return false.
std::cerr << "Failed to add tag, " << tag
<< ", to the hash." << std::endl;
return(false);
}
return(true);
}
else if((unsigned int)vectorIndex < myTags.size())
{
// Found the tag in the hash. So, update the tag if it
// is not the key.
if(myKeyTag != tag)
{
// Not the key, so update the tag.
// If the new value is null and the old one is not, decrement the
// number of active tags.
if((value[0] == 0) && ((myTags[vectorIndex]->getValue())[0] != 0))
{
// Tag was deleted since the new value is blank but the old
// value was not.
--myNumActiveTags;
}
else if((value[0] != 0) &&
((myTags[vectorIndex]->getValue())[0] == 0))
{
// Tag was added since the old value was blank and the new value
// is not.
++myNumActiveTags;
}
// Just modifying a tag, so this does not affect the number
// of active tags.
return(myTags[vectorIndex]->setValue(value));
}
else if(strcmp(value, myTags[vectorIndex]->getValue()) == 0)
{
// The new key value is the same as the previous value, so
// it is not a change, return true.
return(true);
}
else
{
// Can't modify the key tag's value since that will
// screw up the hash.
std::cerr << "Can't modify the key tag, " << tag << " from "
<< myTags[vectorIndex]->getValue() << " to "
<< value << std::endl;
return(false);
}
}
// Got an invalid index from the hash. This is not supposed to happen.
// so return false.
std::cerr << "Invalid tag index found: " << vectorIndex
<< ", but max index is " << myTags.size() << " for tag: "
<< tag << std::endl;
return(false);
}
// Reset this header record to an empty state.
void SamHeaderRecord::reset()
{
// Delete the tag hash.
myTagHash.Clear();
// Loop through deleting all the tags in the vector.
for(unsigned int vectorIndex = 0;
vectorIndex < myTags.size();
vectorIndex++)
{
delete myTags[vectorIndex];
myTags[vectorIndex] = NULL;
}
// Clear the tag vector.
myTags.clear();
myNumActiveTags = 0;
}
// Appends the string representation of this header record
// to the passed in string.
bool SamHeaderRecord::appendString(std::string& header)
{
// Track whether or not the header type has been written.
// Only write the header type if at least one of the tags has
// an associated value.
bool writtenHeader = false;
if(isActiveHeaderRecord() && isValid())
{
// Loop through all the entries in the tag vector.
for(unsigned int vectorIndex = 0;
vectorIndex < myTags.size();
vectorIndex++)
{
if(!writtenHeader && (myTags[vectorIndex]->hasValue()))
{
// The tag has a value and the header type has not yet been written,
// so write it.
header += "@";
header += myTypeString;
writtenHeader = true;
}
myTags[vectorIndex]->getTagString(header);
}
// If a header has been written, add a new line character.
if(writtenHeader)
{
header += "\n";
return(true);
}
}
// Nothing was written, return false.
return(false);
}
// Add the key tag with the specified value.
bool SamHeaderRecord::addKey(const char* value)
{
if(myKeyTag.size() == 0)
{
return(false);
}
return(setTag(myKeyTag.data(), value));
}
// Return the value associated with the specified tag.
const char* SamHeaderRecord::getKeyValue() const
{
// Look up the tag in myTags.
int index = myTagHash.Integer(myKeyTag.c_str());
if(index < 0)
{
// The tag was not found in the hash, so return "".
return("");
}
// The tag was found in the hash, so return the tag value found at the
// index associated with the tag.
return(myTags[index]->getValue());
}
// This header is active if there is at least one tag set.
bool SamHeaderRecord::isActiveHeaderRecord()
{
return(myNumActiveTags != 0);
}
// Return the type of this header record.
const char* SamHeaderRecord::getTypeString()
{
return(myTypeString.c_str());
}
// Return the type of this header record.
SamHeaderRecord::SamHeaderRecordType SamHeaderRecord::getType()
{
return(myType);
}
void SamHeaderRecord::addRequiredTag(const char* requiredTag)
{
myRequiredTags.push_back(requiredTag);
}
void SamHeaderRecord::internalCopy(SamHeaderRecord& newRec) const
{
newRec.myTagHash = myTagHash;
newRec.myTags.clear();
// Loop through copying the tags.
for(unsigned int vectorIndex = 0;
vectorIndex < myTags.size();
vectorIndex++)
{
if(myTags[vectorIndex] != NULL)
{
newRec.myTags.push_back(new SamHeaderTag(*(myTags[vectorIndex])));
}
}
newRec.myRequiredTags = myRequiredTags;
newRec.myNumActiveTags = myNumActiveTags;
}
|