GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/CrossLinker.cpp
Date: 2025-11-19 15:26:11
Lines:
317/399
79.4%
Functions:
35/38
92.1%
Branches:
276/610
45.2%

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/CrossLinker.hpp"
36 #include "MsXpS/libXpertMassCore/PolChemDef.hpp"
37
38
39 namespace MsXpS
40 {
41 namespace libXpertMassCore
42 {
43
44
45 /*!
46 \class MsXpS::libXpertMassCore::CrossLinker
47 \inmodule libXpertMassCore
48 \ingroup PolChemDefAqueousChemicalReactions
49 \inheaderfile CrossLinker.hpp
50
51 \brief The CrossLinker class provides abstractions to define the chemical basis
52 of a cross-linking reaction.
53
54 The notion of a cross-linker is that it is the description of the chemical
55 reaction that is carried out by \l{Monomer}s in a \l Polymer or in an \l
56 Oligomer sequence to form a \l CrossLink.
57
58 There are two different kinds of chemical entities potentially involved in the
59 description of a CrossLinker:
60
61 \list
62 \a Modif instances that are applied to \l Monomer instances.
63 \a the formula that completes the chemical reactions described by
64 the \l Modif instances.
65 \endlist
66 */
67
68
69 /*!
70 \variable MsXpS::libXpertMassCore::CrossLinker::mcsp_polChemDef
71
72 \brief The reference polymer chemistry definition.
73 */
74
75
76 /*!
77 \variable MsXpS::libXpertMassCore::CrossLinker::m_name
78
79 \brief The name of the CrossLinker, \"DisulphideBond\", for example.
80 */
81
82
83 /*!
84 \variable MsXpS::libXpertMassCore::CrossLinker::m_formula
85
86 \brief The formula that must be applied when the cross-link occurs.
87
88 \note This formula can be empty because the CrossLinker can be fully
89 described, from a chemical standpoint, by listing Modif instances in the
90 member m_modifs container of Modif instances.
91 */
92
93 /*!
94 \variable MsXpS::libXpertMassCore::CrossLinker::m_mono
95
96 \brief The monoisotopic mass of the CrossLinker.
97 */
98
99 /*!
100 \variable MsXpS::libXpertMassCore::CrossLinker::m_avg
101
102 \brief The average mass of the CrossLinker.
103 */
104
105 /*!
106 \variable MsXpS::libXpertMassCore::CrossLinker::m_isValid
107
108 \brief Tells if the CrossLinker is valid.
109 */
110
111 /*!
112 \variable MsXpS::libXpertMassCore::CrossLinker::m_modifs
113
114 \brief The container of \l Modif instances that describe the reaction (or the
115 reactions) involved in the formation of a CrossLink between \l{Monomer}s of a
116 \l Polymer (or of an \l Oligomer) sequence.
117 */
118
119 /*!
120 \brief Constructs a CrossLinker instance with the \a pol_chem_def_csp
121 PolChemDef.
122 */
123 CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp)
124 : mcsp_polChemDef(pol_chem_def_csp)
125 {
126 }
127
128 /*!
129 \brief Constructs a CrossLinker instance starting from an XML <clk> \a element
130 and a \a pol_chem_def_csp PolChemDef. The \a version indicates what version of
131 the XML element is to be used.
132
133 This is the current format:
134 \code
135 <clk>
136 <name>CFP-chromophore</name>
137 <formula></formula>
138 <modifname>Chromo-O</modifname>
139 <modifname>Chromo-H3</modifname>
140 <modifname>Chromo-H</modifname>
141 </clk>
142 \endcode
143
144 The rendering of the Modif instances requires that the PolChemDef be available.
145 If this is not the case, the rendering of the CrossLinker is stopped at the
146 first <modifname> element and false is returned.
147
148 \sa renderXmlClkElement()
149 */
150 417 CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp,
151 const QDomElement &element,
152 417 int version)
153
4/6
✓ Branch 2 taken 417 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 417 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 3 times.
✓ Branch 8 taken 414 times.
417 : mcsp_polChemDef(pol_chem_def_csp)
154 {
155
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 414 times.
417 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
156
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 qCritical() << "Constructing CrossLinker with no PolChemDef.";
157
158
3/4
✓ Branch 1 taken 417 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 411 times.
417 if(!renderXmlClkElement(element, version))
159
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 qCritical()
160 << "Failed to fully render or validate the CrossLinker XML element "
161
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 "for construction of CrossLinker instance.";
162
0/2
✗ Branch 3 not taken.
✗ Branch 4 not taken.
417 }
163
164
165 /*!
166 \brief Constructs a CrossLinker instance.
167
168 \list
169 \li \a pol_chem_def_csp: the polymer chemistry definition (\l PolChemDef).
170 \li \a name: the name of this CrossLinker.
171 \li \a formula: the \l Formula that potentially complements the description of
172 the reaction that is the basis of this CrossLinker (used to initialize the
173 Formula base class).
174 \endlist
175 */
176 87 CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp,
177 const QString &name,
178 const QString &formula,
179 double mono,
180 87 double avg)
181 174 : mcsp_polChemDef(pol_chem_def_csp),
182
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 36 times.
87 m_name(name),
183
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 36 times.
87 m_formula(formula),
184 87 m_mono(mono),
185 87 m_avg(avg)
186 {
187 87 }
188
189 /*!
190 \brief Constructs a CrossLinker instance as a copy of \a other.
191 */
192 66 CrossLinker::CrossLinker(const CrossLinker &other)
193 132 : mcsp_polChemDef(other.mcsp_polChemDef),
194
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 m_name(other.m_name),
195
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 54 times.
66 m_formula(other.m_formula),
196 66 m_mono(other.m_mono),
197 132 m_avg(other.m_avg)
198 {
199 66 m_modifs.clear();
200
201 66 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
202 66 other.m_modifs.cbegin();
203 66 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
204 66 other.m_modifs.cend();
205
206
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 66 times.
258 while(the_iterator_cst != the_end_iterator_cst)
207 {
208
1/2
✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
192 m_modifs.push_back((*the_iterator_cst));
209
210 192 ++the_iterator_cst;
211 }
212
213 66 ErrorList error_list;
214
215
1/2
✓ Branch 1 taken 66 times.
✗ Branch 2 not taken.
66 m_isValid = validate(&error_list);
216
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 66 times.
66 if(!m_isValid)
218 qCritical() << "The copy constructor instantiated a CrossLink that is not "
219 "valid, with errors:"
220 << Utils::joinErrorList(error_list, ", ");
221
0/2
✗ Branch 3 not taken.
✗ Branch 4 not taken.
66 }
222
223
224 /*!
225 \brief Destructs this CrossLinker instance
226 */
227 324 CrossLinker::~CrossLinker()
228 {
229 // We do not own the modifications in m_modifList!
230
2/2
✓ Branch 3 taken 150 times.
✓ Branch 4 taken 12 times.
324 }
231
232 /*!
233 \brief Initializes this CrossLinker instance using \a element, according to \a version.
234
235 Returns true if the rendering of \a element was successful, false otherwise.
236
237 \sa renderXmlClkElement()
238 */
239 bool
240 CrossLinker::initialize(const QDomElement &element, int version)
241 {
242 if(!renderXmlClkElement(element, version))
243 {
244 qCritical()
245 << "Failed to fully render or validate the CrossLinker XML element "
246 "for construction of CrossLinker instance.";
247
248 return false;
249 }
250
251 return true;
252 }
253
254 //////////////// THE POLCHEMDEF /////////////////////
255
256 /*!
257 \brief Sets the PolChemDef to \a pol_chem_def_csp.
258 */
259 void
260 54 CrossLinker::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp)
261 {
262 54 mcsp_polChemDef = pol_chem_def_csp;
263
264 54 ErrorList error_list;
265
1/2
✓ Branch 1 taken 54 times.
✗ Branch 2 not taken.
54 m_isValid = validate(&error_list);
266
267
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 15 times.
54 if(!m_isValid)
268
2/4
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
78 qCritical() << "Failed to validate the CrossLinker with errors:\n"
269
3/6
✓ Branch 1 taken 39 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 39 times.
✗ Branch 8 not taken.
78 << Utils::joinErrorList(error_list, ", ");
270 54 }
271
272 /*!
273 \brief Returns the PolChemDef.
274 */
275 const PolChemDefCstSPtr &
276 45 CrossLinker::getPolChemDefCstSPtr() const
277 {
278 45 return mcsp_polChemDef;
279 }
280
281 //////////////// THE NAME /////////////////////
282
283 /*!
284 \brief Sets the \a name of the CrossLinker.
285 */
286 void
287 6 CrossLinker::setName(const QString &name)
288 {
289 6 m_name = name;
290
291 6 ErrorList error_list;
292
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 m_isValid = validate(&error_list);
293
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(!m_isValid)
295 qCritical() << "Failed to validate the CrossLinker with errors:\n"
296 << Utils::joinErrorList(error_list, ", ");
297 6 }
298
299 /*!
300 \brief Returns the name.
301 */
302 QString
303 408 CrossLinker::getName() const
304 {
305
1/4
✓ Branch 0 taken 408 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
408 return m_name;
306 }
307
308 //////////////// THE FORMULA /////////////////////
309 /*!
310 \brief Sets the formula to \a formula_string.
311 */
312 void
313 21 CrossLinker::setFormula(const QString &formula_string)
314 {
315 21 m_formula = formula_string;
316
317 21 ErrorList error_list;
318
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_isValid = validate(&error_list);
319
320
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 6 times.
21 if(!m_isValid)
321
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
30 qCritical() << "Failed to validate the CrossLinker with errors:\n"
322
3/6
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
30 << Utils::joinErrorList(error_list, ", ");
323 21 }
324
325 /*
326 \brief Returns the formula of this CrossLinker.
327 */
328 const QString &
329 63 CrossLinker::getFormula() const
330 {
331 63 return m_formula;
332 }
333
334 //////////////// THE MODIFICATIONS /////////////////////
335
336 /*!
337 \brief Returns a constant reference to the container of Modif instances.
338 */
339 const std::vector<ModifCstSPtr> &
340 573 CrossLinker::getModifsCstRef() const
341 {
342 573 return m_modifs;
343 }
344
345 /*!
346 \brief Returns a reference to the container of Modif instances.
347 */
348 std::vector<ModifCstSPtr> &
349 3 CrossLinker::getModifsRef()
350 {
351 3 return m_modifs;
352 }
353
354 /*!
355 \brief Sets the Modif at index \a index to \a modif_csp.
356
357 \a index cannot be out-of-bounds (fatal error).
358
359 Returns true.
360 */
361 bool
362 27 CrossLinker::insertModifAt(ModifCstSPtr modif_csp, std::size_t index)
363 {
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if(index >= m_modifs.size())
365 qFatalStream() << "The index is out of bounds.";
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 if(modif_csp == nullptr || modif_csp.get() == nullptr)
367 qFatalStream() << "Programming error. The pointer cannot be nullptr";
368
369 27 m_modifs.insert(m_modifs.cbegin() + index, modif_csp);
370
371 27 return true;
372 }
373
374 /*!
375 \brief Adds \a modif_csp to this CrossLinker instance.
376
377 Returns true.
378 */
379 bool
380 54 CrossLinker::appendModif(ModifCstSPtr modif_csp)
381 {
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if(modif_csp == nullptr || modif_csp.get() == nullptr)
383 qFatalStream() << "Programming error. The pointer cannot be nullptr";
384
385 54 m_modifs.push_back(modif_csp);
386
387 54 return true;
388 }
389
390 /*!
391 \brief Returns the \l Modif instance at \a index.
392
393 \a index cannot be out-of-bounds.
394 */
395 ModifCstSPtr
396 57 CrossLinker::getModifAt(std::size_t index) const
397 {
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if(index >= m_modifs.size())
399 qFatalStream() << "Programming error. Index is out of bounds.";
400
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 return m_modifs.at(index);
402 }
403
404
405 /*!
406 \brief Removes the \l Modif instance at \a index.
407
408 \a index cannot be out-of-bounds.
409
410 Returns true.
411 */
412 bool
413 12 CrossLinker::removeModifAt(std::size_t index)
414 {
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(index >= m_modifs.size())
416 qFatalStream() << "Programming error. Index is out of bounds.";
417
418 12 m_modifs.erase(m_modifs.cbegin() + index);
419
420 12 return true;
421 }
422
423 /*!
424 \brief Returns an iterator to the found \l Modif instance by \a modif_name in
425 the container.
426 */
427 bool
428 18 CrossLinker::hasModif(const QString &modif_name)
429 {
430
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst = std::find_if(
431
6/12
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 18 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 18 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 18 times.
✗ Branch 21 not taken.
162 m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) {
432 36 return modif_csp->getName() == modif_name;
433 });
434
435 18 return the_iterator_cst != m_modifs.cend();
436 }
437
438
439 /*!
440 \brief Returns the index of the found \l Modif instance by \a modif_name in
441 the container, -1 otherwise.
442 */
443 int
444 21 CrossLinker::modifIndex(const QString &modif_name)
445 {
446
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst = std::find_if(
447
6/12
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 21 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 21 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 21 times.
✗ Branch 21 not taken.
189 m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) {
448 39 return modif_csp->getName() == modif_name;
449 });
450
451
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 3 times.
21 if(the_iterator_cst == m_modifs.cend())
452 return -1;
453
454 18 return std::distance(m_modifs.cbegin(), the_iterator_cst);
455 }
456
457
458 //////////////// OPERATORS /////////////////////
459 /*!
460 \brief Assigns \a other to this CrossLinker instance.
461
462 Returns a reference to this CrossLinker instance.
463 */
464 CrossLinker &
465 6 CrossLinker::operator=(const CrossLinker &other)
466 {
467
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 if(&other == this)
468 return *this;
469
470 3 mcsp_polChemDef = other.mcsp_polChemDef;
471 3 m_name = other.m_name;
472 3 m_formula = other.m_formula;
473 3 m_mono = other.m_mono;
474 3 m_avg = other.m_avg;
475
476 3 m_modifs.clear();
477
478 3 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
479 3 other.m_modifs.cbegin();
480 3 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
481 3 other.m_modifs.cend();
482
483
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 while(the_iterator_cst != the_end_iterator_cst)
484 {
485 9 m_modifs.push_back((*the_iterator_cst));
486
487 9 ++the_iterator_cst;
488 }
489
490 3 ErrorList error_list;
491
492
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_isValid = validate(&error_list);
493
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(!m_isValid)
495 qCritical()
496 << "The assignment operator copying produced a CrossLink that is not "
497 "valid, with errors:"
498 << Utils::joinErrorList(error_list, ", ");
499
500 3 return *this;
501 3 }
502
503 /*!
504 \brief Returns true if this CrossLinker and \a other are identical.
505 */
506 bool
507 57 CrossLinker::operator==(const CrossLinker &other) const
508 {
509
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 39 times.
57 if(&other == this)
510 return true;
511
512 // We cannot compare the PolChemDef, because that would cause
513 // an infinite loop: (each instance of this class in the PolChemDef would
514 // try to compare the PolChemDef...).
515
516
4/4
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 6 times.
39 if(m_name != other.m_name || m_formula != other.m_formula ||
517
6/8
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 27 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 21 times.
66 m_mono != other.m_mono || m_avg != other.m_avg ||
518
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 21 times.
27 m_modifs.size() != other.m_modifs.size())
519 return false;
520
521
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 60 times.
81 for(std::size_t iter = 0; iter < m_modifs.size(); ++iter)
522 {
523
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
60 if(*m_modifs.at(iter).get() != *other.m_modifs.at(iter).get())
524 {
525 qDebug() << "At least one Modif instance differs in both CrossLinker "
526 "instances.";
527 return false;
528 }
529 }
530
531 return true;
532 }
533
534
535 /*!
536 \brief Returns true if this CrossLinker and \a other are different.
537
538 Returns the negated result of operator==().
539 */
540 bool
541 24 CrossLinker::operator!=(const CrossLinker &other) const
542 {
543
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 3 times.
24 if(&other == this)
544 return false;
545
546 21 return !operator==(other);
547 }
548
549 //////////////// VALIDATIONS /////////////////////
550 /*!
551 \brief Returns the CrossLinker instance from the polymer chemistry definition
552 registered in this instance.
553
554 The key to search the CrossLinker is this instance's member name.
555
556 If there is no PolChemDef available, nullptr is returned.
557
558 If no CrossLinker instance is found by this instance's name, nullptr is
559 returned.
560 */
561 const CrossLinkerCstSPtr
562 3 CrossLinker::getFromPolChemDefByName() const
563 {
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
565 return nullptr;
566
567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(m_name.isEmpty())
568 return nullptr;
569
570 3 std::vector<CrossLinkerSPtr>::const_iterator the_iterator_cst =
571 3 std::find_if(mcsp_polChemDef->getCrossLinkersCstRef().cbegin(),
572 3 mcsp_polChemDef->getCrossLinkersCstRef().cend(),
573 3 [&](const auto &cross_linker_csp) {
574
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
3 return cross_linker_csp->getName() == m_name;
575 });
576
577
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(the_iterator_cst == mcsp_polChemDef->getCrossLinkersCstRef().cend())
578 return nullptr;
579
580 3 return (*the_iterator_cst);
581 }
582
583
584 /*!
585 \brief Returns the status of this CrossLinker instance the polymer chemistry
586 definition registered in this instance.
587
588 The key to search the CrossLinker is this instance's member name.
589
590 If there is no PolChemDef available,
591 Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned.
592
593 If no CrossLinker instance is found by this instance's name,
594 Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise
595 Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned.
596 */
597 Enums::PolChemDefEntityStatus
598 3 CrossLinker::isKnownByNameInPolChemDef() const
599 {
600
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
601 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
602
603
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
6 if(mcsp_polChemDef->getCrossLinkerCstSPtrByName(m_name) != nullptr)
604 3 return Enums::PolChemDefEntityStatus::ENTITY_KNOWN;
605
606 return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN;
607 };
608
609
610 /*!
611 \brief Returns true if this CrossLinker instance validates successfully,
612 false otherwise.
613
614 The validation involves the following:
615
616 \list
617 \li The member polymer chemistry definition must exist.
618 \li The name cannot be empty.
619 \li The formula (if not empty) must validate successfully against the member
620 polymer chemistry definition.
621 \li The list of \l Modif instances must contain either no or more than two
622 Modif instances. In the latter case, the Modif instances must be found in the
623 member polymer chemistry definition and must validate successfully.
624 \endlist
625
626 When an error occurs, the message is stored in \a error_list_p.
627 */
628 bool
629 1044 CrossLinker::validate(ErrorList *error_list_p) const
630 {
631 1044 Q_ASSERT(error_list_p != nullptr);
632
633
3/4
✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 1020 times.
✗ Branch 3 not taken.
2064 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr ||
634
5/8
✓ Branch 1 taken 1020 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1020 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1020 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 1020 times.
3084 mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr ||
635
8/14
✓ Branch 0 taken 1020 times.
✓ Branch 1 taken 24 times.
✓ Branch 3 taken 1020 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1020 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1020 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1020 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1020 times.
✓ Branch 13 taken 24 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
4128 mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr ||
636
4/8
✓ Branch 1 taken 1020 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1020 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1020 times.
✓ Branch 7 taken 24 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
2064 !mcsp_polChemDef->getIsotopicDataCstSPtr()->size())
637 {
638 48 error_list_p->push_back("The PolChemDef or IsotopicData are not available");
639 48 qCritical()
640 << "The polymer chemistry definition member datum is nullptr or "
641 "its isotopic data are be either nullptr or empty. CrossLinker "
642 "validation "
643
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 "failed.";
644
645 24 m_isValid = false;
646 24 return m_isValid;
647 }
648
649
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 984 times.
1020 if(m_name.isEmpty())
650 {
651
1/2
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
36 qCritical() << "The CrossLinker name is empty.";
652 72 error_list_p->push_back("The CrossLinker name is empty");
653 }
654
655 // Remember that the formula of the crosslinker might be empty because the
656 // cross-linker might be fully accounted for by the modifications (below).
657
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 972 times.
1020 if(!m_formula.isEmpty())
658 {
659 48 Formula temp_formula(m_formula);
660
5/8
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 48 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 15 times.
✓ Branch 9 taken 33 times.
96 if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
661 error_list_p))
662 {
663
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 qCritical() << "The CrossLinker formula failed to validate.";
664
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
30 error_list_p->push_back("The CrossLinker formula failed to validate");
665 }
666 48 }
667
668 1020 double mono = 0.0;
669 1020 double avg = 0.0;
670
671
4/6
✓ Branch 2 taken 1020 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1020 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 15 times.
✓ Branch 7 taken 1005 times.
2040 if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg))
672 {
673
1/2
✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
15 qCritical() << "Failed to calculate the CrossLinker masses.";
674 30 error_list_p->push_back("Failed to calculate the CrossLinker masses");
675 }
676
677 // This is mainly a sanity check, as the pointers to Modif in the
678 // list all point to modification objects in the polymer chemistry
679 // definition, which have been validated already...
680
681 // The validation actually is simple, it might be that there are NO
682 // modifs, or if this is not the case there must be at least
683 // 2. Indeed, either none of the monomers in the crosslink get
684 // modified, or each one has to be(otherwise we cannot know which
685 // modif goes to which monomer).
686
687
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
1020 int size = m_modifs.size();
688
689
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1020 times.
1020 if(size > 0 && size < 2)
690 {
691 qCritical() << "The CrossLinker may have either zero or more at least "
692 "two Modif instances.";
693 error_list_p->push_back(
694 "The CrossLinker may have either zero or at least two Modif instances");
695
696 m_isValid = false;
697 return m_isValid;
698 }
699
700
2/2
✓ Branch 0 taken 2283 times.
✓ Branch 1 taken 1020 times.
3303 for(const ModifCstSPtr &modif_csp : m_modifs)
701 {
702 // The modif must be in the context of our PolChemDef.
703
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2283 times.
2283 if(modif_csp->getPolChemDefCstSPtr() != mcsp_polChemDef)
704 {
705 qCritical()
706 << "One Modif:" << modif_csp->getName()
707 << "has not the same PolChemDef as that of this CrossLinker.";
708 error_list_p->push_back(QString("One Modif: %1 has not the same "
709 "PolChemDef as that of this CrossLinker")
710 .arg(modif_csp->getName()));
711 m_isValid = false;
712 1044 return m_isValid;
713 }
714
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2283 times.
2283 if(!modif_csp->validate(error_list_p))
715 {
716 qCritical() << "One Modif:" << modif_csp->getName()
717 << "failed to validate successfully.";
718 error_list_p->push_back(
719 QString("One Modif: %1 ailed to validate successfully")
720 .arg(modif_csp->getName()));
721 m_isValid = false;
722 return m_isValid;
723 }
724 }
725
726 1020 m_isValid = (error_list_p->size() ? false : true);
727 1020 return m_isValid;
728 }
729
730 /*!
731 \brief Returns the validity status of this CrossLinker.
732 */
733 bool
734 459 CrossLinker::isValid() const
735 {
736 459 return m_isValid;
737 }
738
739
740 //////////////// MASS OPERATIONS /////////////////////
741
742 /*!
743 \brief Returns true if the mass calculations for this CrossLinker instance are
744 performed without error, false otherwise.
745
746 The calculation involved accounting masses for the Modifs (if any) and for the
747 Formula (if non-empty). The masses calculated are set to \a mono and \a avg.
748
749 The calculations are performed using reference data in \a isotopic_data_csp.
750 */
751 bool
752 1023 CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp,
753 double &mono,
754 double &avg) const
755 {
756 1023 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
757
758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1023 times.
1023 if(local_isotopic_data_csp == nullptr ||
759 local_isotopic_data_csp.get() == nullptr)
760 {
761 if(mcsp_polChemDef != nullptr)
762 local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr();
763
764 if(local_isotopic_data_csp == nullptr ||
765 local_isotopic_data_csp.get() == nullptr)
766 {
767 qCritical() << "Failed to find usable isotopic data.";
768 m_isValid = false;
769 return m_isValid;
770 }
771 }
772
773 // qDebug() << "Calculating masses for the cross-link";
774
775 1023 mono = 0;
776 1023 avg = 0;
777
778 1023 bool ok;
779
780
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 975 times.
1023 if(!m_formula.isEmpty())
781 {
782 // Formula temp_formula(m_formula);
783
2/4
✓ Branch 1 taken 48 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48 times.
✗ Branch 5 not taken.
144 Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg);
784
785
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 33 times.
48 if(!ok)
786 {
787
3/8
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
30 qCritical() << "Failed accounting masses for CrossLinker:" << m_name
788
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 << "and formula:" << m_formula;
789 15 m_isValid = false;
790 15 return m_isValid;
791 }
792 }
793
794 // Now, for each modif in the crossLinker, have to account their
795 // mass.
796
797 1008 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
798 1008 m_modifs.cbegin();
799 1008 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
800 1008 m_modifs.cend();
801
802
2/2
✓ Branch 0 taken 2244 times.
✓ Branch 1 taken 1008 times.
3252 while(the_iterator_cst != the_end_iterator_cst)
803 {
804
1/2
✓ Branch 1 taken 2244 times.
✗ Branch 2 not taken.
2244 (*the_iterator_cst)->accountMasses(mono, avg);
805 2244 ++the_iterator_cst;
806 }
807
808 // qDebug() << "At this point, the masses of the CrossLinker are:" << m_mono
809 //<< "/" << m_avg;
810
811 return true;
812 1023 }
813
814 /*!
815 \brief Returns true if the mass calculations for this CrossLinker instance are
816 performed without error, false otherwise.
817
818 The calculation involved accounting masses for the Modifs (if any) and for the
819 Formula (if non-empty). The member masses (m_mono and m_avg) are
820 updated.
821
822 The reference data for the computations are accessed at \a isotopic_data_csp.
823 */
824 bool
825 450 CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp)
826 {
827 450 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
828
829
1/2
✓ Branch 0 taken 450 times.
✗ Branch 1 not taken.
450 if(local_isotopic_data_csp == nullptr ||
830 local_isotopic_data_csp.get() == nullptr)
831 {
832
2/2
✓ Branch 0 taken 435 times.
✓ Branch 1 taken 15 times.
450 if(mcsp_polChemDef != nullptr)
833
2/4
✓ Branch 1 taken 435 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 435 times.
870 local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr();
834
835
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 435 times.
450 if(local_isotopic_data_csp == nullptr ||
836 local_isotopic_data_csp.get() == nullptr)
837 {
838
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 qCritical() << "Failed to find usable isotopic data.";
839 15 m_isValid = false;
840 15 return m_isValid;
841 }
842 }
843
844 // qDebug() << "Calculating masses for the cross-link";
845
846 435 m_mono = 0;
847 435 m_avg = 0;
848
849 435 bool ok;
850
851
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 417 times.
435 if(!m_formula.isEmpty())
852 {
853 // Formula temp_formula(m_formula);
854
2/4
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18 times.
✗ Branch 5 not taken.
54 Formula(m_formula).accountMasses(
855 18 ok, local_isotopic_data_csp, m_mono, m_avg);
856
857
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
18 if(!ok)
858 {
859
3/8
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 15 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
30 qCritical() << "Failed accounting masses for CrossLinker:" << m_name
860
2/4
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 15 times.
✗ Branch 5 not taken.
15 << "and formula:" << m_formula;
861 15 m_isValid = false;
862 15 return m_isValid;
863 }
864 }
865
866 // Now, for each modif in the crossLinker, have to account their
867 // mass.
868
869 420 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
870 420 m_modifs.cbegin();
871 420 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
872 420 m_modifs.cend();
873
874
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 420 times.
1476 while(the_iterator_cst != the_end_iterator_cst)
875 {
876
1/2
✓ Branch 1 taken 1056 times.
✗ Branch 2 not taken.
1056 (*the_iterator_cst)->accountMasses(m_mono, m_avg);
877 1056 ++the_iterator_cst;
878 }
879
880 // qDebug() << "At this point, the masses of the CrossLinker are:" << m_mono
881 //<< "/" << m_avg;
882
883 return true;
884 450 }
885
886 /*!
887 \brief Increases \a mono_p and \a avg_p by the corresponding member masses
888 first compounded by \a times.
889
890 Returns this CrossLinker.
891 */
892 const CrossLinker &
893 6 CrossLinker::accountMasses(double *mono_p, double *avg_p, int times) const
894 {
895 // qDebug() << "Accounting masses for cross-linker -- mono:" << m_mono
896 //<< "avg:" << m_avg << "by" << times << "times.";
897
898
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(mono_p)
899 6 *mono_p += m_mono * times;
900
901
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(avg_p)
902 6 *avg_p += m_avg * times;
903
904 6 return *this;
905 }
906
907
908 /*!
909 \brief Increases \a mono and \a avg by the corresponding member masses
910 first compounded by \a times.
911
912 Returns this CrossLinker.
913 */
914 const CrossLinker &
915 162 CrossLinker::accountMasses(double &mono, double &avg, int times) const
916 {
917 // qDebug() << "Accounting masses for cross-linker -- mono:" << m_mono
918 //<< "avg:" << m_avg << "by" << times << "times.";
919
920 162 mono += m_mono * times;
921 162 avg += m_avg * times;
922
923 162 return *this;
924 }
925
926 /*!
927 \brief Returns the mass of the type defined by \a mass_type.
928 */
929 double
930 42 CrossLinker::getMass(Enums::MassType mass_type) const
931 {
932
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 if(mass_type == Enums::MassType::MONO)
933 21 return m_mono;
934
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 else if(mass_type == Enums::MassType::AVG)
935 21 return m_avg;
936
937 qFatalStream()
938 << "Mass cannot be anything else than Enums::MassType::MONO or Enums::MassType::AVG.";
939
940 return -1;
941 }
942
943 //////////////// XML /////////////////////
944 /*!
945 \brief Returns true if parsing of XML \a element, using a \a{version}ed
946 function was successful, false otherwise.
947
948 The data in \a element are set to this CrossLinker instance if they validated
949 successfully, thus essentially initializing this CrossLinker instance.
950
951 The format of the element is:
952
953 \code
954 <clk>
955 <name>CFP-chromophore</name>
956 <formula></formula>
957 <modifname>Chromo-O</modifname>
958 <modifname>Chromo-H3</modifname>
959 <modifname>Chromo-H</modifname>
960 </clk>
961 \endcode
962
963 */
964 bool
965 435 CrossLinker::renderXmlClkElement(const QDomElement &element,
966 [[maybe_unused]] int version)
967 {
968
2/2
✓ Branch 2 taken 429 times.
✓ Branch 3 taken 6 times.
870 if(element.tagName() != "clk")
969 return false;
970
971 429 QDomElement child;
972
973
3/6
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 429 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 429 times.
✗ Branch 8 not taken.
858 child = element.firstChildElement("name");
974
975
3/4
✓ Branch 1 taken 429 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 426 times.
429 if(child.isNull())
976 return false;
977
978
1/2
✓ Branch 1 taken 426 times.
✗ Branch 2 not taken.
426 m_name = child.text();
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 426 times.
426 if(m_name.isEmpty())
980 return false;
981
982 // qDebug() << "The crosslinker name:" << m_name;
983
984
3/6
✓ Branch 1 taken 426 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 426 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 426 times.
✗ Branch 8 not taken.
852 child = child.nextSiblingElement("formula");
985
986
3/4
✓ Branch 1 taken 426 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 423 times.
426 if(child.isNull())
987 {
988
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 qCritical() << "The CrossLinker did not render correctly: the <formula> "
989
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "element tag was not found.";
990 3 m_isValid = false;
991 3 return m_isValid;
992 }
993
994 // Here, it is possible that the formula element be empty because the
995 // crosslinker might be accounted for by using the modifications in it.
996
7/10
✓ Branch 1 taken 423 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 423 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 423 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 420 times.
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 420 times.
846 if(!child.isNull() && !child.text().isEmpty())
997 {
998
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 Formula temp_formula;
999
1000
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if(!temp_formula.renderXmlFormulaElement(child))
1001 {
1002 qCritical()
1003 << "The CrossLinker did not render correctly: the formula did not "
1004 "render correctly.";
1005 m_isValid = false;
1006 return m_isValid;
1007 }
1008
1009 3 ErrorList error_list;
1010
1011
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
6 if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
1012 &error_list))
1013 {
1014
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 qCritical()
1015 << "The CrossLinker did not render correctly: the formula did not "
1016
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "render correctly with errors:"
1017
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 << Utils::joinErrorList(error_list);
1018 3 m_isValid = false;
1019 3 return m_isValid;
1020 }
1021
1022 m_formula = temp_formula.getActionFormula(/*with_title*/ true);
1023 // qDebug() << "The formula:" << m_formula;
1024 3 }
1025
1026 // At this point we need the PolChemDef to render the modifications,
1027 // so if we do not have that, return false.
1028
1029
2/2
✓ Branch 0 taken 414 times.
✓ Branch 1 taken 6 times.
420 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
1030 {
1031
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 qCritical() << "The PolChemDef is unavailable, cannot render any Modif.";
1032 6 return false;
1033 }
1034
1035 // At this point there might be 0, 1 or more "modifname" elements.
1036
3/6
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 414 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 414 times.
✗ Branch 8 not taken.
828 child = child.nextSiblingElement("modifname");
1037
1038 #if 0
1039 // Old version
1040 while(!child.isNull())
1041 {
1042 // qDebug() << "Now handling CrossLinker modif:" << child.text();
1043
1044 int index = Modif::isNameInList(child.text(), refList);
1045
1046 if(index == -1)
1047 {
1048 qDebug() << "Failed to parse one modification of the crosslink:"
1049 << m_name;
1050
1051 return false;
1052 }
1053 else
1054 {
1055 // qDebug()
1056 //<< "Found the CrossLinker modification in the reference list:"
1057 //<< m_name;
1058 }
1059
1060 // Modif *modif = mcsp_polChemDef->modifList().at(index);
1061 // qDebug() << "The found modif has name:" << modif->name()
1062 //<< "and masses:" << modif->mono() << "/" << modif->avg();
1063
1064 m_modifList.append(mcsp_polChemDef->modifList().at(index));
1065
1066 child = child.nextSiblingElement("modifname");
1067 }
1068 #endif
1069
1070
3/4
✓ Branch 1 taken 1452 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1038 times.
✓ Branch 4 taken 414 times.
1452 while(!child.isNull())
1071 {
1072
2/4
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1038 times.
2076 if(child.tagName() != "modifname")
1073 {
1074 qCritical() << "The CrossLinker did not render correctly: the "
1075 "<modifname> element tag was not found.";
1076
1077 return false;
1078 }
1079
1080
1/2
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
1038 QString modif_name = child.text();
1081 1038 qDebug() << "Currently parsed Modif name:" << modif_name;
1082
1083 1038 ModifCstSPtr modif_csp =
1084
1/2
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
1038 mcsp_polChemDef->getModifCstSPtrByName(modif_name);
1085
1086
1/2
✓ Branch 0 taken 1038 times.
✗ Branch 1 not taken.
1038 if(modif_csp != nullptr)
1087
1/2
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
1038 m_modifs.push_back(modif_csp);
1088 else
1089 {
1090 qCritical() << "The CrossLinker references a Modif by name that "
1091 "could not be found in the PolChemDef.";
1092
1093 return false;
1094 }
1095
1096
4/10
✓ Branch 1 taken 1038 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1038 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1038 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 1038 times.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
2076 child = child.nextSiblingElement("modifname");
1097 1038 }
1098
1099
3/6
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 414 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 414 times.
414 if(!calculateMasses(nullptr))
1100 {
1101 qDebug() << __FILE__ << __LINE__
1102 << "Failed to calculate masses for crossLinker" << m_name;
1103
1104 return false;
1105 }
1106
1107 414 ErrorList error_list;
1108
1109
2/4
✓ Branch 1 taken 414 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 414 times.
414 if(!validate(&error_list))
1110 {
1111 qCritical() << "The CrossLinker rendered as an XML <clk> element failed "
1112 "to validate with errors:"
1113 << Utils::joinErrorList(error_list, ", ");
1114
1115 m_isValid = false;
1116 return m_isValid;
1117 }
1118
1119 return true;
1120 843 }
1121
1122 /*!
1123 \brief Formats this CrossLinker instance in a heap-allocated string to be used
1124 as an XML element in the polymer chemistry definition.
1125
1126 The formatting of the XML element takes into account \a offset and \a
1127 indent by prepending the string with \a offset * \a indent character
1128 substring.
1129
1130 \a indent defaults to two spaces.
1131
1132 Returns a dynamically allocated string that needs to be freed after use.
1133 */
1134 QString
1135 9 CrossLinker::formatXmlClkElement(int offset, const QString &indent) const
1136 {
1137 9 QString text;
1138
1139 9 int newOffset;
1140 9 int iter = 0;
1141
1142
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 QString lead("");
1143
1144 // Prepare the lead.
1145 27 newOffset = offset;
1146
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 9 times.
27 while(iter < newOffset)
1147 {
1148
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 lead += indent;
1149 18 ++iter;
1150 }
1151
1152 /* We are willing to create an <modif> node that should look like this:
1153 *
1154 <clk>
1155 <name>Phosphorylation</name>
1156 <formula>-H+H2PO3</formula>
1157 <modifname>Phosphorylation</modifname>
1158 <modifname>Acetylation</modifname>
1159 </clk>
1160 *
1161 */
1162
1163
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
18 text += QString("%1<clk>\n").arg(lead);
1164
1165 // Prepare the lead.
1166 9 ++newOffset;
1167 9 lead.clear();
1168 9 iter = 0;
1169
2/2
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 9 times.
45 while(iter < newOffset)
1170 {
1171
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 lead += indent;
1172 27 ++iter;
1173 }
1174
1175 // Continue with indented elements.
1176
1177
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<name>%2</name>\n").arg(lead).arg(m_name);
1178
1179
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<formula>%2</formula>\n").arg(lead).arg(m_formula);
1180
1181 9 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
1182 9 m_modifs.cbegin();
1183 9 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
1184 9 m_modifs.cend();
1185
1186
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 9 times.
33 while(the_iterator_cst != the_end_iterator_cst)
1187 {
1188
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 text += QString("%1<modifname>%2</modifname>\n")
1189
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
48 .arg(lead)
1190
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
48 .arg((*the_iterator_cst)->getName());
1191
1192 24 ++the_iterator_cst;
1193 }
1194
1195 // Prepare the lead for the closing element.
1196 9 --newOffset;
1197 9 lead.clear();
1198 9 iter = 0;
1199
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 9 times.
36 while(iter < newOffset)
1200 {
1201
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 lead += indent;
1202 18 ++iter;
1203 }
1204
1205
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
18 text += QString("%1</clk>\n").arg(lead);
1206
1207 9 return text;
1208 9 }
1209
1210 //////////////// UTILS /////////////////////
1211 /*!
1212 \brief Returns a string representing this CrossLinker instance.
1213 */
1214 QString
1215 CrossLinker::toString() const
1216 {
1217 QString text;
1218
1219 text += QString("%1 [%2]\n").arg(m_name).arg(m_formula);
1220
1221 if(m_modifs.size())
1222 {
1223 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
1224 m_modifs.cbegin();
1225 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
1226 m_modifs.cend();
1227
1228 while(the_iterator_cst != the_end_iterator_cst)
1229 {
1230 text += QString("%1,").arg((*the_iterator_cst)->getName());
1231 ++the_iterator_cst;
1232 }
1233
1234 text += "\n";
1235 }
1236
1237 text += "masses: ";
1238 text += QString::number(m_mono);
1239 text += " - ";
1240 text += QString::number(m_avg);
1241
1242 return text;
1243 }
1244
1245
1246 } // namespace libXpertMassCore
1247 } // namespace MsXpS
1248