libStatGen Software  1
SamHeaderRecord.cpp
00001 /*
00002  *  Copyright (C) 2010  Regents of the University of Michigan
00003  *
00004  *   This program is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   This program is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 
00018 #include "SamHeaderRecord.h"
00019 
00020 // Constructor
00021 SamHeaderRecord::SamHeaderRecord()
00022     : myTagHash(),
00023       myTags(),
00024       myNumActiveTags(0)
00025 {
00026 }
00027 
00028 
00029 // Destructor
00030 SamHeaderRecord::~SamHeaderRecord()
00031 {
00032     reset();
00033 }
00034 
00035 
00036 // Set the fields from the passed in line.
00037 // Return true if successfully set.
00038 bool SamHeaderRecord::setFields(const StringArray& tokens)
00039 {
00040     bool status = true;
00041    
00042     // Loop through the tags for this type.
00043     // The tags start in column 1 since column 0 contains the type.
00044     for(int columnIndex = 1; columnIndex < tokens.Length(); columnIndex++)
00045     {
00046         // Validate that the tag is at least 3 characters. Two for the token,
00047         // one for the ':'.
00048         if((tokens[columnIndex].Length() < 3) || 
00049            (tokens[columnIndex][2] != ':'))
00050         {
00051             // Continue to the next tag, this one is too small/invalid.
00052             status = false;
00053             std::cerr << "ERROR: Poorly formatted tag in header: " 
00054                       << tokens[columnIndex] << std::endl;
00055             continue;
00056         }
00057       
00058         // Get the tag from the token.
00059         char tag[3];
00060         tag[0] = tokens[columnIndex][0];
00061         tag[1] = tokens[columnIndex][1];
00062         tag[2] = 0;
00063 
00064         // The tag value is the rest of the substring.
00065         String tagValue = (tokens[columnIndex]).SubStr(3);
00066 
00067         // Set the tag.      
00068         status &= setTag(tag, tagValue.c_str());
00069     }
00070 
00071     status &= isValid();
00072 
00073     return(status);
00074 }
00075 
00076 
00077 // Check to see if the record is valid.
00078 bool SamHeaderRecord::isValid()
00079 {
00080     bool status = true;
00081     // Check that the required tags are set. If they aren't, return false.
00082     for(unsigned int reqIndex = 0; reqIndex < myRequiredTags.size(); reqIndex++)
00083     {
00084         // Check to see if the required tag at this index exists and has
00085         // a value.
00086         int index = myTagHash.Integer(myRequiredTags[reqIndex].c_str());
00087         if((index < 0) || !(myTags[index]->hasValue()))
00088         {
00089             // Did not find the tag, stet status to false.
00090             std::cerr << "ERROR: Missing required tag: " 
00091                       << myRequiredTags[reqIndex] << "." << std::endl;
00092             status = false;
00093         }
00094     }
00095     return(status);
00096 }
00097 
00098 
00099 // Return the value associated with the specified tag.
00100 const char* SamHeaderRecord::getTagValue(const char* tag) const
00101 {
00102     // Look up the tag in myTags.
00103     int index = myTagHash.Integer(tag);
00104     if(index < 0)
00105     {
00106         // The tag was not found in the hash, so return "".
00107         return("");
00108     }
00109 
00110     // The tag was found in the hash, so return the tag value found at the 
00111     // index associated with the tag.
00112     return(myTags[index]->getValue());
00113 }
00114 
00115 
00116 // Set the value of the specified tag to the specified value.
00117 // Set value to NULL in order to delete the tag.
00118 // Returns whether or not it was successful.
00119 bool SamHeaderRecord::setTag(const char* tag, const char* value)
00120 {
00121     // Lookup the tag in the hash.
00122     int vectorIndex = myTagHash.Integer(tag);
00123     if(vectorIndex < 0)
00124     {
00125         // The tag was not found in the hash, so create a new one.
00126         SamHeaderTag* tagPtr = new SamHeaderTag(tag, value);
00127       
00128         if(tagPtr == NULL)
00129         {
00130             // Failed to allocate the tag, return false.
00131             std::cerr << "Failed to allocate space (new) for a SamHeaderTag.\n";
00132             return(false);
00133         }
00134 
00135         // Add the new tag to the back of the tag values.
00136         vectorIndex = myTags.size();
00137         myTags.push_back(tagPtr);
00138 
00139         // If the value is not null, increment the number of active tags.
00140         if(value[0] != 0)
00141         {
00142             ++myNumActiveTags;
00143         }
00144 
00145         // Add the tag to the hash.
00146         int hashIndex = myTagHash.Add(tag, vectorIndex);
00147       
00148         if((myTagHash.Integer(hashIndex) != vectorIndex) ||
00149            (myTagHash[hashIndex] != tag))
00150         {
00151             // Failed to add the tag, so return false.
00152             std::cerr << "Failed to add tag, " << tag
00153                       << ", to the hash." << std::endl;
00154             return(false);
00155         }
00156         return(true);
00157     }
00158     else if((unsigned int)vectorIndex < myTags.size())
00159     {
00160         // Found the tag in the hash.  So, update the tag if it
00161         // is not the key.
00162         if(myKeyTag != tag)
00163         {
00164             // Not the key, so update the tag.
00165             // If the new value is null and the old one is not, decrement the
00166             // number of active tags.
00167             if((value[0] == 0) && ((myTags[vectorIndex]->getValue())[0] != 0))
00168             {
00169                 // Tag was deleted since the new value is blank but the old
00170                 // value was not.
00171                 --myNumActiveTags;
00172             }
00173             else if((value[0] != 0) && 
00174                     ((myTags[vectorIndex]->getValue())[0] == 0))
00175             {
00176                 // Tag was added since the old value was blank and the new value
00177                 // is not.
00178                 ++myNumActiveTags;
00179             }
00180 
00181             // Just modifying a tag, so this does not affect the number 
00182             // of active tags.
00183             return(myTags[vectorIndex]->setValue(value));
00184         }
00185         else if(strcmp(value, myTags[vectorIndex]->getValue()) == 0)
00186         {
00187             // The new key value is the same as the previous value, so
00188             // it is not a change, return true.
00189             return(true);
00190         }
00191         else
00192         {
00193             // Can't modify the key tag's value since that will
00194             // screw up the hash.
00195             std::cerr << "Can't modify the key tag, " << tag << " from "
00196                       << myTags[vectorIndex]->getValue() << " to " 
00197                       << value << std::endl;
00198             return(false);
00199         }
00200     }
00201 
00202     // Got an invalid index from the hash.  This is not supposed to happen.
00203     // so return false.
00204     std::cerr << "Invalid tag index found: " << vectorIndex 
00205               << ", but max index is " << myTags.size() << " for tag: " 
00206               << tag << std::endl;
00207     return(false);
00208 }
00209 
00210 
00211 // Reset this header record to an empty state.
00212 void SamHeaderRecord::reset()
00213 {
00214     // Delete the tag hash.
00215     myTagHash.Clear();
00216 
00217     // Loop through deleting all the tags in the vector.
00218     for(unsigned int vectorIndex = 0; 
00219         vectorIndex < myTags.size();
00220         vectorIndex++)
00221     {
00222         delete myTags[vectorIndex];
00223         myTags[vectorIndex] = NULL;
00224     }
00225     // Clear the tag vector.
00226     myTags.clear();
00227 
00228     myNumActiveTags = 0;
00229 }
00230 
00231 
00232 // Appends the string representation of this header record
00233 // to the passed in string.
00234 bool SamHeaderRecord::appendString(std::string& header)
00235 {
00236     // Track whether or not the header type has been written.
00237     // Only write the header type if at least one of the tags has
00238     // an associated value.
00239     bool writtenHeader = false;
00240 
00241     if(isActiveHeaderRecord() && isValid())
00242     {
00243         // Loop through all the entries in the tag vector.
00244         for(unsigned int vectorIndex = 0; 
00245             vectorIndex < myTags.size(); 
00246             vectorIndex++)
00247         {
00248             if(!writtenHeader && (myTags[vectorIndex]->hasValue()))
00249             {
00250                 // The tag has a value and the header type has not yet been written,
00251                 // so write it.
00252                 header += "@";
00253                 header += myTypeString;
00254                 writtenHeader = true;
00255             }
00256             myTags[vectorIndex]->getTagString(header);
00257         }
00258 
00259         // If a header has been written, add a new line character.
00260         if(writtenHeader)
00261         {
00262             header += "\n";
00263             return(true);
00264         }
00265     }
00266 
00267     // Nothing was written, return false.
00268     return(false);
00269 }
00270 
00271 
00272 // Add the key tag with the specified value.
00273 bool SamHeaderRecord::addKey(const char* value)
00274 {
00275     if(myKeyTag.size() == 0)
00276     {
00277         return(false);
00278     }
00279     return(setTag(myKeyTag.data(), value));
00280 }
00281 
00282 
00283 // Return the value associated with the specified tag.
00284 const char* SamHeaderRecord::getKeyValue() const
00285 {
00286     // Look up the tag in myTags.
00287     int index = myTagHash.Integer(myKeyTag.c_str());
00288     if(index < 0)
00289     {
00290         // The tag was not found in the hash, so return "".
00291         return("");
00292     }
00293 
00294     // The tag was found in the hash, so return the tag value found at the 
00295     // index associated with the tag.
00296     return(myTags[index]->getValue());
00297 }
00298 
00299 
00300 // This header is active if there is at least one tag set.
00301 bool SamHeaderRecord::isActiveHeaderRecord()
00302 {
00303     return(myNumActiveTags != 0);
00304 }
00305 
00306 
00307 // Return the type of this header record.
00308 const char* SamHeaderRecord::getTypeString()
00309 {
00310     return(myTypeString.c_str());
00311 }
00312 
00313 
00314 // Return the type of this header record.
00315 SamHeaderRecord::SamHeaderRecordType SamHeaderRecord::getType()
00316 {
00317     return(myType);
00318 }
00319 
00320 
00321 void SamHeaderRecord::addRequiredTag(const char* requiredTag)
00322 {
00323     myRequiredTags.push_back(requiredTag);
00324 }
00325 
00326 
00327 void SamHeaderRecord::internalCopy(SamHeaderRecord& newRec) const
00328 {
00329     newRec.myTagHash = myTagHash;
00330 
00331     newRec.myTags.clear();
00332 
00333     // Loop through copying the tags.
00334     for(unsigned int vectorIndex = 0; 
00335         vectorIndex < myTags.size();
00336         vectorIndex++)
00337     {
00338         if(myTags[vectorIndex] != NULL)
00339         {
00340             newRec.myTags.push_back(new SamHeaderTag(*(myTags[vectorIndex])));
00341         }
00342     }
00343     newRec.myRequiredTags = myRequiredTags;
00344     newRec.myNumActiveTags = myNumActiveTags;
00345 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends