GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/FragmentationPathway.cpp
Date: 2025-11-19 15:26:11
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 1428 FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp,
176 const QDomElement &element,
177 1428 int version)
178
3/6
✓ Branch 2 taken 1428 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1428 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 1428 times.
1428 : mcsp_polChemDef(pol_chem_def_csp)
179 {
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1428 times.
1428 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
181 qCritical() << "Constructing FragmentationPathway with no PolChemDef.";
182
183
2/2
✓ Branch 0 taken 1407 times.
✓ Branch 1 taken 21 times.
1428 if(version == 1)
184 {
185
2/4
✓ Branch 1 taken 1407 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1407 times.
1407 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 21 times.
✗ Branch 1 not taken.
21 else if(version == 2)
191 {
192
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
21 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.
1428 }
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 114 FragmentationPathway::FragmentationPathway(PolChemDefCstSPtr pol_chem_def_csp,
229 const QString &name,
230 const QString &formula_string,
231 Enums::FragEnd frag_end,
232 114 const QString &comment)
233 228 : mcsp_polChemDef(pol_chem_def_csp),
234
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 33 times.
114 m_name(name),
235
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 m_formula(Formula(formula_string)),
236 114 m_fragEnd(frag_end),
237
3/4
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 36 times.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
306 m_comment(comment)
238 {
239 114 qDebug() << "Constructing FragmentationPathway with name:" << m_name;
240
241 114 ErrorList error_list;
242
1/2
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
114 m_isValid = validate(&error_list);
243
244
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 78 times.
114 if(!m_isValid)
245
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
72 qCritical() << "Upon construction of the FragmentationPathway, the "
246 "instance failed to "
247
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 "validate with errors:"
248
3/6
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 36 times.
✗ Branch 8 not taken.
72 << Utils::joinErrorList(error_list, ", ");
249
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
114 }
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 228 FragmentationPathway::FragmentationPathway(const FragmentationPathway &other)
261 456 : mcsp_polChemDef(other.mcsp_polChemDef),
262
1/2
✓ Branch 0 taken 228 times.
✗ Branch 1 not taken.
228 m_name(other.m_name),
263
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 m_formula(other.m_formula),
264 228 m_fragEnd(other.m_fragEnd),
265 228 m_monomerContribution(other.m_monomerContribution),
266
3/4
✓ Branch 1 taken 198 times.
✓ Branch 2 taken 30 times.
✓ Branch 4 taken 228 times.
✗ Branch 5 not taken.
654 m_comment(other.m_comment)
267 {
268
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 m_rules = other.m_rules;
269
270 228 ErrorList error_list;
271
1/2
✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
228 m_isValid = validate(&error_list);
272
273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 228 times.
228 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.
228 }
279
280
281 /*!
282 \brief Destroys this FragmentationPathway instance.
283 */
284 684 FragmentationPathway::~FragmentationPathway()
285 {
286 684 m_rules.clear();
287
1/2
✓ Branch 4 taken 342 times.
✗ Branch 5 not taken.
684 }
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 21 FragmentationPathway::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp)
301 {
302 21 mcsp_polChemDef = pol_chem_def_csp;
303
304 21 ErrorList error_list;
305
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_isValid = validate(&error_list);
306
307
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 if(!m_isValid)
308
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
42 qCritical() << "Upon setting PolChemDef of FragmentationPathway, the "
309 "instance failed to "
310
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 "validate with errors:"
311
3/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 << Utils::joinErrorList(error_list, ", ");
312 21 }
313
314 /*!
315 \brief Returns the PolChemDef (polymer chemistry definition).
316 */
317 PolChemDefCstSPtr
318 9 FragmentationPathway::getPolchemDefCstSPtr() const
319 {
320 9 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 18 FragmentationPathway::setName(const QString &name)
334 {
335 18 m_name = name;
336
337 18 ErrorList error_list;
338
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 m_isValid = validate(&error_list);
339
340
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if(!m_isValid)
341
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
36 qCritical()
342 << "Upon setting name of FragmentationPathway, the instance failed to "
343
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 "validate with errors:"
344
3/6
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
36 << Utils::joinErrorList(error_list, ", ");
345 18 }
346
347 /*!
348 \brief Returns the name.
349 */
350 const QString &
351 3462 FragmentationPathway::getName() const
352 {
353 3462 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 9 FragmentationPathway::setFormula(const Formula &formula)
367 {
368 9 m_formula = formula;
369
370 9 ErrorList error_list;
371
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 m_isValid = validate(&error_list);
372
373
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 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 9 }
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 12 FragmentationPathway::setFormula(const QString &formula_string)
390 {
391
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 m_formula = Formula(formula_string);
392
393 12 ErrorList error_list;
394
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 m_isValid = validate(&error_list);
395
396
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 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 12 }
402
403 /*!
404 \brief Returns a const reference to the member \l Formula.
405 */
406 const Formula &
407 1734 FragmentationPathway::getFormulaCstRef() const
408 {
409 1734 return m_formula;
410 }
411
412 /*!
413 \brief Returns a reference to the member \l Formula.
414 */
415 Formula &
416 9 FragmentationPathway::getFormulaRef()
417 {
418 9 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 15 FragmentationPathway::setFragEnd(Enums::FragEnd frag_end)
435 {
436 15 m_fragEnd = frag_end;
437 15 }
438
439
440 /*!
441 \brief Returns the \l Enums::FragEnd.
442 */
443 Enums::FragEnd
444 111 FragmentationPathway::getFragEnd() const
445 {
446 111 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 855 FragmentationPathway::getMonomerContribution() const
482 {
483 855 return m_monomerContribution;
484 }
485
486 //////////////// THE COMMENT /////////////////////
487 /*!
488 \brief Sets the comment to \a comment.
489 */
490 void
491 6 FragmentationPathway::setComment(const QString &comment)
492 {
493 6 m_comment = comment;
494 6 }
495
496 /*!
497 \brief Returns the comment.
498 */
499 QString
500 15 FragmentationPathway::getComment() const
501 {
502
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 3 times.
15 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 927 FragmentationPathway::getRulesCstRef() const
512 {
513 927 return m_rules;
514 }
515
516 /*!
517 \brief Returns a reference to the container of \l FragmentationRule instances.
518 */
519 std::vector<FragmentationRuleSPtr> &
520 9 FragmentationPathway::getRulesRef()
521 {
522 9 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 9 FragmentationPathway::operator=(const FragmentationPathway &other)
539 {
540
1/2
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
9 if(&other == this)
541 return *this;
542
543 9 mcsp_polChemDef = other.mcsp_polChemDef;
544
545 9 m_name = other.m_name;
546 9 m_formula = other.m_formula;
547 9 m_fragEnd = other.m_fragEnd;
548 9 m_monomerContribution = other.m_monomerContribution;
549 9 m_comment = other.m_comment;
550 9 m_rules = other.m_rules;
551
552 9 ErrorList error_list;
553
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 m_isValid = validate(&error_list);
554
555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 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 9 return *this;
563 9 }
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 42 FragmentationPathway::operator==(const FragmentationPathway &other) const
575 {
576
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 36 times.
42 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 36 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 36 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 36 times.
72 if(!(m_name == other.m_name && m_formula == other.m_formula &&
586
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 m_fragEnd == other.m_fragEnd &&
587
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 m_monomerContribution == other.m_monomerContribution &&
588
2/4
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
36 m_comment == other.m_comment && m_rules.size() == other.m_rules.size()))
589 return false;
590
591
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 21 times.
57 for(std::size_t iter = 0; iter < m_rules.size(); ++iter)
592
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
21 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 33 FragmentationPathway::operator!=(const FragmentationPathway &other) const
605 {
606
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 6 times.
33 if(&other == this)
607 return false;
608
609 27 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 42 FragmentationPathway::addRule(FragmentationRuleSPtr frag_rule_sp)
622 {
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 if(frag_rule_sp == nullptr || frag_rule_sp.get() == nullptr)
624 qFatalStream() << "Programming error. Pointer cannot be nullptr.";
625
626 42 m_rules.push_back(frag_rule_sp);
627
628 42 ErrorList error_list;
629
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 m_isValid = validate(&error_list);
630
631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42 times.
42 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 42 }
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 12 FragmentationPathway::insertRuleAt(FragmentationRuleSPtr frag_rule_sp,
650 std::size_t index)
651 {
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 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 12 times.
12 if(index > m_rules.size())
656 qFatalStream() << "Programming error. Index out of bounds.";
657
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(index == m_rules.size())
659 addRule(frag_rule_sp);
660
661 12 m_rules.insert(m_rules.cbegin() + index, frag_rule_sp);
662
663 12 ErrorList error_list;
664
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 m_isValid = validate(&error_list);
665
666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 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 12 }
673
674 /*!
675 \brief Removes from the container of FragmentationRule instances the item at
676 index \a index.
677 */
678 void
679 9 FragmentationPathway::removeRuleAt(size_t index)
680 {
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(index >= m_rules.size())
682 qFatalStream() << "Programming error. Index is out of bounds.";
683
684 9 m_rules.erase(m_rules.begin() + index);
685 9 }
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 3 FragmentationPathway::getFromPolChemDefByName() const
702 {
703
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
704 return nullptr;
705
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(m_name.isEmpty())
707 return nullptr;
708
709 3 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 3 FragmentationPathway::isKnownByNameInPolChemDef() const
727 {
728
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
729 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
730
731
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
6 if(mcsp_polChemDef->getFragmentationPathwayCstSPtrByName(m_name) != nullptr)
732 3 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 1959 FragmentationPathway::validate(ErrorList *error_list_p) const
763 {
764
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1911 times.
1959 qsizetype error_count = error_list_p->size();
765
766
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1911 times.
1959 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
767 {
768 96 qCritical()
769 << "A FragmentationPathway with no PolChemDef available cannot "
770
1/2
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
48 "validate successfully.";
771 96 error_list_p->push_back(
772 "A FragmentationPathway with no PolChemDef available cannot validate "
773 "successfully");
774 }
775
776
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 1881 times.
1959 if(m_name.isEmpty())
777 {
778 156 qCritical()
779
1/2
✓ Branch 1 taken 78 times.
✗ Branch 2 not taken.
78 << "A FragmentationPathway with no name cannot validate successfully.";
780 156 error_list_p->push_back(
781 "A FragmentationPathway with no name cannot validate successfully");
782 }
783
784
2/2
✓ Branch 2 taken 102 times.
✓ Branch 3 taken 1857 times.
1959 if(m_formula.getActionFormula().isEmpty())
785 {
786 204 qCritical() << "A FragmentationPathway with no formula cannot "
787
1/2
✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
102 "validate successfully.";
788 204 error_list_p->push_back(
789 "A FragmentationPathway with no formula cannot validate "
790 "successfully");
791 }
792
3/4
✓ Branch 0 taken 1857 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1854 times.
3714 else if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr &&
793
3/4
✓ Branch 2 taken 1857 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 1854 times.
3714 !m_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
794 error_list_p))
795 {
796 6 qCritical() << "A FragmentationPathway with an invalid formula cannot "
797
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "validate successfully.";
798 6 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 150 times.
✓ Branch 1 taken 1959 times.
2109 for(const FragmentationRuleSPtr &frag_rule_sp : m_rules)
809 {
810
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 150 times.
150 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 1959 m_isValid = error_list_p->size() > error_count ? false : true;
823
824 1959 return m_isValid;
825 }
826
827 /*!
828 \brief Returns the validity status of this FragmentationPathway.
829 */
830 bool
831 1488 FragmentationPathway::isValid() const
832 {
833 1488 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 3 FragmentationPathway::toString() const
845 {
846 3 QString text = "Fragmentation pathway:\n";
847
848
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 QString frag_end_string = (*(fragEndMap.find(m_fragEnd))).second;
849
850 3 text +=
851
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QString("Name: %1 - Formula: %2 - Frag end: %3 - Monomer contribution: %4")
852
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 .arg(m_name)
853
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 .arg(m_formula.getActionFormula(/*with_title*/ true))
854
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 .arg(frag_end_string)
855
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 .arg(m_monomerContribution);
856
857
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 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 3 times.
✗ Branch 2 not taken.
3 text += "\n";
868
869
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
6 text += QString("isValid: %1\n").arg(m_isValid ? "true" : "false");
870
871 3 return text;
872 3 }
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 1413 FragmentationPathway::renderXmlFgsElement(const QDomElement &element,
893 [[maybe_unused]] int version)
894 {
895 1413 QDomElement child;
896
1/2
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
1413 QDomElement rule_child_element;
897
898 1413 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 1413 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1413 times.
2826 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 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1413 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1413 times.
✗ Branch 8 not taken.
2826 child = element.firstChildElement("name");
949
950
5/10
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1413 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1413 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 1413 times.
2826 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 1413 times.
✗ Branch 2 not taken.
1413 m_name = child.text();
958 // qDebug() << "The name:" << m_name;
959
960
2/4
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1413 times.
✗ Branch 5 not taken.
2826 child = child.nextSiblingElement();
961
962
8/18
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1413 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1413 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1413 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1413 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1413 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 1413 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
4239 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 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 201 times.
✓ Branch 5 taken 1212 times.
2826 if(child.text() == "NE")
971 201 m_fragEnd = Enums::FragEnd::NE;
972
3/4
✓ Branch 1 taken 1212 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 609 times.
✓ Branch 5 taken 603 times.
2424 else if(child.text() == "LE")
973 609 m_fragEnd = Enums::FragEnd::LE;
974
2/4
✓ Branch 1 taken 603 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 603 times.
✗ Branch 5 not taken.
1206 else if(child.text() == "RE")
975 603 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 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1413 times.
✗ Branch 5 not taken.
2826 child = child.nextSiblingElement();
987
988
8/18
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1413 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1413 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1413 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1413 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1413 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 1413 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
4239 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 1413 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1413 times.
1413 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 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1413 times.
✗ Branch 5 not taken.
2826 child = child.nextSiblingElement();
1007
5/10
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1413 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1413 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1413 times.
4239 if(child.isNull() || child.text().isEmpty() ||
1008
3/8
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1413 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
4239 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 1413 times.
✗ Branch 2 not taken.
1413 QString text = child.text();
1016 1413 bool ok = false;
1017
1/2
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
1413 m_monomerContribution = text.toInt(&ok);
1018
1019
2/4
✓ Branch 0 taken 1413 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1413 times.
1413 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 1413 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1413 times.
✗ Branch 5 not taken.
2826 child = child.nextSiblingElement();
1030
1031
3/4
✓ Branch 1 taken 1827 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 414 times.
✓ Branch 4 taken 1413 times.
1827 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 414 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 408 times.
✓ Branch 5 taken 6 times.
828 if(child.tagName() == "comment")
1037 {
1038
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 408 times.
408 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 408 times.
✗ Branch 2 not taken.
408 m_comment = child.text();
1048 408 comment_was_parsed = true;
1049
1050
2/4
✓ Branch 1 taken 408 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 408 times.
✗ Branch 5 not taken.
816 child = child.nextSiblingElement();
1051 408 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 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 6 times.
12 while(!child.isNull())
1058 {
1059 6 FragmentationRuleSPtr frag_rule_sp =
1060
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 std::make_shared<FragmentationRule>(mcsp_polChemDef, child, 1);
1061
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
6 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 6 times.
✗ Branch 2 not taken.
6 m_rules.push_back(frag_rule_sp);
1072
1073
3/8
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
12 child = child.nextSiblingElement();
1074 6 }
1075 }
1076
1077 1413 ErrorList error_list;
1078
1/2
✓ Branch 1 taken 1413 times.
✗ Branch 2 not taken.
1413 m_isValid = validate(&error_list);
1079
1080
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1413 times.
1413 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 1413 return m_isValid;
1086 4239 }
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 21 FragmentationPathway::renderXmlFgpElement(const QDomElement &element,
1108 [[maybe_unused]] int version)
1109 {
1110 21 QDomElement child;
1111
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 QDomElement rule_child_element;
1112
1113 21 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 21 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
42 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 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 child = element.firstChildElement("name");
1164
1165
5/10
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 21 times.
42 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 21 times.
✗ Branch 2 not taken.
21 m_name = child.text();
1174 // qDebug() << "The name:" << m_name;
1175
1176
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 child = child.nextSiblingElement();
1177
1178
8/18
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 21 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 21 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 21 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 21 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
63 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 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 18 times.
42 if(child.text() == "NE")
1188 3 m_fragEnd = Enums::FragEnd::NE;
1189
3/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 9 times.
36 else if(child.text() == "LE")
1190 9 m_fragEnd = Enums::FragEnd::LE;
1191
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
18 else if(child.text() == "RE")
1192 9 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 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 child = child.nextSiblingElement();
1205
1206
8/18
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 21 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 21 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 21 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 21 times.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
63 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 21 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
21 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 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 child = child.nextSiblingElement();
1226
5/10
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 21 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 21 times.
63 if(child.isNull() || child.text().isEmpty() ||
1227
3/8
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
63 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 21 times.
✗ Branch 2 not taken.
21 QString text = child.text();
1236 21 bool ok = false;
1237
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_monomerContribution = text.toInt(&ok);
1238
1239
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 21 times.
21 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 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 child = child.nextSiblingElement();
1251
1252
3/4
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 21 times.
27 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 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
12 if(child.tagName() == "comment")
1258 {
1259
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 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 6 times.
✗ Branch 2 not taken.
6 m_comment = child.text();
1269 6 comment_was_parsed = true;
1270
1271
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
12 child = child.nextSiblingElement();
1272 6 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 21 ErrorList error_list;
1299
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_isValid = validate(&error_list);
1300
1301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 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 21 return m_isValid;
1308 63 }
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 24 FragmentationPathway::formatXmlFgpElement(int offset,
1352 const QString &indent) const
1353 {
1354
1355 24 int newOffset;
1356 24 int iter = 0;
1357
1358 24 QString lead("");
1359 24 QString text;
1360
1361
1362 // Prepare the lead.
1363 24 newOffset = offset;
1364
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 24 times.
90 while(iter < newOffset)
1365 {
1366
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 lead += indent;
1367 66 ++iter;
1368 }
1369
1370
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48 text += QString("%1<fgp>\n").arg(lead);
1371
1372 // Prepare the lead.
1373 24 ++newOffset;
1374 24 lead.clear();
1375 24 iter = 0;
1376
2/2
✓ Branch 1 taken 90 times.
✓ Branch 2 taken 24 times.
138 while(iter < newOffset)
1377 {
1378
1/2
✓ Branch 1 taken 90 times.
✗ Branch 2 not taken.
90 lead += indent;
1379 90 ++iter;
1380 }
1381
1382 // Continue, with indented elements.
1383
1384
3/6
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
48 text += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1385
4/8
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 24 times.
✗ Branch 11 not taken.
48 text += QString("%1<end>%2</end>\n").arg(lead).arg(fragEndMap[m_fragEnd]);
1386
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 text += QString("%1<formula>%2</formula>\n")
1387
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
48 .arg(lead)
1388
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48 .arg(m_formula.getActionFormula());
1389
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 text += QString("%1<sidechaincontrib>%2</sidechaincontrib>\n")
1390
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
48 .arg(lead)
1391
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 .arg(m_monomerContribution);
1392
1393
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 15 times.
24 if(!m_comment.isEmpty())
1394
3/6
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
18 text += QString("%1<comment>%2</comment>\n").arg(lead).arg(m_comment);
1395
1396
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 24 times.
27 for(const FragmentationRuleSPtr &frag_rule_sp : m_rules)
1397
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 text += frag_rule_sp->formatXmlFgrElement(newOffset);
1398
1399 // Prepare the lead.
1400 24 --newOffset;
1401 24 lead.clear();
1402 24 iter = 0;
1403
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 24 times.
114 while(iter < newOffset)
1404 {
1405
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 lead += indent;
1406 66 ++iter;
1407 }
1408
1409
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48 text += QString("%1</fgp>\n").arg(lead);
1410
1411 24 return text;
1412 24 }
1413
1414
1415 } // namespace libXpertMassCore
1416 } // namespace MsXpS
1417