GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/Monomer.cpp
Date: 2025-11-20 01:41:33
Lines:
506/653
77.5%
Functions:
49/57
86.0%
Branches:
523/1138
46.0%

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 /////////////////////// Stdlib includes
35 #include <vector>
36
37
38 /////////////////////// Qt includes
39 #include <QChar>
40 #include <QString>
41 #include <QUuid>
42
43
44 /////////////////////// Local includes
45 #include "MsXpS/libXpertMassCore/globals.hpp"
46 #include "MsXpS/libXpertMassCore/Monomer.hpp"
47 #include "MsXpS/libXpertMassCore/Formula.hpp"
48 #include "MsXpS/libXpertMassCore/Utils.hpp"
49
50
51 int monomerMetaTypeId = qRegisterMetaType<MsXpS::libXpertMassCore::Monomer>(
52 "MsXpS::libXpertMassCore::Monomer");
53
54
55 namespace MsXpS
56 {
57 namespace libXpertMassCore
58 {
59
60
61 /*!
62 \class MsXpS::libXpertMassCore::Monomer
63 \inmodule libXpertMassCore
64 \ingroup PolChemDefBuildingdBlocks
65 \inheaderfile Monomer.hpp
66
67 \brief The Monomer class provides abstractions to work with monomers.
68
69 In libmass, a momomer is an entity that is part of a polymer chemistry
70 definition. A monomer models a chemical entity that is part of a polymer.
71
72 In protein chemistry, that would be a \e{residue}, that is, an amino-acid that
73 has been polymerized into a residue chain (that is, a protein, or a peptide).
74 The chemical reaction that polymerizez an amino acid into an elongating protein
75 structure is a condensation, with loss of H2O from the amino acid to actually
76 lead to a what is called a \e{residue} of a monomer, or for short a \e{residue}.
77
78 \note The monomer, that is partly defined by its formula, has the formula of the
79 \e{residue}, not of the amino acid. This is always true, whatever the polymer
80 chemistry definition at hand: protein, saccharide, nucleic acid.
81 */
82
83
84 /*!
85 \variable MsXpS::libXpertMassCore::Monomer::mcsp_polChemDef
86
87 \brief The polymer chemistry definition that is the context in which the Monomer
88 instance exists.
89 */
90
91 /*!
92 \variable MsXpS::libXpertMassCore::Monomer::m_name
93
94 \brief The name of the monomer, like Lysine, Adenine.
95 */
96
97 /*!
98 \variable MsXpS::libXpertMassCore::Monomer::m_code
99
100 \brief The code of the monomer, like K for lysine, A for adenine.
101 */
102
103 /*!
104 \variable MsXpS::libXpertMassCore::Monomer::m_formula
105
106 \brief The formula of the monomer.
107 */
108
109 /*!
110 \variable MsXpS::libXpertMassCore::Monomer::m_modifs
111
112 \brief The container of \l Modif instances that are involved in the modification
113 of this Monomer.
114
115 \note
116
117 The Modif pointers stored in the m_modifs member are ModifSPtr pointers.
118 */
119
120 /*!
121 \variable MsXpS::libXpertMassCore::Monomer::m_isValid
122
123 \brief The validity status of the Monomer.
124 */
125
126 /*!
127 \typedef MsXpS::libXpertMassCore::MonomerRPtr
128 \relates MsXpS::libXpertMassCore::Monomer
129
130 Synonym for Monomer *.
131 */
132
133 /*!
134 \typedef MsXpS::libXpertMassCore::MonomerCstRPtr
135 \relates MsXpS::libXpertMassCore::Monomer
136
137 Synonym for const Monomer *.
138 */
139
140 /*!
141 \typedef MsXpS::libXpertMassCore::MonomerUPtr
142 \relates MsXpS::libXpertMassCore::Monomer
143
144 Synonym for std::unique_ptr<Monomer>.
145 */
146
147 /*!
148 \typedef MsXpS::libXpertMassCore::MonomerCstUPtr
149 \relates MsXpS::libXpertMassCore::Monomer
150
151 Synonym for std::unique_ptr<const Monomer>.
152 */
153
154 /*!
155 \typedef MsXpS::libXpertMassCore::MonomerSPtr
156 \relates MsXpS::libXpertMassCore::Monomer
157
158 Synonym for std::shared_ptr<Monomer>.
159 */
160
161 /*!
162 \typedef MsXpS::libXpertMassCore::MonomerCstSPtr
163 \relates MsXpS::libXpertMassCore::Monomer
164
165 Synonym for std::shared_ptr<const Monomer>.
166 */
167
168 /*!
169 \typedef MsXpS::libXpertMassCore::MonomerWPtr
170 \relates MsXpS::libXpertMassCore::Monomer
171
172 Synonym for std::weak_ptr<Monomer>.
173 */
174
175 /*!
176 \typedef MsXpS::libXpertMassCore::MonomerCstWPtr
177 \relates MsXpS::libXpertMassCore::Monomer
178
179 Synonym for std::weak_ptr<const Monomer>.
180 */
181
182 /*!
183 \typealias MsXpS::libXpertMassCore::UuidMonomerCstWPtrPair
184 \relates MsXpS::libXpertMassCore::Monomer
185
186 Synonym for std::pair<QString, MonomerCstWPtr> items.
187
188 These pairs are used to store a unique identifier (Uuid) string related to
189 a std::shared_ptr<const Monomer> type. This kind of pair is used in a
190 container in the \l CrossLink class. The fact that the std::shared_ptr is
191 converted to a std::weak_ptr is interesting because the item in the pair will
192 not increase the reference count.
193 */
194
195
196 /*!
197 \brief Constructs a monomer starting from an XML <mnm> \a element according to
198 \a version and using the reference polymer chemistry definition \a
199 pol_chem_def_csp.
200
201 This is the current format:
202 \code
203 <mnm>
204 <name>Glycine</name>
205 <code>G</code>
206 <formula>C2H3N1O1</formula>
207 </mnm>
208 \endcode
209 */
210 1439 Monomer::Monomer(PolChemDefCstSPtr pol_chem_def_csp,
211 const QDomElement &element,
212 1439 int version)
213
5/8
✓ Branch 3 taken 1439 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1439 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 1439 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 1436 times.
1439 : mcsp_polChemDef(pol_chem_def_csp)
214 {
215
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1436 times.
1439 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
216
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 qCritical() << "Constructing Monomer with no PolChemDef.";
217
218
3/4
✓ Branch 1 taken 1439 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 1429 times.
1439 if(!renderXmlMnmElement(element, version))
219
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
20 qCritical() << "Failed to fully render or validate the Monomer XML element "
220
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 "for construction of Monomer instance.";
221
222 // We cannot ask for validation because the polymer chemistry definition
223 // might be unavailable when using the XML element rendering.
224
225 // qDebug() << "Constructed Monomer" << m_name << ":" << toString();
226
0/2
✗ Branch 5 not taken.
✗ Branch 6 not taken.
1439 }
227
228
229 /*!
230 \brief Constructs a monomer with its member data set to \a name, \a code, \a
231 formula_string,
232 The \a pol_chem_def_csp and both masses \a mono and \a avg.
233
234 The member m_isValid validity status is set to the result of this Monomer
235 validation.
236 */
237 25718 Monomer::Monomer(PolChemDefCstSPtr pol_chem_def_csp,
238 const QString &name,
239 const QString &code,
240 const QString &formula_string,
241 double mono,
242 25718 double avg)
243 51436 : mcsp_polChemDef(pol_chem_def_csp),
244
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 25646 times.
25718 m_name(name),
245
2/2
✓ Branch 0 taken 25660 times.
✓ Branch 1 taken 58 times.
25718 m_code(code),
246
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 25657 times.
25718 m_formula(formula_string),
247 25718 m_mono(mono),
248
2/2
✓ Branch 2 taken 25712 times.
✓ Branch 3 taken 6 times.
51436 m_avg(avg)
249 {
250
3/4
✓ Branch 0 taken 25712 times.
✓ Branch 1 taken 6 times.
✓ Branch 3 taken 25712 times.
✗ Branch 4 not taken.
25718 if(mcsp_polChemDef != nullptr && mcsp_polChemDef.get() != nullptr)
251 {
252 25712 ErrorList error_list;
253
1/2
✓ Branch 1 taken 25712 times.
✗ Branch 2 not taken.
25712 m_isValid = validate(&error_list);
254
255
2/2
✓ Branch 0 taken 25652 times.
✓ Branch 1 taken 60 times.
25712 if(!m_isValid)
256 {
257
2/4
✓ Branch 1 taken 25652 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25652 times.
✗ Branch 5 not taken.
51304 qCritical() << "Construction of Monomer with validation errors:\n"
258
3/6
✓ Branch 1 taken 25652 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25652 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25652 times.
✗ Branch 8 not taken.
51304 << Utils::joinErrorList(error_list, ", ");
259 }
260 25712 }
261 else
262 m_isValid = false;
263
0/2
✗ Branch 5 not taken.
✗ Branch 6 not taken.
25718 }
264
265 /*!
266 \brief Constructs a monomer as a copy of \a other.
267 */
268 25650 Monomer::Monomer(const Monomer &other)
269 : PropListHolder(other),
270 51300 mcsp_polChemDef(other.mcsp_polChemDef),
271
1/2
✓ Branch 0 taken 25650 times.
✗ Branch 1 not taken.
25650 m_name(other.m_name),
272
1/2
✓ Branch 0 taken 25650 times.
✗ Branch 1 not taken.
25650 m_code(other.m_code),
273
2/2
✓ Branch 0 taken 25640 times.
✓ Branch 1 taken 10 times.
25650 m_formula(other.m_formula),
274 25650 m_mono(other.m_mono),
275
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 25649 times.
51300 m_avg(other.m_avg)
276 {
277
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25649 times.
25650 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
278
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 qCritical() << "Copy-constructing Monomer with no PolChemDef.";
279
280 // We do a real duplication of the Modif instances.
281
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 25650 times.
25659 for(ModifSPtr modif_sp : other.m_modifs)
282
4/10
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 9 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 9 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
36 storeModif(std::make_shared<Modif>(*modif_sp));
283
284 25650 ErrorList error_list;
285
1/2
✓ Branch 1 taken 25650 times.
✗ Branch 2 not taken.
25650 m_isValid = validate(&error_list);
286
287
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 25639 times.
25650 if(!m_isValid)
288 {
289
2/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
22 qCritical() << "Copy-construction of Monomer with validation errors:\n"
290
3/6
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
22 << Utils::joinErrorList(error_list, ", ");
291 }
292
0/2
✗ Branch 5 not taken.
✗ Branch 6 not taken.
25650 }
293
294 /*!
295 \brief Destroys the monomer.
296 */
297 91462 Monomer::~Monomer()
298 {
299 91462 m_modifs.clear();
300
2/2
✓ Branch 5 taken 45725 times.
✓ Branch 6 taken 6 times.
182912 }
301
302
303 //////////////// THE POLCHEMDEF /////////////////////
304 /*!
305 \brief Sets the polymer chemistry definition to \a pol_chem_def_csp.
306 */
307 void
308 4 Monomer::setPolChemDefCstSPtr(PolChemDefCstSPtr &pol_chem_def_csp)
309 {
310 4 mcsp_polChemDef = pol_chem_def_csp;
311
312 4 ErrorList error_list;
313
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_isValid = validate(&error_list);
314
315
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(!m_isValid)
316
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
8 qCritical() << "Failed to validate the Monomer with errors:\n"
317
3/6
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
8 << Utils::joinErrorList(error_list, ", ");
318 4 }
319
320 /*!
321 \brief Returns the polymer chemistry definition.
322 */
323 const PolChemDefCstSPtr &
324 14 Monomer::getPolChemDefCstSPtr() const
325 {
326 14 return mcsp_polChemDef;
327 }
328
329 //////////////// THE NAME /////////////////////
330
331 /*!
332 \brief Sets the \a name.
333 */
334 void
335 4 Monomer::setName(const QString &name)
336 {
337 4 m_name = name;
338
339 4 ErrorList error_list;
340
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_isValid = validate(&error_list);
341
342
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(!m_isValid)
343
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 qCritical() << "Failed to validate the Monomer with errors:\n"
344
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, ", ");
345 4 }
346
347 /*!
348 \brief Returns the name.
349 */
350 QString
351 236 Monomer::getName() const
352 {
353
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 4 times.
236 return m_name;
354 }
355
356
357 //////////////// THE CODE /////////////////////
358 /*!
359 \brief Sets the code to \a code
360
361 */
362 void
363 14 Monomer::setCode(const QString &code)
364 {
365 14 m_code = code;
366
367 14 ErrorList error_list;
368
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 m_isValid = validate(&error_list);
369
370
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 if(!m_isValid)
371
2/4
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
22 qCritical() << "Failed to validate the Monomer with errors:\n"
372
3/6
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
22 << Utils::joinErrorList(error_list, ", ");
373 14 }
374
375
376 /*!
377 \brief Returns the code
378 */
379 QString
380 756418 Monomer::getCode() const
381 {
382
2/2
✓ Branch 0 taken 756414 times.
✓ Branch 1 taken 4 times.
756418 return m_code;
383 }
384
385
386 /*!
387 \brief Checks the code's syntactic validity.
388
389 If \a code_length is not -1, then that length is used for the
390 check. Otherwise, the length from the polymer chemistry definition (if
391 available) is used.
392
393 The monomer code is verified and has to verify these criteria:
394
395 \list
396 \li It must be non-empty
397 \li Its character length has to be less or equal to the code length parameter
398 in the polymer chemistry definition (see PolChemDef::m_codeLength)
399 \li The first character is uppercase
400 \li All the remaining characters are lowercase
401 \endlist
402
403 Returns true if the code syntax checked successfully, false otherwise.
404
405 \sa validate()
406 */
407 bool
408 80224 Monomer::checkCodeSyntax(int code_length) const
409 {
410 80224 int local_code_length = code_length;
411
412
1/2
✓ Branch 0 taken 80224 times.
✗ Branch 1 not taken.
80224 if(local_code_length == -1)
413 {
414
2/2
✓ Branch 0 taken 80220 times.
✓ Branch 1 taken 4 times.
80224 if(mcsp_polChemDef != nullptr || mcsp_polChemDef.get() != nullptr)
415 {
416 80220 local_code_length = mcsp_polChemDef->getCodeLength();
417 }
418 else
419 {
420 8 qCritical() << "Checking code syntax with unlimited length because "
421
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 "Monomer has no PolChemDef.";
422 4 local_code_length = 1000;
423 }
424 }
425
426 // qDebug() << "Checking code syntax:" << m_code
427 // << "with code length:" << local_code_length;
428
429 // The code has to be at least one character long.
430
431 // The first letter in the code has to be uppercase.
432 // All the remaining authorized characters have to be
433 // lowercase.
434
435
4/4
✓ Branch 0 taken 80157 times.
✓ Branch 1 taken 67 times.
✓ Branch 2 taken 80149 times.
✓ Branch 3 taken 8 times.
80224 if(m_code.length() < 1 || m_code.length() > local_code_length)
436 {
437
2/4
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 75 times.
✗ Branch 6 not taken.
150 qCritical() << "The code has a length:" << m_code.length()
438
1/2
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
75 << "that does not match the expected code length:"
439
1/2
✓ Branch 1 taken 75 times.
✗ Branch 2 not taken.
75 << local_code_length;
440 75 m_isValid = false;
441 75 return false;
442 }
443
444 // Note that the actual monomer code length might be less than the
445 // codeLength member datum in the polymer chemistry
446 // definition.
447
448 80149 QString regexp_string =
449
1/2
✓ Branch 2 taken 80149 times.
✗ Branch 3 not taken.
80149 QString("([A-Z])([a-z]{0,%1})").arg(local_code_length - 1);
450
1/2
✓ Branch 1 taken 80149 times.
✗ Branch 2 not taken.
80149 QRegularExpression regexp(regexp_string);
451
1/2
✓ Branch 1 taken 80149 times.
✗ Branch 2 not taken.
80149 QRegularExpressionMatch match = regexp.match(m_code);
452
453
1/2
✓ Branch 1 taken 80149 times.
✗ Branch 2 not taken.
80149 if(match.hasMatch())
454 {
455 // qDebug() << "First uppercase char:" << match.captured(1);
456 // qDebug() << "Remaining lowercase chars:" << match.captured(2);
457
458 return true;
459 }
460 else
461 {
462 qDebug() << "Failed to RegExp match the monomer code:" << m_code
463 << "against an expected code length of:" << local_code_length;
464 }
465
466 return false;
467
468 #if 0
469 // Old version
470 for(int iter = 0; iter < m_code.size(); ++iter)
471 {
472 // Test that the m_code length is not greater than codeLength.
473 if(iter + 1 > codeLength)
474 {
475 PolChemDefEntity::m_isValid = false;
476 return false;
477 }
478
479 // And now check the character syntax.
480 QChar curChar = m_code.at(iter);
481
482 if(iter == 0)
483 {
484 if(curChar.category() != QChar::Letter_Uppercase)
485 {
486 PolChemDefEntity::m_isValid = false;
487 return false;
488 }
489 }
490 else if(curChar.category() == QChar::Letter_Uppercase)
491 {
492 PolChemDefEntity::m_isValid = false;
493 return false;
494 }
495 }
496
497 return true;
498 #endif
499 80149 }
500
501 //////////////// THE FORMULA /////////////////////
502
503 /*!
504 \brief Sets the formula to \a formula_string.
505 */
506 void
507 3 Monomer::setFormula(const QString &formula_string)
508 {
509 3 m_formula = formula_string;
510
511 3 ErrorList error_list;
512
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_isValid = validate(&error_list);
513
514
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if(!m_isValid)
515
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 qCritical() << "Failed to validate the Monomer with errors:\n"
516
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, ", ");
517 3 }
518
519 /*
520 \brief Returns the formula of this Monomer.
521 */
522 const QString &
523 7366 Monomer::getFormula() const
524 {
525 7366 return m_formula;
526 }
527
528
529 //////////////// THE MODIFICATIONS /////////////////////
530
531 /*!
532 \brief Returns the container of Modif instances that modify this Monomer.
533 */
534 const std::vector<ModifSPtr> &
535 603 Monomer::getModifsCstRef() const
536 {
537 603 return m_modifs;
538 }
539
540
541 /*!
542 \brief Returns the list of Modif names in the same order as the Modif instances
543 are stored in the member container.
544 */
545 std::vector<QString>
546 Monomer::modifNamesInOrder() const
547 {
548 std::vector<QString> names;
549
550 for(const ModifSPtr &modif_sp : m_modifs)
551 names.push_back(modif_sp->getName());
552
553 return names;
554 }
555
556
557 /*
558 \brief Returns true if this monomer is a target of Modif \a modif, false
559 otherwise.
560 */
561 bool
562 468 Monomer::isModifTarget(const Modif &modif) const
563 {
564 // Pure convenience function.
565 // qDebug() << "The modif:" << modif.toString();
566
567 468 return modif.doesTargetMonomer(m_code);
568 }
569
570
571 /*!
572 \brief Modifies this monomer using \a modif.
573
574 These two verifications that are done:
575
576 \list
577
578 \li This monomer must be a target of \a modif;
579
580 \li The count of \a modif modifications in this monomer must be at most the
581 authorized count - 1, to accomodate this new modification [see
582 Modif::maxCount()].
583
584 \endlist
585
586 The two restrictions above can be overridden by setting \a override to true.
587
588 If errors are encountered, these are reported as strings in \a error_list_p.
589
590 Returns a string with the Uuid of the allocated Modif instance.
591 */
592 QString
593 136 Monomer::modify(const Modif &modif, bool override, ErrorList *error_list_p)
594 {
595
3/4
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 134 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
136 if(!isModifTarget(modif) && !override)
596 {
597 // This monomer is not a target for the modif, and no override
598 // is allowed.
599
600 2 error_list_p->push_back(
601 2 QString("%1 not target of Modif %2 (no overriding allowed)")
602
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 .arg(m_name)
603
2/4
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
4 .arg(modif.getName()));
604
605 2 return QString();
606 }
607
608 134 qDebug() << "Good, the monomer is target of this modif.";
609
610
1/2
✓ Branch 2 taken 134 times.
✗ Branch 3 not taken.
134 int count = countModifsByName(modif.getName());
611
612 134 qDebug() << "There are" << count << "modifs by that name.";
613
614
4/4
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 121 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 8 times.
134 if(count >= modif.getMaxCount() && !override)
615 {
616 // This monomer has already the maximum count of 'modif' objects.
617
618 8 error_list_p->push_back(
619 8 QString("%1 already modified %2 times (no overriding allowed)")
620
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
16 .arg(m_name)
621
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
16 .arg(count));
622
623 8 return QString();
624 }
625
626 // We do a real allocation of a new Modif instance.
627 126 qDebug() << "Now allocating ModifSPtr:" << modif.getName()
628 << "and calling storeModifSPtrAsUuid with it.";
629
630 126 ModifSPtr modif_sp = std::make_shared<Modif>(modif);
631
632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if(modif_sp == nullptr || modif_sp.get() == nullptr)
633 qFatalStream() << "Failed to allocated new Modif object.";
634
635
1/2
✓ Branch 1 taken 126 times.
✗ Branch 2 not taken.
126 QString uuid = storeModif(modif_sp);
636
637 126 qDebug() << "The stored Modif:" << uuid << getModifForUuid(uuid)->getName();
638
639 126 return uuid;
640
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
252 }
641
642
643 /*!
644 \brief Modifies this monomer using \a modif_name.
645
646 \a modif_name is used to find a Modif instance in the polymer chemistry
647 definition.
648
649 If such a Modif is found, it is used to modify this Monomer.
650
651 Returns a string with the Uuid of the allocated Modif instance.
652 */
653 QString
654 2 Monomer::modify(const QString &modif_name, bool override, ErrorList *error_list_p)
655 {
656 2 ModifCstSPtr modif_csp = mcsp_polChemDef->getModifCstSPtrByName(modif_name);
657
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(modif_csp == nullptr)
659 {
660 qCritical() << "Modif by name" << modif_name << "is not known.";
661 return QString();
662 }
663
664
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 return modify(*modif_csp, override, error_list_p);
665 2 }
666
667 /*!
668 \brief Removes from this monomer the Modif instance tagged using the Uuid \a
669 uuid string.
670
671 Returns true if the unmodification was actually performed, false otherwise.
672 */
673 bool
674 13 Monomer::unmodify(const QString &uuid)
675 {
676
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 std::vector<UuidModifWPtrPair>::iterator the_iterator = std::find_if(
677 m_uuidModifPairs.begin(),
678 m_uuidModifPairs.end(),
679
8/22
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
✓ Branch 14 taken 13 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 13 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 13 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 13 times.
✗ Branch 23 not taken.
130 [uuid](UuidModifWPtrPair &pair) { return pair.first == uuid; });
680
681
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(the_iterator == m_uuidModifPairs.end())
682 {
683 qCritical() << "The modification was not found.";
684 return false;
685 }
686
687 // Sanity check
688 65 if((*the_iterator).second.expired() ||
689
5/10
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 13 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 13 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 13 times.
39 (*the_iterator).second.lock() == nullptr ||
690
2/6
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
26 !hasModif((*the_iterator).second.lock()))
691 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
692
693
3/6
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 13 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 13 times.
26 if(!removeModif((*the_iterator).second.lock()))
694 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
695
696 return true;
697 }
698
699
700 /*!
701 \brief Removes \e{all} the modification from this monomer.
702
703 Returns true if at least one Modif instance was removed, false if this Monomer
704 instance was not modified.
705 */
706 bool
707 2 Monomer::unmodify()
708 {
709
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 std::size_t modif_count = m_modifs.size();
710
711 // Sanity check
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(m_modifs.size() != m_uuidModifPairs.size())
713 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
714
715 2 m_modifs.clear();
716
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 m_uuidModifPairs.clear();
717
718 2 return modif_count != 0;
719 }
720
721
722 /*!
723 \brief Returns true if this monomer has at least one modification, false
724 otherwise.
725 */
726 bool
727 16806 Monomer::isModified() const
728 {
729 16806 return m_modifs.size();
730 }
731
732 /*!
733 \brief Returns the count of modifications by name \a modif_name in this
734 monomer.
735 */
736 int
737 143 Monomer::countModifsByName(const QString &modif_name)
738 {
739
1/2
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
143 int count = std::count_if(
740
4/8
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 143 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 143 times.
✗ Branch 6 not taken.
✓ Branch 13 taken 143 times.
✗ Branch 14 not taken.
1001 m_modifs.begin(), m_modifs.end(), [modif_name](const ModifSPtr &modif_sp) {
741 77 return modif_sp->getName() == modif_name;
742 143 });
743
744 143 return count;
745 }
746
747
748 //////////////// OPERATORS /////////////////////
749
750 /*!
751 \brief Assigns \a other's member data to this monomer.
752
753 The copy is deep, in particular with the mpa_modifList being copied.
754
755 Returns a reference to this monomer.
756 */
757 Monomer &
758 25643 Monomer::operator=(const Monomer &other)
759 {
760
1/2
✓ Branch 0 taken 25643 times.
✗ Branch 1 not taken.
25643 if(&other == this)
761 return *this;
762
763 25643 mcsp_polChemDef = other.mcsp_polChemDef;
764 25643 m_name = other.m_name;
765 25643 m_code = other.m_code;
766 25643 m_formula = other.m_formula;
767 25643 m_mono = other.m_mono;
768 25643 m_avg = other.m_avg;
769
770 // We want the modifs to be stored anew. So first clear.
771 25643 m_modifs.clear();
772
773 // We do a real duplication of the Modif instances.
774
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 25643 times.
25645 for(const ModifSPtr &modif_sp : other.m_modifs)
775
2/4
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
6 storeModif(std::make_shared<Modif>(*modif_sp));
776
777 25643 PropListHolder::operator=(other);
778
779 25643 ErrorList error_list;
780
781
1/2
✓ Branch 1 taken 25643 times.
✗ Branch 2 not taken.
25643 m_isValid = validate(&error_list);
782
783
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 25642 times.
25643 if(!m_isValid)
784 {
785
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 qCritical() << "Assignment of Monomer with validation errors:\n"
786
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, ", ");
787 }
788
789 // qDebug() << "Assignment of Monomer produced *this Monomer:" << toString();
790
791 25643 return *this;
792 25643 }
793
794
795 /*!
796 \brief Returns true if this monomer and \a other are identical, false
797 otherwise.
798
799 The comparison involves also the comparison of the Modif objects in
800 mpa_modifList.
801 */
802 bool
803 91 Monomer::operator==(const Monomer &other) const
804 {
805
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 80 times.
91 if(&other == this)
806 return true;
807
808 // We cannot compare the PolChemDef, because that would cause
809 // an infinite loop: (each instance of this class in the PolChemDef would
810 // try to compare the PolChemDef...).
811
812
6/6
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 70 times.
✓ Branch 5 taken 2 times.
80 if(m_name != other.m_name || m_code != other.m_code ||
813
5/6
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
152 m_formula != other.m_formula || m_mono != other.m_mono ||
814
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 m_avg != other.m_avg)
815 return false;
816
817
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if(m_modifs.size() != other.m_modifs.size())
818 {
819 // qDebug() << "The Modif containers have different sizes.";
820 return false;
821 }
822
823 // We do a deep Modif instance comparison.
824
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 4 times.
68 for(std::size_t iter = 0; iter < m_modifs.size(); ++iter)
825 {
826
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
4 if(*m_modifs.at(iter) != *other.m_modifs.at(iter))
827 {
828 // qDebug() << "At least one Modif instance differs in both Monomer
829 // instances.";
830 return false;
831 }
832 }
833
834 return true;
835 }
836
837
838 /*!
839 \brief Returns true if \c this monomer and \a other differ, false
840 otherwise.
841
842 Returns the negated result of operator==(other).
843 */
844 bool
845 1967 Monomer::operator!=(const Monomer &other) const
846 {
847
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 1894 times.
1967 if(&other == this)
848 return false;
849
850 73 return !operator==(other);
851 }
852
853
854 //////////////// VALIDATIONS /////////////////////
855 /*!
856 \brief Returns the Monomer instance from the polymer chemistry definition
857 registered in this instance.
858
859 The key to search the Monomer is this instance's member name.
860
861 If there is no PolChemDef available, nullptr is returned.
862
863 If no Monomer instance is found by this instance's name, nullptr is returned.
864 */
865 MonomerSPtr
866 2 Monomer::getFromPolChemDefByName() const
867 {
868
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
869 return nullptr;
870
871
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(m_name.isEmpty())
872 return nullptr;
873
874 2 return mcsp_polChemDef->getMonomerCstSPtrByName(m_name);
875 }
876
877 /*!
878 \brief Returns the Monomer instance from the polymer chemistry definition
879 registered in this instance.
880
881 The key to search the Monomer is this instance's member code.
882
883 If there is no PolChemDef available, nullptr is returned.
884
885 If no Monomer instance is found by this instance's code, nullptr is returned.
886 */
887 MonomerSPtr
888 2 Monomer::getFromPolChemDefByCode() const
889 {
890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
891 return nullptr;
892
893
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(m_code.isEmpty())
894 return nullptr;
895
896 2 return mcsp_polChemDef->getMonomerCstSPtrByCode(m_code);
897 }
898
899 /*!
900 \brief Returns the status of this Monomer instance the polymer chemistry
901 definition registered in this instance.
902
903 The key to search the Monomer is this instance's member name.
904
905 If there is no PolChemDef available,
906 Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned.
907
908 If no Monomer instance is found by this instance's name,
909 Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise
910 Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned.
911 */
912 Enums::PolChemDefEntityStatus
913 7 Monomer::isKnownByNameInPolChemDef() const
914 {
915
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
916 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
917
918
4/4
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 2 times.
10 if(mcsp_polChemDef->getMonomerCstSPtrByName(m_name) != nullptr)
919 4 return Enums::PolChemDefEntityStatus::ENTITY_KNOWN;
920
921 return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN;
922 }
923
924 /*!
925 \brief Returns the status of this Monomer instance the polymer chemistry
926 definition registered in this instance.
927
928 The key to search the Monomer is this instance's member code.
929
930 If there is no PolChemDef available,
931 Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned.
932
933 If no Monomer instance is found by this instance's code,
934 Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise
935 Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned.
936 */
937 Enums::PolChemDefEntityStatus
938 40067 Monomer::isKnownByCodeInPolChemDef() const
939 {
940
1/2
✓ Branch 0 taken 40067 times.
✗ Branch 1 not taken.
40067 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
941 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
942
943
4/4
✓ Branch 1 taken 40066 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 40066 times.
✓ Branch 4 taken 1 times.
80133 if(mcsp_polChemDef->getMonomerCstSPtrByCode(m_code) != nullptr)
944 40066 return Enums::PolChemDefEntityStatus::ENTITY_KNOWN;
945
946 return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN;
947 }
948
949
950 /*!
951 \brief Returns true if this monomer is valid, false otherwise.
952
953 Validation of the monomer occurs if:
954
955 \list
956 \li Its name is not empty
957 \li Its code is not empty and its syntax is correct
958 \li Its formula validates
959 \li Its modifications (if any) validate
960 \endlist
961
962 If errors are encountered, describing message are stored in \a error_list_p.
963
964 \sa checkCodeSyntax()
965 */
966 bool
967 78797 Monomer::validate(ErrorList *error_list_p) const
968 {
969
2/2
✓ Branch 0 taken 78788 times.
✓ Branch 1 taken 9 times.
78797 qsizetype error_count = error_list_p->size();
970
971
3/4
✓ Branch 0 taken 78788 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 78788 times.
✗ Branch 3 not taken.
157585 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr ||
972
5/8
✓ Branch 1 taken 78788 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 78788 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 78788 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 9 times.
✓ Branch 8 taken 78788 times.
236373 mcsp_polChemDef->getIsotopicDataCstSPtr() == nullptr ||
973
8/14
✓ Branch 0 taken 78788 times.
✓ Branch 1 taken 9 times.
✓ Branch 3 taken 78788 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 78788 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 78788 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 78788 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 78788 times.
✓ Branch 13 taken 9 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
315170 mcsp_polChemDef->getIsotopicDataCstSPtr().get() == nullptr ||
974
4/8
✓ Branch 1 taken 78788 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78788 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 78788 times.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
157585 !mcsp_polChemDef->getIsotopicDataCstSPtr()->size())
975 {
976 18 error_list_p->push_back("The PolChemDef or IsotopicData are not available");
977 18 qCritical()
978 << "The polymer chemistry definition member datum is nullptr or "
979 "its isotopic data are be either nullptr or empty. Monomer "
980 "validation "
981
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 "failed.";
982
983 9 m_isValid = false;
984 9 return false;
985 }
986
987
2/2
✓ Branch 0 taken 25648 times.
✓ Branch 1 taken 53140 times.
78788 if(m_name.isEmpty())
988 {
989
1/2
✓ Branch 2 taken 25648 times.
✗ Branch 3 not taken.
25648 qCritical() << "The Monomer name is empty.";
990 51296 error_list_p->push_back("The Monomer name is empty");
991 }
992
993 // qDebug() << "Now checking the code syntax.";
994
2/2
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 78704 times.
78788 if(!checkCodeSyntax())
995 {
996
1/2
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 qCritical() << "The Monomer code does not pass the syntax test.";
997 168 error_list_p->push_back("The Monomer code does not pass the syntax test");
998 }
999
1000 78788 Formula temp_formula(m_formula);
1001
5/8
✓ Branch 1 taken 78788 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78788 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 78788 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 25678 times.
✓ Branch 9 taken 53110 times.
157576 if(!temp_formula.validate(mcsp_polChemDef->getIsotopicDataCstSPtr(),
1002 error_list_p))
1003 {
1004
2/4
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25678 times.
✗ Branch 5 not taken.
25678 qCritical() << "The Monomer formula failed to validate.";
1005
1/2
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
51356 error_list_p->push_back("The Monomer formula failed to validate");
1006 }
1007
1008 78788 double mono = 0.0;
1009 78788 double avg = 0.0;
1010
1011
5/8
✓ Branch 1 taken 78788 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78788 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 78788 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 25678 times.
✓ Branch 9 taken 53110 times.
157576 if(!calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr(), mono, avg))
1012 {
1013
2/4
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25678 times.
✗ Branch 5 not taken.
25678 qCritical() << "Failed to calculate the Monomer masses.";
1014
1/2
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
51356 error_list_p->push_back("Failed to calculate the Monomer masses");
1015 }
1016
1017 // qDebug() << qSetRealNumberPrecision(6)
1018 // << "Validation calculated masses (not member data):" << mono << "-"
1019 // << avg;
1020
1021
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 78788 times.
78807 for(const ModifSPtr &modif_sp : m_modifs)
1022 {
1023 19 ErrorList local_error_list;
1024
3/4
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 18 times.
19 if(!modif_sp->validate(&local_error_list))
1025 {
1026
4/8
✓ 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.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
2 qCritical() << "Monomer's Modif" << modif_sp->getName()
1027
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 << "failed to validate successfully.";
1028
1029
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 error_list_p->push_back(QString("Failed to validate Monomer's Modif %1")
1030
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 .arg(modif_sp->getName()));
1031 }
1032 19 }
1033
1034 // If we added errors, then that means that the Monomer was not valid.
1035 78788 m_isValid = (error_list_p->size() > error_count ? false : true);
1036
1037 78788 return m_isValid;
1038 78788 }
1039
1040 /*!
1041 \brief Returns the validity status of this Monomer.
1042
1043 \sa validate()
1044 */
1045 bool
1046 1473 Monomer::isValid() const
1047 {
1048 1473 return m_isValid;
1049 }
1050
1051
1052 //////////////// MASS OPERATIONS /////////////////////
1053
1054 /*!
1055 \brief Calculates this monomer's monoisotopic and avg masses and sets the
1056 results to \a mono and \a avg.
1057
1058 The calculation is performed by computing the masses of this monomer's formula,
1059 accounting the chemical entities defined by \a monomer_chemical_entities.
1060
1061 The calculations are performed using reference data in \a isotopic_data_csp.
1062
1063 Returns true if the calculations were successful, false otherwise.
1064
1065 \sa Formula::accountMasses()
1066 */
1067 bool
1068 78792 Monomer::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp,
1069 double &mono,
1070 double &avg,
1071 Enums::ChemicalEntity monomer_chemical_entities) const
1072 {
1073 78792 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
1074
1075
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 78792 times.
78792 if(local_isotopic_data_csp == nullptr ||
1076 local_isotopic_data_csp.get() == nullptr)
1077 {
1078 if(mcsp_polChemDef != nullptr)
1079 local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr();
1080
1081 if(local_isotopic_data_csp == nullptr ||
1082 local_isotopic_data_csp.get() == nullptr)
1083 {
1084 qCritical() << "Failed to find usable isotopic data.";
1085 m_isValid = false;
1086 return m_isValid;
1087 }
1088 }
1089
1090 78792 mono = 0;
1091 78792 avg = 0;
1092
1093 78792 bool ok;
1094
1095 // Formula temp_formula(m_formula);
1096
2/4
✓ Branch 1 taken 78792 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 78792 times.
✗ Branch 5 not taken.
236376 Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, mono, avg);
1097
1098
2/2
✓ Branch 0 taken 25678 times.
✓ Branch 1 taken 53114 times.
78792 if(!ok)
1099 {
1100
3/8
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25678 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 25678 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
51356 qCritical() << "Failed accounting masses for Monomer:" << m_name
1101
2/4
✓ Branch 1 taken 25678 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 25678 times.
✗ Branch 5 not taken.
25678 << "and formula:" << m_formula;
1102 25678 m_isValid = false;
1103 25678 return m_isValid;
1104 }
1105
1106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53114 times.
53114 if(static_cast<int>(monomer_chemical_entities) &
1107 static_cast<int>(Enums::ChemicalEntity::MODIF))
1108 {
1109 for(const ModifSPtr &modif_sp : m_modifs)
1110 modif_sp->accountMasses(mono, avg, /*times*/ 1);
1111 }
1112
1113 return true;
1114 78792 }
1115
1116 /*!
1117 \brief Calculates this monomer's monoisotopic and avg masses.
1118
1119 The calculation is performed by computing the masses
1120 of this monomer's formula, accounting or not the entities described by \a
1121 monomer_chemical_entities.
1122
1123 The reference data for the computations are accessed at \a isotopic_data_csp.
1124
1125 Returns true if the calculations were successful, false otherwise.
1126
1127 \sa Formula::accountMasses()
1128 */
1129 bool
1130 3887 Monomer::calculateMasses(const IsotopicDataCstSPtr &isotopic_data_csp,
1131 Enums::ChemicalEntity monomer_chemical_entities)
1132 {
1133 3887 IsotopicDataCstSPtr local_isotopic_data_csp = isotopic_data_csp;
1134
1135
2/2
✓ Branch 0 taken 3885 times.
✓ Branch 1 taken 2 times.
3887 if(local_isotopic_data_csp == nullptr ||
1136 local_isotopic_data_csp.get() == nullptr)
1137 {
1138
1/2
✓ Branch 0 taken 3885 times.
✗ Branch 1 not taken.
3885 if(mcsp_polChemDef != nullptr)
1139
2/4
✓ Branch 1 taken 3885 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3885 times.
7770 local_isotopic_data_csp = mcsp_polChemDef->getIsotopicDataCstSPtr();
1140
1141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3885 times.
3885 if(local_isotopic_data_csp == nullptr ||
1142 local_isotopic_data_csp.get() == nullptr)
1143 {
1144 qCritical() << "Failed to find usable isotopic data.";
1145 m_isValid = false;
1146 return m_isValid;
1147 }
1148 }
1149
1150 3887 m_mono = 0;
1151 3887 m_avg = 0;
1152
1153 3887 bool ok;
1154
1155 // Formula temp_formula(m_formula);
1156
2/4
✓ Branch 1 taken 3887 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3887 times.
✗ Branch 6 not taken.
7774 Formula(m_formula).accountMasses(ok, local_isotopic_data_csp, m_mono, m_avg);
1157
1158
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3887 times.
3887 if(!ok)
1159 {
1160 qCritical() << "Failed accounting masses for Monomer:" << m_name
1161 << "and formula:" << m_formula;
1162 m_isValid = false;
1163 return m_isValid;
1164 }
1165
1166 // qDebug() << "After calculating the masses: the Monomer:" << toString();
1167 // qDebug() << "flags:" << static_cast<int>(monomer_chemical_entities)
1168 // << chemicalEntityMap[monomer_chemical_entities];
1169
1170
2/2
✓ Branch 0 taken 2456 times.
✓ Branch 1 taken 1431 times.
3887 if(static_cast<int>(monomer_chemical_entities) &
1171 static_cast<int>(Enums::ChemicalEntity::MODIF))
1172 {
1173
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 2456 times.
2485 for(const ModifSPtr &modif_sp : m_modifs)
1174
1/2
✓ Branch 1 taken 29 times.
✗ Branch 2 not taken.
29 modif_sp->accountMasses(&m_mono, &m_avg, /*times*/ 1);
1175 }
1176
1177 return true;
1178 3887 }
1179
1180 /*!
1181 \brief Calculates this monomer's monoisotopic and avg masses.
1182
1183 The calculation is performed by computing the masses
1184 of this monomer's formula.
1185
1186 If \a monomer_chemical_entities & MONOMER_CHEMENT_MODIF is true, then the
1187 masses are updated to account for the mass of modifications.
1188
1189 Set \a ok to true if the calculations were successful, false otherwise.
1190
1191 Returns this object.
1192
1193 \sa Formula::accountMasses()
1194 */
1195 Monomer &
1196 2 Monomer::calculateMasses(bool &ok,
1197 const IsotopicDataCstSPtr &isotopic_data_csp,
1198 Enums::ChemicalEntity monomer_chemical_entities)
1199 {
1200 2 ok = calculateMasses(isotopic_data_csp, monomer_chemical_entities);
1201 2 return *this;
1202 }
1203
1204 /*!
1205 \brief Increases \a mono_p and \a avg_p by the corresponding member masses
1206 first compounded by \a times.
1207
1208 Returns true.
1209 */
1210 const Monomer &
1211 6 Monomer::accountMasses(double *mono_p, double *avg_p, int times) const
1212 {
1213
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(mono_p != nullptr)
1214 6 *mono_p += m_mono * times;
1215
1216
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(avg_p != nullptr)
1217 6 *avg_p += m_avg * times;
1218
1219 6 return *this;
1220 }
1221
1222
1223 /*!
1224 \brief Increases \a mono and \a avg by the corresponding member masses first
1225 compounded by \a times.
1226
1227 Returns true.
1228 */
1229 const Monomer &
1230 18978 Monomer::accountMasses(double &mono, double &avg, int times) const
1231 {
1232 18978 mono += m_mono * times;
1233 18978 avg += m_avg * times;
1234
1235 18978 return *this;
1236 }
1237
1238
1239 /*!
1240 \brief Returns the mass of the type defined by \a mass_type.
1241 */
1242 double
1243 16 Monomer::getMass(Enums::MassType mass_type) const
1244 {
1245
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 if(mass_type == Enums::MassType::MONO)
1246 8 return m_mono;
1247
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 else if(mass_type == Enums::MassType::AVG)
1248 8 return m_avg;
1249
1250 qFatalStream()
1251 << "Mass cannot be anything else than Enums::MassType::MONO or Enums::MassType::AVG.";
1252
1253 return -1;
1254 }
1255
1256
1257 //////////////// FORMULA OPERATIONS /////////////////////
1258
1259 /*
1260 \brief Calculates a Formula representing this monomer .
1261
1262 The calculated formula accounts for this monomer's formula and for its
1263 modifications formulas if any and if \a (monomer_chemical_entities &
1264 MONOMER_CHEMENT_MODIF) is true.
1265
1266 This monomer's formula must validate using Modif::validate.
1267
1268 Returns the Formula.
1269
1270 \sa Modif::accountFormula()
1271 */
1272 QString
1273 287 Monomer::calculateFormula(Enums::ChemicalEntity monomer_chemical_entities) const
1274 {
1275 // We want to return the calculated formula of this monomer that accounts
1276 // for its modifications if so is requested.
1277
1278 287 IsotopicDataCstSPtr isotopic_data_csp =
1279 287 mcsp_polChemDef->getIsotopicDataCstSPtr();
1280
1281 // qDebug() << "Calculating formula for monomer: " << m_name
1282 //<< "with chemical entities:" << monomer_chemical_entities;
1283
1284
1/2
✓ Branch 1 taken 287 times.
✗ Branch 2 not taken.
287 Formula formula(m_formula);
1285
1286 // The m_formula above is only a text string. We need to convert that
1287 // into the symbol/count pair by validating it with true true params.
1288 // The formula is asked to validate with storage of the found symbol/count
1289 // pairs and with resetting of the previous contents of the symbol/count
1290 // map.
1291
1292 // We need to seed the symbol/count pairs because the following call
1293 // (accountFormula()) will update the pairs' values.
1294 287 ErrorList error_list;
1295
4/6
✓ Branch 2 taken 287 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 287 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 286 times.
574 if(!formula.validate(
1296 isotopic_data_csp, /*store*/ true, /*reset*/ true, &error_list))
1297 {
1298 1 qDebug() << "Formula:" << formula.getActionFormula()
1299 << "failed to validate with errors:"
1300 << Utils::joinErrorList(error_list, ", ");
1301
1302 1 return QString();
1303 }
1304
1305 286 bool ok = false;
1306
1307
2/2
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 148 times.
286 if(static_cast<int>(monomer_chemical_entities) &
1308 static_cast<int>(Enums::ChemicalEntity::MODIF))
1309 {
1310
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 138 times.
144 for(const ModifSPtr &modif_sp : m_modifs)
1311 {
1312
3/8
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 6 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
12 formula.accountFormula(modif_sp->formula(/*with_title*/ false),
1313
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 mcsp_polChemDef->getIsotopicDataCstSPtr(),
1314 1,
1315 ok);
1316
1317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(!ok)
1318 return QString();
1319 }
1320 }
1321
1322
1/2
✓ Branch 1 taken 286 times.
✗ Branch 2 not taken.
286 return formula.getActionFormula();
1323
1/2
✓ Branch 1 taken 287 times.
✗ Branch 2 not taken.
574 }
1324
1325 /*!
1326 \brief Parses the monomer XML \a element specifically for \a version.
1327
1328 Parses the monomer XML element passed as argument and for each
1329 encountered data will set the data to this monomer (this is
1330 called XML rendering).The parsing is delegated to a function that is specific
1331 for for \a version of the polymer chemistry definition.
1332
1333 The XML element is found in the polymer chemistry definition and has the
1334 following form:
1335
1336 \code
1337 <monomers>
1338 <mnm>
1339 <name>Glycine</name>
1340 <code>G</code>
1341 <formula>C2H3N1O1</formula>
1342 </mnm>
1343 <mnm>
1344 <name>Alanine</name>
1345 <code>A</code>
1346 <formula>C3H5N1O1</formula>
1347 </mnm>
1348 \endcode
1349
1350 After setting all the data, this monomer calculates it masses and
1351 validates itself. If any of these steps fails, the error is reported
1352 by returning false.
1353
1354 \sa validate()
1355 */
1356 bool
1357 1439 Monomer::renderXmlMnmElement(const QDomElement &element,
1358 [[maybe_unused]] int version)
1359 {
1360
1361 /* In a polymer chemistry definition, the xml node we are in is
1362 * structured this way:
1363 *
1364 * <mnm>
1365 * <name>Glycine</name>
1366 * <code>G</code>
1367 * <formula>C2H3N1O1</formula>
1368 * </mnm>
1369 *
1370 * And the element parameter points to the
1371 *
1372 * <mnm> element tag:
1373 * ^
1374 * |
1375 * +----- here we are right now.
1376 *
1377 * Which means that element.tagName() == "mnm" and that we'll have
1378 * to go one step down to the first child of the current node in
1379 * order to get to the <name> element.
1380 *
1381 */
1382
1383 1439 m_isValid = true;
1384
1385 // QString str;
1386 // QTextStream stream(&str);
1387 // QDomNode node = element;
1388 // node.save(stream, 2 /*indent*/);
1389 // qDebug().noquote() << "The element:\n" << str;
1390
1391
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1438 times.
2878 if(element.tagName() != "mnm")
1392 {
1393
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 qCritical() << "The expected <mnm> element is not found.";
1394 1 m_isValid = false;
1395 1 return m_isValid;
1396 }
1397
1398 1438 QDomElement child;
1399
1400
3/6
✓ Branch 1 taken 1438 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1438 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1438 times.
✗ Branch 8 not taken.
2876 child = element.firstChildElement("name");
1401
1402
8/10
✓ Branch 1 taken 1438 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1437 times.
✓ Branch 4 taken 1 times.
✓ Branch 6 taken 1437 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1436 times.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 1436 times.
2875 if(child.isNull() || child.text().isEmpty())
1403 {
1404
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 qCritical() << "The Monomer did not render correctly: problem with the "
1405
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 "<name> element.";
1406 2 m_isValid = false;
1407 2 return m_isValid;
1408 }
1409
1/2
✓ Branch 1 taken 1436 times.
✗ Branch 2 not taken.
1436 m_name = child.text();
1410 // qDebug() << "The name:" << m_name;
1411
1412
3/6
✓ Branch 1 taken 1436 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1436 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1436 times.
✗ Branch 8 not taken.
2872 child = child.nextSiblingElement("code");
1413
1414
3/4
✓ Branch 1 taken 1436 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1435 times.
1436 if(child.isNull())
1415 {
1416
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical() << "The Monomer did not render correctly: problem with the "
1417
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "<code> element.";
1418 1 m_isValid = false;
1419 1 return m_isValid;
1420 }
1421
1/2
✓ Branch 1 taken 1435 times.
✗ Branch 2 not taken.
1435 m_code = child.text();
1422 // qDebug() << "The code:" << m_code;
1423
1424
3/4
✓ Branch 1 taken 1435 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1434 times.
1435 if(!checkCodeSyntax())
1425 {
1426
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical() << "The Monomer did not render correctly: problem with the "
1427
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "code text.";
1428 1 m_isValid = false;
1429 1 return m_isValid;
1430 }
1431
1432
3/6
✓ Branch 1 taken 1434 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1434 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1434 times.
✗ Branch 8 not taken.
2868 child = child.nextSiblingElement("formula");
1433
1434
3/4
✓ Branch 1 taken 1434 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1433 times.
1434 if(child.isNull())
1435 {
1436
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical() << "The Monomer did not render correctly: problem with the "
1437
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "<formula> element.";
1438 1 m_isValid = false;
1439 1 return m_isValid;
1440 }
1441
1442
1/2
✓ Branch 1 taken 1433 times.
✗ Branch 2 not taken.
1433 Formula temp_formula;
1443
1444
3/4
✓ Branch 1 taken 1433 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1432 times.
1433 if(!temp_formula.renderXmlFormulaElement(child))
1445 {
1446
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 qCritical()
1447 << "The Monomer did not render correctly: the formula did not "
1448
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 "render correctly.";
1449 1 m_isValid = false;
1450 1 return m_isValid;
1451 }
1452
1/2
✓ Branch 1 taken 1432 times.
✗ Branch 2 not taken.
1432 m_formula = temp_formula.getActionFormula(/*with_title*/ true);
1453 // qDebug() << "The formula:" << m_formula;
1454
1455 1432 ErrorList error_list;
1456
1/2
✓ Branch 1 taken 1432 times.
✗ Branch 2 not taken.
1432 m_isValid = validate(&error_list);
1457
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1429 times.
1432 if(!m_isValid)
1458 {
1459
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 qCritical() << "The Monomer did not validate successfully after "
1460
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "rendering, with errors:";
1461
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
6 Utils::joinErrorList(error_list, ", ");
1462 }
1463 else
1464 {
1465 // At this point, because we are creating a Monomer from scratch,
1466 // and not by copying or by assignment, we calculate the masses
1467 // explicitely (validate() does that but not on member m_mono/m_avg
1468 // because the method is const.).
1469
1470
3/6
✓ Branch 1 taken 1429 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1429 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1429 times.
1429 if(!calculateMasses(nullptr))
1471 {
1472 qCritical() << "The Monomer's masses could not be calculated.";
1473 }
1474 }
1475
1476 // qDebug() << "Correctly rendered Monomer" << m_name << ":" << toString();
1477
1478 1432 return m_isValid;
1479 2871 }
1480
1481
1482 /*!
1483 \brief Formats this monomer's data as a string suitable to be used as an XML
1484 element in the polymer chemistry definition.
1485
1486 The typical monomer element that is generated in this function looks like
1487 this:
1488
1489 \code
1490 <monomers>
1491 <mnm>
1492 <name>Glycine</name>
1493 <code>G</code>
1494 <formula>C2H3N1O1</formula>
1495 </mnm>
1496 \endcode
1497
1498 The formatting of the XML element takes into account \a offset and \a
1499 indent by prepending the string with \a offset * \a indent character
1500 substring.
1501
1502 \a indent defaults to two spaces.
1503
1504 Returns a dynamically allocated string that needs to be freed after
1505 use.
1506 */
1507 QString
1508 21 Monomer::formatXmlMnmElement(int offset, const QString &indent) const
1509 {
1510 21 int newOffset;
1511 21 int iter = 0;
1512
1513 21 QString lead("");
1514 21 QString text;
1515
1516 // Prepare the lead.
1517 21 newOffset = offset;
1518
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 21 times.
84 while(iter < newOffset)
1519 {
1520
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
63 lead += indent;
1521 63 ++iter;
1522 }
1523
1524
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 text += QString("%1<mnm>\n").arg(lead);
1525
1526 // Prepare the lead.
1527 21 ++newOffset;
1528 21 lead.clear();
1529 21 iter = 0;
1530
2/2
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 21 times.
126 while(iter < newOffset)
1531 {
1532
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 lead += indent;
1533 84 ++iter;
1534 }
1535
1536 // Continue with indented elements.
1537
1538
3/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 text += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1539
1540
3/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 text += QString("%1<code>%2</code>\n").arg(lead).arg(m_code);
1541
1542
3/6
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 21 times.
✗ Branch 8 not taken.
42 text += QString("%1<formula>%2</formula>\n").arg(lead).arg(m_formula);
1543
1544 // Prepare the lead for the closing element.
1545 21 --newOffset;
1546 21 lead.clear();
1547 21 iter = 0;
1548
2/2
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 21 times.
105 while(iter < newOffset)
1549 {
1550
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
63 lead += indent;
1551 63 ++iter;
1552 }
1553
1554
2/4
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
42 text += QString("%1</mnm>\n").arg(lead);
1555
1556 21 return text;
1557 21 }
1558
1559
1560 /*!
1561 \brief Parses into this monomer the XML monomer \a element passed as argument.
1562
1563 The XML element comes from a polymer sequence file, where the monomer is
1564 singled out (not in a sequence string) because it might be modified, like
1565 this:
1566
1567 \code
1568 <monomer>
1569 <code>S</code>
1570 <mdf>
1571 <name>Phosphorylation</name>
1572 <formula>H1O3P1</formula>
1573 <targets>*</targets>
1574 <maxcount>1</maxcount>
1575 </mdf>
1576 </monomer>
1577 <codes>GRKASGSSPTSPINADKVENEDAFLEEVAEEKPHVKPYFTKTILDMEVVEGSAARFDCKIEGYPDPEVM</codes>
1578 <monomer>
1579 <code>W</code>
1580 <mdf>
1581 <name>Oxidation</name>
1582 <formula>O1</formula>
1583 <targets>*</targets>
1584 <maxcount>1</maxcount>
1585 </mdf>
1586 </monomer>
1587 <codes>YKDDQPVKESRHFQIDYDEEGNCSLTISEVCGDDDAKYTCKAVNSLGEATCTAELLVETMGKEGEGEGEGEEDEEEEEE</codes>
1588 \endcode
1589
1590 \a version indicates the format version of this XML \a element.
1591
1592 As soon as the monomer code is known, while parsing the \a element, the
1593 corresponding monomer is searched in the list of monomers in the member
1594 polymer chemistry definition (\c mcsp_polChemDef). Then, the found monomer is
1595 copied into \c this monomer so that both monomers are identical, effectively
1596 initializing this monomer to the monomer described by the \a element.
1597
1598 If the \a element contains one or more \c mdf modifications, these
1599 modifications are allocated as \l{Modif}'s and validated. If these
1600 modifications validate successfully, they are appended to this monomer's list
1601 of modifications.
1602
1603 Returns true if initialization of his monomer with the contents of \a
1604 element succeeded, false otherwise.
1605
1606 \sa formatXmlMonomerElement(int offset, const QString &indent)
1607 */
1608 bool
1609 52 Monomer::renderXmlMonomerElement(const QDomElement &element,
1610 [[maybe_unused]] int version)
1611 {
1612 52 qDebug();
1613
1614 52 m_isValid = true;
1615
1616 // QString str;
1617 // QTextStream stream(&str);
1618 // QDomNode node = element;
1619 // node.save(stream, 2 /*indent*/);
1620 // qDebug().noquote() << "The element:\n" << str;
1621
1622
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
104 if(element.tagName() != "monomer")
1623 {
1624 qCritical() << "The expected <monomer> element is not found.";
1625 m_isValid = false;
1626 return m_isValid;
1627 }
1628
1629 52 qDebug() << "Indeed a monomer element.";
1630
1631 52 QDomElement child;
1632
1633
3/6
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 52 times.
✗ Branch 8 not taken.
104 child = element.firstChildElement("code");
1634
1635
5/10
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 52 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 52 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 52 times.
104 if(child.isNull() || child.text().isEmpty())
1636 {
1637 qCritical() << "The Monomer did not render correctly: problem with the "
1638 "<code> element.";
1639 m_isValid = false;
1640 return m_isValid;
1641 }
1642
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 QString code = child.text();
1643
1644 52 qDebug() << "The code:" << code;
1645
1646 // Use the code to access the corresponding Monomer in the list of Monomers
1647 // in the polymer chemistry definition.
1648
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 MonomerSPtr monomer_csp = mcsp_polChemDef->getMonomerCstSPtrByCode(code);
1649
1650 52 qDebug() << "monomer pointer:" << monomer_csp.get();
1651
1652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if(monomer_csp == nullptr)
1653 {
1654 qCritical() << "The monomer's code is not found in the polymer chemistry "
1655 "definition.";
1656
1657 m_isValid = false;
1658 return m_isValid;
1659 }
1660
1661 52 qDebug() << "monomer name:" << monomer_csp->getName();
1662
1663
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 *this = *monomer_csp;
1664
1665 // Sanity check
1666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if(m_code != code)
1667 qFatal("Programming error. Both codes should be identical.");
1668
1669 // And now we have to manage the mdf objects.
1670
2/4
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
104 child = child.nextSiblingElement();
1671
1672
3/4
✓ Branch 1 taken 104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
✓ Branch 4 taken 52 times.
104 while(!child.isNull())
1673 {
1674
2/4
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 52 times.
104 if(child.tagName() != "mdf")
1675 {
1676 qCritical()
1677 << "The Monomer did not render correctly: problem with the "
1678 "<mdf> element.";
1679
1680 m_isValid = false;
1681 return m_isValid;
1682 }
1683
1684
1/4
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
52 Modif modif(mcsp_polChemDef, child, version);
1685
1686 52 qDebug() << "20250428-Created Modif:" << modif.getName()
1687 << "with Formula:" << modif.getFormula();
1688
1689
4/8
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 52 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 52 times.
104 if(!modif.calculateMasses(mcsp_polChemDef->getIsotopicDataCstSPtr()))
1690 {
1691 qCritical() << "Failed to calculate masses for Monomer's Modif"
1692 << modif.getName();
1693
1694 m_isValid = false;
1695 return m_isValid;
1696 }
1697
1698 // The validation will take care of checking that the <targets>
1699 // element did have correct text inside.
1700
1701 52 ErrorList error_list;
1702
2/4
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
52 if(!modif.validate(&error_list))
1703 {
1704 qCritical() << "Failed to validate modification" << modif.getName()
1705 << "with errors:"
1706 << Utils::joinErrorList(error_list, ", ");
1707
1708 m_isValid = false;
1709 return m_isValid;
1710 }
1711
1712 52 error_list.clear();
1713
1714 // qDebug() << "At this point, going to modify the monomer.";
1715
1/2
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
52 QString uuid = modify(modif, /*override*/ false, &error_list);
1716
1717 // qDebug() << "The returned uuid:" << uuid;
1718
1719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if(uuid.isEmpty())
1720 {
1721 qCritical() << "The monomer could not be modified, with errors:"
1722 << Utils::joinErrorList(error_list, ", ");
1723
1724 m_isValid = false;
1725 return m_isValid;
1726 }
1727
1728
2/4
✓ Branch 1 taken 52 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
104 child = child.nextSiblingElement();
1729 52 }
1730
1731 52 m_isValid = true;
1732 52 return m_isValid;
1733 104 }
1734
1735
1736 /*!
1737 \brief Formats a string suitable to be used as an XML element in a
1738 polymer sequence file.
1739
1740 The typical monomer element that is generated in this function looks like
1741 this:
1742
1743 \code
1744 <monomer>
1745 <code>S</code>
1746 <prop>
1747 <name>MODIF</name>
1748 <data>Phosphorylation</data>
1749 </prop>
1750 <prop>
1751 <name>COMMENT</name>
1752 <data>Phosphorylation is only partial</data>
1753 </prop>
1754 </monomer>
1755 \endcode
1756
1757 The formatting of the XML element takes into account \a offset and \a
1758 indent by prepending the string with \a offset * \a indent character
1759 substring.
1760
1761 \a indent defaults to two spaces.
1762
1763 Returns a string.
1764 */
1765 QString
1766 1 Monomer::formatXmlMonomerElement(int offset, const QString &indent) const
1767 {
1768 1 int newOffset;
1769 1 int iter = 0;
1770
1771 1 QString lead("");
1772 1 QString text;
1773
1774 // Prepare the lead.
1775 1 newOffset = offset;
1776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 while(iter < newOffset)
1777 {
1778 lead += indent;
1779 ++iter;
1780 }
1781
1782
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 text.append(QString("%1<monomer>\n").arg(lead));
1783
1784 // Prepare the lead for the <code> child that is indented.
1785 1 ++newOffset;
1786 1 lead.clear();
1787 1 iter = 0;
1788
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
3 while(iter < newOffset)
1789 {
1790
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 lead += indent;
1791 1 ++iter;
1792 }
1793
1794 1 QString code_element_string =
1795
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.
1 QString("%1<code>%2</code>\n").arg(lead).arg(m_code);
1796 1 qDebug() << "code element:" << code_element_string;
1797
1798
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 text.append(code_element_string);
1799
1800 // The monomer may have any number of modif objects, which we have
1801 // to document here.
1802
1803 // Continue with indented <mdf> element(s) (same indent as for <code>).
1804
1805
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for(const ModifSPtr &modif_sp : m_modifs)
1806
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 text.append(modif_sp->formatXmlMdfElement(newOffset, indent));
1807
1808 // Prepare the lead for the closing element.
1809 1 --newOffset;
1810 1 lead.clear();
1811 1 iter = 0;
1812
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
2 while(iter < newOffset)
1813 {
1814 lead += indent;
1815 ++iter;
1816 }
1817
1818
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 text.append(QString("%1</monomer>\n").arg(lead));
1819
1820 // QString debug_message =
1821 // QString("%1\n%2\n").arg("Returning string:").arg(text);
1822 // qDebug().noquote() << debug_message;
1823
1824 1 return text;
1825 1 }
1826
1827
1828 //////////////// UTILS /////////////////////
1829
1830 /*!
1831 \brief Stores the Modif instance \a modif_sp pointer in the member container.
1832
1833 The \a modif_sp is stored as is, without duplication.
1834
1835 Returns the Uuid string associated to the stored Modif.
1836 */
1837 QString
1838 137 Monomer::storeModif(const ModifSPtr &modif_sp)
1839 {
1840
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 if(modif_sp == nullptr)
1841 qFatalStream() << "The provided ModifSPtr is nullptr.";
1842
1843 137 qDebug() << "Right before storage, there are currently" << m_modifs.size()
1844 << "modifications.";
1845
1846 // Do not store an item twice.
1847
3/6
✓ Branch 1 taken 137 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 137 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 137 times.
274 if(hasModif(modif_sp) || !getUuidForModif(modif_sp).isEmpty())
1848 qFatalStream() << "It is prohibited to store the same ModifSPtr more than once.";
1849
1850 137 qDebug() << "Ok, we can go on.";
1851
1852 // Even if we get a ref to shared_ptr, the reference count increment will
1853 // occur.
1854 137 m_modifs.push_back(modif_sp);
1855 137 QString uuid = QUuid::createUuid().toString();
1856
1/2
✓ Branch 2 taken 137 times.
✗ Branch 3 not taken.
137 m_uuidModifPairs.push_back(UuidModifWPtrPair(uuid, modif_sp));
1857
1858 137 qDebug() << "Right after storage, there are currently" << m_modifs.size()
1859 << "modifications.";
1860
1861 137 return uuid;
1862 }
1863
1864 /*!
1865 \brief Stores the Modif instance \a modif in the member container.
1866
1867 The \a modif is used to craft a ModifSPtr that is stored.
1868
1869 Returns the Uuid string associated to the stored Modif.
1870 */
1871 QString
1872 Monomer::storeModif(const Modif &modif)
1873 {
1874 ModifSPtr modif_sp = std::make_shared<Modif>(modif);
1875
1876 if(modif_sp == nullptr)
1877 {
1878 qFatalStream() << "Failed to allocate ModifSPtr.";
1879 return "";
1880 }
1881
1882 return storeModif(modif_sp);
1883 }
1884
1885
1886 /*!
1887 \brief Returns true if \a modif_sp was found in the member container of Modif
1888 instances, false otherwise.
1889 */
1890 bool
1891 310 Monomer::hasModif(const ModifSPtr &modif_sp) const
1892 {
1893
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 310 times.
310 if(modif_sp == nullptr)
1894 qFatalStream() << "Pointer cannot be nullptr.";
1895
1896 310 std::vector<ModifSPtr>::const_iterator the_iterator_cst =
1897 620 std::find_if(m_modifs.cbegin(),
1898 m_modifs.cend(),
1899
5/10
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 310 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 310 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 310 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 310 times.
✗ Branch 15 not taken.
1860 [modif_sp](const ModifSPtr &the_modif_sp) {
1900
11/14
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 6 times.
✓ Branch 9 taken 11 times.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 26 times.
✓ Branch 12 taken 17 times.
✓ Branch 13 taken 42 times.
115 return the_modif_sp == modif_sp;
1901 });
1902
1903
2/2
✓ Branch 0 taken 274 times.
✓ Branch 1 taken 36 times.
310 if(the_iterator_cst == m_modifs.cend())
1904 {
1905 274 std::vector<UuidModifWPtrPair>::const_iterator the_pair_iterator_cst =
1906 548 std::find_if(m_uuidModifPairs.cbegin(),
1907 m_uuidModifPairs.cend(),
1908
5/10
✓ Branch 2 taken 274 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 274 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 274 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 274 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 274 times.
✗ Branch 16 not taken.
1644 [modif_sp](const UuidModifWPtrPair &the_pair) {
1909
2/2
✓ Branch 1 taken 76 times.
✓ Branch 2 taken 2 times.
78 return the_pair.second.lock() == modif_sp;
1910 });
1911
1912
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 274 times.
274 if(the_pair_iterator_cst != m_uuidModifPairs.cend())
1913 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
1914
1915 274 return false;
1916 }
1917
1918 return true;
1919 }
1920
1921 /*!
1922 \brief Returns true if \a modif_sp was found in the member container of
1923 Uuid-Modif pairs, false otherwise.
1924 */
1925 bool
1926 Monomer::hasUuid(const ModifSPtr &modif_sp) const
1927 {
1928 if(modif_sp == nullptr)
1929 qFatalStream() << "Pointer cannot be nullptr.";
1930
1931 std::vector<UuidModifWPtrPair>::const_iterator the_iterator_cst =
1932 std::find_if(m_uuidModifPairs.cbegin(),
1933 m_uuidModifPairs.cend(),
1934 [modif_sp](const UuidModifWPtrPair &the_pair) {
1935 return the_pair.second.lock() == modif_sp;
1936 });
1937
1938 if(the_iterator_cst == m_uuidModifPairs.cend())
1939 return false;
1940
1941 // Sanity check
1942
1943 if(!hasModif(modif_sp))
1944 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
1945
1946 return true;
1947 }
1948
1949 /*!
1950 \brief Returns the Monomer instance pointer in the member container that is
1951 associated to the \a uuid Uuid string.
1952
1953 If no such Monomer instance pointer is found, nullptr is returned.
1954 */
1955 ModifSPtr
1956 11 Monomer::getModifForUuid(const QString &uuid) const
1957 {
1958 11 qDebug() << "There are currently" << m_modifs.size()
1959 << "modifications. The Modif uuid that is asked for:" << uuid;
1960
1961
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 std::vector<std::pair<QString, ModifWPtr>>::const_iterator the_iterator_cst =
1962 22 std::find_if(m_uuidModifPairs.cbegin(),
1963 m_uuidModifPairs.cend(),
1964
8/8
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 1 times.
✓ Branch 8 taken 10 times.
✓ Branch 9 taken 1 times.
95 [uuid](const UuidModifWPtrPair &the_pair) {
1965 19 qDebug() << "Iterating into" << the_pair.first
1966 << "while searching for" << uuid;
1967
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 9 times.
✓ Branch 13 taken 1 times.
16 return the_pair.first == uuid;
1968 });
1969
1970
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10 times.
11 if(the_iterator_cst == m_uuidModifPairs.cend())
1971 1 return nullptr;
1972
1973 10 ModifSPtr modif_sp = (*the_iterator_cst).second.lock();
1974
1975 // Sanity check
1976
1977
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 if(!hasModif(modif_sp))
1978 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
1979
1980 10 return modif_sp;
1981 10 }
1982
1983 /*!
1984 \brief Returns the UUID string identifying \a modif_sp in the member container.
1985
1986 If no such Modif is found, an empty string is returned.
1987 */
1988 QString
1989 150 Monomer::getUuidForModif(const ModifSPtr &modif_sp) const
1990 {
1991
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
150 if(modif_sp == nullptr)
1992 qFatalStream() << "Pointer cannot be nullptr.";
1993
1994 150 std::vector<UuidModifWPtrPair>::const_iterator the_iterator_cst =
1995 300 std::find_if(m_uuidModifPairs.cbegin(),
1996 m_uuidModifPairs.cend(),
1997
5/10
✓ Branch 2 taken 150 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 150 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 150 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 150 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 150 times.
✗ Branch 16 not taken.
900 [modif_sp](const UuidModifWPtrPair &the_pair) {
1998 // Do not query the modif_sp managed object because it can
1999 // be nullptr!
2000
2/2
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 1 times.
52 return the_pair.second.lock() == modif_sp;
2001 });
2002
2003
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 13 times.
150 if(the_iterator_cst == m_uuidModifPairs.cend())
2004 {
2005 // Sanity check
2006
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 137 times.
137 if(hasModif(modif_sp))
2007 qFatalStream() << "Inconsistency between the m_modifs and the "
2008 "m_uuidModifPairs vectors.";
2009
2010 137 return QString();
2011 }
2012
2013 // Sanity check
2014
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13 times.
13 if(!hasModif(modif_sp))
2015 qFatalStream()
2016 << "Inconsistency between the m_modifs and the m_uuidModifPairs vectors.";
2017
2018
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
163 return (*the_iterator_cst).first;
2019 }
2020
2021
2022 /*!
2023 \brief Returns a container of QString instances that correspond to the UUID
2024 strings that identify all the Modif instances involved in this Monomer.
2025
2026 If no Modif is found, an empty container is returned.
2027 */
2028 std::vector<QString>
2029 Monomer::getAllModifUuids() const
2030 {
2031 std::vector<QString> the_uuid_strings;
2032
2033 for(const UuidModifCstWPtrPair pair : m_uuidModifPairs)
2034 the_uuid_strings.push_back(pair.first);
2035
2036 // Sanity check
2037 if(the_uuid_strings.size() != m_modifs.size())
2038 qFatalStream() << "Inconsistency between the <object>_s and <uuid-object> pairs.";
2039
2040 return the_uuid_strings;
2041 }
2042
2043 void
2044 Monomer::cleanupModifs()
2045 {
2046 qDebug() << "At beginning, count of UUID-Modif pairs:"
2047 << m_uuidModifPairs.size();
2048
2049 std::vector<UuidModifWPtrPair>::iterator the_iterator =
2050 m_uuidModifPairs.begin();
2051 std::vector<UuidModifWPtrPair>::iterator the_end_iterator =
2052 m_uuidModifPairs.end();
2053
2054 while(the_iterator != the_end_iterator)
2055 {
2056 if((*the_iterator).second.expired() ||
2057 (*the_iterator).second.lock() == nullptr ||
2058 !hasModif((*the_iterator).second.lock()))
2059 the_iterator = m_uuidModifPairs.erase(the_iterator);
2060 else
2061 ++the_iterator;
2062 }
2063
2064 qDebug() << "At end, count of UUID-Modif pairs:" << m_uuidModifPairs.size();
2065 }
2066
2067
2068 bool
2069 13 Monomer::removeModif(ModifSPtr modif_sp)
2070 {
2071
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(modif_sp == nullptr || modif_sp.get() == nullptr)
2072 qFatalStream() << "Cannot be that pointer is nullptr.";
2073
2074 // We will need this anyway.
2075 13 QString uuid = getUuidForModif(modif_sp);
2076
2077 // Some controls are in order: we have to check that at index, there is
2078 // a ModifSPtr that is present both in m_modifs and in
2079 // m_uuidModifPairs.
2080
2081
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
26 std::vector<ModifSPtr>::const_iterator the_iterator_cst = std::find_if(
2082
2/4
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
39 m_modifs.begin(), m_modifs.end(), [modif_sp](ModifSPtr iter_modif_sp) {
2083
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 return iter_modif_sp == modif_sp;
2084
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 });
2085
2086
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(the_iterator_cst == m_modifs.end())
2087 {
2088 qCritical() << "The ModifSPtr was not found in the container.";
2089
2090 if(!uuid.isEmpty())
2091 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
2092
2093 return false;
2094 }
2095
2096 // At this point, both containers contain modif_sp.
2097
2098 13 m_modifs.erase(the_iterator_cst);
2099
2100
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 std::vector<UuidModifWPtrPair>::const_iterator the_pair_iterator_cst =
2101 26 std::find_if(m_uuidModifPairs.cbegin(),
2102 m_uuidModifPairs.cend(),
2103
2/4
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 13 times.
✗ Branch 4 not taken.
65 [uuid](const UuidModifWPtrPair &the_pair) {
2104 // Do not query the modif_sp managed object because it can
2105 // be nullptr!
2106
4/14
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 4 times.
✗ Branch 13 not taken.
13 return the_pair.first == uuid;
2107 });
2108
2109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if(the_pair_iterator_cst == m_uuidModifPairs.cend())
2110 qFatalStream() << "Inconsistency between m_modifs and m_uuidModifPairs.";
2111
2112 13 m_uuidModifPairs.erase(the_pair_iterator_cst);
2113
2114 13 return true;
2115 13 }
2116
2117 /*!
2118 \brief Clears all the data of the Monomer instance.
2119 */
2120 void
2121 Monomer::clear()
2122 {
2123 mcsp_polChemDef = nullptr;
2124 m_name = "";
2125 m_code = "";
2126 m_formula = "";
2127
2128 m_mono = 0.0;
2129 m_avg = 0.0;
2130
2131 m_modifs.clear();
2132
2133 m_isValid = false;
2134 }
2135
2136 /*!
2137 \brief Returns a text string representing this Monomer instance.
2138 */
2139 QString
2140 Monomer::toString() const
2141 {
2142 return QString(
2143 "Monomer: %1 - %2 - %3 - %4 - %5 - modifs: %6 - is valid: %7\n")
2144 .arg(m_name)
2145 .arg(m_code)
2146 .arg(m_formula)
2147 .arg(m_mono, 0, 'f', ATOM_DEC_PLACES)
2148 .arg(m_avg, 0, 'f', ATOM_DEC_PLACES)
2149 .arg(m_modifs.size())
2150 .arg(m_isValid);
2151 }
2152
2153
2154 } // namespace libXpertMassCore
2155 } // namespace MsXpS
2156