GCC Code Coverage Report


./
File: src/XpertMass/MassDataCborMassSpectrumHandler.cpp
Date: 2024-08-24 11:26:06
Lines:
0/192
0.0%
Functions:
0/17
0.0%
Branches:
0/246
0.0%

Line Branch Exec Source
1 /* BEGIN software license
2 *
3 * MsXpertSuite - mass spectrometry software suite
4 * -----------------------------------------------
5 * Copyright (C) 2009--2020 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 /////////////////////// Std lib includes
35
36
37 /////////////////////// Qt includes
38 #include <QDebug>
39 #include <QFileInfo>
40
41
42 /////////////////////// IsoSpec
43
44
45 /////////////////////// Local includes
46 #include "MassDataCborMassSpectrumHandler.hpp"
47
48
49 namespace MsXpS
50 {
51
52 namespace libXpertMass
53 {
54
55 // FIXME TODO DOCUMENTATION
56
57 MassDataCborMassSpectrumHandler::MassDataCborMassSpectrumHandler(
58 QObject *parent_p)
59 : MassDataCborBaseHandler(parent_p)
60 {
61 }
62
63
64 // When providing a Trace, it is implicit that this is to write that to file.
65 MassDataCborMassSpectrumHandler::MassDataCborMassSpectrumHandler(
66 QObject *parent_p, const pappso::Trace &trace)
67 : MassDataCborBaseHandler(parent_p), m_trace(trace)
68 {
69 }
70
71
72 MassDataCborMassSpectrumHandler::~MassDataCborMassSpectrumHandler()
73 {
74 }
75
76
77 bool
78 MassDataCborMassSpectrumHandler::writeFile(const QString &output_file_name)
79 {
80 // The format for this kind of CBOR pappso::Trace data is the following:
81
82 // First the quint64 that represents MassDataType. In our specific case, that
83 // must be MassDataType::MASS_SPECTRUM.
84
85 // Then there is the title of data, which is according to the specification:
86 //
87 // Major type 3: a text string, specifically a string of Unicode characters
88 // that is encoded as UTF-8 (that is, not a QByteArray, but a QString).
89
90 // Then there is a MAP with the following key/value pairs:
91
92 // start_map
93
94 // "X_LABEL" / value (text string)
95 // "Y_LABEL" / value (text string)
96 //
97 // "X_DATA" / value (base64 ByteArray)
98 // "Y_DATA" / value (base64 ByteArray)
99
100 // end_map
101
102 QString local_file_name = output_file_name;
103
104 if(local_file_name.isEmpty())
105 local_file_name = m_outputFileName;
106
107 QFile file(local_file_name);
108
109 bool res = file.open(QIODevice::WriteOnly);
110
111 if(!res)
112 {
113 qDebug() << "Failed to open the file for write.";
114 return false;
115 }
116
117 msp_writer = std::make_shared<QCborStreamWriter>(&file);
118
119 // qDebug() << "Now writing data to file:" << local_file_name;
120
121 // Start of array containing all mapped items
122 msp_writer->startMap(7);
123
124 // First the MassDataType: this is the very *first* data bit in the data
125 // stream.
126 msp_writer->append("DATA_TYPE");
127 msp_writer->append(static_cast<quint64>(MassDataType::MASS_SPECTRUM));
128
129 msp_writer->append("TITLE");
130 msp_writer->append(m_title);
131
132 msp_writer->append("TRACE_COLOR");
133 msp_writer->append(m_colorByteArray);
134
135 msp_writer->append("X_LABEL");
136 msp_writer->append("m/z");
137
138 msp_writer->append("X_DATA");
139 msp_writer->append(m_trace.xAsBase64Encoded());
140
141 msp_writer->append("Y_LABEL");
142 msp_writer->append("intensity");
143
144 msp_writer->append("Y_DATA");
145 msp_writer->append(m_trace.yAsBase64Encoded());
146
147 msp_writer->endMap();
148 // Close the map now.
149
150 file.close();
151
152 return res;
153 }
154
155
156 void
157 MassDataCborMassSpectrumHandler::writeByteArray(QByteArray &byte_array)
158 {
159 msp_writer = std::make_shared<QCborStreamWriter>(&byte_array);
160
161 // qDebug() << "Now writing data byte array.";
162
163 // Start of array containing all mapped items
164 msp_writer->startMap(7);
165
166 // First the MassDataType: this is the very *first* data bit in the data
167 // stream.
168 msp_writer->append("DATA_TYPE");
169 msp_writer->append(static_cast<quint64>(MassDataType::MASS_SPECTRUM));
170
171 msp_writer->append("TITLE");
172 msp_writer->append(m_title);
173
174 msp_writer->append("TRACE_COLOR");
175 msp_writer->append(m_colorByteArray);
176
177 msp_writer->append("X_LABEL");
178 msp_writer->append("m/z");
179
180 msp_writer->append("X_DATA");
181 msp_writer->append(m_trace.xAsBase64Encoded());
182
183 msp_writer->append("Y_LABEL");
184 msp_writer->append("intensity");
185
186 msp_writer->append("Y_DATA");
187 msp_writer->append(m_trace.yAsBase64Encoded());
188
189 msp_writer->endMap();
190 // Close the map now.
191 }
192
193
194 bool
195 MassDataCborMassSpectrumHandler::readContext(QCborStreamReaderSPtr &reader_sp)
196 {
197 // The format for this kind of CBOR pappso::Trace data is the following:
198
199 // The whole file is actually a map that contains the key/value
200 // pairs below.
201
202 // The text string values below are described in this way in the specif:
203 // Major type 3: a text string, specifically a string of Unicode characters
204 // that is encoded as UTF-8 (that is, not a QByteArray, but a QString).
205
206 // The containers, map, strings and byte arrays dot not need the reader to
207 // explicitely advance to next().
208
209 // The simple value, like integers, do need that reader's next() call.
210
211 // So now, the MAP with the following key/value pairs:
212
213 // start_map
214
215 // "DATA_TYPE" / value (quint64)
216 // "TITLE" / value (text string)
217 // "X_LABEL" / value (text string)
218 // "Y_LABEL" / value (text string)
219 //
220 // "X_DATA" / value (base64 ByteArray)
221 // "Y_DATA" / value (base64 ByteArray)
222
223 // end_map
224
225 // These are needed to store the x and y data as two different byte arrays.
226
227 while(reader_sp->lastError() == QCborError::NoError && reader_sp->hasNext())
228 {
229 QCborStreamReader::Type type = reader_sp->type();
230 // qDebug() << "Type is:" << type;
231
232 if(type == QCborStreamReader::UnsignedInteger)
233 {
234 // In this format, the QCborStreamReader::UnsignedInteger datum is
235 // only got as the very first data bit in the file. This bit of
236 // information is a quint64 representing MassDataType::MASS_SPECTRUM.
237
238 // Now check that the read value actually corresponds to the expected
239 // mass data type for which this object is working!
240
241 if(m_currentKey == "DATA_TYPE")
242 {
243 // quint64 data_type = 0;
244 // data_type = reader_sp->toUnsignedInteger();
245 // qDebug() << "The mass data type:" << data_type;
246
247 if(static_cast<MassDataType>(reader_sp->toUnsignedInteger()) !=
248 MassDataType::MASS_SPECTRUM)
249 {
250 qDebug()
251 << "The expected DATA_TYPE::MASS_SPECTRUM was not found.";
252 return false;
253 }
254
255 m_massDataType = MassDataType::MASS_SPECTRUM;
256
257 m_currentKey = QString();
258 }
259
260 // qDebug() << "At this point, check if it has next:"
261 //<< reader_sp->hasNext();
262
263 // We had what we wanted, go to next.
264 reader_sp->next();
265 }
266 else if(type == QCborStreamReader::String)
267 {
268 // Read a CBOR string, concatenating all
269 // the chunks into a single string.
270 QString str;
271 auto chunk = reader_sp->readString();
272 while(chunk.status == QCborStreamReader::Ok)
273 {
274 str += chunk.data;
275 chunk = reader_sp->readString();
276 }
277
278 if(chunk.status == QCborStreamReader::Error)
279 {
280 // handle error condition
281 qDebug() << "There was an error reading string chunk.";
282 str.clear();
283 }
284
285 // qDebug() << "The string that was read:" << str;
286
287 // If the current key is empty, this string value is a new key or tag.
288 // Otherwise, it's a value.
289
290 if(m_currentKey.isEmpty())
291 {
292 // qDebug() << "Setting m_currentKey:" << str;
293 m_currentKey = str;
294 }
295 else
296 {
297 // Depending on what we had already, we will set the proper member
298 // datum.
299 if(m_currentKey == "TITLE")
300 {
301 // qDebug() << "Setting m_title:" << str;
302 m_title = str;
303 }
304
305 if(m_currentKey == "X_LABEL")
306 {
307 // qDebug() << "Setting m_xLabel:" << str;
308
309 m_xLabel = str;
310 }
311
312 if(m_currentKey == "Y_LABEL")
313 {
314 // qDebug() << "Setting m_yLabel:" << str;
315 m_yLabel = str;
316 }
317
318 // At this point we can reset m_currentKey.
319 m_currentKey = QString();
320 }
321 }
322
323 // The ByteArray data for X_DATA and Y_DATA.
324 // They are preceded by the keys "X_DATA" and "Y_DATA" respectively.
325
326 else if(type == QCborStreamReader::ByteArray)
327 {
328 // Read a byte array. That could be either the X_DATA or the Y_DATA.
329
330 QByteArray array;
331
332 auto chunk = reader_sp->readByteArray();
333
334 while(chunk.status == QCborStreamReader::Ok)
335 {
336 array.append(chunk.data);
337 chunk = reader_sp->readByteArray();
338 }
339
340 if(m_currentKey == "X_DATA")
341 {
342 m_xBase64Data = array;
343 }
344 else if(m_currentKey == "Y_DATA")
345 {
346 m_yBase64Data = array;
347 }
348 else if(m_currentKey == "TRACE_COLOR")
349 {
350 m_colorByteArray = array;
351 }
352 else
353 {
354 qDebug() << "Error in the data read from file.";
355 return false;
356 }
357
358 m_currentKey = QString();
359 }
360
361 // The MAP has the following key/value pairs:
362
363 // start_map
364
365 // "TITLE" / value (text string)
366 // "X_LABEL" / value (text string)
367 // "Y_LABEL" / value (text string)
368 //
369 // "X_DATA" / value (base64 ByteArray)
370 // "Y_DATA" / value (base64 ByteArray)
371
372 // end_map
373
374 else if(type == QCborStreamReader::Map)
375 {
376
377 reader_sp->enterContainer();
378
379 // Read elements until end of map is reached
380 bool res = readContext(reader_sp);
381 if(res)
382 reader_sp->leaveContainer();
383 else
384 return false;
385 }
386 else
387 {
388 // Ignore all other types, go to the next element
389 reader_sp->next();
390 }
391 }
392
393 if(reader_sp->lastError() != QCborError::NoError)
394 qDebug() << "There was an error: " << reader_sp->lastError()
395 << "Returning false.";
396
397 // Return true if there were no errors
398 // qDebug() << "Returning: " << !reader_sp->lastError();
399
400 return !reader_sp->lastError();
401 }
402
403
404 bool
405 MassDataCborMassSpectrumHandler::readByteArray(const QByteArray &byte_array)
406 {
407 msp_reader = std::make_shared<QCborStreamReader>(byte_array);
408
409 bool res = readContext(msp_reader);
410
411 // qDebug() << "After finishing the read, m_title is:" << m_title
412 //<< "and mass data type is:" << static_cast<quint64>(m_massDataType);
413
414 // At this point we need to decode the arrays and with the data initialize the
415 // pappso::Trace.
416
417 QByteArray x_array;
418 QByteArray y_array;
419
420 QByteArray::FromBase64Result decoding_result = QByteArray::fromBase64Encoding(
421 m_xBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
422
423 if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok)
424 x_array = decoding_result.decoded;
425 else
426 {
427 qDebug() << "Failed to decode the " << m_xLabel << "data";
428 return false;
429 }
430
431 decoding_result = QByteArray::fromBase64Encoding(
432 m_yBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
433
434 if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok)
435 y_array = decoding_result.decoded;
436 else
437 {
438 qDebug() << "Failed to decode the " << m_yLabel << "data";
439 return false;
440 }
441
442 m_trace.clear();
443
444 m_trace.initialize(QString(x_array), QString(y_array));
445
446 return res;
447 }
448
449
450 bool
451 MassDataCborMassSpectrumHandler::readFile(const QString &input_file_name)
452 {
453 // The format for this kind of CBOR pappso::Trace data is the following:
454
455 // First the quint64 that represents MassDataType. In our specific case, that
456 // must be MassDataType::MASS_SPECTRUM.
457
458 // Then there is the title of data, which is according to the specification:
459 //
460 // Major type 3: a text string, specifically a string of Unicode characters
461 // that is encoded as UTF-8 (that is, not a QByteArray, but a QString).
462
463 // Then there is a MAP with the following key/value pairs:
464
465 // start_map
466
467 // "X_LABEL" / value (text string)
468 // "Y_LABEL" / value (text string)
469 //
470 // "X_DATA" / value (base64 ByteArray)
471 // "Y_DATA" / value (base64 ByteArray)
472
473 // end_map
474
475
476 QString local_file_name = input_file_name;
477
478 if(local_file_name.isEmpty())
479 local_file_name = m_inputFileName;
480
481 QFileInfo file_info(local_file_name);
482
483 if(!file_info.exists())
484 {
485 qDebug() << "File not found.";
486 return false;
487 }
488
489 QFile file(local_file_name);
490
491 bool res = file.open(QIODevice::ReadOnly);
492
493 if(!res)
494 {
495 qDebug() << "Failed to open the file for read.";
496 return false;
497 }
498
499 // qDebug() << "Now starting the CBOR data read.";
500
501 msp_reader = std::make_shared<QCborStreamReader>(&file);
502
503 res = readContext(msp_reader);
504
505 file.close();
506
507 // qDebug() << "After finishing the read, m_title is:" << m_title
508 //<< "and mass data type is:" << static_cast<quint64>(m_massDataType);
509
510 // At this point we need to decode the arrays and with the data initialize the
511 // pappso::Trace.
512
513 QByteArray x_array;
514 QByteArray y_array;
515
516 QByteArray::FromBase64Result decoding_result = QByteArray::fromBase64Encoding(
517 m_xBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
518
519 if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok)
520 x_array = decoding_result.decoded;
521 else
522 {
523 qDebug() << "Failed to decode the " << m_xLabel << "data";
524 return false;
525 }
526
527 decoding_result = QByteArray::fromBase64Encoding(
528 m_yBase64Data, QByteArray::Base64Encoding | QByteArray::OmitTrailingEquals);
529
530 if(decoding_result.decodingStatus == QByteArray::Base64DecodingStatus::Ok)
531 y_array = decoding_result.decoded;
532 else
533 {
534 qDebug() << "Failed to decode the " << m_yLabel << "data";
535 return false;
536 }
537
538 m_trace.clear();
539
540 m_trace.initialize(QString(x_array), QString(y_array));
541
542 return res;
543 }
544
545
546 void
547 MassDataCborMassSpectrumHandler::setXLabel(const QString &label)
548 {
549 m_xLabel = label;
550 }
551
552
553 QString
554 MassDataCborMassSpectrumHandler::getXLabel() const
555 {
556 return m_xLabel;
557 }
558
559
560 void
561 MassDataCborMassSpectrumHandler::setYLabel(const QString &label)
562 {
563 m_yLabel = label;
564 }
565
566
567 QString
568 MassDataCborMassSpectrumHandler::getYLabel() const
569 {
570 return m_yLabel;
571 }
572
573 void
574 MassDataCborMassSpectrumHandler::setTrace(const pappso::Trace &trace)
575 {
576 m_trace = trace;
577 }
578
579
580 pappso::Trace
581 MassDataCborMassSpectrumHandler::getTrace() const
582 {
583 return m_trace;
584 };
585
586
587 void
588 MassDataCborMassSpectrumHandler::clearTrace()
589 {
590 m_trace.clear();
591 }
592
593
594 void
595 MassDataCborMassSpectrumHandler::setTraceColor(
596 const QByteArray &color_byte_array)
597 {
598 m_colorByteArray = color_byte_array;
599 }
600
601
602 QByteArray
603 MassDataCborMassSpectrumHandler::getTraceColor() const
604 {
605 return m_colorByteArray;
606 }
607
608
609 } // namespace libXpertMass
610
611
612 } // namespace MsXpS
613
614