File: SamHeaderRecord.cpp

package info (click to toggle)
libstatgen 1.0.15-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,588 kB
  • sloc: cpp: 49,624; ansic: 1,408; makefile: 320; sh: 60
file content (345 lines) | stat: -rw-r--r-- 10,088 bytes parent folder | download | duplicates (4)
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;
}