GCC Code Coverage Report


./
File: src/XpertMass/FragRule.cpp
Date: 2024-08-24 11:26:06
Lines:
0/175
0.0%
Functions:
0/19
0.0%
Branches:
0/236
0.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 /////////////////////// Local includes
35 #include "FragRule.hpp"
36 #include "PolChemDef.hpp"
37
38
39 namespace MsXpS
40 {
41
42 namespace libXpertMass
43 {
44
45
46 /*!
47 \class MsXpS::libXpertMass::FragRule
48 \inmodule libXpertMass
49 \ingroup PolChemDefGasPhaseChemicalReactions
50 \inheaderfile FragRule.hpp
51
52 \brief The FragRule class provides a model for specifying gas phase
53 fragmentation rules for refining fragmentation specifications (\l FragSpec)
54 of \l{Oligomer} \l{Sequence}s.
55
56 Fragmentation rules characterize in more detail the chemical reaction that
57 governs the fragmentation of the polymer in the gas-phase. The rule is a
58 conditional rule. Its logic is based on the presence of specified monomers
59 right at the place of the fragmentation and before or after that precise
60 location.
61
62 In saccharide chemistry, fragmentations are a very complex topic. This is
63 because a given monomer will fragment according to a given chemistry if it is
64 preceded in the sequence by a monomer of a given identity and according to
65 another chemistry if its direct environment is different.
66
67 This paradigm is implemented using a sequence environment logic based on
68 conditions that can be formulated thanks to three monomer codes:
69
70 \list
71 \li The monomer at which the fragmentation takes place(current code);
72 \li The monomer preceeding the current code (previous code);
73 \li The monomer following the current code (following code);
74 \endlist
75
76 The use of these codes is typically according to this logic:
77
78 \e{If current monomer is Glu and that previous monomer is Gly and
79 following monomer is Arg, then fragmentation should occur according
80 to this formula : "-H2O"}.
81
82 \sa FragSpec
83 */
84
85
86 /*!
87 \variable MsXpS::libXpertMass::FragRule::m_prevCode
88
89 \brief The \l Monomer code located before the actual fragmentation site.
90 */
91
92
93 /*!
94 \variable MsXpS::libXpertMass::FragRule::m_currCode
95
96 \brief The \l Monomer code at the actual fragmentation site.
97 */
98
99
100 /*!
101 \variable MsXpS::libXpertMass::FragRule::m_nextCode
102
103 \brief The \l Monomer code located after the actual fragmentation site.
104 */
105
106
107 /*!
108 \variable MsXpS::libXpertMass::FragRule::m_comment
109
110 \brief A comment associated to the FragRule.
111 */
112
113
114 /*!
115 \brief Constructs a fragmentation rule.
116
117
118 \a pol_chem_def_csp Polymer chemistry definition. Cannot be nullptr.
119
120 \a name Name. Cannot be empty.
121
122 \a prevCode Previous monomer code. Defaults to the null string.
123
124 \a currCode Current monomer code. Defaults to the null string.
125
126 \a nextCode Next monomer code. Defaults to the null string.
127
128 \a formula Formula. Defaults to the null string.
129
130 \a comment Comment. Defaults to the null string.
131 */
132 FragRule::FragRule(PolChemDefCstSPtr pol_chem_def_csp,
133 QString name,
134 QString prevCode,
135 QString currCode,
136 QString nextCode,
137 QString formula,
138 const QString &comment)
139 : PolChemDefEntity(pol_chem_def_csp, name),
140 Formula(formula),
141 m_prevCode(prevCode),
142 m_currCode(currCode),
143 m_nextCode(nextCode),
144 m_comment(comment)
145 {
146 }
147
148
149 /*!
150 \brief Constructs a FragRule instance as a copy of \a other.
151 */
152 FragRule::FragRule(const FragRule &other)
153 : PolChemDefEntity(other),
154 Formula(other),
155 m_prevCode(other.m_prevCode),
156 m_currCode(other.m_currCode),
157 m_nextCode(other.m_nextCode),
158 m_comment(other.m_comment)
159 {
160 }
161
162
163 /*!
164 \brief Destructs the fragmentation rule.
165 */
166 FragRule::~FragRule()
167 {
168 }
169
170
171 /*!
172 \brief Assigns \a other to this FragRule instance.
173
174 Returns a reference to this fragmentation rule.
175 */
176 FragRule &
177 FragRule::operator=(const FragRule &other)
178 {
179 if(&other == this)
180 return *this;
181
182 PolChemDefEntity::operator=(other);
183 Formula::operator=(other);
184
185 m_prevCode = other.m_prevCode;
186 m_currCode = other.m_currCode;
187 m_nextCode = other.m_nextCode;
188 m_comment = other.m_comment;
189
190 return *this;
191 }
192
193 /*!
194 \brief Returns true if \c this and \a other are identical.
195 */
196 bool
197 FragRule::operator==(const FragRule &other) const
198 {
199 if(&other == this)
200 return true;
201
202 if(m_prevCode != other.m_prevCode)
203 return false;
204 if(m_currCode != other.m_currCode)
205 return false;
206 if(m_nextCode != other.m_nextCode)
207 return false;
208 if(m_comment != other.m_comment)
209 return false;
210
211 return true;
212 }
213
214 /*!
215 \brief Returns true if \c this and \a other are different.
216 */
217 bool
218 FragRule::operator!=(const FragRule &other) const
219 {
220 if(&other == this)
221 return false;
222
223 return !operator==(other);
224 }
225
226 /*!
227 \brief Sets the previous monomer \a code.
228 */
229 void
230 FragRule::setPrevCode(const QString &code)
231 {
232 m_prevCode = code;
233 }
234
235
236 /*!
237 \brief Returns the previous monomer code.
238 */
239 QString
240 FragRule::prevCode() const
241 {
242 return m_prevCode;
243 }
244
245
246 /*!
247 \brief Sets the current monomer \a code.
248 */
249 void
250 FragRule::setCurrCode(const QString &code)
251 {
252 m_currCode = code;
253 }
254
255
256 /*!
257 \brief Returns the current monomer code.
258 */
259 QString
260 FragRule::currCode() const
261 {
262 return m_currCode;
263 }
264
265
266 /*!
267 \brief Sets the next monomer \a code.
268 */
269 void
270 FragRule::setNextCode(const QString &code)
271 {
272 m_nextCode = code;
273 }
274
275
276 /*!
277 \brief Returns the next monomer code.
278 */
279 QString
280 FragRule::nextCode() const
281 {
282 return m_nextCode;
283 }
284
285
286 /*!
287 \brief Sets the \a comment.
288 */
289 void
290 FragRule::setComment(const QString &comment)
291 {
292 m_comment = comment;
293 }
294
295
296 /*!
297 \brief Returns the comment.
298 */
299 QString
300 FragRule::comment() const
301 {
302 return m_comment;
303 }
304
305 /*!
306 \brief Returns the \l Formula as a string.
307 */
308 QString
309 FragRule::formula() const
310 {
311 return Formula::toString();
312 }
313
314
315 /*!
316 \brief Searches by \a name a FragRule in \a frag_rule_list.
317
318 If a FragRule instance is found and \a other is non-nullptr, the found
319 fragmentation rule's data are copied into \a other.
320
321 Returns the index of the found FragRule or -1 if none is found or if \a name
322 is empty.
323 */
324 int
325 FragRule::isNameInList(const QString &name,
326 const QList<FragRule *> &frag_rule_list,
327 FragRule *other)
328 {
329 FragRule *fragRule = 0;
330
331 if(name.isEmpty())
332 return -1;
333
334 for(int iter = 0; iter < frag_rule_list.size(); ++iter)
335 {
336 fragRule = frag_rule_list.at(iter);
337 Q_ASSERT(fragRule);
338
339 if(fragRule->m_name == name)
340 {
341 if(other)
342 *other = *fragRule;
343
344 return iter;
345 }
346 }
347
348 return -1;
349 }
350
351
352 /*!
353 \brief Validates the FragRule.
354
355 The validation involves checking that:
356
357 \list
358 \li The name is not empty.
359 \li The previous code is valid if non empty.
360 \li The current code is valid if non empty.
361 \li The next code is valid if non empty.
362 \endlist
363
364 Returns true upon success, false otherwise.
365 */
366 bool
367 FragRule::validate()
368 {
369 const QList<Monomer *> &monomerRefList = mcsp_polChemDef->monomerList();
370
371 if(m_name.isEmpty())
372 return false;
373
374 if(!m_prevCode.isEmpty())
375 if(Monomer::isCodeInList(m_prevCode, monomerRefList) == -1)
376 return false;
377
378 if(!m_currCode.isEmpty())
379 if(Monomer::isCodeInList(m_currCode, monomerRefList) == -1)
380 return false;
381
382 if(!m_nextCode.isEmpty())
383 if(Monomer::isCodeInList(m_nextCode, monomerRefList) == -1)
384 return false;
385
386 IsotopicDataCstSPtr isotopic_data_csp =
387 mcsp_polChemDef->getIsotopicDataCstSPtr();
388
389 return Formula::validate(isotopic_data_csp);
390
391 return true;
392 }
393
394
395 /*!
396 \brief Parses the FragRule XML \a element.
397
398 Upon parsing and validation of the parsed data, the member data are updated,
399 thus essentially initializing this FragRule instance.
400
401 Returns true if parsing and formula validation were successful, false otherwise.
402 */
403 bool
404 FragRule::renderXmlFgrElement(const QDomElement &element)
405 {
406 QDomElement child;
407
408 bool prevSet = false;
409 bool currSet = false;
410 bool nextSet = false;
411 bool commentSet = false;
412
413 /* The xml node we are in is structured this way:
414 *
415 * <fgr>
416 * <name>one_rule</name>
417 * <formula>+H2O</formula>
418 * <prev-mnm-code>M</prev-mnm-code>
419 * <this-mnm-code>Y</this-mnm-code>
420 * <next-mnm-code>T</next-mnm-code>
421 * <comment>opt_comment</comment>
422 * </fgr>
423 *
424 * And the element parameter points to the
425 *
426 * <fgr> element tag:
427 * ^
428 * |
429 * +----- here we are right now.
430 *
431 * Which means that element.tagName() == "fgr" and that
432 * we'll have to go one step down to the first child of the
433 * current node in order to get to the <name> element.
434 *
435 * The DTD:
436 * <!ELEMENT fgr(name,formula,prev-mnm-code?,
437 * this-mnm-code?,next-mnm-code?,comment?)>
438 */
439
440 if(element.tagName() != "fgr")
441 return false;
442
443 child = element.firstChildElement();
444
445 if(child.isNull() || child.tagName() != "name")
446 return false;
447
448 m_name = child.text();
449
450 child = child.nextSiblingElement();
451
452 if(child.isNull() || child.tagName() != "formula")
453 return false;
454
455 if(!Formula::renderXmlFormulaElement(child))
456 return false;
457
458 // Since the following items are not obligatory, we have to while()
459 // until we have no more items...
460
461 child = child.nextSiblingElement();
462
463 while(!child.isNull())
464 {
465 if(child.tagName() == "prev-mnm-code")
466 {
467 if(prevSet)
468 return false;
469 else
470 {
471 m_prevCode = child.text();
472 prevSet = true;
473 }
474 }
475 else if(child.tagName() == "curr-mnm-code")
476 {
477 if(currSet)
478 return false;
479 else
480 {
481 m_currCode = child.text();
482 currSet = true;
483 }
484 }
485 else if(child.tagName() == "next-mnm-code")
486 {
487 if(nextSet)
488 return false;
489 else
490 {
491 m_nextCode = child.text();
492 nextSet = true;
493 }
494 }
495 else if(child.tagName() == "comment")
496 {
497 if(commentSet)
498 return false;
499 else
500 {
501 m_comment = child.text();
502 commentSet = true;
503 }
504 }
505
506 child = child.nextSiblingElement();
507 }
508
509 if(!validate())
510 return false;
511
512 return true;
513 }
514
515
516 /*!
517 \brief Formats a string suitable to use as an XML element.
518
519
520 The string is suitable to be used as an XML element in a polymer chemistry
521 definition file. The typical fragmentation rule element that is generated in
522 this function looks like this:
523
524 \code
525 <fgr>
526 <name>a-fgr-2</name>
527 <formula>+H100</formula>
528 <prev-mnm-code>F</prev-mnm-code>
529 <curr-mnm-code>D</curr-mnm-code>
530 <next-mnm-code>E</next-mnm-code>
531 <comment>comment here!</comment>
532 </fgr>
533 \endcode
534
535 The formatting of the XML element takes into account \a offset and \a
536 indent by prepending the string with \a offset * \a indent character substring.
537
538 \a indent defaults to two spaces.
539
540 Returns a dynamically allocated string that needs to be freed after use.
541 */
542 QString *
543 FragRule::formatXmlFgrElement(int offset, const QString &indent)
544 {
545
546 int newOffset;
547 int iter = 0;
548
549 QString lead("");
550 QString *string = new QString();
551
552
553 // Prepare the lead.
554 newOffset = offset;
555 while(iter < newOffset)
556 {
557 lead += indent;
558 ++iter;
559 }
560
561 *string += QString("%1<fgr>\n").arg(lead);
562
563 // Prepare the lead.
564 ++newOffset;
565 lead.clear();
566 iter = 0;
567 while(iter < newOffset)
568 {
569 lead += indent;
570 ++iter;
571 }
572
573 // Continue with indented elements.
574
575 *string += QString("%1<name>%2</name>\n").arg(lead).arg(m_name);
576
577 *string += QString("%1<formula>%2</formula>\n").arg(lead).arg(m_formula);
578
579 if(!m_prevCode.isEmpty())
580 *string += QString("%1<prev-mnm-code>%2</prev-mnm-code>\n")
581 .arg(lead)
582 .arg(m_prevCode);
583
584 if(!m_currCode.isEmpty())
585 *string += QString("%1<curr-mnm-code>%2</curr-mnm-code>\n")
586 .arg(lead)
587 .arg(m_currCode);
588
589 if(!m_nextCode.isEmpty())
590 *string += QString("%1<next-mnm-code>%2</next-mnm-code>\n")
591 .arg(lead)
592 .arg(m_nextCode);
593
594 if(!m_comment.isEmpty())
595 *string += QString("%1<comment>%2</comment>\n").arg(lead).arg(m_comment);
596
597 // Prepare the lead.
598 --newOffset;
599 lead.clear();
600 iter = 0;
601 while(iter < newOffset)
602 {
603 lead += indent;
604 ++iter;
605 }
606
607 *string += QString("%1</fgr>\n").arg(lead);
608
609
610 return string;
611 }
612
613 } // namespace libXpertMass
614
615 } // namespace MsXpS
616