GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/CrossLinker.cpp
Date: 2025-11-20 01:41:33
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 139 CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp,
151 const QDomElement &element,
152 139 int version)
153
4/6
✓ Branch 2 taken 139 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 139 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 138 times.
139 : mcsp_polChemDef(pol_chem_def_csp)
154 {
155
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 138 times.
139 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
156
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 qCritical() << "Constructing CrossLinker with no PolChemDef.";
157
158
3/4
✓ Branch 1 taken 139 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 137 times.
139 if(!renderXmlClkElement(element, version))
159
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 qCritical()
160 << "Failed to fully render or validate the CrossLinker XML element "
161
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 "for construction of CrossLinker instance.";
162
0/2
✗ Branch 3 not taken.
✗ Branch 4 not taken.
139 }
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 29 CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp,
177 const QString &name,
178 const QString &formula,
179 double mono,
180 29 double avg)
181 58 : mcsp_polChemDef(pol_chem_def_csp),
182
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 12 times.
29 m_name(name),
183
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 12 times.
29 m_formula(formula),
184 29 m_mono(mono),
185 29 m_avg(avg)
186 {
187 29 }
188
189 /*!
190 \brief Constructs a CrossLinker instance as a copy of \a other.
191 */
192 22 CrossLinker::CrossLinker(const CrossLinker &other)
193 44 : mcsp_polChemDef(other.mcsp_polChemDef),
194
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
22 m_name(other.m_name),
195
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
22 m_formula(other.m_formula),
196 22 m_mono(other.m_mono),
197 44 m_avg(other.m_avg)
198 {
199 22 m_modifs.clear();
200
201 22 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
202 22 other.m_modifs.cbegin();
203 22 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
204 22 other.m_modifs.cend();
205
206
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 22 times.
86 while(the_iterator_cst != the_end_iterator_cst)
207 {
208
1/2
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
64 m_modifs.push_back((*the_iterator_cst));
209
210 64 ++the_iterator_cst;
211 }
212
213 22 ErrorList error_list;
214
215
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 m_isValid = validate(&error_list);
216
217
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 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.
22 }
222
223
224 /*!
225 \brief Destructs this CrossLinker instance
226 */
227 108 CrossLinker::~CrossLinker()
228 {
229 // We do not own the modifications in m_modifList!
230
2/2
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 4 times.
108 }
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 18 CrossLinker::setPolChemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp)
261 {
262 18 mcsp_polChemDef = pol_chem_def_csp;
263
264 18 ErrorList error_list;
265
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 m_isValid = validate(&error_list);
266
267
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
18 if(!m_isValid)
268
2/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
26 qCritical() << "Failed to validate the CrossLinker with errors:\n"
269
3/6
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 13 times.
✗ Branch 8 not taken.
26 << Utils::joinErrorList(error_list, ", ");
270 18 }
271
272 /*!
273 \brief Returns the PolChemDef.
274 */
275 const PolChemDefCstSPtr &
276 15 CrossLinker::getPolChemDefCstSPtr() const
277 {
278 15 return mcsp_polChemDef;
279 }
280
281 //////////////// THE NAME /////////////////////
282
283 /*!
284 \brief Sets the \a name of the CrossLinker.
285 */
286 void
287 2 CrossLinker::setName(const QString &name)
288 {
289 2 m_name = name;
290
291 2 ErrorList error_list;
292
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 m_isValid = validate(&error_list);
293
294
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(!m_isValid)
295 qCritical() << "Failed to validate the CrossLinker with errors:\n"
296 << Utils::joinErrorList(error_list, ", ");
297 2 }
298
299 /*!
300 \brief Returns the name.
301 */
302 QString
303 136 CrossLinker::getName() const
304 {
305
1/4
✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
136 return m_name;
306 }
307
308 //////////////// THE FORMULA /////////////////////
309 /*!
310 \brief Sets the formula to \a formula_string.
311 */
312 void
313 7 CrossLinker::setFormula(const QString &formula_string)
314 {
315 7 m_formula = formula_string;
316
317 7 ErrorList error_list;
318
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 m_isValid = validate(&error_list);
319
320
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if(!m_isValid)
321
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
10 qCritical() << "Failed to validate the CrossLinker with errors:\n"
322
3/6
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
10 << Utils::joinErrorList(error_list, ", ");
323 7 }
324
325 /*
326 \brief Returns the formula of this CrossLinker.
327 */
328 const QString &
329 21 CrossLinker::getFormula() const
330 {
331 21 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 191 CrossLinker::getModifsCstRef() const
341 {
342 191 return m_modifs;
343 }
344
345 /*!
346 \brief Returns a reference to the container of Modif instances.
347 */
348 std::vector<ModifCstSPtr> &
349 1 CrossLinker::getModifsRef()
350 {
351 1 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 9 CrossLinker::insertModifAt(ModifCstSPtr modif_csp, std::size_t index)
363 {
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(index >= m_modifs.size())
365 qFatalStream() << "The index is out of bounds.";
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if(modif_csp == nullptr || modif_csp.get() == nullptr)
367 qFatalStream() << "Programming error. The pointer cannot be nullptr";
368
369 9 m_modifs.insert(m_modifs.cbegin() + index, modif_csp);
370
371 9 return true;
372 }
373
374 /*!
375 \brief Adds \a modif_csp to this CrossLinker instance.
376
377 Returns true.
378 */
379 bool
380 18 CrossLinker::appendModif(ModifCstSPtr modif_csp)
381 {
382
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if(modif_csp == nullptr || modif_csp.get() == nullptr)
383 qFatalStream() << "Programming error. The pointer cannot be nullptr";
384
385 18 m_modifs.push_back(modif_csp);
386
387 18 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 19 CrossLinker::getModifAt(std::size_t index) const
397 {
398
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 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 19 times.
19 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 4 CrossLinker::removeModifAt(std::size_t index)
414 {
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(index >= m_modifs.size())
416 qFatalStream() << "Programming error. Index is out of bounds.";
417
418 4 m_modifs.erase(m_modifs.cbegin() + index);
419
420 4 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 6 CrossLinker::hasModif(const QString &modif_name)
429 {
430
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst = std::find_if(
431
6/12
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 6 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 6 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 6 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 6 times.
✗ Branch 21 not taken.
54 m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) {
432 12 return modif_csp->getName() == modif_name;
433 });
434
435 6 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 7 CrossLinker::modifIndex(const QString &modif_name)
445 {
446
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst = std::find_if(
447
6/12
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 7 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 7 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 7 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 7 times.
✗ Branch 21 not taken.
63 m_modifs.cbegin(), m_modifs.cend(), [modif_name](const auto &modif_csp) {
448 13 return modif_csp->getName() == modif_name;
449 });
450
451
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if(the_iterator_cst == m_modifs.cend())
452 return -1;
453
454 6 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 2 CrossLinker::operator=(const CrossLinker &other)
466 {
467
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(&other == this)
468 return *this;
469
470 1 mcsp_polChemDef = other.mcsp_polChemDef;
471 1 m_name = other.m_name;
472 1 m_formula = other.m_formula;
473 1 m_mono = other.m_mono;
474 1 m_avg = other.m_avg;
475
476 1 m_modifs.clear();
477
478 1 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
479 1 other.m_modifs.cbegin();
480 1 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
481 1 other.m_modifs.cend();
482
483
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 while(the_iterator_cst != the_end_iterator_cst)
484 {
485 3 m_modifs.push_back((*the_iterator_cst));
486
487 3 ++the_iterator_cst;
488 }
489
490 1 ErrorList error_list;
491
492
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 m_isValid = validate(&error_list);
493
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 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 1 return *this;
501 1 }
502
503 /*!
504 \brief Returns true if this CrossLinker and \a other are identical.
505 */
506 bool
507 19 CrossLinker::operator==(const CrossLinker &other) const
508 {
509
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
19 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 11 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 2 times.
13 if(m_name != other.m_name || m_formula != other.m_formula ||
517
6/8
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 7 times.
22 m_mono != other.m_mono || m_avg != other.m_avg ||
518
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7 times.
9 m_modifs.size() != other.m_modifs.size())
519 return false;
520
521
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 20 times.
27 for(std::size_t iter = 0; iter < m_modifs.size(); ++iter)
522 {
523
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20 times.
20 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 8 CrossLinker::operator!=(const CrossLinker &other) const
542 {
543
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if(&other == this)
544 return false;
545
546 7 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 1 CrossLinker::getFromPolChemDefByName() const
563 {
564
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
565 return nullptr;
566
567
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(m_name.isEmpty())
568 return nullptr;
569
570 1 std::vector<CrossLinkerSPtr>::const_iterator the_iterator_cst =
571 1 std::find_if(mcsp_polChemDef->getCrossLinkersCstRef().cbegin(),
572 1 mcsp_polChemDef->getCrossLinkersCstRef().cend(),
573 1 [&](const auto &cross_linker_csp) {
574
0/2
✗ Branch 0 not taken.
✗ Branch 1 not taken.
1 return cross_linker_csp->getName() == m_name;
575 });
576
577
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(the_iterator_cst == mcsp_polChemDef->getCrossLinkersCstRef().cend())
578 return nullptr;
579
580 1 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 1 CrossLinker::isKnownByNameInPolChemDef() const
599 {
600
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
601 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
602
603
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
2 if(mcsp_polChemDef->getCrossLinkerCstSPtrByName(m_name) != nullptr)
604 1 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 348 CrossLinker::validate(ErrorList *error_list_p) const
630 {
631 348 Q_ASSERT(error_list_p != nullptr);
632
633
3/4
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 340 times.
✗ Branch 3 not taken.
688 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr ||
634
5/8
✓ Branch 1 taken 340 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 340 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 340 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 340 times.
1028 mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr ||
635
8/14
✓ Branch 0 taken 340 times.
✓ Branch 1 taken 8 times.
✓ Branch 3 taken 340 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 340 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 340 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 340 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 340 times.
✓ Branch 13 taken 8 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
1376 mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr ||
636
4/8
✓ Branch 1 taken 340 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 340 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 340 times.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
688 !mcsp_polChemDef->getIsotopicDataCstSPtr()->size())
637 {
638 16 error_list_p->push_back("The PolChemDef or IsotopicData are not available");
639 16 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 8 times.
✗ Branch 2 not taken.
8 "failed.";
644
645 8 m_isValid = false;
646 8 return m_isValid;
647 }
648
649
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 328 times.
340 if(m_name.isEmpty())
650 {
651
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 qCritical() << "The CrossLinker name is empty.";
652 24 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 16 times.
✓ Branch 1 taken 324 times.
340 if(!m_formula.isEmpty())
658 {
659 16 Formula temp_formula(m_formula);
660
5/8
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 16 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 11 times.
32 if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
661 error_list_p))
662 {
663
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 qCritical() << "The CrossLinker formula failed to validate.";
664
1/2
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
10 error_list_p->push_back("The CrossLinker formula failed to validate");
665 }
666 16 }
667
668 340 double mono = 0.0;
669 340 double avg = 0.0;
670
671
4/6
✓ Branch 2 taken 340 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 340 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
✓ Branch 7 taken 335 times.
680 if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg))
672 {
673
1/2
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 qCritical() << "Failed to calculate the CrossLinker masses.";
674 10 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 340 times.
340 int size = m_modifs.size();
688
689
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 340 times.
340 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 761 times.
✓ Branch 1 taken 340 times.
1101 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 761 times.
761 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 348 return m_isValid;
713 }
714
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 761 times.
761 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 340 m_isValid = (error_list_p->size() ? false : true);
727 340 return m_isValid;
728 }
729
730 /*!
731 \brief Returns the validity status of this CrossLinker.
732 */
733 bool
734 153 CrossLinker::isValid() const
735 {
736 153 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 341 CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp,
753 double &mono,
754 double &avg) const
755 {
756 341 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
757
758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 341 times.
341 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 341 mono = 0;
776 341 avg = 0;
777
778 341 bool ok;
779
780
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 325 times.
341 if(!m_formula.isEmpty())
781 {
782 // Formula temp_formula(m_formula);
783
2/4
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16 times.
✗ Branch 5 not taken.
48 Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg);
784
785
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
16 if(!ok)
786 {
787
3/8
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
10 qCritical() << "Failed accounting masses for CrossLinker:" << m_name
788
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 << "and formula:" << m_formula;
789 5 m_isValid = false;
790 5 return m_isValid;
791 }
792 }
793
794 // Now, for each modif in the crossLinker, have to account their
795 // mass.
796
797 336 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
798 336 m_modifs.cbegin();
799 336 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
800 336 m_modifs.cend();
801
802
2/2
✓ Branch 0 taken 748 times.
✓ Branch 1 taken 336 times.
1084 while(the_iterator_cst != the_end_iterator_cst)
803 {
804
1/2
✓ Branch 1 taken 748 times.
✗ Branch 2 not taken.
748 (*the_iterator_cst)->accountMasses(mono, avg);
805 748 ++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 341 }
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 150 CrossLinker::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp)
826 {
827 150 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
828
829
1/2
✓ Branch 0 taken 150 times.
✗ Branch 1 not taken.
150 if(local_isotopic_data_csp == nullptr ||
830 local_isotopic_data_csp.get() == nullptr)
831 {
832
2/2
✓ Branch 0 taken 145 times.
✓ Branch 1 taken 5 times.
150 if(mcsp_polChemDef != nullptr)
833
2/4
✓ Branch 1 taken 145 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 145 times.
290 local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr();
834
835
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 145 times.
150 if(local_isotopic_data_csp == nullptr ||
836 local_isotopic_data_csp.get() == nullptr)
837 {
838
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 qCritical() << "Failed to find usable isotopic data.";
839 5 m_isValid = false;
840 5 return m_isValid;
841 }
842 }
843
844 // qDebug() << "Calculating masses for the cross-link";
845
846 145 m_mono = 0;
847 145 m_avg = 0;
848
849 145 bool ok;
850
851
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 139 times.
145 if(!m_formula.isEmpty())
852 {
853 // Formula temp_formula(m_formula);
854
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
18 Formula(m_formula).accountMasses(
855 6 ok, local_isotopic_data_csp, m_mono, m_avg);
856
857
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if(!ok)
858 {
859
3/8
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 5 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
10 qCritical() << "Failed accounting masses for CrossLinker:" << m_name
860
2/4
✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
5 << "and formula:" << m_formula;
861 5 m_isValid = false;
862 5 return m_isValid;
863 }
864 }
865
866 // Now, for each modif in the crossLinker, have to account their
867 // mass.
868
869 140 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
870 140 m_modifs.cbegin();
871 140 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
872 140 m_modifs.cend();
873
874
2/2
✓ Branch 0 taken 352 times.
✓ Branch 1 taken 140 times.
492 while(the_iterator_cst != the_end_iterator_cst)
875 {
876
1/2
✓ Branch 1 taken 352 times.
✗ Branch 2 not taken.
352 (*the_iterator_cst)->accountMasses(m_mono, m_avg);
877 352 ++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 150 }
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 2 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 2 times.
✗ Branch 1 not taken.
2 if(mono_p)
899 2 *mono_p += m_mono * times;
900
901
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(avg_p)
902 2 *avg_p += m_avg * times;
903
904 2 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 54 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 54 mono += m_mono * times;
921 54 avg += m_avg * times;
922
923 54 return *this;
924 }
925
926 /*!
927 \brief Returns the mass of the type defined by \a mass_type.
928 */
929 double
930 14 CrossLinker::getMass(Enums::MassType mass_type) const
931 {
932
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 if(mass_type == Enums::MassType::MONO)
933 7 return m_mono;
934
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 else if(mass_type == Enums::MassType::AVG)
935 7 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 145 CrossLinker::renderXmlClkElement(const QDomElement &element,
966 [[maybe_unused]] int version)
967 {
968
2/2
✓ Branch 2 taken 143 times.
✓ Branch 3 taken 2 times.
290 if(element.tagName() != "clk")
969 return false;
970
971 143 QDomElement child;
972
973
3/6
✓ Branch 1 taken 143 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 143 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 143 times.
✗ Branch 8 not taken.
286 child = element.firstChildElement("name");
974
975
3/4
✓ Branch 1 taken 143 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 142 times.
143 if(child.isNull())
976 return false;
977
978
1/2
✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
142 m_name = child.text();
979
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 142 times.
142 if(m_name.isEmpty())
980 return false;
981
982 // qDebug() << "The crosslinker name:" << m_name;
983
984
3/6
✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 142 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 142 times.
✗ Branch 8 not taken.
284 child = child.nextSiblingElement("formula");
985
986
3/4
✓ Branch 1 taken 142 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 141 times.
142 if(child.isNull())
987 {
988
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical() << "The CrossLinker did not render correctly: the <formula> "
989
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "element tag was not found.";
990 1 m_isValid = false;
991 1 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 141 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 141 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 141 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 140 times.
✓ Branch 10 taken 1 times.
✓ Branch 11 taken 140 times.
282 if(!child.isNull() && !child.text().isEmpty())
997 {
998
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 Formula temp_formula;
999
1000
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 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 1 ErrorList error_list;
1010
1011
4/8
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
2 if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
1012 &error_list))
1013 {
1014
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 qCritical()
1015 << "The CrossLinker did not render correctly: the formula did not "
1016
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "render correctly with errors:"
1017
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
2 << Utils::joinErrorList(error_list);
1018 1 m_isValid = false;
1019 1 return m_isValid;
1020 }
1021
1022 m_formula = temp_formula.getActionFormula(/*with_title*/ true);
1023 // qDebug() << "The formula:" << m_formula;
1024 1 }
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 138 times.
✓ Branch 1 taken 2 times.
140 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
1030 {
1031
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 qCritical() << "The PolChemDef is unavailable, cannot render any Modif.";
1032 2 return false;
1033 }
1034
1035 // At this point there might be 0, 1 or more "modifname" elements.
1036
3/6
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 138 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 138 times.
✗ Branch 8 not taken.
276 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 484 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 346 times.
✓ Branch 4 taken 138 times.
484 while(!child.isNull())
1071 {
1072
2/4
✓ Branch 1 taken 346 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 346 times.
692 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 346 times.
✗ Branch 2 not taken.
346 QString modif_name = child.text();
1081 346 qDebug() << "Currently parsed Modif name:" << modif_name;
1082
1083 346 ModifCstSPtr modif_csp =
1084
1/2
✓ Branch 1 taken 346 times.
✗ Branch 2 not taken.
346 mcsp_polChemDef->getModifCstSPtrByName(modif_name);
1085
1086
1/2
✓ Branch 0 taken 346 times.
✗ Branch 1 not taken.
346 if(modif_csp != nullptr)
1087
1/2
✓ Branch 1 taken 346 times.
✗ Branch 2 not taken.
346 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 346 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 346 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 346 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 346 times.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
692 child = child.nextSiblingElement("modifname");
1097 346 }
1098
1099
3/6
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 138 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 138 times.
138 if(!calculateMasses(nullptr))
1100 {
1101 qDebug() << __FILE__ << __LINE__
1102 << "Failed to calculate masses for crossLinker" << m_name;
1103
1104 return false;
1105 }
1106
1107 138 ErrorList error_list;
1108
1109
2/4
✓ Branch 1 taken 138 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 138 times.
138 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 281 }
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 3 CrossLinker::formatXmlClkElement(int offset, const QString &indent) const
1136 {
1137 3 QString text;
1138
1139 3 int newOffset;
1140 3 int iter = 0;
1141
1142
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 QString lead("");
1143
1144 // Prepare the lead.
1145 9 newOffset = offset;
1146
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 while(iter < newOffset)
1147 {
1148
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 lead += indent;
1149 6 ++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 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 text += QString("%1<clk>\n").arg(lead);
1164
1165 // Prepare the lead.
1166 3 ++newOffset;
1167 3 lead.clear();
1168 3 iter = 0;
1169
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
15 while(iter < newOffset)
1170 {
1171
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 lead += indent;
1172 9 ++iter;
1173 }
1174
1175 // Continue with indented elements.
1176
1177
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
6 text += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1178
1179
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
6 text += QString("%1<formula>%2</formula>\n").arg(lead).arg(m_formula);
1180
1181 3 std::vector<ModifCstSPtr>::const_iterator the_iterator_cst =
1182 3 m_modifs.cbegin();
1183 3 std::vector<ModifCstSPtr>::const_iterator the_end_iterator_cst =
1184 3 m_modifs.cend();
1185
1186
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 3 times.
11 while(the_iterator_cst != the_end_iterator_cst)
1187 {
1188
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 text += QString("%1<modifname>%2</modifname>\n")
1189
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 .arg(lead)
1190
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 .arg((*the_iterator_cst)->getName());
1191
1192 8 ++the_iterator_cst;
1193 }
1194
1195 // Prepare the lead for the closing element.
1196 3 --newOffset;
1197 3 lead.clear();
1198 3 iter = 0;
1199
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
12 while(iter < newOffset)
1200 {
1201
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 lead += indent;
1202 6 ++iter;
1203 }
1204
1205
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 text += QString("%1</clk>\n").arg(lead);
1206
1207 3 return text;
1208 3 }
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