GCC Code Coverage Report


./
File: src/XpertMass/CleaveRule.cpp
Date: 2024-08-24 11:26:06
Lines:
83/162
51.2%
Functions:
6/18
33.3%
Branches:
82/212
38.7%

Line Branch Exec Source
1 /* BEGIN software license
2 *
3 * MsXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright(C) 2009,...,2018 Filippo Rusconi
6 *
7 * http://www.msxpertsuite.org
8 *
9 * This file is part of the MsXpertSuite project.
10 *
11 * The MsXpertSuite project is the successor of the massXpert project. This
12 * project now includes various independent modules:
13 *
14 * - massXpert, model polymer chemistries and simulate mass spectrometric data;
15 * - mineXpert, a powerful TIC chromatogram/mass spectrum viewer/miner;
16 *
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 *
30 * END software license
31 */
32
33
34 /////////////////////// Local includes
35 #include "CleaveRule.hpp"
36 #include "PolChemDef.hpp"
37
38
39 namespace MsXpS
40 {
41
42 namespace libXpertMass
43 {
44
45
46 /*!
47 \class MsXpS::libXpertMass::CleaveRule
48 \inmodule libXpertMass
49 \ingroup PolChemDefAqueousChemicalReactions
50 \inheaderfile CleaveRule.hpp
51
52 \brief The CleaveRule class provides a model for specifying aqueous cleavage
53 rules for refining cleavage specifications (\l CleaveSpec) of \l{Polymer}
54 \l{Sequence}s.
55
56 Cleavage rules help refine the description of the chemical reaction that is the
57 basis of a cleavage (either enzymatic or chemical).
58
59 While a number of cleavage agents (like a number of enzymes) do not make
60 unexpected reactions upon the cleavage (enzymes usually hydrolyze their
61 substrates), there are chemical agents that while cleaving their
62 polymer sequence substrate chemically modify the ends of the generated
63 oligomers. One notorious example is the case of cyanogen bromide, that cleaves
64 proteins right of methionyl residues. Upon such cleavage, the monomer at the
65 right side of the generated oligomer (methionyl residue) gets modified
66 according to this actionformula: "-CH2S+O". This reaction is modelled using a
67 CleaveRule.
68
69 \sa CleaveMotif, CleaveSpec
70 */
71
72
73 /*!
74 \variable MsXpS::libXpertMass::CleaveRule::m_leftCode
75
76 \brief The \l Monomer code at the left of the cleavage site.
77 */
78
79 /*!
80 \variable MsXpS::libXpertMass::CleaveRule::m_leftFormula
81
82 \brief The \l Formula to be applied onto the left monomer code.
83 */
84
85 /*!
86 \variable MsXpS::libXpertMass::CleaveRule::m_rightCode
87
88 \brief The \l Monomer code at the right of the cleavage site.
89 */
90
91 /*!
92 \variable MsXpS::libXpertMass::CleaveRule::m_rightFormula
93
94 \brief The \l Formula to be applied onto the right monomer code.
95 */
96
97 /*!
98 \brief Constructs a CleaveRule instance
99
100 \list
101 \li \a pol_chem_def_csp: Polymer chemistry definition. Cannot be nullptr.
102 \li \a name: the name.
103 \li \a leftCode: The \l Monomer code at the left of the cleavage site.
104 \li \a leftFormula: .The \l Formula to be applied onto the left monomer code.
105 \li \a rightCode: .The \l Monomer code at the right of the cleavage site.
106 \li \a rightFormula: .The \l Formula to be applied onto the right monomer code.
107 \endlist
108 */
109 392 CleaveRule::CleaveRule(PolChemDefCstSPtr pol_chem_def_csp,
110 QString name,
111 QString leftCode,
112 QString leftFormula,
113 QString rightCode,
114 392 QString rightFormula)
115 : PolChemDefEntity(pol_chem_def_csp, name),
116 392 m_leftCode(leftCode),
117
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 m_leftFormula(leftFormula),
118 392 m_rightCode(rightCode),
119
2/4
✓ Branch 2 taken 392 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 392 times.
✗ Branch 8 not taken.
784 m_rightFormula(rightFormula)
120 {
121 392 }
122
123 /*!
124 \brief Constructs a CleaveRule instance as a copy of \a other.
125 */
126 CleaveRule::CleaveRule(const CleaveRule &other)
127 : PolChemDefEntity(other),
128 m_leftCode(other.m_leftCode),
129 m_leftFormula(other.m_leftFormula),
130 m_rightCode(other.m_rightCode),
131 m_rightFormula(other.m_rightFormula)
132 {
133 }
134
135 /*!
136 \brief Destructs this CleaveRule instance
137 */
138 CleaveRule::~CleaveRule()
139 {
140 }
141
142 /*!
143 \brief Assigns to \a other to this CleaveRule instance.
144
145 Returns a reference to this CleaveRule instance.
146 */
147 CleaveRule &
148 CleaveRule::operator=(const CleaveRule &other)
149 {
150 if(&other == this)
151 return *this;
152
153 PolChemDefEntity::operator=(other);
154
155 m_leftCode = other.m_leftCode;
156 m_leftFormula = other.m_leftFormula;
157
158 m_rightCode = other.m_rightCode;
159 m_rightFormula = other.m_rightFormula;
160
161 return *this;
162 }
163
164 /*!
165 \brief Returns true if \c this and \a other are identical.
166 */
167 bool
168 CleaveRule::operator==(const CleaveRule &other) const
169 {
170 int differences = 0;
171
172 differences += m_leftCode != other.m_leftCode;
173 differences += m_leftFormula != other.m_leftFormula;
174 differences += m_rightCode != other.m_rightCode;
175 differences += m_rightFormula != other.m_rightFormula;
176
177 return (differences ? false : true);
178 }
179
180 /*!
181 \brief Returns true if \c this and \a other are different.
182 */
183 bool
184 CleaveRule::operator!=(const CleaveRule &other) const
185 {
186 return !operator==(other);
187 }
188
189 /*!
190 \brief Sets the left \a code.
191 */
192 void
193 CleaveRule::setLeftCode(const QString &code)
194 {
195 m_leftCode = code;
196 }
197
198 /*!
199 \brief Returns the left code.
200 */
201 const QString &
202 CleaveRule::leftCode()
203 {
204 return m_leftCode;
205 }
206
207 /*!
208 \brief Sets the right \a code.
209 */
210 void
211 CleaveRule::setRightCode(const QString &code)
212 {
213 m_rightCode = code;
214 }
215
216 /*!
217 \brief Returns the right code.
218 */
219 const QString &
220 4 CleaveRule::rightCode()
221 {
222 4 return m_rightCode;
223 }
224
225 /*!
226 \brief Sets the left \a formula.
227 */
228 void
229 CleaveRule::setLeftFormula(const Formula &formula)
230 {
231 m_leftFormula = formula;
232 }
233
234 /*!
235 \brief Returns the left formula.
236 */
237 const Formula &
238 CleaveRule::leftFormula()
239 {
240 return m_leftFormula;
241 }
242
243 /*!
244 \brief Sets the right \a formula.
245 */
246 void
247 CleaveRule::setRightFormula(const Formula &formula)
248 {
249 m_rightFormula = formula;
250 }
251
252 /*!
253 \brief Returns the right formula.
254 */
255 const Formula &
256 4 CleaveRule::rightFormula()
257 {
258 4 return m_rightFormula;
259 }
260
261 /*!
262 \brief Searches for a CleaveRule instance by \a name in \a cleave_rule_list.
263
264 If the instance is found, and \a other is non-nullptr, it is copied to \a other.
265
266 Returns the index of the found CleaveRule instance in \a cleave_rule_list or -1
267 is the cleavage rule was not found.
268 */
269 int
270 CleaveRule::isNameInList(const QString &name,
271 const QList<CleaveRule *> &cleave_rule_list,
272 CleaveRule *other)
273 {
274 CleaveRule *cleaveRule = 0;
275
276 if(name.isEmpty())
277 return -1;
278
279 for(int iter = 0; iter < cleave_rule_list.size(); ++iter)
280 {
281 cleaveRule = cleave_rule_list.at(iter);
282 Q_ASSERT(cleaveRule);
283
284 if(cleaveRule->m_name == name)
285 {
286 if(other)
287 *other = *cleaveRule;
288
289 return iter;
290 }
291 }
292
293 return -1;
294 }
295
296 /*!
297 \brief Validates this CleaveRule instance.
298
299 Validation entails the following:
300
301 \list
302 \li If the left monomer code is not empty, it must be known to the polymer
303 chemistry definition. In that case, if the left formula is not empty, it needs
304 to validate successfully.
305 \li The same logic is applied to the monomer at the right hand side of the
306 cleavage site.
307 \endlist
308
309 Returns true if the validation is successful, false otherwise.
310 */
311 bool
312 1176 CleaveRule::validate()
313 {
314 IsotopicDataCstSPtr isotopic_data_csp =
315
1/2
✓ Branch 2 taken 1176 times.
✗ Branch 3 not taken.
1176 mcsp_polChemDef->getIsotopicDataCstSPtr();
316
317
1/2
✓ Branch 2 taken 1176 times.
✗ Branch 3 not taken.
1176 const QList<Monomer *> &monomerRefList = mcsp_polChemDef->monomerList();
318
319
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1176 times.
1176 if(!m_leftCode.isEmpty())
320 {
321 if(Monomer::isCodeInList(m_leftCode, monomerRefList) == -1)
322 return false;
323
324 if(m_leftFormula.toString().isEmpty())
325 return false;
326
327 if(!m_leftFormula.validate(isotopic_data_csp))
328 return false;
329 }
330
331
1/2
✓ Branch 1 taken 1176 times.
✗ Branch 2 not taken.
1176 if(!m_rightCode.isEmpty())
332 {
333
2/4
✓ Branch 1 taken 1176 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1176 times.
1176 if(Monomer::isCodeInList(m_rightCode, monomerRefList) == -1)
334 return false;
335
336
2/4
✓ Branch 1 taken 1176 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1176 times.
1176 if(m_rightFormula.toString().isEmpty())
337 return false;
338
339 // qDebug() << "Validating right end cleave rule formula:" << m_rightFormula.toString();
340
341
2/4
✓ Branch 2 taken 1176 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 1176 times.
1176 if(!m_rightFormula.validate(isotopic_data_csp))
342 return false;
343 }
344
345 1176 return true;
346 1176 }
347
348 /*!
349 \brief Parses the CleaveRule XML \a element using a \a{version}ed function.
350
351 Upon parsing of the \a element, its data are validated and set to this
352 CleaveRule instance, thus essentially initializing it.
353
354 Returns true if parsing and validation were successful, false otherwise.
355 */
356 bool
357 392 CleaveRule::renderXmlClrElement(const QDomElement &element, int version)
358 {
359
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 QDomElement child;
360
361 392 bool leftCodeSet = false;
362 392 bool leftFormulaSet = false;
363 392 bool rightCodeSet = false;
364 392 bool rightFormulaSet = false;
365
366 /* The xml node we are in is structured this way:
367 *
368 * <clr>
369 * <name>Homeseryl</name>
370 * <le-mnm-code>M</le-mnm-code>
371 * <le-formula>-C1H2S1+O1</le-formula>
372 * <re-mnm-code>M</re-mnm-code>
373 * <re-formula>-C1H2S1+O1</re-formula>
374 * </clr>
375 *
376 * And the element parameter points to the
377 *
378 * <clr> element tag:
379 * ^
380 * |
381 * +----- here we are right now.
382 *
383 * Which means that xml_node->name == "clr" and that
384 * we'll have to go one step down to the first child of the
385 * current node in order to get to the <code> element.
386 *
387 * Note that the DTD stipulates that there can be no or one at most
388 * of each left end and/or right end set of data. So be careful
389 * with the assertions !
390 * This is the DTD material:
391 * <!ELEMENT clr((le-mnm-code,le-formula)?,
392 *(re-mnm-code,re-formula)?)>
393 */
394
395
2/4
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 392 times.
392 if(element.tagName() != "clr")
396 return false;
397
398
2/4
✓ Branch 3 taken 392 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 392 times.
✗ Branch 7 not taken.
392 child = element.firstChildElement();
399
400
1/2
✓ Branch 0 taken 392 times.
✗ Branch 1 not taken.
392 if(version == 1)
401 {
402 // no-op
403
404 392 version = 1;
405 }
406
407
2/4
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 392 times.
392 if(child.tagName() != "name")
408 return false;
409
410
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 m_name = child.text();
411
412
2/4
✓ Branch 3 taken 392 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 392 times.
✗ Branch 7 not taken.
392 child = child.nextSiblingElement();
413
414
3/4
✓ Branch 1 taken 1176 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 784 times.
✓ Branch 4 taken 392 times.
1176 while(!child.isNull())
415 {
416 // OK, apparently there is a child element, so let's try to see
417 // what's going on. It can either be "le-mnm-code" or "re-mnm-code".
418
419
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 784 times.
784 if(child.tagName() == "le-mnm-code")
420 {
421 m_leftCode = child.text();
422 leftCodeSet = true;
423 }
424
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 784 times.
784 else if(child.tagName() == "le-formula")
425 {
426 m_leftFormula.setFormula(child.text());
427 leftFormulaSet = true;
428 }
429
3/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 392 times.
✓ Branch 6 taken 392 times.
784 else if(child.tagName() == "re-mnm-code")
430 {
431
1/2
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
392 m_rightCode = child.text();
432 392 rightCodeSet = true;
433 }
434
2/4
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 392 times.
✗ Branch 6 not taken.
392 else if(child.tagName() == "re-formula")
435 {
436
2/4
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 392 times.
✗ Branch 5 not taken.
392 m_rightFormula.setFormula(child.text());
437 392 rightFormulaSet = true;
438 }
439
440
2/4
✓ Branch 3 taken 784 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 784 times.
✗ Branch 7 not taken.
784 child = child.nextSiblingElement();
441 }
442
443 // OK, we just finished parsing this <clr> element. Check what we
444 // got.
445
446
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if(leftCodeSet)
447 {
448 if(!leftFormulaSet)
449 return false;
450 }
451
452
1/2
✓ Branch 0 taken 392 times.
✗ Branch 1 not taken.
392 if(rightCodeSet)
453 {
454
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 392 times.
392 if(!rightFormulaSet)
455 return false;
456 }
457
458 // It cannot be that no single code could be set.
459
2/4
✓ Branch 0 taken 392 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 392 times.
392 if(!leftCodeSet && !rightCodeSet)
460 return false;
461
462
2/4
✓ Branch 1 taken 392 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 392 times.
392 if(!validate())
463 return false;
464
465 392 return true;
466 392 }
467
468 /*!
469 \brief Formats a string representing this CleaveRule instance suitable to use
470 as an XML element.
471
472 The typical cleavage rule element that is generated in this function looks like
473 this:
474
475 \code
476 <clr>
477 <re-mnm-code>M</re-mnm-code>
478 <re-formula>-CH2S+O</re-formula>
479 </clr>
480 \endcode
481
482 The formatting of the XML element takes into account \a offset and \a
483 indent by prepending the string with \a offset * \a indent character substring.
484
485 \a indent defaults to two spaces.
486
487 Returns a dynamically allocated string that needs to be freed after use.
488 */
489 QString *
490 4 CleaveRule::formatXmlClrElement(int offset, const QString &indent)
491 {
492
493 int newOffset;
494 4 int iter = 0;
495
496
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QString lead("");
497
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 QString *string = new QString();
498
499
500 // Prepare the lead.
501 4 newOffset = offset;
502
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 while(iter < newOffset)
503 {
504
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 lead += indent;
505 16 ++iter;
506 }
507
508 /*
509 <clr>
510 <re-mnm-code>M</re-mnm-code>
511 <re-formula>-CH2S+O</re-formula>
512 </clr>
513 */
514
515
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 *string += QString("%1<clr>\n").arg(lead);
516
517 // Prepare the lead.
518 4 ++newOffset;
519 4 lead.clear();
520 4 iter = 0;
521
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 while(iter < newOffset)
522 {
523
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
20 lead += indent;
524 20 ++iter;
525 }
526
527 // Continue with indented elements.
528
529
4/8
✓ 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.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
12 *string += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
530
531
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if(!m_leftCode.isEmpty())
532 {
533 Q_ASSERT(!m_leftFormula.toString().isEmpty());
534
535 *string +=
536 QString("%1<le-mnm-code>%2</le-mnm-code>\n").arg(lead).arg(m_leftCode);
537
538 *string += QString("%1<le-formula>%2</le-formula>\n")
539 .arg(lead)
540 .arg(m_leftFormula.toString());
541 }
542
543
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if(!m_rightCode.isEmpty())
544 {
545
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 Q_ASSERT(!m_rightFormula.toString().isEmpty());
546
547 *string +=
548
4/8
✓ 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.
✓ Branch 10 taken 4 times.
✗ Branch 11 not taken.
12 QString("%1<re-mnm-code>%2</re-mnm-code>\n").arg(lead).arg(m_rightCode);
549
550
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 *string += QString("%1<re-formula>%2</re-formula>\n")
551
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
8 .arg(lead)
552
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 .arg(m_rightFormula.toString());
553 }
554
555 // Prepare the lead for the closing element.
556 4 --newOffset;
557 4 lead.clear();
558 4 iter = 0;
559
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 while(iter < newOffset)
560 {
561
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 lead += indent;
562 16 ++iter;
563 }
564
565
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 *string += QString("%1</clr>\n").arg(lead);
566
567 4 return string;
568 4 }
569
570 } // namespace libXpertMass
571
572 } // namespace MsXpS
573