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