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 "CrossLinker.hpp" |
36 |
|
|
#include "PolChemDef.hpp" |
37 |
|
|
|
38 |
|
|
|
39 |
|
|
namespace MsXpS |
40 |
|
|
{ |
41 |
|
|
|
42 |
|
|
namespace libXpertMass |
43 |
|
|
{ |
44 |
|
|
|
45 |
|
|
|
46 |
|
|
/*! |
47 |
|
|
\class MsXpS::libXpertMass::CrossLinker |
48 |
|
|
\inmodule libXpertMass |
49 |
|
|
\ingroup PolChemDefAqueousChemicalReactions |
50 |
|
|
\inheaderfile CrossLinker.hpp |
51 |
|
|
|
52 |
|
|
\brief The CrossLinker class provides abstractions to define the chemical basis |
53 |
|
|
of a cross-linking reaction. |
54 |
|
|
|
55 |
|
|
The notion of a cross-linker is that it is the description of the chemical |
56 |
|
|
reaction that is carried out by \l{Monomer}s in a \l Polymer or in an \l |
57 |
|
|
Oligomer sequence to form a \l CrossLink. |
58 |
|
|
|
59 |
|
|
There are two different kinds of chemical entities potentially involved in the |
60 |
|
|
description of a CrossLinker: |
61 |
|
|
|
62 |
|
|
\list |
63 |
|
|
\a Modif instances that are applied to \l Monomer instances. |
64 |
|
|
\a the \l Formula base class that completes the chemical reactions described by |
65 |
|
|
the \l Modif instances. |
66 |
|
|
\endlist |
67 |
|
|
*/ |
68 |
|
|
|
69 |
|
|
/*! |
70 |
|
|
\variable MsXpS::libXpertMass::CrossLinker::m_modifList |
71 |
|
|
|
72 |
|
|
\brief The list of \l Modif instances that describe the reaction (or the |
73 |
|
|
reactions) involved in the formation of a CrossLink between \l{Monomer}s of a |
74 |
|
|
\l Polymer (or of an \l Oligomer) sequence. |
75 |
|
|
*/ |
76 |
|
|
|
77 |
|
|
/*! |
78 |
|
|
\brief Constructs a CrossLinker instance. |
79 |
|
|
|
80 |
|
|
\list |
81 |
|
|
\li \a pol_chem_def_csp: the polymer chemistry definition (\l PolChemDef). |
82 |
|
|
\li \a name: the name of this CrossLinker. |
83 |
|
|
\li \a formula: the \l Formula that potentially complements the description of |
84 |
|
|
the reaction that is the basis of this CrossLinker (used to initialize the |
85 |
|
|
Formula base class). |
86 |
|
|
\endlist |
87 |
|
|
*/ |
88 |
|
784 |
CrossLinker::CrossLinker(PolChemDefCstSPtr pol_chem_def_csp, |
89 |
|
|
const QString &name, |
90 |
|
784 |
const QString &formula) |
91 |
3/6
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 784 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 784 times.
✗ Branch 10 not taken.
|
784 |
: PolChemDefEntity(pol_chem_def_csp, name), Formula(formula) |
92 |
|
|
{ |
93 |
|
784 |
} |
94 |
|
|
|
95 |
|
|
/*! |
96 |
|
|
\brief Constructs a CrossLinker instance as a copy of \a other. |
97 |
|
|
*/ |
98 |
|
✗ |
CrossLinker::CrossLinker(const CrossLinker &other) |
99 |
|
✗ |
: PolChemDefEntity(other), Formula(other), Ponderable(other) |
100 |
|
|
{ |
101 |
|
✗ |
for(int iter = 0; iter < other.m_modifList.size(); ++iter) |
102 |
|
✗ |
m_modifList.append(other.m_modifList.at(iter)); |
103 |
|
✗ |
} |
104 |
|
|
|
105 |
|
|
|
106 |
|
|
/*! |
107 |
|
|
\brief Destructs this CrossLinker instance |
108 |
|
|
*/ |
109 |
|
✗ |
CrossLinker::~CrossLinker() |
110 |
|
|
{ |
111 |
|
|
// We do not own the modifications in m_modifList! |
112 |
|
✗ |
} |
113 |
|
|
|
114 |
|
|
/*! |
115 |
|
|
\brief Sets the Modif at index \a index to \a modif. |
116 |
|
|
|
117 |
|
|
\a index cannot be out-of-bounds. |
118 |
|
|
|
119 |
|
|
Returns true. |
120 |
|
|
*/ |
121 |
|
|
bool |
122 |
|
✗ |
CrossLinker::setModifAt(Modif *modif, int index) |
123 |
|
|
{ |
124 |
|
✗ |
Q_ASSERT(modif); |
125 |
|
✗ |
Q_ASSERT(index >= 0 && index < m_modifList.size()); |
126 |
|
|
|
127 |
|
✗ |
m_modifList.replace(index, modif); |
128 |
|
|
|
129 |
|
✗ |
return true; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
|
|
133 |
|
|
/*! |
134 |
|
|
\brief Adds \a modif to this CrossLinker instance. |
135 |
|
|
|
136 |
|
|
Returns true. |
137 |
|
|
*/ |
138 |
|
|
bool |
139 |
|
✗ |
CrossLinker::appendModif(Modif *modif) |
140 |
|
|
{ |
141 |
|
✗ |
Q_ASSERT(modif); |
142 |
|
✗ |
m_modifList.append(modif); |
143 |
|
|
|
144 |
|
✗ |
return true; |
145 |
|
|
} |
146 |
|
|
|
147 |
|
|
|
148 |
|
|
/*! |
149 |
|
|
\brief Returns the \l Modif instance at \a index. |
150 |
|
|
|
151 |
|
|
\a index cannot be out-of-bounds. |
152 |
|
|
*/ |
153 |
|
|
const Modif * |
154 |
|
✗ |
CrossLinker::modifAt(int index) const |
155 |
|
|
{ |
156 |
|
✗ |
Q_ASSERT(index >= 0 && index < m_modifList.size()); |
157 |
|
|
|
158 |
|
✗ |
return m_modifList.at(index); |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
|
162 |
|
|
/*! |
163 |
|
|
\brief Removes the \l Modif instance at \a index. |
164 |
|
|
|
165 |
|
|
\a index cannot be out-of-bounds. |
166 |
|
|
|
167 |
|
|
Returns true. |
168 |
|
|
*/ |
169 |
|
|
bool |
170 |
|
✗ |
CrossLinker::removeModifAt(int index) |
171 |
|
|
{ |
172 |
|
✗ |
Q_ASSERT(index < m_modifList.size()); |
173 |
|
|
|
174 |
|
✗ |
m_modifList.removeAt(index); |
175 |
|
|
|
176 |
|
✗ |
return true; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
|
180 |
|
|
/*! |
181 |
|
|
\brief Returns the \l Formula. |
182 |
|
|
*/ |
183 |
|
|
QString |
184 |
|
8 |
CrossLinker::formula() const |
185 |
|
|
{ |
186 |
|
8 |
return Formula::toString(); |
187 |
|
|
} |
188 |
|
|
|
189 |
|
|
|
190 |
|
|
/*! |
191 |
|
|
\brief Returns the list of \l Modif instances. |
192 |
|
|
*/ |
193 |
|
|
QList<Modif *> & |
194 |
|
8 |
CrossLinker::modifList() |
195 |
|
|
{ |
196 |
|
8 |
return m_modifList; |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
|
200 |
|
|
/*! |
201 |
|
|
\brief Returns true if a \l Modif instance by \a modif_name is in the list of |
202 |
|
|
Modif instances, false otherwise. |
203 |
|
|
*/ |
204 |
|
|
int |
205 |
|
✗ |
CrossLinker::hasModif(const QString &modif_name) |
206 |
|
|
{ |
207 |
|
|
// Iterate in the list of modifications, and check if one of these |
208 |
|
|
// has the name passed as argument. If so return the index of the |
209 |
|
|
// found item in the list, otherwise return -1. |
210 |
|
|
|
211 |
|
✗ |
for(int iter = 0; iter < m_modifList.size(); ++iter) |
212 |
|
|
{ |
213 |
|
✗ |
Modif *modif = m_modifList.at(iter); |
214 |
|
|
|
215 |
|
✗ |
if(modif->name() == modif_name) |
216 |
|
✗ |
return iter; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
✗ |
return -1; |
220 |
|
|
} |
221 |
|
|
|
222 |
|
|
/*! |
223 |
|
|
\brief Assigns \a other to this CrossLinker instance. |
224 |
|
|
|
225 |
|
|
Returns a reference to this CrossLinker instance. |
226 |
|
|
*/ |
227 |
|
|
CrossLinker & |
228 |
|
✗ |
CrossLinker::operator=(const CrossLinker &other) |
229 |
|
|
{ |
230 |
|
✗ |
if(&other == this) |
231 |
|
✗ |
return *this; |
232 |
|
|
|
233 |
|
✗ |
PolChemDefEntity::operator=(other); |
234 |
|
✗ |
Formula::operator=(other); |
235 |
|
✗ |
Ponderable::operator=(other); |
236 |
|
|
|
237 |
|
✗ |
while(!m_modifList.isEmpty()) |
238 |
|
✗ |
m_modifList.removeFirst(); |
239 |
|
|
|
240 |
|
✗ |
for(int iter = 0; iter < other.m_modifList.size(); ++iter) |
241 |
|
✗ |
m_modifList.append(other.m_modifList.at(iter)); |
242 |
|
|
|
243 |
|
✗ |
return *this; |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
/*! |
247 |
|
|
\brief Returns true if this CrossLinker and \a other are identical. |
248 |
|
|
*/ |
249 |
|
|
bool |
250 |
|
✗ |
CrossLinker::operator==(const CrossLinker &other) const |
251 |
|
|
{ |
252 |
|
✗ |
if(&other == this) |
253 |
|
✗ |
return true; |
254 |
|
|
|
255 |
|
✗ |
if(PolChemDefEntity::operator!=(other)) |
256 |
|
✗ |
return false; |
257 |
|
✗ |
if(Formula::operator!=(other)) |
258 |
|
✗ |
return false; |
259 |
|
✗ |
if(Ponderable::operator!=(other)) |
260 |
|
✗ |
return false; |
261 |
|
|
|
262 |
|
✗ |
if(m_modifList.size() != other.m_modifList.size()) |
263 |
|
✗ |
return false; |
264 |
|
|
|
265 |
|
✗ |
for(int iter = 0; iter < m_modifList.size(); ++iter) |
266 |
|
|
{ |
267 |
|
✗ |
if(*m_modifList.at(iter) != *other.m_modifList.at(iter)) |
268 |
|
✗ |
return false; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
✗ |
return true; |
272 |
|
|
} |
273 |
|
|
|
274 |
|
|
|
275 |
|
|
/*! |
276 |
|
|
\brief Returns true if this CrossLinker and \a other are different. |
277 |
|
|
|
278 |
|
|
Returns the negated result of operator==(). |
279 |
|
|
*/ |
280 |
|
|
bool |
281 |
|
✗ |
CrossLinker::operator!=(const CrossLinker &other) const |
282 |
|
|
{ |
283 |
|
✗ |
if(&other == this) |
284 |
|
✗ |
return false; |
285 |
|
|
|
286 |
|
✗ |
return !operator==(other); |
287 |
|
|
} |
288 |
|
|
|
289 |
|
|
/*! |
290 |
|
|
\brief Returns true if this CrossLinker instance is found in the member polymer |
291 |
|
|
chemistry definition, false otherwise. |
292 |
|
|
*/ |
293 |
|
|
int |
294 |
|
✗ |
CrossLinker::isNameKnown() |
295 |
|
|
{ |
296 |
|
✗ |
const QList<CrossLinker *> &refList = mcsp_polChemDef->crossLinkerList(); |
297 |
|
|
|
298 |
|
✗ |
if(m_name.isEmpty()) |
299 |
|
✗ |
return -1; |
300 |
|
|
|
301 |
|
✗ |
for(int iter = 0; iter < refList.size(); ++iter) |
302 |
|
|
{ |
303 |
|
✗ |
if(refList.at(iter)->m_name == m_name) |
304 |
|
✗ |
return iter; |
305 |
|
|
} |
306 |
|
|
|
307 |
|
✗ |
return -1; |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
/*! |
311 |
|
|
\brief Returns the index of a CrossLinker found in the \a cross_linker_list |
312 |
|
|
list of CrossLinker instances by name \a name, -1 otherwise. |
313 |
|
|
|
314 |
|
|
If \a other is non-nullptr, the found CrossLinker instance is copied into it. |
315 |
|
|
*/ |
316 |
|
|
int |
317 |
|
✗ |
CrossLinker::isNameInList(const QString &name, |
318 |
|
|
const QList<CrossLinker *> &cross_linker_list, |
319 |
|
|
CrossLinker *other) |
320 |
|
|
{ |
321 |
|
✗ |
CrossLinker *crossLinker = 0; |
322 |
|
|
|
323 |
|
✗ |
if(name.isEmpty()) |
324 |
|
✗ |
return -1; |
325 |
|
|
|
326 |
|
✗ |
for(int iter = 0; iter < cross_linker_list.size(); ++iter) |
327 |
|
|
{ |
328 |
|
✗ |
crossLinker = cross_linker_list.at(iter); |
329 |
|
✗ |
Q_ASSERT(crossLinker); |
330 |
|
|
|
331 |
|
✗ |
if(crossLinker->m_name == name) |
332 |
|
|
{ |
333 |
|
✗ |
if(other != nullptr) |
334 |
|
✗ |
*other = *crossLinker; |
335 |
|
|
|
336 |
|
✗ |
return iter; |
337 |
|
|
} |
338 |
|
|
} |
339 |
|
|
|
340 |
|
✗ |
return -1; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
/*! |
344 |
|
|
\brief Returns true if this CrossLinker instance validates successfully, |
345 |
|
|
false otherwise. |
346 |
|
|
|
347 |
|
|
The validation involves the following: |
348 |
|
|
|
349 |
|
|
\list |
350 |
|
|
\li The member polymer chemistry definition must exist. |
351 |
|
|
\li The name cannot be empty. |
352 |
|
|
\li The formula (if not empty) must validate successfully agains the member |
353 |
|
|
polymer chemistry definition. |
354 |
|
|
\li The list of \l Modif instances must contain either no or more than two |
355 |
|
|
Modif instances. In the latter case, the Modif instances must be found in the |
356 |
|
|
member polymer chemistry definition and must validate successfully. |
357 |
|
|
\endlist |
358 |
|
|
*/ |
359 |
|
|
bool |
360 |
|
784 |
CrossLinker::validate() |
361 |
|
|
{ |
362 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(!mcsp_polChemDef) |
363 |
|
✗ |
return false; |
364 |
|
|
|
365 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(m_name.isEmpty()) |
366 |
|
✗ |
return false; |
367 |
|
|
|
368 |
|
|
// Remember that the formula of the crosslinker might be empty because the |
369 |
|
|
// crosslinker might be fully accounted for by the modifications (below). |
370 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(!m_formula.isEmpty()) |
371 |
|
|
{ |
372 |
|
✗ |
Formula formula(m_formula); |
373 |
|
|
|
374 |
|
|
IsotopicDataCstSPtr isotopic_data_csp = |
375 |
|
✗ |
mcsp_polChemDef->getIsotopicDataCstSPtr(); |
376 |
|
|
|
377 |
|
✗ |
if(!formula.validate(isotopic_data_csp)) |
378 |
|
✗ |
return false; |
379 |
|
✗ |
} |
380 |
|
|
|
381 |
|
|
// This is mainly a sanity check, as the pointers to Modif in the |
382 |
|
|
// list all point to modification objects in the polymer chemistry |
383 |
|
|
// definition, which have been validated already... |
384 |
|
|
|
385 |
|
|
// The validation actually is simple, it might be that there are NO |
386 |
|
|
// modifs, or if this is not the case there must be at least |
387 |
|
|
// 2. Indeed, either none of the monomers in the crosslink get |
388 |
|
|
// modified, or each one has to be(otherwise we cannot know which |
389 |
|
|
// modif goes to which monomer). |
390 |
|
|
|
391 |
|
784 |
int size = m_modifList.size(); |
392 |
|
|
|
393 |
2/4
✓ Branch 0 taken 784 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 784 times.
|
784 |
if(size > 0 && size < 2) |
394 |
|
✗ |
return false; |
395 |
|
|
|
396 |
2/2
✓ Branch 0 taken 1960 times.
✓ Branch 1 taken 784 times.
|
2744 |
for(int iter = 0; iter < size; ++iter) |
397 |
|
|
{ |
398 |
|
|
// Make sure the modification is known to the polymer chemistry |
399 |
|
|
// definition. This check is not performed by the modif's |
400 |
|
|
// validation function. |
401 |
|
|
|
402 |
3/6
✓ Branch 3 taken 1960 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1960 times.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 1960 times.
|
1960 |
if(!mcsp_polChemDef->referenceModifByName(m_modifList.at(iter)->name())) |
403 |
|
✗ |
return false; |
404 |
|
|
|
405 |
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1960 times.
|
1960 |
if(!m_modifList.at(iter)->validate()) |
406 |
|
✗ |
return false; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
784 |
return true; |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
/*! |
413 |
|
|
\brief Returns true if the mass calculations for this CrossLinker instance are |
414 |
|
|
performed without error, false otherwise. |
415 |
|
|
|
416 |
|
|
The calculation involved accounting masses for the Modifs (if any) and for the |
417 |
|
|
Formula (if non-empty). The member masses (\l Ponderable base class) are |
418 |
|
|
updated. |
419 |
|
|
*/ |
420 |
|
|
bool |
421 |
|
784 |
CrossLinker::calculateMasses() |
422 |
|
|
{ |
423 |
|
|
// qDebug() << "Calculating masses for the cross-link"; |
424 |
|
|
|
425 |
|
784 |
m_mono = 0; |
426 |
|
784 |
m_avg = 0; |
427 |
|
|
|
428 |
|
|
// Account the masses of the formula parent class. |
429 |
|
|
IsotopicDataCstSPtr isotopic_data_csp = |
430 |
1/2
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
|
784 |
mcsp_polChemDef->getIsotopicDataCstSPtr(); |
431 |
|
|
|
432 |
|
|
// Here is a special case because the formula for the crosslinker might be |
433 |
|
|
// empty because the crosslinker might be accounted for only but the |
434 |
|
|
// modifications below. |
435 |
|
|
|
436 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(!m_formula.isEmpty()) |
437 |
|
|
{ |
438 |
|
|
// qDebug() << "The formula of the crosslinker is:" << m_formula |
439 |
|
|
//<< "accounting for its masses."; |
440 |
|
|
|
441 |
|
✗ |
if(!Formula::accountMasses(isotopic_data_csp, &m_mono, &m_avg)) |
442 |
|
✗ |
return false; |
443 |
|
|
} |
444 |
|
|
else |
445 |
|
|
{ |
446 |
|
|
// qDebug() << "The formula of the crosslinker is empty."; |
447 |
|
|
} |
448 |
|
|
// Now, for each modif in the crossLinker, have to account their |
449 |
|
|
// mass. |
450 |
|
|
|
451 |
2/2
✓ Branch 1 taken 1960 times.
✓ Branch 2 taken 784 times.
|
2744 |
for(int iter = 0; iter < m_modifList.size(); iter++) |
452 |
|
|
{ |
453 |
|
1960 |
Modif *modif = m_modifList.at(iter); |
454 |
|
|
|
455 |
|
|
// qDebug() << "Accounting for crosslinker modif's" << modif->name() |
456 |
|
|
//<< "masses"; |
457 |
|
|
|
458 |
2/4
✓ Branch 1 taken 1960 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1960 times.
|
1960 |
if(!modif->accountMasses(&m_mono, &m_avg)) |
459 |
|
|
{ |
460 |
|
✗ |
qDebug() |
461 |
|
✗ |
<< "Failed to account for masses of the cross-linker's modif."; |
462 |
|
✗ |
return false; |
463 |
|
|
} |
464 |
|
|
} |
465 |
|
|
|
466 |
|
|
// qDebug() << "At this point, the masses of the CrossLinker are:" << m_mono |
467 |
|
|
//<< "/" << m_avg; |
468 |
|
|
|
469 |
|
784 |
return true; |
470 |
|
784 |
} |
471 |
|
|
|
472 |
|
|
/*! |
473 |
|
|
\brief Copies the member masses (\l Ponderable base class) to \a mono and \a |
474 |
|
|
avg if these are non-nullptr. |
475 |
|
|
|
476 |
|
|
Upon copying, the masses are multiplied by the compounding factor \a |
477 |
|
|
times. |
478 |
|
|
|
479 |
|
|
Returns true. |
480 |
|
|
*/ |
481 |
|
|
bool |
482 |
|
✗ |
CrossLinker::accountMasses(double *mono, double *avg, int times) |
483 |
|
|
{ |
484 |
|
|
// qDebug() << "Accounting masses for crossliker -- mono:" << m_mono |
485 |
|
|
//<< "avg:" << m_avg; |
486 |
|
|
|
487 |
|
✗ |
if(mono) |
488 |
|
✗ |
*mono += m_mono * times; |
489 |
|
|
|
490 |
|
✗ |
if(avg) |
491 |
|
✗ |
*avg += m_avg * times; |
492 |
|
|
|
493 |
|
✗ |
return true; |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
/*! |
497 |
|
|
\brief Copies the member masses (\l Ponderable base class) to \a ponderable. |
498 |
|
|
|
499 |
|
|
\a ponderable cannot be nullptr. |
500 |
|
|
|
501 |
|
|
Upon copying, the masses are multiplied by the compounding factor \a |
502 |
|
|
times. |
503 |
|
|
|
504 |
|
|
Returns true. |
505 |
|
|
*/ |
506 |
|
|
bool |
507 |
|
✗ |
CrossLinker::accountMasses(Ponderable *ponderable, int times) |
508 |
|
|
{ |
509 |
|
✗ |
Q_ASSERT(ponderable); |
510 |
|
|
|
511 |
|
✗ |
ponderable->rmono() += m_mono * times; |
512 |
|
✗ |
ponderable->ravg() += m_avg * times; |
513 |
|
|
|
514 |
|
✗ |
return true; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
/*! |
518 |
|
|
\brief Returns true if parsing of XML \a element, using a \a{version}ed |
519 |
|
|
function was successful, false otherwise. |
520 |
|
|
|
521 |
|
|
The data in \a element are set to this CrossLinker instance if they validated |
522 |
|
|
successfully, thus essentially initializing this CrossLinker instance. |
523 |
|
|
*/ |
524 |
|
|
bool |
525 |
|
784 |
CrossLinker::renderXmlClkElement(const QDomElement &element, int version) |
526 |
|
|
{ |
527 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 784 times.
|
784 |
if(element.tagName() != "clk") |
528 |
|
✗ |
return false; |
529 |
|
|
|
530 |
1/2
✓ Branch 0 taken 784 times.
✗ Branch 1 not taken.
|
784 |
if(version == 1) |
531 |
|
|
{ |
532 |
|
|
// no-op |
533 |
|
|
|
534 |
|
784 |
version = 1; |
535 |
|
|
} |
536 |
|
|
|
537 |
1/2
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
|
784 |
QDomElement child; |
538 |
|
|
|
539 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 784 times.
|
784 |
if(element.tagName() != "clk") |
540 |
|
✗ |
return false; |
541 |
|
|
|
542 |
3/6
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 784 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 784 times.
✗ Branch 9 not taken.
|
784 |
child = element.firstChildElement("name"); |
543 |
|
|
|
544 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 784 times.
|
784 |
if(child.isNull()) |
545 |
|
✗ |
return false; |
546 |
|
|
|
547 |
1/2
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
|
784 |
m_name = child.text(); |
548 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(m_name.isEmpty()) |
549 |
|
✗ |
return false; |
550 |
|
|
|
551 |
|
|
// qDebug() << "The crosslinker name:" << m_name; |
552 |
|
|
|
553 |
3/6
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 784 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 784 times.
✗ Branch 9 not taken.
|
784 |
child = child.nextSiblingElement("formula"); |
554 |
|
|
|
555 |
|
|
// Here, it is possible that the formula element be empty because the |
556 |
|
|
// crosslinker might be accounted for by using the modifications in it. |
557 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 784 times.
✗ Branch 4 not taken.
|
784 |
if(!child.isNull()) |
558 |
|
|
{ |
559 |
1/2
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
|
784 |
QString formula = child.text(); |
560 |
|
|
|
561 |
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
|
784 |
if(!formula.isEmpty()) |
562 |
|
|
{ |
563 |
|
✗ |
if(!Formula::renderXmlFormulaElement(child)) |
564 |
|
✗ |
return false; |
565 |
|
|
} |
566 |
|
|
// else |
567 |
|
|
// qDebug() << "The formula element of crosslinker is empty."; |
568 |
1/2
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
|
784 |
} |
569 |
|
|
else |
570 |
|
✗ |
qDebug() << "The formula element of crosslinker is null."; |
571 |
|
|
|
572 |
1/2
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
|
784 |
const QList<Modif *> &refList = mcsp_polChemDef->modifList(); |
573 |
|
|
|
574 |
|
|
// At this point there might be 0, 1 or more "modifname" elements. |
575 |
3/6
✓ Branch 2 taken 784 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 784 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 784 times.
✗ Branch 9 not taken.
|
784 |
child = child.nextSiblingElement("modifname"); |
576 |
|
|
|
577 |
3/4
✓ Branch 1 taken 2744 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1960 times.
✓ Branch 4 taken 784 times.
|
2744 |
while(!child.isNull()) |
578 |
|
|
{ |
579 |
|
|
// qDebug() << "Now handling CrossLinker modif:" << child.text(); |
580 |
|
|
|
581 |
2/4
✓ Branch 1 taken 1960 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1960 times.
✗ Branch 5 not taken.
|
1960 |
int index = Modif::isNameInList(child.text(), refList); |
582 |
|
|
|
583 |
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1960 times.
|
1960 |
if(index == -1) |
584 |
|
|
{ |
585 |
|
✗ |
qDebug() << "Failed to parse one modification of the crosslink:" |
586 |
|
✗ |
<< m_name; |
587 |
|
|
|
588 |
|
✗ |
return false; |
589 |
|
|
} |
590 |
|
|
else |
591 |
|
|
{ |
592 |
|
|
// qDebug() |
593 |
|
|
//<< "Found the CrossLinker modification in the reference list:" |
594 |
|
|
//<< m_name; |
595 |
|
|
} |
596 |
|
|
|
597 |
|
|
// Modif *modif = mcsp_polChemDef->modifList().at(index); |
598 |
|
|
// qDebug() << "The found modif has name:" << modif->name() |
599 |
|
|
//<< "and masses:" << modif->mono() << "/" << modif->avg(); |
600 |
|
|
|
601 |
2/4
✓ Branch 2 taken 1960 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1960 times.
✗ Branch 7 not taken.
|
1960 |
m_modifList.append(mcsp_polChemDef->modifList().at(index)); |
602 |
|
|
|
603 |
3/6
✓ Branch 2 taken 1960 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1960 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1960 times.
✗ Branch 9 not taken.
|
1960 |
child = child.nextSiblingElement("modifname"); |
604 |
|
|
} |
605 |
|
|
|
606 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 784 times.
|
784 |
if(!calculateMasses()) |
607 |
|
|
{ |
608 |
|
✗ |
qDebug() << __FILE__ << __LINE__ |
609 |
|
✗ |
<< "Failed to calculate masses for crossLinker" << m_name; |
610 |
|
|
|
611 |
|
✗ |
return false; |
612 |
|
|
} |
613 |
|
|
|
614 |
2/4
✓ Branch 1 taken 784 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 784 times.
|
784 |
if(!validate()) |
615 |
|
✗ |
return false; |
616 |
|
|
|
617 |
|
784 |
return true; |
618 |
|
784 |
} |
619 |
|
|
|
620 |
|
|
/*! |
621 |
|
|
\brief Formats this CrossLinker instance in a heap-allocated string to be used |
622 |
|
|
as an XML element in the polymer chemistry definition. |
623 |
|
|
|
624 |
|
|
The formatting of the XML element takes into account \a offset and \a |
625 |
|
|
indent by prepending the string with \a offset * \a indent character substring. |
626 |
|
|
|
627 |
|
|
\a indent defaults to two spaces. |
628 |
|
|
|
629 |
|
|
Returns a dynamically allocated string that needs to be freed after use. |
630 |
|
|
*/ |
631 |
|
|
QString * |
632 |
|
8 |
CrossLinker::formatXmlClkElement(int offset, const QString &indent) |
633 |
|
|
{ |
634 |
|
|
int newOffset; |
635 |
|
8 |
int iter = 0; |
636 |
|
|
|
637 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
QString lead(""); |
638 |
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 |
QString *string = new QString(); |
639 |
|
|
|
640 |
|
|
// Prepare the lead. |
641 |
|
8 |
newOffset = offset; |
642 |
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 |
while(iter < newOffset) |
643 |
|
|
{ |
644 |
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 |
lead += indent; |
645 |
|
24 |
++iter; |
646 |
|
|
} |
647 |
|
|
|
648 |
|
|
/* We are willing to create an <modif> node that should look like this: |
649 |
|
|
* |
650 |
|
|
<clk> |
651 |
|
|
<name>Phosphorylation</name> |
652 |
|
|
<formula>-H+H2PO3</formula> |
653 |
|
|
<modifname>Phosphorylation</modifname> |
654 |
|
|
<modifname>Acetylation</modifname> |
655 |
|
|
</clk> |
656 |
|
|
* |
657 |
|
|
*/ |
658 |
|
|
|
659 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
*string += QString("%1<clk>\n").arg(lead); |
660 |
|
|
|
661 |
|
|
// Prepare the lead. |
662 |
|
8 |
++newOffset; |
663 |
|
8 |
lead.clear(); |
664 |
|
8 |
iter = 0; |
665 |
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 8 times.
|
40 |
while(iter < newOffset) |
666 |
|
|
{ |
667 |
1/2
✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 |
lead += indent; |
668 |
|
32 |
++iter; |
669 |
|
|
} |
670 |
|
|
|
671 |
|
|
// Continue with indented elements. |
672 |
|
|
|
673 |
4/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
|
24 |
*string += QString("%1<name>%2</name>\n").arg(lead).arg(m_name); |
674 |
|
|
|
675 |
4/8
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 8 times.
✗ Branch 11 not taken.
|
24 |
*string += QString("%1<formula>%2</formula>\n").arg(lead).arg(m_formula); |
676 |
|
|
|
677 |
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 8 times.
|
28 |
for(int iter = 0; iter < m_modifList.size(); ++iter) |
678 |
|
|
{ |
679 |
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
|
20 |
*string += QString("%1<modifname>%2</modifname>\n") |
680 |
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
|
40 |
.arg(lead) |
681 |
3/6
✓ Branch 2 taken 20 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 20 times.
✗ Branch 9 not taken.
|
40 |
.arg(m_modifList.at(iter)->name()); |
682 |
|
|
} |
683 |
|
|
|
684 |
|
|
// Prepare the lead for the closing element. |
685 |
|
8 |
--newOffset; |
686 |
|
8 |
lead.clear(); |
687 |
|
8 |
iter = 0; |
688 |
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 8 times.
|
32 |
while(iter < newOffset) |
689 |
|
|
{ |
690 |
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 |
lead += indent; |
691 |
|
24 |
++iter; |
692 |
|
|
} |
693 |
|
|
|
694 |
3/6
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8 times.
✗ Branch 8 not taken.
|
16 |
*string += QString("%1</clk>\n").arg(lead); |
695 |
|
|
|
696 |
|
8 |
return string; |
697 |
|
8 |
} |
698 |
|
|
|
699 |
|
|
} // namespace libXpertMass |
700 |
|
|
|
701 |
|
|
} // namespace MsXpS |
702 |
|
|
|