GCC Code Coverage Report


./
File: src/XpertMass/Modif.cpp
Date: 2024-08-24 11:26:06
Lines:
196/369
53.1%
Functions:
20/32
62.5%
Branches:
209/621
33.7%

Line Branch Exec Source
1 /* BEGIN software license
2 *
3 * MsXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright(C) 2009,...,2018 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This file is part of the MsXpertSuite project.
10 *
11 * The MsXpertSuite project is the successor of the massXpert project. This
12 * project now includes various independent modules:
13 *
14 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
15 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * END software license
31 */
32
33
34 /////////////////////// Local includes
35 #include "PolChemDefEntity.hpp"
36 #include "globals.hpp"
37 #include "Modif.hpp"
38 #include "PolChemDef.hpp"
39 #include "Monomer.hpp"
40 #include <qdebug.h>
41
42 namespace MsXpS
43 {
44
45 namespace libXpertMass
46 {
47
48
49 /*!
50 \class MsXpS::libXpertMass::Modif
51 \inmodule libXpertMass
52 \ingroup PolChemDefBuildingdBlocks
53 \inheaderfile Modif.hpp
54
55 \brief The Modif class provides abstractions to work with
56 chemical modifications.
57
58 The Modif class provides a chemical modification that can be set to any monomer
59 in a polymer sequence or to any one of the polymer sequence ends.In the protein
60 world, chemical modifications of proteins that occur in the living cell are
61 called post-translational modifications.This class aims at modelling, among
62 others, such modifications.
63
64 The chemical reaction described by the Modif class is encoded as an
65 actionformula (see \l{Formula}).
66 */
67
68 /*!
69 \variable int MsXpS::libXpertMass::Modif::m_targets
70
71 \brief String that holds a list of all the target monomers of this
72 modification.
73
74 If there are more than one target, the targets (monomer codes)
75 must be separated by ';' characters.
76
77 If any monomer in the polymer chemistry definition might be modified by this
78 Modif object, then, the "*" string can be used to indicate so.
79 */
80
81 /*!
82 \variable int MsXpS::libXpertMass::Modif::m_maxCount
83
84 \brief Value indicating the maximum number of times this modification
85 can be set to a target entity (monomer or polymer).
86 */
87
88
89 4 Modif::Modif(PolChemDefCstSPtr pol_chem_def_csp,
90 const QDomElement &element,
91 4 [[maybe_unused]] int version)
92
5/10
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 4 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 4 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 4 times.
✗ Branch 19 not taken.
4 : PolChemDefEntity(pol_chem_def_csp, "NOT_SET")
93 {
94
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
4 if(element.tagName() != "mdf")
95 return;
96
97
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QDomElement child;
98
99
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
4 if(element.tagName() != "mdf")
100 return;
101
102
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 child = element.firstChildElement("name");
103
104
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(child.isNull())
105 return;
106
107
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_name = child.text();
108
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if(m_name.isEmpty())
109 return;
110
111
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 child = child.nextSiblingElement("formula");
112
113
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(child.isNull())
114 return;
115
116
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(!Formula::renderXmlFormulaElement(child))
117 return;
118
119
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(!calculateMasses())
120 return;
121
122
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 child = child.nextSiblingElement("targets");
123
124
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(child.isNull())
125 return;
126
127
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_targets = child.text();
128
129
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 child = child.nextSiblingElement("maxcount");
130
131
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(child.isNull())
132 return;
133
134 4 bool ok = false;
135
136
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
4 m_maxCount = child.text().toInt(&ok);
137
138
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if(!m_maxCount && !ok)
139 return;
140
141 // The validation will take care of checking that the <targets>
142 // element did have correct text inside and that <maxcount> be
143 // correct also.
144
145
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
4 if(!validate())
146 qDebug("The Modif being constructed failed to validate.");
147
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 }
148
149 /*!
150 \brief Constructs a modification.
151
152 A Modif instance cannot be of any use if it is not associated logically to a
153 polymer chemistry definition (\a pol_chem_def_csp). The formula is defined by
154 its \a name and its \a formula as as string (that defaults to empty in this
155 constructor).
156
157 Being able to construct a Modif without a formula string is necessary when Modif
158 objects are intialized piecemeal upon reading XML elements that describe the
159 Modif.
160
161 The \a formula might be a simple formula ("O", for an oxydation) or an
162 actionformula, if that is required to best characterize the modification
163 ("-H20+CH3COOH", for example, for an acetylation). The formula string can also
164 have a title, like "\"Acetylation\"-H20+CH3COOH".
165
166 The Ponderable base class' mono and avg masses are initialized to (0,0).
167
168 The targets (m_targets) is set to "all", that is \c "*" and the maximum count
169 that this modification can bet set to a target is set to \c 1 by default, like a
170 Seryl residue can only be phosphorylated once, for example.
171
172 If \a pol_chem_def_csp and \a modif_name are usable, then \c this modification
173 is initialized by looking into the polymer chemistry definition's list of Modif
174 instances and using the found Modif that has the same name as \a modif_name.
175 */
176 10508 Modif::Modif(PolChemDefCstSPtr pol_chem_def_csp,
177 QString modif_name,
178 10508 QString formula_string)
179 : PolChemDefEntity(pol_chem_def_csp, modif_name),
180 Formula(formula_string),
181 Ponderable(0, 0),
182 10508 m_targets("*"),
183
5/10
✓ Branch 2 taken 10508 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 10508 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 10508 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 10508 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 10508 times.
✗ Branch 16 not taken.
10508 m_maxCount(1)
184 {
185 // By default a modification will target any monomer. By default
186 // the modification will be able to modify an entity only once at maximum,
187 // like a Seryl may be phosphorylated only once.
188
189
6/8
✓ Branch 1 taken 10508 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10508 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10448 times.
✓ Branch 7 taken 60 times.
✓ Branch 8 taken 10448 times.
✓ Branch 9 taken 60 times.
21016 if(pol_chem_def_csp != nullptr && pol_chem_def_csp.get() != nullptr &&
190 10508 formula_string.isEmpty())
191 {
192 [[maybe_unused]] bool result;
193
1/2
✓ Branch 2 taken 10448 times.
✗ Branch 3 not taken.
10448 result = pol_chem_def_csp->referenceModifByName(modif_name, this);
194
195 // if(!result)
196 // qWarning() << "Modification by name" << modif_name
197 // << "is not known to the polymer chemistry definition.";
198 }
199 10508 }
200
201
202 /*!
203 \brief Constructs a Modif object as a copy of \a other.
204 */
205 2160 Modif::Modif(const Modif &other)
206 : PolChemDefEntity(other),
207 Formula(other),
208 Ponderable(other),
209 PropListHolder(other),
210 2160 m_targets(other.m_targets),
211
3/6
✓ Branch 2 taken 2160 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2160 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 2160 times.
✗ Branch 9 not taken.
2160 m_maxCount(other.m_maxCount)
212 {
213 2160 }
214
215 /*!
216 \brief Destructs this Modif.
217 */
218 4976 Modif::~Modif()
219 {
220 4976 }
221
222
223 /*!
224 \brief Assigns \a other to this modification.
225
226 Returns a reference to this modification.
227 */
228 Modif &
229 128 Modif::operator=(const Modif &other)
230 {
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 128 times.
128 if(&other == this)
232 return *this;
233
234 128 PolChemDefEntity::operator=(other);
235 128 Formula::operator=(other);
236 128 Ponderable::operator=(other);
237 128 PropListHolder::operator=(other);
238
239 128 return *this;
240 }
241
242
243 /*! Resets this modification to an empty object.
244 */
245 void
246 Modif::reset()
247 {
248 m_name = "NOT_SET";
249
250 m_formula.clear();
251 m_plusFormula.clear();
252 m_minusFormula.clear();
253
254 // When m_targets is empty, the modification cannot modify
255 // anything. Useful for testing purposes.
256 m_targets.clear();
257 m_maxCount = 1;
258
259 m_mono = 0;
260 m_avg = 0;
261
262 while(!m_propList.isEmpty())
263 delete m_propList.takeFirst();
264 }
265
266 /*!
267 \brief Sets the \a targets for this modification.
268
269 Setting \e{targets} means specifying which target might be modified using
270 this modification. For example, for \c Phosphorylation, in protein chemistry,
271 one would define targets as \c Serine, \c Threonine, \c Tyrosine (there are
272 other targets, but very rarely encountered).
273
274 Multiple targets are separated using ';'.
275
276 */
277 QString &
278 24 Modif::setTargets(QString targets)
279 {
280
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
24 if(targets.isEmpty())
281 {
282 m_targets.clear();
283 return m_targets;
284 }
285
286 // qDebug() << __FILE__ << __LINE__
287 // << "Before unspacification" << targets;
288
289 // Remove any space from 'targets'.
290 24 m_targets = unspacifyString(targets);
291
292 // qDebug() << __FILE__ << __LINE__
293 // << "After unspacification" << m_targets;
294
295 // Validate and simplify: true is by default the bool param.
296
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
24 if(!validateTargets())
297 4 m_targets = QString();
298
299 24 return m_targets;
300 }
301
302 /*!
303 \brief Returns the tagets of this modification.
304 */
305 QString
306 32 Modif::targets() const
307 {
308 32 return m_targets;
309 }
310
311 /*!
312 \brief Returns the tagets of this modification in the form a string list.
313
314 The member m_targets string is split using ';' as a delimitor, the
315 obtained list of strings is set to \a string_list and the size of the list is
316 returned.
317 */
318 int
319 Modif::targets(QStringList &string_list) const
320 {
321 // Return a string list after splitting at ';'.
322
323 string_list.clear();
324
325 string_list = m_targets.split(';', Qt::SkipEmptyParts, Qt::CaseSensitive);
326 return string_list.size();
327 }
328
329 /*!
330 \brief Returns true if monomer \a code is found among the targets of this
331 modifications, false otherwise.
332 */
333 bool
334 44 Modif::hasMonomerTarget(QString code) const
335 {
336
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 16 times.
44 if(m_targets == "*")
337 28 return true;
338
339
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if(m_targets == "!")
340 return false;
341
342
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
32 QString delimitedCode = QString(";%1;").arg(code);
343
344 // The m_targets string is in the form ";code;code;code;".
345
346
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 return m_targets.contains(delimitedCode, Qt::CaseSensitive);
347 16 }
348
349 /*!
350 \brief Validates the target of this modification.
351
352 The target list is split using ';' as a delimiter. If '*' is found and \a
353 simplify is true, the function returns true immediately (as all the
354 monomers in the polymer chemistry definition might be a target of this
355 modification).
356
357 If at least one target is found, then each target is a monomer
358 code and that code is looked for in the member polymer chemistry definition list
359 of monomers. If the code is found, that code is added to a temporary string. If
360 the code is not found, m_targets is set to that temporary string (only having in
361 it monomer codes found in the polymer chemistry definition's list of monomers)
362 and the function returns false.
363
364 Returns true if the targets of this modification all validated successfully.
365 */
366 bool
367 12188 Modif::validateTargets(bool simplify)
368 {
369 // A targets string cannot contain both a '*' and a '!'
370 // character. We check that immediately.
371
6/10
✓ Branch 2 taken 12188 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1184 times.
✓ Branch 5 taken 11004 times.
✓ Branch 8 taken 1184 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1184 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 12188 times.
12188 if(m_targets.contains('*') && m_targets.contains('!'))
372 return false;
373
374
1/2
✓ Branch 2 taken 12188 times.
✗ Branch 3 not taken.
12188 QList<Monomer *> monomerList = mcsp_polChemDef->monomerList();
375
376 12188 QString result;
377
378 // If the m_targets is empty, this is an error, because we cannot
379 // know what's to be done with the modification.
380
381
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12188 times.
12188 if(m_targets.isEmpty())
382 {
383 return false;
384 }
385
386 // A targets string looks like "Ser ; Thr ; Tyr".
387 QStringList stringList =
388
1/2
✓ Branch 3 taken 12188 times.
✗ Branch 4 not taken.
12188 m_targets.split(';', Qt::SkipEmptyParts, Qt::CaseSensitive);
389
390
2/2
✓ Branch 1 taken 20432 times.
✓ Branch 2 taken 11000 times.
31432 for(int iter = 0; iter < stringList.size(); ++iter)
391 {
392 20432 QString currentString = stringList.at(iter);
393
394 // There are two character that might be encountered: '*' is the
395 // equivalent of "all the monomers in the definition"; '!' is
396 // equivalent to "none of the monomers in the definition". But
397 // it is not possible that both * and ! be present in the same
398 // targets string.
399
400
5/6
✓ Branch 1 taken 19248 times.
✓ Branch 2 taken 1184 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19248 times.
✓ Branch 6 taken 1184 times.
✓ Branch 7 taken 19248 times.
20432 if(currentString == "*" || currentString == "!")
401 {
402 // Simplification asked: if '*' is found then it can be
403 // there alone. Same for '!'. '*' means that any monomer in
404 // the definition might be modified with this modification,
405 // '!' means that none of the monomers might be modified.
406
407
1/2
✓ Branch 0 taken 1184 times.
✗ Branch 1 not taken.
1184 if(simplify)
408 {
409 1184 m_targets = currentString;
410
411 1184 return true;
412 }
413 else
414 {
415 result.append(QString("%1;").arg(currentString));
416 }
417
418 continue;
419 }
420
421 // At this point, we have something to check as a monomer code:
422
423
3/4
✓ Branch 1 taken 19248 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 19244 times.
19248 if(Monomer::isCodeInList(currentString, monomerList) == -1)
424 {
425
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 qDebug() << "Monomer code is not known:" << currentString;
426
427 4 m_targets = result;
428
429 4 return false;
430 }
431 else
432 {
433 // Want the string to be ;code;code;code;(notice the first
434 // and last ';'), so that we can later ask easily if ;Lys;
435 // is found in the targets string, without risking to also
436 // match ;Ly;.
437
2/2
✓ Branch 1 taken 11004 times.
✓ Branch 2 taken 8240 times.
19244 if(!result.size())
438
3/6
✓ Branch 1 taken 11004 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11004 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11004 times.
✗ Branch 8 not taken.
22008 result.append(QString(";%1;").arg(currentString));
439 else
440
3/6
✓ Branch 1 taken 8240 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8240 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8240 times.
✗ Branch 8 not taken.
16480 result.append(QString("%1;").arg(currentString));
441 }
442
2/3
✓ Branch 1 taken 19244 times.
✓ Branch 2 taken 1188 times.
✗ Branch 3 not taken.
20432 }
443
444
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11000 times.
11000 if(result.isEmpty())
445 return false;
446
447 11000 m_targets = result;
448
449 11000 return true;
450 12188 }
451
452
453 /*!
454 \brief Set the maximum count of times that this modification might be set to
455 a target to \a value.
456 */
457 void
458 Modif::setMaxCount(int value)
459 {
460 Q_ASSERT(value > 0);
461
462 m_maxCount = value;
463 }
464
465 /*!
466 \brief Returns the maximum count of times that this modification might be
467 set to a target.
468 */
469 int
470 56 Modif::maxCount() const
471 {
472 56 return m_maxCount;
473 }
474
475 /*
476 \brief Returns the formula describing this modification.
477 */
478 QString
479 32 Modif::formula() const
480 {
481 32 return Formula::toString();
482 }
483
484 /*!
485 \brief Returns the index of \c this modification in the member
486 polymer chemistry definition's list of modifications, -1 otherwise
487 */
488 int
489 4 Modif::isNameKnown()
490 {
491 4 const QList<Modif *> &refList = mcsp_polChemDef->modifList();
492
493
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if(m_name.isEmpty())
494 return -1;
495
496
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 for(int iter = 0; iter < refList.size(); ++iter)
497 {
498
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if(refList.at(iter)->m_name == m_name)
499 4 return iter;
500 }
501
502 return -1;
503 }
504
505
506 /*!
507 \brief Searches for a modification by name \a name in the reference list \a
508 refList.
509
510 If a modification is found, and \a other is non-nullptr, the found modif is
511 copied to \a other.
512
513 Returns true if the modification was found, false otherwise.
514 */
515 int
516 2056 Modif::isNameInList(const QString &name,
517 const QList<Modif *> &refList,
518 Modif *other)
519 {
520 2056 Modif *iter_modif_p = 0;
521
522
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2056 times.
2056 if(name.isEmpty())
523 return -1;
524
525 // qDebug() << "Looking for modif by name" << name;
526
527
1/2
✓ Branch 1 taken 26840 times.
✗ Branch 2 not taken.
26840 for(int iter = 0; iter < refList.size(); ++iter)
528 {
529 26840 iter_modif_p = refList.at(iter);
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26840 times.
26840 Q_ASSERT(iter_modif_p);
531
532
2/2
✓ Branch 1 taken 2056 times.
✓ Branch 2 taken 24784 times.
26840 if(iter_modif_p->m_name == name)
533 {
534
1/2
✓ Branch 1 taken 2056 times.
✗ Branch 2 not taken.
2056 Modif test_modif(*iter_modif_p);
535
536
2/2
✓ Branch 0 taken 92 times.
✓ Branch 1 taken 1964 times.
2056 if(other)
537 {
538
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
92 *other = *iter_modif_p;
539 }
540 2056 return iter;
541 2056 }
542 }
543
544 return -1;
545 }
546
547
548 /*!
549 \brief Validates this modification.
550
551 The modification validates successfully if:
552
553 \list
554 \li The member polymer chemistry definition must be set
555 \li The name is not empty
556 \li The formula validates successfully
557 \li The targets validate successfully
558 \li The m_maxCount member is greater than 0
559 \endlist
560
561 Returns true if the modification validates successfully, false otherwise.
562 */
563 bool
564 12164 Modif::validate()
565 {
566
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12164 times.
12164 if(mcsp_polChemDef == nullptr)
567 return false;
568
569
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12164 times.
12164 if(m_name.isEmpty())
570 return false;
571
572
1/2
✓ Branch 1 taken 12164 times.
✗ Branch 2 not taken.
12164 Formula formula(m_formula);
573
574 IsotopicDataCstSPtr isotopic_data_csp =
575
1/2
✓ Branch 2 taken 12164 times.
✗ Branch 3 not taken.
12164 mcsp_polChemDef->getIsotopicDataCstSPtr();
576
577
2/4
✓ Branch 2 taken 12164 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 12164 times.
12164 if(!formula.validate(isotopic_data_csp))
578 {
579 qDebug() << "The formula failed to validate.";
580
581 return false;
582 }
583
584
2/4
✓ Branch 1 taken 12164 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12164 times.
12164 if(!validateTargets())
585 {
586 qDebug() << "The targets failed to validate.";
587 return false;
588 }
589
590
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12164 times.
12164 if(m_maxCount <= 0)
591 return false;
592
593 12164 return true;
594 12164 }
595
596
597 /*!
598 \brief Calculates the net masses of this modification.
599
600 The masses of the modification are the masses (monoisotopic and average) that
601 are added to the target as a result of that target being modified with this
602 modification.
603
604 The calculated masses are set to the Ponderable base class' m_mono and m_avg
605 members.
606
607 Returns true if the mass calculations were successful, false otherwise.
608
609 \sa Formula::accountMasses()
610 */
611 bool
612 10332 Modif::calculateMasses()
613 {
614 IsotopicDataCstSPtr isotopic_data_csp =
615
1/2
✓ Branch 2 taken 10332 times.
✗ Branch 3 not taken.
10332 mcsp_polChemDef->getIsotopicDataCstSPtr();
616
617 10332 m_mono = 0;
618 10332 m_avg = 0;
619
620 // qDebug() << "Right before accounting modif formula masses for formula:"
621 // << m_formula;
622
623
2/4
✓ Branch 2 taken 10332 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 10332 times.
10332 if(!Formula::accountMasses(isotopic_data_csp, &m_mono, &m_avg))
624 {
625 qDebug() << "Failed accounting masses for modif:" << m_name
626 << "and formula:" << m_formula;
627 return false;
628 }
629
630 // qDebug() << "Right after accounting modif formula masses for formula:"
631 // << m_formula << m_mono << "-" << m_avg;
632
633 10332 return true;
634 10332 }
635
636
637 /*!
638 \brief Adds to \a mono and \a avg the corresponding mass of this
639 modification.
640
641 The m_mono and m_avg masses are added to the arguments. The masses are
642 compounded by factor \a times before the addition.
643
644 Returns true.
645 */
646 bool
647 2036 Modif::accountMasses(double *mono, double *avg, int times)
648 {
649
1/2
✓ Branch 0 taken 2036 times.
✗ Branch 1 not taken.
2036 if(mono)
650 2036 *mono += m_mono * times;
651
652
1/2
✓ Branch 0 taken 2036 times.
✗ Branch 1 not taken.
2036 if(avg)
653 2036 *avg += m_avg * times;
654
655 2036 return true;
656 }
657
658
659 /*!
660 \brief Parses the modification XML \a element specifically for \a version.
661
662 Parses the modif \c mdf XML element passed as argument and for each
663 encountered data will set the data to this modif (this is
664 called XML rendering).The parsing is delegated to a function that is specific
665 for \a version of the polymer chemistry definition.
666
667 The \c mdf XML element is found in the polymer chemistry definition and has
668 the following form:
669
670
671 \code
672 <mdf>
673 <name>Acetylation</name>
674 <formula>C2H2O1</formula>
675 <targets>;K;</targets>
676 <maxcount>1</maxcount>
677 </mdf>
678 <mdf>
679 <name>AmidationAsp</name>
680 <formula>H1N1-O1</formula>
681 <targets>;D;</targets>
682 <maxcount>1</maxcount>
683 </mdf>
684 \endcode
685
686 After setting all the data, this modification calculates it masses and
687 validates itself. If any of these steps fails, the error is reported
688 by returning false.
689
690 Returns true if parsing was successful, false otherwise.
691 */
692 bool
693 10192 Modif::renderXmlMdfElement(const QDomElement &element, int version)
694 {
695
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 10192 times.
10192 if(element.tagName() != "mdf")
696 return false;
697
698
1/2
✓ Branch 0 taken 10192 times.
✗ Branch 1 not taken.
10192 if(version == 1)
699 {
700 // no-op
701 10192 version = 1;
702 }
703
704
1/2
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
10192 QDomElement child;
705
706
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 10192 times.
10192 if(element.tagName() != "mdf")
707 return false;
708
709
3/6
✓ Branch 2 taken 10192 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10192 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10192 times.
✗ Branch 9 not taken.
10192 child = element.firstChildElement("name");
710
711
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(child.isNull())
712 return false;
713
714
1/2
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
10192 m_name = child.text();
715
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10192 times.
10192 if(m_name.isEmpty())
716 return false;
717
718
3/6
✓ Branch 2 taken 10192 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10192 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10192 times.
✗ Branch 9 not taken.
10192 child = child.nextSiblingElement("formula");
719
720
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(child.isNull())
721 return false;
722
723
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(!Formula::renderXmlFormulaElement(child))
724 return false;
725
726
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(!calculateMasses())
727 {
728 qDebug() << "Failed accounting masses for modif: " << m_name
729 << "with formula:" << m_formula;
730 return false;
731 }
732
733
3/6
✓ Branch 2 taken 10192 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10192 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10192 times.
✗ Branch 9 not taken.
10192 child = child.nextSiblingElement("targets");
734
735
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(child.isNull())
736 return false;
737
738
1/2
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
10192 m_targets = child.text();
739
740
3/6
✓ Branch 2 taken 10192 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 10192 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 10192 times.
✗ Branch 9 not taken.
10192 child = child.nextSiblingElement("maxcount");
741
742
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(child.isNull())
743 return false;
744
745 10192 bool ok = false;
746
747
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10192 times.
✗ Branch 5 not taken.
10192 m_maxCount = child.text().toInt(&ok);
748
749
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10192 if(!m_maxCount && !ok)
750 return false;
751
752 // The validation will take care of checking that the <targets>
753 // element did have correct text inside and that <maxcount> be
754 // correct also.
755
756
2/4
✓ Branch 1 taken 10192 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10192 times.
10192 if(!validate())
757 return false;
758
759 10192 return true;
760 10192 }
761
762
763 /*!
764 \brief Formats this modification's data as a string suitable to be used as a
765 \c mdf XML element in the polymer chemistry definition.
766
767 The typical modification element that is generated in this function looks
768 like this:
769
770 \code
771 <mdf>
772 <name>Acetylation</name>
773 <formula>C2H2O1</formula>
774 <targets>;K;</targets>
775 <maxcount>1</maxcount>
776 </mdf>
777 <mdf>
778 <name>AmidationAsp</name>
779 <formula>H1N1-O1</formula>
780 <targets>;D;</targets>
781 <maxcount>1</maxcount>
782 </mdf>
783 \endcode
784
785 The formatting of the XML element takes into account \a offset and \a
786 indent by prepending the string with \a offset * \a indent character substring.
787
788 \a indent defaults to two spaces.
789
790 Returns a dynamically allocated string that needs to be freed after
791 use.
792 */
793 QString *
794 112 Modif::formatXmlMdfElement(int offset, const QString &indent)
795 {
796 int newOffset;
797 112 int iter = 0;
798
799
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 QString lead("");
800
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
112 QString *string = new QString();
801
802 // Prepare the lead.
803 112 newOffset = offset;
804
2/2
✓ Branch 0 taken 316 times.
✓ Branch 1 taken 112 times.
428 while(iter < newOffset)
805 {
806
1/2
✓ Branch 1 taken 316 times.
✗ Branch 2 not taken.
316 lead += indent;
807 316 ++iter;
808 }
809
810 /* We are willing to create an <modif> node that should look like this:
811 *
812 <mdf>
813 <name>Phosphorylation</name>
814 <formula>-H+H2PO3</formula>
815 <targets>S;T;Y</targets>
816 <maxcount>1</maxcount>
817 </mdf>
818 *
819 */
820
821
3/6
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
224 *string += QString("%1<mdf>\n").arg(lead);
822
823 // Prepare the lead.
824 112 ++newOffset;
825 112 lead.clear();
826 112 iter = 0;
827
2/2
✓ Branch 0 taken 428 times.
✓ Branch 1 taken 112 times.
540 while(iter < newOffset)
828 {
829
1/2
✓ Branch 1 taken 428 times.
✗ Branch 2 not taken.
428 lead += indent;
830 428 ++iter;
831 }
832
833 // Continue with indented elements.
834
835
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
336 *string += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
836
837
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
336 *string += QString("%1<formula>%2</formula>\n").arg(lead).arg(m_formula);
838
839
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
336 *string += QString("%1<targets>%2</targets>\n").arg(lead).arg(m_targets);
840
841
4/8
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
336 *string += QString("%1<maxcount>%2</maxcount>\n").arg(lead).arg(m_maxCount);
842
843 // Prepare the lead for the closing element.
844 112 --newOffset;
845 112 lead.clear();
846 112 iter = 0;
847
2/2
✓ Branch 0 taken 316 times.
✓ Branch 1 taken 112 times.
428 while(iter < newOffset)
848 {
849
1/2
✓ Branch 1 taken 316 times.
✗ Branch 2 not taken.
316 lead += indent;
850 316 ++iter;
851 }
852
853
3/6
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
224 *string += QString("%1</mdf>\n").arg(lead);
854
855 112 return string;
856 112 }
857
858
859 /*!
860 \brief Outputs a string representing this modification using qDebug().
861 */
862 void
863 Modif::debugPutStdErr()
864 {
865 qDebug() << m_name << m_formula << m_targets;
866 }
867
868
869 //////////////////////// ModifProp ////////////////////////
870 //////////////////////// ModifProp ////////////////////////
871
872
873 /*!
874 \class MsXpS::libXpertMass::ModifProp
875 \inmodule libXpertMass
876 \ingroup ThePropSystem
877
878 \brief The ModifProp class provides a Prop instance of which the member data
879 points to a dynamically allocated \l Modif instance.
880
881 The member datum \l m_name is set to "MODIF".
882 */
883
884 /*!
885 \brief Constructs a ModifProp instance using \a modif.
886
887 The \a modif pointer is set to the \l mpa_data member.
888 */
889 ModifProp::ModifProp(Modif *modif) : Prop("MODIF")
890 {
891 mpa_data = static_cast<void *>(modif);
892 }
893
894
895 /*!
896 \brief Constructs a ModifProp instance as a copy of \a other.
897 */
898 ModifProp::ModifProp(const ModifProp &other) : Prop(other)
899 {
900 if(other.mpa_data != nullptr)
901 {
902 Modif *modif = static_cast<Modif *>(other.mpa_data);
903
904 mpa_data = static_cast<void *>(new Modif(*modif));
905 }
906 else
907 mpa_data = nullptr;
908 }
909
910 /*!
911 \brief Destructs this ModifProp instance.
912
913 The deletion of the data are delegated to \l deleteData().
914 */
915 ModifProp::~ModifProp()
916 {
917 deleteData();
918 }
919
920 /*!
921 \brief Deletes the member data.
922 */
923 void
924 ModifProp::deleteData()
925 {
926 if(mpa_data != nullptr)
927 {
928 delete static_cast<Modif *>(mpa_data);
929 mpa_data = nullptr;
930 }
931 }
932
933 /*!
934 \brief Assigns \a other to this ModifProp instance.
935
936 The member data are first deleted and then set to a copy of those in \a other.
937 */
938 ModifProp &
939 ModifProp::operator=(const ModifProp &other)
940 {
941 if(&other == this)
942 return *this;
943
944 Prop::operator=(other);
945
946 if(mpa_data != nullptr)
947 deleteData();
948
949 if(other.mpa_data)
950 {
951 Modif *modif = static_cast<Modif *>(other.mpa_data);
952
953 mpa_data = static_cast<void *>(new Modif(*modif));
954 }
955 else
956 mpa_data = nullptr;
957
958 return *this;
959 }
960
961 /*!
962 \brief Returns true if this and the \a other modifications are identical,
963 false otherwise.
964 */
965 bool
966 72 Modif::operator==(const Modif &other)
967 {
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
72 if(&other == this)
969 return true;
970
971
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 64 times.
72 if(PolChemDefEntity::operator!=(other))
972 8 return false;
973
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 56 times.
64 if(Formula::operator!=(other))
974 8 return false;
975
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 48 times.
56 if(Ponderable::operator!=(other))
976 8 return false;
977
978
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 40 times.
48 if(m_targets != other.m_targets)
979 8 return false;
980
981 40 return true;
982 }
983
984 /*!
985 \brief Returns true if this and the \a other modifications differ,
986 false otherwise.
987
988 Returns the negated result of operator==().
989 */
990 bool
991 36 Modif::operator!=(const Modif &other)
992 {
993
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if(&other == this)
994 return false;
995
996 36 return !operator==(other);
997 }
998
999
1000 /*!
1001 \brief Duplicates this ModifProp instance and returns its pointer.
1002 */
1003 ModifProp *
1004 ModifProp::cloneOut() const
1005 {
1006 ModifProp *new_p = new ModifProp(*this);
1007
1008 return new_p;
1009 }
1010
1011
1012 /*!
1013 \brief Parses the property XML \a element using a \a{version}ed
1014 function.
1015
1016 The element looks like this:
1017
1018 \code
1019 <prop>
1020 <name>MODIF</name>
1021 <data>Phosphorylation</data> // That is the Modif name
1022 <data>-H+H2PO3</data> // -------------------- formula
1023 <data>S;T;Y</data> // -------------------- targets
1024 </prop>
1025 \endcode
1026
1027 As the data in \a element are parsed they are set to the member data, thus
1028 essentially initializing the Modif object pointed to by the member data.
1029
1030 Returns true if parsing was successful, false otherwise.
1031 */
1032 bool
1033 ModifProp::renderXmlElement(const QDomElement &element, int version)
1034 {
1035 if(element.tagName() != "prop")
1036 return false;
1037
1038 if(version == 1)
1039 {
1040 // no-op
1041 version = 1;
1042 }
1043
1044 QDomElement child;
1045
1046 // The element looks like this:
1047 //
1048 // <prop>
1049 // <name>MODIF</name>
1050 // <data>Phosphorylation</data> // That is the Modif name
1051 // <data>-H+H2PO3</data> // -------------------- formula
1052 // <data>S;T;Y</data> // -------------------- targets
1053 // </prop>
1054
1055
1056 if(element.tagName() != "prop")
1057 return false;
1058
1059 child = element.firstChildElement("name");
1060
1061 if(child.isNull())
1062 return false;
1063
1064 m_name = child.text();
1065
1066 if(m_name != "MODIF")
1067 return false;
1068
1069 // And now we have to manage the prop's data elements. When this
1070 // ModifProp object was allocated, one Modif was allocated and
1071 // set as the data of the Prop. Get to it.
1072
1073 Modif *modif = static_cast<Modif *>(mpa_data);
1074
1075 // Next sibling is the modif's name data.
1076
1077 child = child.nextSiblingElement("data");
1078
1079 if(child.isNull())
1080 return false;
1081
1082 modif->setName(child.text());
1083
1084 // Next sibling is the modif's formula data.
1085
1086 child = child.nextSiblingElement("data");
1087
1088 if(child.isNull())
1089 return false;
1090
1091 modif->setFormula(child.text());
1092
1093 if(!modif->calculateMasses())
1094 {
1095 qDebug() << __FILE__ << __LINE__
1096 << "Failed to calculate masses for modification:"
1097 << modif->name();
1098 return false;
1099 }
1100
1101 // Next sibling is the modif's targets data.
1102
1103 child = child.nextSiblingElement("data");
1104
1105 if(child.isNull())
1106 return false;
1107
1108 modif->setTargets(child.text());
1109
1110 // The validation will take care of checking that the <targets>
1111 // element did have correct text inside.
1112
1113 if(!modif->validate())
1114 return false;
1115
1116 return true;
1117 }
1118
1119
1120 /*!
1121 \brief Formats a string suitable to use as an XML element.
1122
1123 Formats a string suitable to be used as an XML element in an XML file (a
1124 polymer sequence file, for example). Typical ModifProp elements that might be
1125 generated in this function look like this:
1126
1127 \code
1128 <prop>
1129 <name>MODIF</name>
1130 <data>Phosphorylation</data> // That is the Modif name
1131 <data>-H+H2PO3</data> // -------------------- formula
1132 <data>S;T;Y</data> // -------------------- targets
1133 </prop>
1134 \endcode
1135
1136 \a offset times the \a indent string must be used as a lead in the
1137 formatting of elements.
1138
1139 Returns a dynamically allocated string that needs to be freed after use.
1140 */
1141 QString *
1142 ModifProp::formatXmlElement(int offset, const QString &indent)
1143 {
1144 int newOffset;
1145 int iter = 0;
1146
1147 QString lead("");
1148 QString *string = new QString();
1149
1150 // Prepare the lead.
1151 newOffset = offset;
1152 while(iter < newOffset)
1153 {
1154 lead += indent;
1155 ++iter;
1156 }
1157
1158 // The property has its data member that points to a Modif. Thus
1159 // the formatting of the element should produce something like this:
1160 //
1161 // <prop>
1162 // <name>MODIF</name>
1163 // <data>Phosphorylation</data> // That is the Modif name
1164 // <data>-H+H2PO3</data> // -------------------- formula
1165 // <data>S;T;Y</data> // -------------------- targets
1166 // </prop>
1167
1168 *string += QString("%1<prop>\n").arg(lead);
1169
1170 // Prepare the lead.
1171 ++newOffset;
1172 lead.clear();
1173 iter = 0;
1174 while(iter < newOffset)
1175 {
1176 lead += indent;
1177 ++iter;
1178 }
1179
1180 // Continue with indented elements.
1181
1182 *string += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1183
1184 *string += QString("%1<data>%2</data>\n")
1185 .arg(lead)
1186 .arg(static_cast<Modif *>(mpa_data)->name());
1187
1188 *string += QString("%1<data>%2</data>\n")
1189 .arg(lead)
1190 .arg(static_cast<Modif *>(mpa_data)->formula());
1191
1192 *string += QString("%1<data>%2</data>\n")
1193 .arg(lead)
1194 .arg(static_cast<Modif *>(mpa_data)->targets());
1195
1196 // Prepare the lead for the closing element.
1197 --newOffset;
1198 lead.clear();
1199 iter = 0;
1200 while(iter < newOffset)
1201 {
1202 lead += indent;
1203 ++iter;
1204 }
1205
1206 *string += QString("%1</prop>\n").arg(lead);
1207
1208 return string;
1209 }
1210
1211 } // namespace libXpertMass
1212
1213 } // namespace MsXpS
1214