GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/FragmentationPathway.cpp
Date: 2025-11-20 01:41:33
Lines:
307/448
68.5%
Functions:
33/34
97.1%
Branches:
307/812
37.8%

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 "MsXpS/libXpertMassCore/FragmentationPathway.hpp"
36 #include "MsXpS/libXpertMassCore/PolChemDef.hpp"
37
38
39 namespace MsXpS
40 {
41 namespace libXpertMassCore
42 {
43
44
45 /*!
46 \class MsXpS::libXpertMassCore::FragmentationPathway
47 \inmodule libXpertMassCore
48 \ingroup PolChemDefGasPhaseChemicalReactions
49 \inheaderfile FragmentationPathway.hpp
50
51 \brief The FragmentationPathway class provides a model for specifying gas phase
52 fragmentation pathways of \l{Oligomer} \l{Sequence}s.
53
54 The FragmentationPathway class provides the description of a fragmentation
55 pathway. Fragmentation pathways determine the chemical reaction that governs the
56 fragmentation of the polymer in the gas-phase. The chemical reaction is embodied
57 by a formula. The side of the polymer (left or right) that makes the fragment
58 after the fragmentation has occurred is described by a fragmentation-end
59 enumeration.
60
61 A fragmentation specification might not be enough information to determine the
62 manner in which a polymer fragments in the gas-phase. Fragmentation rules
63 (\l{FragmentationRule}s) might be required to refine the specification. A
64 fragmentation specification might hold as many \l{FragmentationRule}s as
65 required.
66 */
67
68 /*!
69 \variable MsXpS::libXpertMassCore::FragmentationPathway::mcsp_polChemDef
70
71 \brief The PolChemDef (polymer chemistry definition) that is the context in
72 which the Oligomer being fragmented exists.
73 */
74
75 /*!
76 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_name
77
78 \brief The name of the FragmentationPathway.
79 */
80
81 /*!
82 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_formula
83
84 \brief A \l{Formula} instance describing the fragmentation reaction occurring on
85 the Monomer at which the decomposition occurs.
86 */
87
88 /*!
89 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_fragEnd
90
91 \brief The end of the Oligomer precursor ion that is found in the product ion.
92
93 This member datum defines the end of the Oligomer sequence being fragmented that
94 is kept in the product ions (a, b, c ions keep the left end of the Oligomer,
95 while x, y, z ions keep the right end of the Oligomer; instead in protein
96 ammonium ions, no end is found in the product ions).
97 */
98
99 /*!
100 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_monomerContribution
101
102 \brief Contribution of the Monomer skeleton when decomposition occurs.
103
104 In some situations (nucleic acids, for example), upon fragmentation,
105 the Oligomer looses the nucleic base at the location of the backbone
106 decomposition (which yields an abasic ion product). This member allows to
107 indicate that the monomer (that is, the residue) is to be accounted for (by a
108 -1 value, the program accounts that the monomer is lost during decomposition).
109 But removing the whole monomer is not correct because that removes too much
110 material, so the member formula should account for the compensation of the
111 removed backbone.
112 */
113
114 /*!
115 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_comment
116
117 \brief A comment associated to the FragmentationPathway.
118 */
119
120 /*!
121 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_rules
122
123 \brief The container of FragmentationRuleSPtr that allow refining how the
124 fragmentation occurs at site depending on the identity of the Monomer occurring
125 either at previous position or next position with respect to the position in the
126 Oligomer undergoing fragmentation. The FragmentationRule instances are required
127 in the gas phase chemistry of sugars where the way decomposition occurs at a
128 given site depends on the identity of the immediate proximity of the decomposing
129 backbone region.
130 */
131
132
133 /*!
134 \variable MsXpS::libXpertMassCore::FragmentationPathway::m_isValid
135
136 \brief The validity status of this FragmentationPathway instance.
137 */
138
139
140 /*!
141 \brief Constructs a FragmentationPathway instance starting from an XML <fgp> \a
142 element according to \a version and using a reference \a pol_chem_def_csp
143 polymer chemistry definition.
144
145 This is the current format (FAKE fgr):
146 \code
147 <fgp>
148 <name>c</name>
149 <end>LE</end>
150 <formula>+N1H2</formula>
151 <sidechaincontrib>0</sidechaincontrib>
152 <comment>thefragmentationpathwaycomment</comment>
153 <fgr>
154 <name>a-fgr-2</name>
155 <formula>+H100</formula>
156 <prev-mnm-code>F</prev-mnm-code>
157 <curr-mnm-code>D</curr-mnm-code>
158 <next-mnm-code>E</next-mnm-code>
159 <comment>therulecomment</comment>
160 </fgr>
161 </fgp>
162
163 \endcode
164
165 The rendering of the FragmentationPathway instances requires that the PolChemDef
166 be available.
167
168 Depending on \a version, two different functions are used to actually render the
169 XML element. Before version 2, the XML element was named <fgs> (FragSpec class)
170 and starting with version 2, the XML is named <fgp> (FragmentationPathway
171 class).
172
173 \sa renderXmlFgpElement(), renderXmlFgsElement()
174 */
175 476 FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp,
176 const QDomElement &element,
177 476 int version)
178
3/6
✓ Branch 2 taken 476 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 476 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 476 times.
476 : mcsp_polChemDef(pol_chem_def_csp)
179 {
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 476 times.
476 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
181 qCritical() << "Constructing FragmentationPathway with no PolChemDef.";
182
183
2/2
✓ Branch 0 taken 469 times.
✓ Branch 1 taken 7 times.
476 if(version == 1)
184 {
185
2/4
✓ Branch 1 taken 469 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 469 times.
469 if(!renderXmlFgsElement(element, version))
186 qCritical()
187 << "Failed to fully render or validate the FragSpec XML element "
188 "for construction of FragSpec instance.";
189 }
190
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 else if(version == 2)
191 {
192
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if(!renderXmlFgpElement(element, version))
193 qCritical() << "Failed to fully render or validate the "
194 "FragmentationPathway XML element "
195 "for construction of FragmentationPathway instance.";
196 }
197 else
198 qFatalStream() << "Programming error. The polymer chemistry definition version "
199 "is not correct.";
200
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
476 }
201
202 /*!
203 \brief Constructs a FragmentationPathway instance with a number of parameters.
204
205 \list
206
207 \li \a pol_chem_def_csp The polymer chemistry definition (PolChemDef)
208
209 \li \a name The name of the fragmentation pathway (like 'y' for protein gas
210 phase chemistry)
211
212 \li \a formula_string The formula that describes the gas phase reaction
213
214 \li \a frag_end The end of the Oligomer sequence being fragmented that is kept
215 in the product ions (a, b, c ions keep the left end of the Oligomer, while x,
216 y, z ions keep the right end of the Oligomer)
217
218 \li \a comment A comment associated to the fragmentation pathway ("Observed with
219 high energy collisions", for example)
220
221 \endlist
222
223 Validation of this instance occurs after member data initialization and the
224 result of the validation process is set to member datum m_isValid.
225
226 \sa validate()
227 */
228 38 FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp,
229 const QString &name,
230 const QString &formula_string,
231 Enums::FragEnd frag_end,
232 38 const QString &comment)
233 76 : mcsp_polChemDef(pol_chem_def_csp),
234
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 11 times.
38 m_name(name),
235
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 m_formula(Formula(formula_string)),
236 38 m_fragEnd(frag_end),
237
3/4
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 12 times.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
102 m_comment(comment)
238 {
239 38 qDebug() << "Constructing FragmentationPathway with name:" << m_name;
240
241 38 ErrorList error_list;
242
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 m_isValid = validate(&error_list);
243
244
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 26 times.
38 if(!m_isValid)
245
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
24 qCritical() << "Upon construction of the FragmentationPathway, the "
246 "instance failed to "
247
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 "validate with errors:"
248
3/6
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
24 << Utils::joinErrorList(error_list, ", ");
249
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
38 }
250
251
252 /*!
253 \brief Constructs a FragmentationPathway instance as a copy of \a other.
254
255 Validation of this instance occurs after member data initialization and the
256 result of the validation process is set to member datum m_isValid.
257
258 \sa validate()
259 */
260 76 FragmentationPathway::FragmentationPathway(const FragmentationPathway &other)
261 152 : mcsp_polChemDef(other.mcsp_polChemDef),
262
1/2
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
76 m_name(other.m_name),
263
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 m_formula(other.m_formula),
264 76 m_fragEnd(other.m_fragEnd),
265 76 m_monomerContribution(other.m_monomerContribution),
266
3/4
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 10 times.
✓ Branch 4 taken 76 times.
✗ Branch 5 not taken.
218 m_comment(other.m_comment)
267 {
268
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 m_rules = other.m_rules;
269
270 76 ErrorList error_list;
271
1/2
✓ Branch 1 taken 76 times.
✗ Branch 2 not taken.
76 m_isValid = validate(&error_list);
272
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 76 times.
76 if(!m_isValid)
274 qCritical() << "Upon copy-construction of the FragmentationPathway, the "
275 "instance failed to "
276 "validate with errors:"
277 << Utils::joinErrorList(error_list, ", ");
278
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
76 }
279
280
281 /*!
282 \brief Destroys this FragmentationPathway instance.
283 */
284 228 FragmentationPathway::~FragmentationPathway()
285 {
286 228 m_rules.clear();
287
1/2
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
228 }
288
289 //////////////// THE POLCHEMDEF /////////////////////
290 /*!
291 \brief Sets the PolChemDef (polymer chemistry definition) to \a
292 pol_chem_def_csp.
293
294 Validation of this instance occurs after member data initialization and the
295 result of the validation process is set to member datum m_isValid.
296
297 \sa validate()
298 */
299 void
300 7 FragmentationPathway::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp)
301 {
302 7 mcsp_polChemDef = pol_chem_def_csp;
303
304 7 ErrorList error_list;
305
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_isValid = validate(&error_list);
306
307
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 if(!m_isValid)
308
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 qCritical() << "Upon setting PolChemDef of FragmentationPathway, the "
309 "instance failed to "
310
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 "validate with errors:"
311
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
14 << Utils::joinErrorList(error_list, ", ");
312 7 }
313
314 /*!
315 \brief Returns the PolChemDef (polymer chemistry definition).
316 */
317 PolChemDefCstSPtr
318 3 FragmentationPathway::getPolchemDefCstSPtr() const
319 {
320 3 return mcsp_polChemDef;
321 }
322
323 //////////////// THE NAME /////////////////////
324 /*!
325 \brief Sets the name to \a name.
326
327 Validation of this instance occurs after member data initialization and the
328 result of the validation process is set to member datum m_isValid.
329
330 \sa validate()
331 */
332 void
333 6 FragmentationPathway::setName(const QString &name)
334 {
335 6 m_name = name;
336
337 6 ErrorList error_list;
338
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 m_isValid = validate(&error_list);
339
340
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(!m_isValid)
341
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 qCritical()
342 << "Upon setting name of FragmentationPathway, the instance failed to "
343
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 "validate with errors:"
344
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
12 << Utils::joinErrorList(error_list, ", ");
345 6 }
346
347 /*!
348 \brief Returns the name.
349 */
350 const QString &
351 1154 FragmentationPathway::getName() const
352 {
353 1154 return m_name;
354 }
355
356 //////////////// THE FORMULA /////////////////////
357 /*!
358 \brief Sets the member Formula to \a formula.
359
360 Validation of this instance occurs after member data initialization and the
361 result of the validation process is set to member datum m_isValid.
362
363 \sa validate()
364 */
365 void
366 3 FragmentationPathway::setFormula(const Formula &formula)
367 {
368 3 m_formula = formula;
369
370 3 ErrorList error_list;
371
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_isValid = validate(&error_list);
372
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(!m_isValid)
374 qCritical()
375 << "Upon setting Formula of FragmentationPathway, the instance failed to "
376 "validate with errors:"
377 << Utils::joinErrorList(error_list, ", ");
378 3 }
379
380 /*!
381 \brief Sets the member Formula using \a formula_string.
382
383 Validation of this instance occurs after member data initialization and the
384 result of the validation process is set to member datum m_isValid.
385
386 \sa validate()
387 */
388 void
389 4 FragmentationPathway::setFormula(const QString &formula_string)
390 {
391
1/2
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 m_formula = Formula(formula_string);
392
393 4 ErrorList error_list;
394
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_isValid = validate(&error_list);
395
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(!m_isValid)
397 qCritical()
398 << "Upon setting Formula of FragmentationPathway, the instance failed to "
399 "validate with errors:"
400 << Utils::joinErrorList(error_list, ", ");
401 4 }
402
403 /*!
404 \brief Returns a const reference to the member \l Formula.
405 */
406 const Formula &
407 578 FragmentationPathway::getFormulaCstRef() const
408 {
409 578 return m_formula;
410 }
411
412 /*!
413 \brief Returns a reference to the member \l Formula.
414 */
415 Formula &
416 3 FragmentationPathway::getFormulaRef()
417 {
418 3 return m_formula;
419 }
420
421 //////////////// THE FRAG END /////////////////////
422
423 /*!
424 \brief Sets the precursor end in the fragment to \a frag_end.
425
426 Upon fragmentation, two fragments are generated, one holding the left end of the
427 initial precursor ion and one holding the right end. \a frag_end indicates if
428 the fragment is a left end fragment or a right end fragment. For example, in
429 protein gas phase chemistry, the ions of the series a, b and c hold the left
430 end of the precursor ion, while the ions of the series x, y, and z hold the
431 right end of the precursor ion.
432 */
433 void
434 5 FragmentationPathway::setFragEnd(Enums::FragEnd frag_end)
435 {
436 5 m_fragEnd = frag_end;
437 5 }
438
439
440 /*!
441 \brief Returns the \l Enums::FragEnd.
442 */
443 Enums::FragEnd
444 37 FragmentationPathway::getFragEnd() const
445 {
446 37 return m_fragEnd;
447 }
448
449 //////////////// THE MONOMER CONTRIBUTION /////////////////////
450 /*!
451 \brief Sets the Monomer contribution to \a monomer_contribution.
452
453 In certain fragmentation pathways, the monomer undergoing decomposition might
454 loose a portion of its structure. This is the case in nucleic acids
455 fragmentation, when the nucleic base might be detached from the monomer
456 undergoing the fragmentation reaction.
457
458 This member allows to indicate if a part of the monomer is detaching upon
459 decomposition. In this case, this value is negative (-1) and indicates that the
460 mass of the monomer is to be removed from the mass of the fragment. However,
461 because it is not the full monomer that decomposes away, but only a part of it,
462 then it is the responsibility of the polymer chemistry definition designer to
463 add back the formula of the conserved monomer structure.
464
465 For example, in DNA fragmentation, the \c{abasic a} fragmentation pathway is
466 defined with a monomer contribution of -1, with the \c{-HOH+C5H8O5P} formula to
467 account for the remaining structure of the monomer (this combination has a net
468 loss of the base only and does not account for loss of the phospho moiety of the
469 monomer).
470 */
471 void
472 FragmentationPathway::setMonomerContribution(int monomer_contribution)
473 {
474 m_monomerContribution = monomer_contribution;
475 }
476
477 /*!
478 \brief Returns the Monomer contribution.
479 */
480 int
481 285 FragmentationPathway::getMonomerContribution() const
482 {
483 285 return m_monomerContribution;
484 }
485
486 //////////////// THE COMMENT /////////////////////
487 /*!
488 \brief Sets the comment to \a comment.
489 */
490 void
491 2 FragmentationPathway::setComment(const QString &comment)
492 {
493 2 m_comment = comment;
494 2 }
495
496 /*!
497 \brief Returns the comment.
498 */
499 QString
500 5 FragmentationPathway::getComment() const
501 {
502
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 return m_comment;
503 }
504
505 //////////////// THE RULES CONTAINER /////////////////////
506 /*!
507 \brief Returns a const reference to the container of \l FragmentationRule
508 instances.
509 */
510 const std::vector<FragmentationRuleSPtr> &
511 309 FragmentationPathway::getRulesCstRef() const
512 {
513 309 return m_rules;
514 }
515
516 /*!
517 \brief Returns a reference to the container of \l FragmentationRule instances.
518 */
519 std::vector<FragmentationRuleSPtr> &
520 3 FragmentationPathway::getRulesRef()
521 {
522 3 return m_rules;
523 }
524
525
526 //////////////// OPERATORS /////////////////////
527 /*!
528 \brief Assigns \a other to this FragmentationPathway instance.
529
530 Returns a reference to this FragmentationPathway.
531
532 Validation of this instance occurs after member data initialization and the
533 result of the validation process is set to member datum m_isValid.
534
535 \sa validate()
536 */
537 FragmentationPathway &
538 3 FragmentationPathway::operator=(const FragmentationPathway &other)
539 {
540
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(&other == this)
541 return *this;
542
543 3 mcsp_polChemDef = other.mcsp_polChemDef;
544
545 3 m_name = other.m_name;
546 3 m_formula = other.m_formula;
547 3 m_fragEnd = other.m_fragEnd;
548 3 m_monomerContribution = other.m_monomerContribution;
549 3 m_comment = other.m_comment;
550 3 m_rules = other.m_rules;
551
552 3 ErrorList error_list;
553
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_isValid = validate(&error_list);
554
555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(!m_isValid)
556 qCritical()
557 << "Upon assignment-configuration of the FragmentationPathway, the "
558 "instance failed to "
559 "validate with errors:"
560 << Utils::joinErrorList(error_list, ", ");
561
562 3 return *this;
563 3 }
564
565 /*!
566 \brief Returns true if \c this and \a other are identical.
567
568 The member PolChemDef is compared deeply and the member rules also.
569
570 Because the FragmentationRule instances are not a reference to the PolChemDef,
571 their comparison is deep.
572 */
573 bool
574 14 FragmentationPathway::operator==(const FragmentationPathway &other) const
575 {
576
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14 if(&other == this)
577 return true;
578
579 // We cannot do this, because each chemical entity that references the
580 // polymer chemistry definition will call this function and an
581 // infinite loop ensues.
582 // if(!mcsp_polChemDef->isChemicallyEquivalent(*other.mcsp_polChemDef))
583 // return false;
584
585
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 12 times.
24 if(!(m_name == other.m_name && m_formula == other.m_formula &&
586
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 m_fragEnd == other.m_fragEnd &&
587
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 m_monomerContribution == other.m_monomerContribution &&
588
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 m_comment == other.m_comment && m_rules.size() == other.m_rules.size()))
589 return false;
590
591
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 7 times.
19 for(std::size_t iter = 0; iter < m_rules.size(); ++iter)
592
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if((*m_rules.at(iter)) != (*other.m_rules.at(iter)))
593 return false;
594
595 return true;
596 }
597
598 /*!
599 \brief Returns true if \c this and \a other are different.
600
601 Returns the negated result of operator==().
602 */
603 bool
604 11 FragmentationPathway::operator!=(const FragmentationPathway &other) const
605 {
606
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 2 times.
11 if(&other == this)
607 return false;
608
609 9 return !operator==(other);
610 }
611
612 //////////////// RULES /////////////////////
613 /*!
614 \brief Adds the \a frag_rule_sp FragmentationRule instance to the member
615 container of FragmentationRule instances.
616
617 Validation of this instance occurs after member data initialization and the
618 result of the validation process is set to member datum m_isValid.
619 */
620 void
621 14 FragmentationPathway::addRule(FragmentationRuleSPtr frag_rule_sp)
622 {
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(frag_rule_sp == nullptr || frag_rule_sp.get() == nullptr)
624 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
625
626 14 m_rules.push_back(frag_rule_sp);
627
628 14 ErrorList error_list;
629
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 m_isValid = validate(&error_list);
630
631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(!m_isValid)
632 qCritical()
633 << "Upon assignment-configuration of the FragmentationPathway, the "
634 "instance failed to "
635 "validate with errors:"
636 << Utils::joinErrorList(error_list, ", ");
637 14 }
638
639
640 /*!
641 \brief Inserts in the container of FragmentationRule instances at \a index the
642 \a frag_rule_sp FragmentationRule instance. If index is the container size, the
643 FragmentationRule is added at the end of the container.
644
645 Validation of this instance occurs after member data initialization and the
646 result of the validation process is set to member datum m_isValid.
647 */
648 void
649 4 FragmentationPathway::insertRuleAt(FragmentationRuleSPtr frag_rule_sp,
650 std::size_t index)
651 {
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(frag_rule_sp == nullptr || frag_rule_sp.get() == nullptr)
653 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
654
655
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(index > m_rules.size())
656 qFatalStream() << "Programming error. Index out of bounds.";
657
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(index == m_rules.size())
659 addRule(frag_rule_sp);
660
661 4 m_rules.insert(m_rules.cbegin() + index, frag_rule_sp);
662
663 4 ErrorList error_list;
664
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_isValid = validate(&error_list);
665
666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(!m_isValid)
667 qCritical()
668 << "Upon assignment-configuration of the FragmentationPathway, the "
669 "instance failed to "
670 "validate with errors:"
671 << Utils::joinErrorList(error_list, ", ");
672 4 }
673
674 /*!
675 \brief Removes from the container of FragmentationRule instances the item at
676 index \a index.
677 */
678 void
679 3 FragmentationPathway::removeRuleAt(size_t index)
680 {
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(index >= m_rules.size())
682 qFatalStream() << "Programming error. Index is out of bounds.";
683
684 3 m_rules.erase(m_rules.begin() + index);
685 3 }
686
687
688 //////////////// VALIDATIONS /////////////////////
689 /*!
690 \brief Returns the FragmentationPathway instance from the polymer chemistry
691 definition registered in this instance.
692
693 The key to search the FragmentationPathway is this instance's name member.
694
695 If there is no PolChemDef available, nullptr is returned.
696
697 If no FragmentationPathway instance is found by this instance's name, nullptr is
698 returned.
699 */
700 FragmentationPathwayCstSPtr
701 1 FragmentationPathway::getFromPolChemDefByName() const
702 {
703
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
704 return nullptr;
705
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(m_name.isEmpty())
707 return nullptr;
708
709 1 return mcsp_polChemDef->getFragmentationPathwayCstSPtrByName(m_name);
710 }
711
712 /*!
713 \brief Returns the status of this FragmentationPathway instance the polymer
714 chemistry definition registered in this instance.
715
716 The key to search the FragmentationPathway is this instance's name member.
717
718 If there is no PolChemDef available,
719 Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned.
720
721 If no FragmentationPathway instance is found by this instance's name,
722 Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise
723 Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned.
724 */
725 Enums::PolChemDefEntityStatus
726 1 FragmentationPathway::isKnownByNameInPolChemDef() const
727 {
728
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
729 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
730
731
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 if(mcsp_polChemDef->getFragmentationPathwayCstSPtrByName(m_name) != nullptr)
732 1 return Enums::PolChemDefEntityStatus::ENTITY_KNOWN;
733
734 return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN;
735 }
736
737
738 /*!
739 \brief Validates the FragmentationPathway, recording any error with a message in
740 \a error_list_p.
741
742 The validation involves checking that:
743
744 \list
745
746 \li The polymer chemistry definition is available (non-nullptr)
747
748 \li The name is not empty
749
750 \li The formula is not empty and validates successfully
751
752 \li Any defined fragmentation rule validates successfully
753
754 \endlist
755
756 If the validation is successful, m_isValid is set to true, otherwise it is set
757 to false.
758
759 Returns the outcome of the validation.
760 */
761 bool
762 653 FragmentationPathway::validate(ErrorList *error_list_p) const
763 {
764
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 637 times.
653 qsizetype error_count = error_list_p->size();
765
766
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 637 times.
653 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
767 {
768 32 qCritical()
769 << "A FragmentationPathway with no PolChemDef available cannot "
770
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 "validate successfully.";
771 32 error_list_p->push_back(
772 "A FragmentationPathway with no PolChemDef available cannot validate "
773 "successfully");
774 }
775
776
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 627 times.
653 if(m_name.isEmpty())
777 {
778 52 qCritical()
779
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 << "A FragmentationPathway with no name cannot validate successfully.";
780 52 error_list_p->push_back(
781 "A FragmentationPathway with no name cannot validate successfully");
782 }
783
784
2/2
✓ Branch 2 taken 34 times.
✓ Branch 3 taken 619 times.
653 if(m_formula.getActionFormula().isEmpty())
785 {
786 68 qCritical() << "A FragmentationPathway with no formula cannot "
787
1/2
✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
34 "validate successfully.";
788 68 error_list_p->push_back(
789 "A FragmentationPathway with no formula cannot validate "
790 "successfully");
791 }
792
3/4
✓ Branch 0 taken 619 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 618 times.
1238 else if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr &&
793
3/4
✓ Branch 2 taken 619 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 618 times.
1238 !m_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
794 error_list_p))
795 {
796 2 qCritical() << "A FragmentationPathway with an invalid formula cannot "
797
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "validate successfully.";
798 2 error_list_p->push_back(
799 "A FragmentationPathway with an invalid formula cannot validate "
800 "successfully");
801 }
802
803 // Do not test contribution, that might be any value.
804 // Comment is optional
805
806 // FragmentationRule instances are optional, but if there, they should
807 // validate successfully.
808
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 653 times.
703 for(const FragmentationRuleSPtr &frag_rule_sp : m_rules)
809 {
810
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
50 if(!frag_rule_sp->validate(error_list_p))
811 {
812 qCritical() << "A FragmentationPathway with an invalid fragmentation "
813 "rule cannot "
814 "validate successfully.";
815 error_list_p->push_back(
816 "A FragmentationPathway with an invalid fragmentation rule cannot "
817 "validate "
818 "successfully");
819 }
820 }
821
822 653 m_isValid = error_list_p->size() > error_count ? false : true;
823
824 653 return m_isValid;
825 }
826
827 /*!
828 \brief Returns the validity status of this FragmentationPathway.
829 */
830 bool
831 496 FragmentationPathway::isValid() const
832 {
833 496 return m_isValid;
834 }
835
836
837 //////////////// UTILITIES /////////////////////
838
839 /*!
840 \brief Returns a string with the textual representation of this
841 FragmentationPathway instance.
842 */
843 QString
844 1 FragmentationPathway::toString() const
845 {
846 1 QString text = "Fragmentation pathway:\n";
847
848
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 QString frag_end_string = (*(fragEndMap.find(m_fragEnd))).second;
849
850 1 text +=
851
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 QString("Name: %1 - Formula: %2 - Frag end: %3 - Monomer contribution: %4")
852
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(m_name)
853
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 .arg(m_formula.getActionFormula(/*with_title*/ true))
854
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(frag_end_string)
855
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 .arg(m_monomerContribution);
856
857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(m_rules.size())
858 {
859 text += " - Rules:\n";
860 for(const FragmentationRuleCstSPtr frag_rule_sp : m_rules)
861 {
862 text += frag_rule_sp->toString();
863 }
864 text += "\n";
865 }
866 else
867
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 text += "\n";
868
869
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
2 text += QString("isValid: %1\n").arg(m_isValid ? "true" : "false");
870
871 1 return text;
872 1 }
873
874 //////////////// XML DATA LOADING WRITING /////////////////////
875 /*!
876 \brief Parses a fragmentation specification XML \a element using a
877 \a{version}ed function.
878
879 This function is used for polymer chemistry definition documents of version less
880 than 2 (the XML element tag is <fgs>, for FragSpec class).
881
882 Upon parsing and validation of the parsed data, the member data are updated,
883 thus essentially initializing this FragmentationPathway instance.
884
885 Validation of this instance occurs after member data initialization.
886
887 Returns true if parsing and formula validation were successful, false otherwise.
888
889 \sa renderXmlFgpElement(), validate()
890 */
891 bool
892 471 FragmentationPathway::renderXmlFgsElement(const QDomElement &element,
893 [[maybe_unused]] int version)
894 {
895 471 QDomElement child;
896
1/2
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
471 QDomElement rule_child_element;
897
898 471 bool comment_was_parsed = false;
899
900 /* The xml node we are in is structured this way:
901 *
902 <fgs>
903 <name>a</name>
904 <end>LE</end>
905 <formula>-C1O1</formula>
906 <sidechaincontrib>0</sidechaincontrib>
907 <comment>opt_comment</comment>
908 <fgr>
909 <name>a-fgr-1</name>
910 <formula>+H200</formula>
911 <prev-mnm-code>E</prev-mnm-code>
912 <curr-mnm-code>D</curr-mnm-code>
913 <next-mnm-code>F</next-mnm-code>
914 <comment>opt_comment</comment>
915 </fgr>
916 <fgr>
917 <name>a-fgr-2</name>
918 <formula>+H100</formula>
919 <prev-mnm-code>F</prev-mnm-code>
920 <curr-mnm-code>D</curr-mnm-code>
921 <next-mnm-code>E</next-mnm-code>
922 <comment>opt_comment</comment>
923 </fgr>
924 </fgs>
925 *
926 * And the element parameter points to the
927 *
928 * <fgs> element tag:
929 * ^
930 * |
931 * +----- here we are right now.
932 *
933 * Which means that element.tagName() == "fgs" and that
934 * we'll have to go one step down to the first child of the
935 * current node in order to get to the <name> element.
936 *
937 * The DTD says this:
938 * <!ELEMENT fgs(name, end, formula, comment?, fgr*)>
939 */
940
941
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 471 times.
942 if(element.tagName() != "fgs")
942 {
943 qCritical() << "The expected <fgs> element is not found.";
944 m_isValid = false;
945 return m_isValid;
946 }
947
948
3/6
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 471 times.
✗ Branch 8 not taken.
942 child = element.firstChildElement("name");
949
950
5/10
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 471 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 471 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 471 times.
942 if(child.isNull() || child.text().isEmpty())
951 {
952 qCritical() << "The FragSpec did not render correctly: problem with the "
953 "<name> element.";
954 m_isValid = false;
955 return m_isValid;
956 }
957
1/2
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
471 m_name = child.text();
958 // qDebug() << "The name:" << m_name;
959
960
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
942 child = child.nextSiblingElement();
961
962
8/18
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 471 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 471 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 471 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 471 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 471 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 471 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
1413 if(child.isNull() || child.text().isEmpty() || child.tagName() != "end")
963 {
964 qCritical() << "The FragSpec did not render correctly: problem with the "
965 "<end> element.";
966 m_isValid = false;
967 return m_isValid;
968 }
969
970
3/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 67 times.
✓ Branch 5 taken 404 times.
942 if(child.text() == "NE")
971 67 m_fragEnd = Enums::FragEnd::NE;
972
3/4
✓ Branch 1 taken 404 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 203 times.
✓ Branch 5 taken 201 times.
808 else if(child.text() == "LE")
973 203 m_fragEnd = Enums::FragEnd::LE;
974
2/4
✓ Branch 1 taken 201 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 201 times.
✗ Branch 5 not taken.
402 else if(child.text() == "RE")
975 201 m_fragEnd = Enums::FragEnd::RE;
976 else if(child.text() == "BE")
977 m_fragEnd = Enums::FragEnd::BE;
978 else
979 {
980 qCritical() << "The FragSpec did not render correctly: problem with the "
981 "<end> element's text value.";
982 m_isValid = false;
983 return m_isValid;
984 }
985
986
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
942 child = child.nextSiblingElement();
987
988
8/18
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 471 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 471 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 471 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 471 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 471 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 471 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
1413 if(child.isNull() || child.text().isEmpty() || child.tagName() != "formula")
989 {
990 qCritical() << "The FragSpec did not render correctly: problem with the "
991 "<formula> element.";
992 m_isValid = false;
993 return m_isValid;
994 }
995
996
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 471 times.
471 if(!m_formula.renderXmlFormulaElement(child))
997 {
998 qCritical()
999 << "The FragSpec did not render correctly: the formula did not "
1000 "render correctly.";
1001 m_isValid = false;
1002 return m_isValid;
1003 }
1004
1005 // The next element must be <sidechaincontrib>
1006
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
942 child = child.nextSiblingElement();
1007
5/10
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 471 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 471 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 471 times.
1413 if(child.isNull() || child.text().isEmpty() ||
1008
3/8
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 471 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
1413 child.tagName() != "sidechaincontrib")
1009 {
1010 qCritical() << "The FragSpec did not render correctly: problem with the "
1011 "<sidechaincontrib> element.";
1012 m_isValid = false;
1013 return m_isValid;
1014 }
1015
1/2
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
471 QString text = child.text();
1016 471 bool ok = false;
1017
1/2
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
471 m_monomerContribution = text.toInt(&ok);
1018
1019
2/4
✓ Branch 0 taken 471 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 471 times.
471 if(!m_monomerContribution && !ok)
1020 {
1021 qCritical() << "The FragSpec did not render correctly: problem with the "
1022 "<sidechaincontrib> element's value.";
1023 m_isValid = false;
1024 return m_isValid;
1025 }
1026
1027 // The next element might be either comment or(none, one or more)
1028 // fgr.
1029
2/4
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 471 times.
✗ Branch 5 not taken.
942 child = child.nextSiblingElement();
1030
1031
3/4
✓ Branch 1 taken 609 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 138 times.
✓ Branch 4 taken 471 times.
609 while(!child.isNull())
1032 {
1033 // Is it a comment or the first of one|more <fgr> elements ?
1034 // Remember: <!ELEMENT fgs(name, end, formula, comment?, fgr*)>
1035
1036
3/4
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 136 times.
✓ Branch 5 taken 2 times.
276 if(child.tagName() == "comment")
1037 {
1038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 136 times.
136 if(comment_was_parsed)
1039 {
1040 qCritical()
1041 << "The FragSpec did not render correctly: problem with "
1042 "multiple <comment> elements.";
1043 m_isValid = false;
1044 return m_isValid;
1045 }
1046
1047
1/2
✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
136 m_comment = child.text();
1048 136 comment_was_parsed = true;
1049
1050
2/4
✓ Branch 1 taken 136 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 136 times.
✗ Branch 5 not taken.
272 child = child.nextSiblingElement();
1051 136 continue;
1052 }
1053
1054 // At this point, yes, if there is still a sibling, then it
1055 // has to be one <fgr>, either alone or the first of multiple.
1056
1057
3/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
4 while(!child.isNull())
1058 {
1059 2 FragmentationRuleSPtr frag_rule_sp =
1060
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 std::make_shared<FragmentationRule>(mcsp_polChemDef, child, 1);
1061
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if(!frag_rule_sp->isValid())
1062 {
1063 qCritical()
1064 << "The FragSpec did not render correctly: problem with "
1065 "a <fgr> FragRule element.";
1066 frag_rule_sp.reset();
1067 m_isValid = false;
1068 return m_isValid;
1069 }
1070
1071
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m_rules.push_back(frag_rule_sp);
1072
1073
3/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
4 child = child.nextSiblingElement();
1074 2 }
1075 }
1076
1077 471 ErrorList error_list;
1078
1/2
✓ Branch 1 taken 471 times.
✗ Branch 2 not taken.
471 m_isValid = validate(&error_list);
1079
1080
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 471 times.
471 if(!m_isValid)
1081 qCritical() << "Failed to validate FragSpec instance right after "
1082 "rendering it from <fgs> XML element, with errors:"
1083 << Utils::joinErrorList(error_list, ", ");
1084
1085 471 return m_isValid;
1086 1413 }
1087
1088
1089 /*!
1090 \brief Parses a fragmentation pathway XML \a element using a
1091 \a{version}ed function.
1092
1093 This function is used for polymer chemistry definition documents of version
1094 greater or equal to 2 (the XML element tag is <fgp>, for FragmentationPathway
1095 class).
1096
1097 Upon parsing and validation of the parsed data, the member data are updated,
1098 thus essentially initializing this FragmentationPathway instance.
1099
1100 Validation of this instance occurs after member data initialization.
1101
1102 Returns true if parsing and formula validation were successful, false otherwise.
1103
1104 \sa renderXmlFgsElement(), validate()
1105 */
1106 bool
1107 7 FragmentationPathway::renderXmlFgpElement(const QDomElement &element,
1108 [[maybe_unused]] int version)
1109 {
1110 7 QDomElement child;
1111
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 QDomElement rule_child_element;
1112
1113 7 bool comment_was_parsed = false;
1114
1115 /* The xml node we are in is structured this way:
1116 *
1117 <fgp>
1118 <name>a</name>
1119 <end>LE</end>
1120 <formula>-C1O1</formula>
1121 <sidechaincontrib>0</sidechaincontrib>
1122 <comment>opt_comment</comment>
1123 <fgr>
1124 <name>a-fgr-1</name>
1125 <formula>+H200</formula>
1126 <prev-mnm-code>E</prev-mnm-code>
1127 <curr-mnm-code>D</curr-mnm-code>
1128 <next-mnm-code>F</next-mnm-code>
1129 <comment>opt_comment</comment>
1130 </fgr>
1131 <fgr>
1132 <name>a-fgr-2</name>
1133 <formula>+H100</formula>
1134 <prev-mnm-code>F</prev-mnm-code>
1135 <curr-mnm-code>D</curr-mnm-code>
1136 <next-mnm-code>E</next-mnm-code>
1137 <comment>opt_comment</comment>
1138 </fgr>
1139 </fgs>
1140 *
1141 * And the element parameter points to the
1142 *
1143 * <fgp> element tag:
1144 * ^
1145 * |
1146 * +----- here we are right now.
1147 *
1148 * Which means that element.tagName() == "fgs" and that
1149 * we'll have to go one step down to the first child of the
1150 * current node in order to get to the <name> element.
1151 *
1152 * The DTD says this:
1153 * <!ELEMENT fgs(name, end, formula, comment?, fgr*)>
1154 */
1155
1156
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
14 if(element.tagName() != "fgp")
1157 {
1158 qCritical() << "The expected <fgp> element is not found.";
1159 m_isValid = false;
1160 return m_isValid;
1161 }
1162
1163
3/6
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
14 child = element.firstChildElement("name");
1164
1165
5/10
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 7 times.
14 if(child.isNull() || child.text().isEmpty())
1166 {
1167 qCritical() << "The FragmentationPathway did not render correctly: "
1168 "problem with the "
1169 "<name> element.";
1170 m_isValid = false;
1171 return m_isValid;
1172 }
1173
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_name = child.text();
1174 // qDebug() << "The name:" << m_name;
1175
1176
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 child = child.nextSiblingElement();
1177
1178
8/18
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 7 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
21 if(child.isNull() || child.text().isEmpty() || child.tagName() != "end")
1179 {
1180 qCritical() << "The FragmentationPathway did not render correctly: "
1181 "problem with the "
1182 "<end> element.";
1183 m_isValid = false;
1184 return m_isValid;
1185 }
1186
1187
3/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
14 if(child.text() == "NE")
1188 1 m_fragEnd = Enums::FragEnd::NE;
1189
3/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
12 else if(child.text() == "LE")
1190 3 m_fragEnd = Enums::FragEnd::LE;
1191
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 else if(child.text() == "RE")
1192 3 m_fragEnd = Enums::FragEnd::RE;
1193 else if(child.text() == "BE")
1194 m_fragEnd = Enums::FragEnd::BE;
1195 else
1196 {
1197 qCritical() << "The FragmentationPathway did not render correctly: "
1198 "problem with the "
1199 "<end> element's text value.";
1200 m_isValid = false;
1201 return m_isValid;
1202 }
1203
1204
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 child = child.nextSiblingElement();
1205
1206
8/18
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 7 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 7 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 7 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 7 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
21 if(child.isNull() || child.text().isEmpty() || child.tagName() != "formula")
1207 {
1208 qCritical() << "The FragmentationPathway did not render correctly: "
1209 "problem with the "
1210 "<formula> element.";
1211 m_isValid = false;
1212 return m_isValid;
1213 }
1214
1215
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
7 if(!m_formula.renderXmlFormulaElement(child))
1216 {
1217 qCritical() << "The FragmentationPathway did not render correctly: the "
1218 "formula did not "
1219 "render correctly.";
1220 m_isValid = false;
1221 return m_isValid;
1222 }
1223
1224 // The next element must be <sidechaincontrib>
1225
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 child = child.nextSiblingElement();
1226
5/10
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 7 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 7 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 7 times.
21 if(child.isNull() || child.text().isEmpty() ||
1227
3/8
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
21 child.tagName() != "sidechaincontrib")
1228 {
1229 qCritical() << "The FragmentationPathway did not render correctly: "
1230 "problem with the "
1231 "<sidechaincontrib> element.";
1232 m_isValid = false;
1233 return m_isValid;
1234 }
1235
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 QString text = child.text();
1236 7 bool ok = false;
1237
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_monomerContribution = text.toInt(&ok);
1238
1239
2/4
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
7 if(!m_monomerContribution && !ok)
1240 {
1241 qCritical() << "The FragmentationPathway did not render correctly: "
1242 "problem with the "
1243 "<sidechaincontrib> element's value.";
1244 m_isValid = false;
1245 return m_isValid;
1246 }
1247
1248 // The next element might be either comment or(none, one or more)
1249 // fgr.
1250
2/4
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 7 times.
✗ Branch 5 not taken.
14 child = child.nextSiblingElement();
1251
1252
3/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 7 times.
9 while(!child.isNull())
1253 {
1254 // Is it a comment or the first of one|more <fgr> elements ?
1255 // Remember: <!ELEMENT fgs(name, end, formula, comment?, fgr*)>
1256
1257
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 if(child.tagName() == "comment")
1258 {
1259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(comment_was_parsed)
1260 {
1261 qCritical() << "The FragmentationPathway did not render "
1262 "correctly: problem with "
1263 "multiple <comment> elements.";
1264 m_isValid = false;
1265 return m_isValid;
1266 }
1267
1268
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m_comment = child.text();
1269 2 comment_was_parsed = true;
1270
1271
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 child = child.nextSiblingElement();
1272 2 continue;
1273 }
1274
1275 // At this point, yes, if there is still a sibling, then it
1276 // has to be one <fgr>, either alone or the first of multiple.
1277
1278 while(!child.isNull())
1279 {
1280 FragmentationRuleSPtr frag_rule_sp =
1281 std::make_shared<FragmentationRule>(mcsp_polChemDef, child, 1);
1282 if(!frag_rule_sp->isValid())
1283 {
1284 qCritical() << "The FragmentationPathway did not render "
1285 "correctly: problem with "
1286 "a <fgr> FragmentationRule element.";
1287 frag_rule_sp.reset();
1288 m_isValid = false;
1289 return m_isValid;
1290 }
1291
1292 m_rules.push_back(frag_rule_sp);
1293
1294 child = child.nextSiblingElement();
1295 }
1296 }
1297
1298 7 ErrorList error_list;
1299
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_isValid = validate(&error_list);
1300
1301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if(!m_isValid)
1302 qCritical()
1303 << "Failed to validate FragmentationPathway instance right after "
1304 "rendering it from <fgp> XML element, with errors:"
1305 << Utils::joinErrorList(error_list, ", ");
1306
1307 7 return m_isValid;
1308 21 }
1309
1310 /*!
1311 \brief Formats a string representing this FragmentationPathway instance suitable
1312 to use as an XML element.
1313
1314 The typical fragmentation pathway element that is generated in this function
1315 looks like this:
1316
1317 \code
1318 <fgp>
1319 <name>a</name>
1320 <end>LE</end>
1321 <formula>-C1O1</formula>
1322 <sidechaincontrib>0</sidechaincontrib>
1323 <comment>opt_comment</comment>
1324 <fgr>
1325 <name>a-fgr-1</name>
1326 <formula>+H200</formula>
1327 <prev-mnm-code>E</prev-mnm-code>
1328 <curr-mnm-code>D</curr-mnm-code>
1329 <next-mnm-code>F</next-mnm-code>
1330 <comment>opt_comment</comment>
1331 </fgr>
1332 <fgr>
1333 <name>a-fgr-2</name>
1334 <formula>+H100</formula>
1335 <prev-mnm-code>F</prev-mnm-code>
1336 <curr-mnm-code>D</curr-mnm-code>
1337 <next-mnm-code>E</next-mnm-code>
1338 <comment>opt_comment</comment>
1339 </fgr>
1340 </fgp>
1341 \endcode
1342
1343 The formatting of the XML element takes into account \a offset and \a
1344 indent by prepending the string with \a offset * \a indent character substring.
1345
1346 \a indent defaults to two spaces.
1347
1348 Returns a dynamically allocated string that needs to be freed after use.
1349 */
1350 QString
1351 8 FragmentationPathway::formatXmlFgpElement(int offset,
1352 const QString &indent) const
1353 {
1354
1355 8 int newOffset;
1356 8 int iter = 0;
1357
1358 8 QString lead("");
1359 8 QString text;
1360
1361
1362 // Prepare the lead.
1363 8 newOffset = offset;
1364
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
30 while(iter < newOffset)
1365 {
1366
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 lead += indent;
1367 22 ++iter;
1368 }
1369
1370
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 text += QString("%1<fgp>\n").arg(lead);
1371
1372 // Prepare the lead.
1373 8 ++newOffset;
1374 8 lead.clear();
1375 8 iter = 0;
1376
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 8 times.
46 while(iter < newOffset)
1377 {
1378
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 lead += indent;
1379 30 ++iter;
1380 }
1381
1382 // Continue, with indented elements.
1383
1384
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
16 text += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1385
4/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
16 text += QString("%1<end>%2</end>\n").arg(lead).arg(fragEndMap[m_fragEnd]);
1386
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 text += QString("%1<formula>%2</formula>\n")
1387
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 .arg(lead)
1388
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 .arg(m_formula.getActionFormula());
1389
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 text += QString("%1<sidechaincontrib>%2</sidechaincontrib>\n")
1390
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 .arg(lead)
1391
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
8 .arg(m_monomerContribution);
1392
1393
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if(!m_comment.isEmpty())
1394
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
6 text += QString("%1<comment>%2</comment>\n").arg(lead).arg(m_comment);
1395
1396
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 for(const FragmentationRuleSPtr &frag_rule_sp : m_rules)
1397
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 text += frag_rule_sp->formatXmlFgrElement(newOffset);
1398
1399 // Prepare the lead.
1400 8 --newOffset;
1401 8 lead.clear();
1402 8 iter = 0;
1403
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 8 times.
38 while(iter < newOffset)
1404 {
1405
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 lead += indent;
1406 22 ++iter;
1407 }
1408
1409
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 text += QString("%1</fgp>\n").arg(lead);
1410
1411 8 return text;
1412 8 }
1413
1414
1415 } // namespace libXpertMassCore
1416 } // namespace MsXpS
1417