GCC Code Coverage Report


source/XpertMassCore/src/
File: source/XpertMassCore/src/CleavageAgent.cpp
Date: 2025-11-20 01:41:33
Lines:
280/384
72.9%
Functions:
30/31
96.8%
Branches:
263/686
38.3%

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 <map>
36
37
38 /////////////////////// Local includes
39 #include "MsXpS/libXpertMassCore/CleavageAgent.hpp"
40
41 namespace MsXpS
42 {
43 namespace libXpertMassCore
44 {
45
46
47 /*!
48 \class MsXpS::libXpertMassCore::CleavageAgent
49 \inmodule libXpertMassCore
50 \ingroup PolChemDefAqueousChemicalReactions
51 \inheaderfile CleavageAgent.hpp
52
53 \brief The CleavageAgent class provides a model for specifying aqueous cleavage
54 specifications (patterns) of \l{Polymer} \l{Sequence}s.
55
56 Cleavage specifications determine the specificity of cleavage in a
57 polymer sequence using a simple syntax. For example, Trypsin is able
58 to cleave after lysyl and arginyl residues. Its cleavage pattern is
59 thus "Lys/;Arg/". However, it is known that Trypsin fails to cleave
60 after Lys if that monomer is followed by a Prolyl residue, thus the
61 complete cleavage agent specification for Trypsin comprises three cleavage
62 patterns: "Lys/;Arg/;-Lys/Pro".
63
64 A cleavage agent specification might not be enough information to
65 determine the manner in which a polymer is cleaved. Cleavage rules
66 might be required to refine the specification. A cleavage
67 agent specification might hold as many cleavage rules as required.
68
69 \sa CleavageMotif, CleavageRule
70 */
71
72
73 /*!
74 \variable MsXpS::libXpertMassCore::CleavageAgent::mcsp_polChemDef
75
76 \brief The \l PolChemDef polymer chemistry definition.
77 */
78
79 /*!
80 \variable MsXpS::libXpertMassCore::CleavageAgent::m_name
81
82 \brief The name of the CleavageAgent.
83 */
84
85 /*!
86 \variable MsXpS::libXpertMassCore::CleavageAgent::m_pattern
87
88 \brief The cleavage pattern, that might comprise more than one \l CleavageMotif.
89
90 \sa CleavageMotif, CleavageRule
91 */
92
93 /*!
94 \variable MsXpS::libXpertMassCore::CleavageAgent::m_motifs
95
96 \brief The container of \l{CleavageMotif}s that together make the CleavageAgent.
97
98 \sa CleavageMotif
99 */
100
101 /*!
102 \variable MsXpS::libXpertMassCore::CleavageAgent::m_rules
103
104 \brief The container of \l{CleavageRule}s that might be requied to refine the
105 CleavageAgent.
106
107 \sa CleavageRule
108 */
109
110
111 /*!
112 \variable MsXpS::libXpertMassCore::CleavageAgent::m_isValid
113
114 \brief The validity status of the instance.
115 */
116
117
118 /*!
119 \typedef MsXpS::libXpertMassCore::CleavageAgentSPtr
120 \relates MsXpS::libXpertMassCore::CleavageAgent.
121
122 Synonym for std::shared_ptr<CleavageAgent>.
123 */
124
125 /*!
126 \typedef MsXpS::libXpertMassCore::CleavageAgentCstSPtr
127 \relates MsXpS::libXpertMassCore::CleavageAgent.
128
129 Synonym for std::shared_ptr<const CleavageAgent>.
130 */
131
132
133 /*!
134 \brief Constructs a totally empty CleavageAgent instance
135 */
136
2/4
✓ Branch 2 taken 21 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 21 times.
✗ Branch 6 not taken.
21 CleavageAgent::CleavageAgent(QObject *parent): QObject(parent)
137 {
138
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
21 }
139
140 /*!
141 \brief Constructs a CleavageAgent instance starting from an XML \a element
142 according to \a version and a \a pol_chem_def_csp PolChemDef.
143
144 The \a version indicates what version of the XML element is to be used.
145
146 Before version 2, the CleavageAgent class was named CleaveSpec and thus the
147 XML element was called <cls>:
148
149 This is the current format (FAKE cls):
150 \code
151 <cls>
152 <name>CyanogenBromide</name>
153 <pattern>M/A</pattern>
154 <clr>
155 <le-mnm-code>M</le-mnm-code>
156 <le-formula>-C1H2S1+O1</le-formula>
157 <re-mnm-code>A</re-mnm-code>
158 <re-formula>-CH3COOH</re-formula>
159 </clr>
160 </cls>
161
162 \endcode
163
164
165 After version 2, the CleaveSpec class has been named CleavageAgent and thus the
166 XML element is now called <cla>.The inner structure of the element has not
167 changed.
168
169 Depending on the \a version argument, one function (older) or the other
170 (current) is used to parse the XML element.
171
172 The rendering of the CleavageRule instances requires that the PolChemDef be
173 available.
174
175 \sa renderXmlClsElement(), renderXmlClaElement()
176 */
177 544 CleavageAgent::CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp,
178 const QDomElement &element,
179 int version,
180 544 QObject *parent)
181
3/6
✓ Branch 3 taken 544 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 544 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 544 times.
544 : QObject(parent), mcsp_polChemDef(pol_chem_def_csp)
182 {
183
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 544 times.
544 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
184 qCritical() << "Constructing CleavageAgent with no PolChemDef.";
185
186
2/2
✓ Branch 0 taken 536 times.
✓ Branch 1 taken 8 times.
544 if(version == 1)
187 {
188 536 qDebug() << "Rendering XmlClsElement version" << version;
189
190
2/4
✓ Branch 1 taken 536 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 536 times.
536 if(!renderXmlClsElement(element, version))
191 qCritical()
192 << "Failed to fully render or validate the CleavageAgent XML element "
193 "for construction of CleavageAgent instance.";
194 }
195
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 else if(version == 2)
196 {
197 8 qDebug() << "Rendering XmlClaElement version" << version;
198
199
2/4
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
8 if(!renderXmlClaElement(element, version))
200 qCritical()
201 << "Failed to fully render or validate the CleavageAgent XML element "
202 "for construction of CleavageAgent instance.";
203 }
204 else
205 qFatalStream()
206 << "Programming error. The polymer chemistry definition version "
207 "is not correct.";
208
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
544 }
209
210 /*!
211 \brief Constructs a CleavageAgent instance.
212
213 \list
214 \li \a pol_chem_def_csp: The polymer chemistry definition.
215 \li \a name: The name of the CleavageAgent.
216 \li \a pattern: The pattern of the CleavageAgent, like ("Lys/;Arg/;-Lys/Pro").
217 \endlist
218
219 Upon setting all the member data, the \a pattern is parsed. After this, this
220 instance is validated and the member m_isValid is set to the result.
221 */
222 44 CleavageAgent::CleavageAgent(PolChemDefCstSPtr pol_chem_def_csp,
223 const QString &name,
224 const QString &pattern,
225 44 QObject *parent)
226 : QObject(parent),
227 88 mcsp_polChemDef(pol_chem_def_csp),
228
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 6 times.
44 m_name(name),
229
3/4
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 4 times.
✓ Branch 5 taken 44 times.
✗ Branch 6 not taken.
128 m_pattern(pattern)
230 {
231
3/4
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 33 times.
44 if(!parse())
232
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
22 qCritical()
233 << "Upon construction of the CleavageAgent, the pattern could not "
234
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 "be parsed.";
235
236 44 ErrorList error_list;
237
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 m_isValid = validate(&error_list);
238
239
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 38 times.
44 if(!m_isValid)
240
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
12 qCritical()
241 << "Upon construction of the CleavageAgent, the instance failed to "
242
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 "validate with errors:"
243
3/6
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
12 << Utils::joinErrorList(error_list, ", ");
244
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
44 }
245
246 /*!
247 \brief Constructs a CleavageAgent instance as a copy of \a other.
248
249 The copying of the member containers of CleavageMotif and CleavageRule instances
250 is shallow (only the shared pointers are copied).
251
252 Upon setting all the member data, this instance is validated and the member
253 m_isValid is set to the result.
254 */
255 31 CleavageAgent::CleavageAgent(const CleavageAgent &other, QObject *parent)
256 : QObject(parent),
257 62 mcsp_polChemDef(other.mcsp_polChemDef),
258
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 m_name(other.m_name),
259
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 m_pattern(other.m_pattern),
260
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 m_motifs(other.m_motifs),
261
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
62 m_rules(other.m_rules)
262 {
263 31 ErrorList error_list;
264
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 m_isValid = validate(&error_list);
265
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if(!m_isValid)
267 qCritical()
268 << "Upon copy-construction of the CleavageAgent, the instance failed to "
269 "validate with errors:"
270 << Utils::joinErrorList(error_list, ", ");
271
0/2
✗ Branch 4 not taken.
✗ Branch 5 not taken.
31 }
272
273 /*!
274 \brief Destructs this CleavageAgent instance.
275 */
276 192 CleavageAgent::~CleavageAgent()
277 {
278
1/2
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
384 }
279
280 /*!
281 \brief Initialized this CleavageAgent using \a other.
282
283 After initialization, validity is checked, the result is set to m_isValid and is
284 returned.
285 */
286 bool
287 21 CleavageAgent::initialize(const CleavageAgent &other)
288 {
289 21 mcsp_polChemDef = other.mcsp_polChemDef;
290 21 m_name = other.m_name;
291 21 m_pattern = other.m_pattern;
292 21 m_motifs.assign(other.m_motifs.begin(), other.m_motifs.end());
293 21 m_rules.assign(other.m_rules.begin(), other.m_rules.end());
294
295 21 ErrorList error_list;
296
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
21 m_isValid = validate(&error_list);
297
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if(!m_isValid)
299 qCritical() << "Failed to initalize the CleavageAgent instance.";
300
301 21 return m_isValid;
302 21 }
303
304 //////////////// THE POLCHEMDEF /////////////////////
305 /*!
306 \brief Sets the PolChemDef to \a pol_chem_def_csp.
307
308 Upon setting the member data, this instance is validated and the member
309 m_isValid is set to the result.
310 */
311 void
312 3 CleavageAgent::setPolchemDefCstSPtr(PolChemDefCstSPtr pol_chem_def_csp)
313 {
314 3 mcsp_polChemDef = pol_chem_def_csp;
315
316 3 ErrorList error_list;
317
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_isValid = validate(&error_list);
318
319
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(!m_isValid)
320
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 qCritical()
321 << "Upon setting PolChemDef of CleavageAgent, the instance failed to "
322
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "validate with errors:"
323
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, ", ");
324 3 }
325
326 /*!
327 \brief Returns the PolChemDef.
328 */
329 PolChemDefCstSPtr
330 1 CleavageAgent::getPolchemDefCstSPtr() const
331 {
332 1 return mcsp_polChemDef;
333 }
334
335 //////////////// THE NAME /////////////////////
336 /*!
337 \brief Sets the name to \a name.
338
339 Upon setting the member data, this instance is validated and the member
340 m_isValid is set to the result.
341 */
342 void
343 4 CleavageAgent::setName(const QString &name)
344 {
345 4 m_name = name;
346
347 4 ErrorList error_list;
348
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 m_isValid = validate(&error_list);
349
350
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(!m_isValid)
351
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
4 qCritical() << "Upon setting name of CleavageAgent, the instance failed to "
352
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 "validate with errors:"
353
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
4 << Utils::joinErrorList(error_list, ", ");
354 4 }
355
356 /*!
357 \brief Returns the name.
358 */
359 const QString &
360 482 CleavageAgent::getName() const
361 {
362 482 return m_name;
363 }
364
365 //////////////// THE PATTERN /////////////////////
366 /*!
367 \brief Sets the \a pattern.
368
369 Upon setting the member data, this instance is validated and the member
370 m_isValid is set to the result.
371 */
372 void
373 1 CleavageAgent::setPattern(const QString &pattern)
374 {
375 1 m_pattern = pattern;
376
377
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(!parse())
378 qCritical() << "The pattern that was set could not be parsed.";
379
380 1 ErrorList error_list;
381
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 m_isValid = validate(&error_list);
382
383
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(!m_isValid)
384 qCritical()
385 << "Upon setting pattern of CleavageAgent, the instance failed to "
386 "validate with errors:"
387 << Utils::joinErrorList(error_list, ", ");
388 1 }
389
390 /*!
391 \brief Returns the pattern.
392 */
393 const QString &
394 7 CleavageAgent::getPattern() const
395 {
396 7 return m_pattern;
397 }
398
399 /*!
400 \brief Returns a const reference to the container of CleavageMotif instances.
401 */
402 const std::vector<CleavageMotifSPtr> &
403 80 CleavageAgent::getMotifsCstRef() const
404 {
405 80 return m_motifs;
406 }
407
408 /*!
409 \brief Returns a reference to the container of CleavageMotif instances.
410 */
411 std::vector<CleavageMotifSPtr> &
412 5 CleavageAgent::getMotifsRef()
413 {
414 5 return m_motifs;
415 }
416
417 /*!
418 \brief Returns a const reference to the container of CleavageRule instances.
419 */
420 const std::vector<CleavageRuleSPtr> &
421 413 CleavageAgent::getRulesCstRef() const
422 {
423 413 return m_rules;
424 }
425
426 /*!
427 \brief Returns a reference to the container of CleavageRule instances.
428 */
429 std::vector<CleavageRuleSPtr> &
430 20 CleavageAgent::getRulesRef()
431 {
432 20 return m_rules;
433 }
434
435 /*!
436 \brief Returns the CleavageRule that has name \a name, or nullptr if not found.
437 */
438 CleavageRuleSPtr
439 3 CleavageAgent::getCleavageRuleSPtrByName(const QString &name) const
440 {
441
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 std::vector<CleavageRuleSPtr>::const_iterator the_iterator_cst =
442 6 std::find_if(m_rules.cbegin(),
443 m_rules.cend(),
444
6/12
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 3 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
27 [name](const CleavageRuleSPtr &cleavage_rule_sp) {
445
1/14
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 3 times.
✗ Branch 20 not taken.
3 return cleavage_rule_sp->getName() == name;
446 });
447
448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(the_iterator_cst == m_rules.cend())
449 return nullptr;
450
451 3 return *the_iterator_cst;
452 }
453
454 //////////////// PARSING /////////////////////
455 /*!
456 \brief Parses this CleavageAgent instance.
457
458 The parsing involves separating the components found in the m_pattern string
459 and making CleavageMotif instances out of them.
460
461 Starting from a pattern "Lys/;Arg/;-Lys/Pro", the parsing would
462 first split it into three cleavage site strings:
463
464 \list
465 \li "Lys/";
466 \li "Arg/";
467 \li "-Lys/Pro"
468 \endlist
469
470 Each of these site strings will be deconstructed into motifs, stored
471 in CleavageMotif objects:
472
473 \list
474 \li First motif has a code container "[0] Lys", an offset of 1 and is for
475 cleavage;
476 \li Second motif has a code container "[0] Arg", an offset of 1 and is for
477 cleavage;
478 \li Third motif has a code container "[0] Lys, [1] Pro", an offset of 1
479 and is not for cleavage (mind the '-')
480 \endlist
481
482 If the pattern string is empty, m_isValid is set to false and false is
483 returned.
484
485 Returns true if parsing is successful, false otherwise.
486 */
487 bool
488 593 CleavageAgent::parse()
489 {
490
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 589 times.
593 if(m_pattern.isEmpty())
491 {
492 4 m_isValid = false;
493 4 return false;
494 }
495
496 // The 'm_pattern' is a ';'-delimited string, in which each
497 // sub-string is a 'site'. Each site is in turn constituted by a
498 // motif and a '/' that indicates where the motif is actually
499 // cleaved.
500 //
501 // For example the "-Lys/Pro" site is actually a motif of sequence
502 // "LysPro" and the site holds two more informations with respect to
503 // the mere motif: it says that the motif should not be cleaved
504 //('-') and that if the '-' were not there, the cleavage would
505 // occur between the Lys and the Pro('/' symbolizes the cleavage).
506
507 // For example, if the cleavageagent had a "Lys/;Arg/;-Lys/Pro" string,
508 // it would be split into 3 strings: "Lys/" and "Arg/" and
509 // "-Lys/Pro"(these are 'site' strings). These three site string
510 // would further be deconstructed into motif string(removal of '-'
511 // and '/' characters). Where would these 3 motif strings be stored?
512 // They would be set into one cleavagemotif instance for each
513 // motif. Thus, for example, "-Lys/Pro" would yield a cleavagemotif of
514 // 'motif' LysPro, with a FALSE cleave member and a 1 offset member.
515
516 // Will return the number of cleavagemotif instances that were created.
517 // Upon error -1 is returned.
518
519
520 // "Lys/;Arg/;-Lys/Pro" --> [0] Lys/, [1] Arg/, [2] -Lys/Pro
521
1/2
✓ Branch 2 taken 589 times.
✗ Branch 3 not taken.
589 QStringList sites = m_pattern.split(";", Qt::SkipEmptyParts);
522
523 // for (int iter = 0; iter < sites.size() ; ++iter)
524 // qDebug() << __FILE__ << __LINE__
525 // << sites.at(iter);
526
527 589 Enums::CleavageAction cleavage_action;
528
529
2/2
✓ Branch 0 taken 1133 times.
✓ Branch 1 taken 582 times.
1715 for(int iter = 0; iter < sites.size(); ++iter)
530 {
531 1133 cleavage_action = Enums::CleavageAction::CLEAVE;
532
533
1/2
✓ Branch 0 taken 1133 times.
✗ Branch 1 not taken.
1133 QString iter_cleavage_site = sites.at(iter);
534
535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1133 times.
1133 if(iter_cleavage_site.length() < 2)
536 {
537 qCritical() << iter_cleavage_site << "is an invalid cleavage site.";
538
539 m_isValid = false;
540 return false;
541 }
542
543
1/2
✓ Branch 1 taken 1133 times.
✗ Branch 2 not taken.
1133 int count = iter_cleavage_site.count(QChar('/'));
544
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1126 times.
1133 if(count != 1)
545 {
546
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
14 qCritical()
547
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 << "The must be one and only one '/' in the cleavage site.";
548
549 7 m_isValid = false;
550 7 return false;
551 }
552
553 // Remove spaces.
554
3/6
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1126 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1126 times.
✗ Branch 8 not taken.
1126 iter_cleavage_site.remove(QRegularExpression("\\s+"));
555
556 // Is there a '-' in the site string indicating that this site
557 // is NOT for cleavage? If so, there should be only one such
558 // sign and in position 0.
559
560
1/2
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
1126 count = iter_cleavage_site.count(QChar('-'));
561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1126 times.
1126 if(count > 1)
562 {
563 qCritical() << "There must be at most one '-' in the cleavage site.";
564
565 m_isValid = false;
566 return false;
567 }
568
2/2
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 956 times.
1126 else if(count == 1)
569 {
570
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 170 times.
170 if(iter_cleavage_site.indexOf(QChar('-'), 0, Qt::CaseInsensitive) !=
571 0)
572 {
573 qCritical() << "The '-' in the cleavage site must be at the "
574 "beginning of the cleavage site.";
575
576 m_isValid = false;
577 return false;
578 }
579
580 170 cleavage_action = Enums::CleavageAction::NO_CLEAVE;
581
582 // We can remove the '-' character, as we now know
583 // that the site is NOT for cleavage.
584
1/2
✓ Branch 1 taken 170 times.
✗ Branch 2 not taken.
170 iter_cleavage_site.remove(0, 1);
585 }
586 // else: site is for cleavage: no '-' found.
587
588 // We can create a new cleavage motif.
589 1126 CleavageMotifSPtr cleavage_motif_sp =
590
1/2
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
1126 std::make_shared<CleavageMotif>(mcsp_polChemDef);
591
1/2
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
1126 cleavage_motif_sp->setCleavageAction(cleavage_action);
592
1/2
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
1126 cleavage_motif_sp->parseSite(iter_cleavage_site);
593
594
2/4
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1126 times.
1126 if(!cleavage_motif_sp->isValid())
595 {
596 qCritical() << "Failed to create a valid CleavageMotif by parsing "
597 "cleavage site:"
598 << iter_cleavage_site;
599 cleavage_motif_sp.reset();
600 return false;
601 }
602
603
1/2
✓ Branch 1 taken 1126 times.
✗ Branch 2 not taken.
1126 m_motifs.push_back(cleavage_motif_sp);
604 1133 }
605
606 return true;
607 589 }
608
609 //////////////// OPERATORS /////////////////////
610 /*!
611 \brief Returns true if \c this and \a other are identical.
612
613 Because the CleavageMotifSPtr in m_motifs and the CleavageRuleSPtr in m_rules do
614 not necessarily reference data from the PolChemDef, the comparison of the two
615 instances involves a deep comparison (not a comparison of the pointers
616 themselves).
617
618 */
619 bool
620 28 CleavageAgent::operator==(const CleavageAgent &other) const
621 {
622
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if(&other == this)
623 return true;
624
625 // We cannot compare the PolChemDef, because that would cause
626 // an infinite loop: (each instance of this class in the PolChemDef would
627 // try to compare the PolChemDef...).
628
629
3/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 26 times.
28 if(m_name != other.m_name || m_pattern != other.m_pattern)
630 {
631 qDebug() << "Either the name or the pattern or both is/are different.";
632 return false;
633 }
634
635
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 24 times.
26 if(m_motifs.size() != other.m_motifs.size())
636 {
637 qDebug() << "The CleavageMotif containers have differing sizes.";
638 return false;
639 }
640
641
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 24 times.
87 for(std::size_t iter = 0; iter < m_motifs.size(); ++iter)
642 {
643 // qDebug() << "Will compare two motifs:" << m_motifs.at(iter)->getMotif()
644 // << "versus" << other.m_motifs.at(iter)->getMotif();
645
646
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 63 times.
63 if(*m_motifs.at(iter).get() != *other.m_motifs.at(iter).get())
647 {
648 qDebug()
649 << "At least one CleavageMotif is different in both CleavageAgent "
650 "instances.";
651 return false;
652 }
653 }
654
655
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 22 times.
24 if(m_rules.size() != other.m_rules.size())
656 {
657 qDebug() << "The CleavageRule containers have differing sizes.";
658 return false;
659 }
660
661
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 19 times.
41 for(std::size_t iter = 0; iter < m_rules.size(); ++iter)
662 {
663
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 19 times.
19 if(*m_rules.at(iter).get() != *other.m_rules.at(iter).get())
664 {
665 qDebug()
666 << "At least one CleavageMotif is different in both CleavageAgent "
667 "instances.";
668 return false;
669 }
670 }
671
672 return true;
673 }
674
675 /*!
676 \brief Returns true if \c this and \a other are different.
677 */
678 bool
679 19 CleavageAgent::operator!=(const CleavageAgent &other) const
680 {
681
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if(&other == this)
682 return false;
683
684 19 return !operator==(other);
685 }
686
687 //////////////// VALIDATIONS /////////////////////
688 /*!
689 \brief Returns the CleavageAgent instance from the polymer chemistry definition
690 registered in this instance.
691
692 The key to search the CleavageAgent is this instance's member name.
693
694 If there is no PolChemDef available, nullptr is returned.
695
696 If no CleavageAgent instance is found by this instance's name, nullptr is
697 returned.
698 */
699 CleavageAgentCstSPtr
700 2 CleavageAgent::getFromPolChemDefByName() const
701 {
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
703 return nullptr;
704
705
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(m_name.isEmpty())
706 1 return nullptr;
707
708 1 return mcsp_polChemDef->getCleavageAgentCstSPtrByName(m_name);
709 }
710
711 /*!
712 \brief Returns the status of this CleavageAgent instance the polymer chemistry
713 definition registered in this instance.
714
715 The key to search the CleavageAgent is this instance's member name.
716
717 If there is no PolChemDef available,
718 Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE is returned.
719
720 If no CleavageAgent instance is found by this instance's name,
721 Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN is returned, otherwise
722 Enums::PolChemDefEntityStatus::ENTITY_KNOWN is returned.
723 */
724 Enums::PolChemDefEntityStatus
725 2 CleavageAgent::isKnownByNameInPolChemDef() const
726 {
727
728
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
729 return Enums::PolChemDefEntityStatus::POL_CHEM_DEF_NOT_AVAILABLE;
730
731
4/4
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
3 if(mcsp_polChemDef->getCleavageAgentCstSPtrByName(m_name) != nullptr)
732 1 return Enums::PolChemDefEntityStatus::ENTITY_KNOWN;
733
734 return Enums::PolChemDefEntityStatus::ENTITY_NOT_KNOWN;
735 }
736
737 /*!
738 \brief Returns the CleavageRule from the container that has \a name.
739
740 If no \l CleavageRule is found, nullptr is returned.
741 */
742 CleavageRuleCstSPtr
743 1 CleavageAgent::getCleavageRuleCstSPtrByName(const QString &name) const
744 {
745
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::vector<CleavageRuleSPtr>::const_iterator the_iterator_cst =
746 2 std::find_if(m_rules.cbegin(),
747 m_rules.cend(),
748
6/12
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
9 [name](const CleavageRuleSPtr &cleave_rule_sp) {
749
1/14
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
1 return cleave_rule_sp->getName() == name;
750 });
751
752
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(the_iterator_cst == m_rules.cend())
753 return nullptr;
754
755 1 return *(the_iterator_cst);
756 }
757
758 /*!
759 \brief Returns the index of the CleavageRule instance named \a name in the
760 member container of CleavageRule intances. If no CleavageRule by name \a name is
761 found, -1 is returned.
762 */
763 int
764 1 CleavageAgent::getCleavageRuleIndexByName(const QString &name) const
765 {
766
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::vector<CleavageRuleSPtr>::const_iterator the_iterator_cst =
767 1 std::find_if(m_rules.cbegin(),
768 m_rules.cend(),
769
6/12
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 1 times.
✗ Branch 13 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
9 [name](const CleavageRuleSPtr &cleave_rule_sp) {
770
1/14
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
1 return cleave_rule_sp->getName() == name;
771 });
772
773
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(the_iterator_cst == m_rules.cend())
774 return -1;
775
776 1 return std::distance(m_rules.cbegin(), the_iterator_cst);
777 }
778
779 /*!
780 \brief Validates this CleavageAgent instance setting error messages to \a
781 error_list_p.
782
783 The validation involves checking that:
784
785 \list
786 \li The PolChemDef must be available;
787 \li The name is not empty;
788 \li The pattern is nt empty;
789 \li The parsing of the pattern is successful.;
790 \li Each CleavageRule instance (if any) validates successfully.
791 \endlist
792
793 Returns true if the validation is successful, false otherwise.
794 */
795 bool
796 662 CleavageAgent::validate(ErrorList *error_list_p) const
797 {
798
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 659 times.
662 qsizetype error_count = error_list_p->size();
799
800
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 659 times.
662 if(mcsp_polChemDef == nullptr || mcsp_polChemDef.get() == nullptr)
801 {
802 6 qCritical() << "A CleavageAgent with no PolChemDef available cannot "
803
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 "validate successfully.";
804 6 error_list_p->push_back(
805 "A CleavageAgent with no PolChemDef available cannot validate "
806 "successfully");
807 }
808
809
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 649 times.
662 if(m_name.isEmpty())
810 {
811 26 qCritical()
812
1/2
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
13 << "A CleavageAgent with no name cannot validate successfully.";
813 26 error_list_p->push_back(
814 "A CleavageAgent with no name cannot validate successfully");
815 }
816
817
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 648 times.
662 if(m_pattern.isEmpty())
818 {
819 28 qCritical()
820
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 << "A CleavageAgent with no pattern cannot validate successfully.";
821 28 error_list_p->push_back(
822 "A CleavageAgent with no pattern cannot validate successfully");
823 }
824
825 // If there are motifs, we have to check them all.
826
2/2
✓ Branch 0 taken 1273 times.
✓ Branch 1 taken 662 times.
1935 for(const CleavageMotifSPtr &cleavage_motif_sp : m_motifs)
827 {
828
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1273 times.
1273 if(!cleavage_motif_sp->validate(error_list_p))
829 {
830 qCritical() << "A CleavageAgent with invalid CleavageMotif instances "
831 "cannot validate successfully.";
832 error_list_p->push_back(
833 "A CleavageAgent with invalid CleavageMotif instances cannot "
834 "validate "
835 "successfully");
836 break;
837 }
838 }
839
840 // If there are rules, we have to check them all.
841
2/2
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 662 times.
770 for(const CleavageRuleSPtr &cleavage_rule_sp : m_rules)
842 {
843
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 108 times.
108 if(!cleavage_rule_sp->validate(error_list_p))
844 {
845 qCritical() << "A CleavageAgent with invalid CleavageRule instances "
846 "cannot validate successfully.";
847 error_list_p->push_back(
848 "A CleavageAgent with invalid CleavageRule instances cannot "
849 "validate "
850 "successfully");
851 break;
852 }
853 }
854
855 662 m_isValid = error_list_p->size() > error_count ? false : true;
856
857 662 return m_isValid;
858 }
859
860 /*!
861 \brief Returns the validity status of this instance.
862 */
863 bool
864 556 CleavageAgent::isValid() const
865 {
866 556 return m_isValid;
867 }
868
869 //////////////// XML DATA LOADING WRITING /////////////////////
870 /*!
871 \brief Parses a cleavage specification XML <cls> \a element using a
872 \a{version}ed function.
873
874 The data in the element are validated and if successful they are set to this
875 instance, thus initializing it.
876
877 After setting the member data, the instance is validated and the result is set
878 to m_isValid that is returned.
879 */
880 bool
881 538 CleavageAgent::renderXmlClsElement(const QDomElement &element,
882 [[maybe_unused]] int version)
883 {
884 // For the time being the version is not necessary here. As of
885 // version up to 2, the current function works ok.
886
887 538 QDomElement child;
888
1/2
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
538 QDomElement childRule;
889
890 /* The xml node we are in is structured this way:
891 *
892 * <cls>
893 * <name>CyanogenBromide</name>
894 * <pattern>M/</pattern>
895 * <clr>
896 * <le-mnm-code>M</le-mnm-code>
897 * <le-formula>-C1H2S1+O1</le-formula>
898 * <re-mnm-code>M</re-mnm-code>
899 * <re-formula>-C1H2S1+O1</re-formula>
900 * </clr>
901 * </cls>
902 *
903 * And the element parameter points to the
904 *
905 * <cls> element tag:
906 * ^
907 * |
908 * +----- here we are right now.
909 *
910 * Which means that element.tagName() == "cls" and that
911 * we'll have to go one step down to the first child of the
912 * current node in order to get to the <name> element.
913 */
914
915
2/4
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 538 times.
1076 if(element.tagName() != "cls")
916 {
917 qCritical() << "The expected <cls> element is not found.";
918 m_isValid = false;
919 return m_isValid;
920 }
921
922
3/6
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 538 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 538 times.
✗ Branch 8 not taken.
1076 child = element.firstChildElement("name");
923
924
5/10
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 538 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 538 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 538 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 538 times.
1076 if(child.isNull() || child.text().isEmpty())
925 {
926 qCritical()
927 << "The CleaveSpec did not render correctly: problem with the "
928 "<name> element.";
929 m_isValid = false;
930 return m_isValid;
931 }
932
1/2
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
538 m_name = child.text();
933 // qDebug() << "The name:" << m_name;
934
935
3/6
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 538 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 538 times.
✗ Branch 8 not taken.
1076 child = child.nextSiblingElement("pattern");
936
937
5/10
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 538 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 538 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 538 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 538 times.
1076 if(child.isNull() || child.text().isEmpty())
938 {
939 qCritical()
940 << "The CleaveSpec did not render correctly: problem with the "
941 "<pattern> element.";
942 m_isValid = false;
943 return m_isValid;
944 }
945
1/2
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
538 m_pattern = child.text();
946
947
2/4
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 538 times.
538 if(!parse())
948 {
949 qCritical() << "The CleaveSpec being initialized with a <cls> XML "
950 "element had an unparseable pattern.";
951 m_isValid = false;
952 return false;
953 }
954
955 // At this point there might be 0, 1 or more cleavage rules.
956
3/6
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 538 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 538 times.
✗ Branch 8 not taken.
1076 child = child.nextSiblingElement("clr");
957
958
3/4
✓ Branch 1 taken 607 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
✓ Branch 4 taken 538 times.
607 while(!child.isNull())
959 {
960 69 CleavageRuleSPtr cleavage_rule_sp =
961
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 std::make_shared<CleavageRule>(mcsp_polChemDef);
962
963
2/4
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
69 if(!cleavage_rule_sp->renderXmlClrElement(child, version))
964 {
965 qCritical() << "Failed to render CleaveRule from <clr> XML element.";
966 m_isValid = false;
967 cleavage_rule_sp.reset();
968 return false;
969 }
970
971 69 ErrorList error_list;
972
2/4
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 69 times.
69 if(!cleavage_rule_sp->validate(&error_list))
973 {
974 qCritical() << "Failed to validate CleaveRule rendered from <clr> "
975 "XML element, with errors:";
976 Utils::joinErrorList(error_list, ", ");
977 cleavage_rule_sp.reset();
978 return false;
979 }
980
981
1/2
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
69 m_rules.push_back(cleavage_rule_sp);
982
983
3/6
✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 69 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 69 times.
✗ Branch 8 not taken.
138 child = child.nextSiblingElement("clr");
984
1/2
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
138 }
985
986 538 ErrorList error_list;
987
1/2
✓ Branch 1 taken 538 times.
✗ Branch 2 not taken.
538 m_isValid = validate(&error_list);
988
989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 538 times.
538 if(!m_isValid)
990 qCritical() << "Failed to validate CleaveSpec instance right after "
991 "rendering it from <cls> XML element, with errors:"
992 << Utils::joinErrorList(error_list, ", ");
993
994 538 return m_isValid;
995 1076 }
996
997 /*!
998 \brief Parses a cleavage agent XML <cla> \a element using a \a{version}ed
999 function.
1000
1001 The data in the element are validated and if successful they are set to this
1002 instance, thus initializing it.
1003
1004 After setting the member data, the instance is validated and the result is set
1005 to m_isValid that is returned.
1006 */
1007 bool
1008 10 CleavageAgent::renderXmlClaElement(const QDomElement &element,
1009 [[maybe_unused]] int version)
1010 {
1011 // For the time being the version is not necessary here. As of
1012 // version up to 2, the current function works ok.
1013
1014 10 QDomElement child;
1015
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 QDomElement childRule;
1016
1017 /* The xml node we are in is structured this way:
1018 *
1019 * <cla>
1020 * <name>CyanogenBromide</name>
1021 * <pattern>M/</pattern>
1022 * <clr>
1023 * <le-mnm-code>M</le-mnm-code>
1024 * <le-formula>-C1H2S1+O1</le-formula>
1025 * <re-mnm-code>M</re-mnm-code>
1026 * <re-formula>-C1H2S1+O1</re-formula>
1027 * </clr>
1028 * </cla>
1029 *
1030 * And the element parameter points to the
1031 *
1032 * <cla> element tag:
1033 * ^
1034 * |
1035 * +----- here we are right now.
1036 *
1037 * Which means that element.tagName() == "cla" and that
1038 * we'll have to go one step down to the first child of the
1039 * current node in order to get to the <name> element.
1040 */
1041
1042
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 10 times.
20 if(element.tagName() != "cla")
1043 {
1044 qCritical() << "The expected <cla> element is not found.";
1045 m_isValid = false;
1046 return m_isValid;
1047 }
1048
1049
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
20 child = element.firstChildElement("name");
1050
1051
5/10
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 10 times.
20 if(child.isNull() || child.text().isEmpty())
1052 {
1053 qCritical()
1054 << "The CleavageAgent did not render correctly: problem with the "
1055 "<name> element.";
1056 m_isValid = false;
1057 return m_isValid;
1058 }
1059
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 m_name = child.text();
1060 10 qDebug() << "The CleavageAgent has name:" << m_name;
1061
1062
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
20 child = child.nextSiblingElement("pattern");
1063
1064
5/10
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 10 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 10 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 10 times.
20 if(child.isNull() || child.text().isEmpty())
1065 {
1066 qCritical()
1067 << "The CleavageAgent did not render correctly: problem with the "
1068 "<pattern> element.";
1069 m_isValid = false;
1070 return m_isValid;
1071 }
1072
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 m_pattern = child.text();
1073 10 qDebug() << "The CleavageAgent has pattern:" << m_pattern;
1074
1075
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10 times.
10 if(!parse())
1076 {
1077 qCritical() << "The CleavageAgent being initialized with a <cls> XML "
1078 "element had an unparseable pattern.";
1079 m_isValid = false;
1080 return false;
1081 }
1082 10 qDebug() << "The CleavageAgent pattern was parsed successfully.";
1083
1084 // At this point there might be 0, 1 or more cleavage rules.
1085
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
20 child = child.nextSiblingElement("clr");
1086
1087
3/4
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 10 times.
13 while(!child.isNull())
1088 {
1089 3 qDebug() << "Iterating in <clr> child element.";
1090
1091 3 CleavageRuleSPtr cleavage_rule_sp =
1092
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 std::make_shared<CleavageRule>(mcsp_polChemDef);
1093
1094 3 qDebug() << "Will now render the <clr> element.";
1095
1096
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if(!cleavage_rule_sp->renderXmlClrElement(child, version))
1097 {
1098 qCritical()
1099 << "Failed to render CleavageRule from <clr> XML element.";
1100 m_isValid = false;
1101 cleavage_rule_sp.reset();
1102 return false;
1103 }
1104
1105 3 qDebug() << "The <clr> element was parsed successfully.";
1106
1107 3 ErrorList error_list;
1108
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
3 if(!cleavage_rule_sp->validate(&error_list))
1109 {
1110 qCritical() << "Failed to validate CleavageRule rendered from <clr> "
1111 "XML element, with errors:";
1112 Utils::joinErrorList(error_list, ", ");
1113 cleavage_rule_sp.reset();
1114 return false;
1115 }
1116
1117 3 qDebug() << "Now storing the CleavageRule in the container.";
1118
1119
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 m_rules.push_back(cleavage_rule_sp);
1120
1121
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 child = child.nextSiblingElement("clr");
1122
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 }
1123
1124 10 ErrorList error_list;
1125
1/2
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
10 m_isValid = validate(&error_list);
1126
1127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if(!m_isValid)
1128 qCritical() << "Failed to validate CleavageAgent instance right after "
1129 "rendering it from <cls> XML element, with errors:"
1130 << Utils::joinErrorList(error_list, ", ");
1131 else
1132 10 qDebug() << "Successfully parsed the <cla> element.";
1133
1134 10 return m_isValid;
1135 20 }
1136
1137 /*!
1138 \brief Formats and returns a string describing this CleavageAgent instance in a
1139 format suitable to be used as an XML element.
1140
1141 The XML element is typically used in a polymer chemistry defintion and looks
1142 like this:
1143
1144 \code
1145 <cla>
1146 <name>CyanogenBromide</name>
1147 <pattern>M/</pattern>
1148 <clr>
1149 <re-mnm-code>M</re-mnm-code>
1150 <re-formula>-CH2S+O</re-formula>
1151 </clr>
1152 </cla>
1153 \endcode
1154
1155
1156 The formatting of the XML element takes into account \a offset and \a
1157 indent by prepending the string with \a offset * \a indent character
1158 substring.
1159
1160 \a indent defaults to two spaces.
1161
1162 Returns a string.
1163 */
1164 QString
1165 10 CleavageAgent::formatXmlClaElement(int offset, const QString &indent) const
1166 {
1167 10 int newOffset;
1168 10 int iter = 0;
1169
1170 10 QString lead("");
1171 10 QString text;
1172
1173
1174 // Prepare the lead.
1175 10 newOffset = offset;
1176
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 10 times.
36 while(iter < newOffset)
1177 {
1178
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 lead += indent;
1179 26 ++iter;
1180 }
1181
1182
1183
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 text += QString("%1<cla>\n").arg(lead);
1184
1185 // Prepare the lead.
1186 10 ++newOffset;
1187 10 lead.clear();
1188 10 iter = 0;
1189
2/2
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 10 times.
56 while(iter < newOffset)
1190 {
1191
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 lead += indent;
1192 36 ++iter;
1193 }
1194
1195 // Continue with indented elements.
1196
1197
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
20 text += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
1198
3/6
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10 times.
✗ Branch 8 not taken.
20 text += QString("%1<pattern>%2</pattern>\n").arg(lead).arg(m_pattern);
1199
1200
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 for(const CleavageRuleSPtr &cleavage_rule_sp : m_rules)
1201
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
6 text += cleavage_rule_sp->formatXmlClrElement(newOffset);
1202
1203 // Prepare the lead for the closing element.
1204 10 --newOffset;
1205 10 lead.clear();
1206 10 iter = 0;
1207
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 10 times.
46 while(iter < newOffset)
1208 {
1209
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 lead += indent;
1210 26 ++iter;
1211 }
1212
1213
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
20 text += QString("%1</cla>\n").arg(lead);
1214
1215 10 return text;
1216 10 }
1217
1218 //////////////// UTILS /////////////////////
1219 /*!
1220 \brief Returns a string describing this CleavageAgent instance.
1221 */
1222 QString
1223 1 CleavageAgent::toString() const
1224 {
1225 1 return QString("%1 - %2 - %3 motifs - %4 rules")
1226
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(m_name)
1227
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(m_pattern)
1228
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
2 .arg(m_motifs.size())
1229
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 .arg(m_rules.size());
1230 }
1231
1232 void
1233
1234 CleavageAgent::registerJsConstructor(QJSEngine *engine)
1235
1236 {
1237 if(!engine)
1238 {
1239 qWarning() << "Cannot register CleavageAgent class: engine is null";
1240 return;
1241 }
1242
1243 // Register the meta object as a constructor
1244
1245 QJSValue jsMetaObject =
1246 engine->newQMetaObject(&CleavageAgent::staticMetaObject);
1247 engine->globalObject().setProperty("CleavageAgent", jsMetaObject);
1248 }
1249
1250
1251 } // namespace libXpertMassCore
1252 } // namespace MsXpS
1253