00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #ifndef OPENMS_FORMAT_HANDLERS_MZXMLHANDLER_H
00036 #define OPENMS_FORMAT_HANDLERS_MZXMLHANDLER_H
00037
00038 #include <OpenMS/CONCEPT/ProgressLogger.h>
00039 #include <OpenMS/FORMAT/Base64.h>
00040 #include <OpenMS/FORMAT/OPTIONS/PeakFileOptions.h>
00041 #include <OpenMS/FORMAT/HANDLERS/XMLHandler.h>
00042 #include <OpenMS/DATASTRUCTURES/String.h>
00043 #include <OpenMS/KERNEL/MSExperiment.h>
00044
00045 #include <stack>
00046
00047 namespace OpenMS
00048 {
00049 class MetaInfoInterface;
00050
00051 namespace Internal
00052 {
00059 template <typename MapType>
00060 class MzXMLHandler :
00061 public XMLHandler
00062 {
00063 public:
00066
00067 MzXMLHandler(MapType & exp, const String & filename, const String & version, ProgressLogger & logger) :
00068 XMLHandler(filename, version),
00069 exp_(&exp),
00070 cexp_(0),
00071 decoder_(),
00072 peak_count_(0),
00073 char_rest_(),
00074 skip_spectrum_(false),
00075 spec_write_counter_(1),
00076 logger_(logger)
00077 {
00078 init_();
00079 }
00080
00082 MzXMLHandler(const MapType & exp, const String & filename, const String & version, const ProgressLogger & logger) :
00083 XMLHandler(filename, version),
00084 exp_(0),
00085 cexp_(&exp),
00086 decoder_(),
00087 peak_count_(0),
00088 char_rest_(),
00089 skip_spectrum_(false),
00090 spec_write_counter_(1),
00091 logger_(logger)
00092 {
00093 init_();
00094 }
00095
00097 virtual ~MzXMLHandler()
00098 {
00099 }
00100
00102
00103
00104 virtual void endElement(const XMLCh * const uri, const XMLCh * const local_name, const XMLCh * const qname);
00105
00106
00107 virtual void startElement(const XMLCh * const uri, const XMLCh * const local_name, const XMLCh * const qname, const xercesc::Attributes & attributes);
00108
00109
00110 virtual void characters(const XMLCh * const chars, const XMLSize_t length);
00111
00113 void writeTo(std::ostream & os);
00114
00116 void setOptions(const PeakFileOptions & options)
00117 {
00118 options_ = options;
00119 }
00120
00121 private:
00122
00123 void init_()
00124 {
00125 cv_terms_.resize(6);
00126
00127 String("any;+;-").split(';', cv_terms_[0]);
00128
00129
00130
00131 String(";ESI;EI;CI;FAB;;;;;;;;;;;;;APCI;;;;;;;;MALDI").split(';', cv_terms_[2]);
00132 cv_terms_[2].resize(IonSource::SIZE_OF_IONIZATIONMETHOD);
00133
00134 String(";Quadrupole;Quadrupole Ion Trap;;;TOF;Magnetic Sector;FT-ICR;").split(';', cv_terms_[3]);
00135 cv_terms_[3].resize(MassAnalyzer::SIZE_OF_ANALYZERTYPE);
00136
00137 String(";EMT;;;Faraday Cup;;;;;Channeltron;Daly;Microchannel plate").split(';', cv_terms_[4]);
00138 cv_terms_[4].resize(IonDetector::SIZE_OF_TYPE);
00139
00140 String(";FWHM;TenPercentValley;Baseline").split(';', cv_terms_[5]);
00141 cv_terms_[5].resize(MassAnalyzer::SIZE_OF_RESOLUTIONMETHOD);
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157 }
00158
00159 protected:
00160
00162 typedef typename MapType::PeakType PeakType;
00164 typedef MSSpectrum<PeakType> SpectrumType;
00165
00166 typedef typename SpectrumType::Iterator PeakIterator;
00167
00169 MapType * exp_;
00171 const MapType * cexp_;
00172
00174 PeakFileOptions options_;
00175
00178 Base64 decoder_;
00179 UInt peak_count_;
00180 String precision_;
00181 String compressionType_;
00182 String char_rest_;
00184
00186 bool skip_spectrum_;
00187
00189 UInt spec_write_counter_;
00190
00192 const ProgressLogger & logger_;
00193
00195 inline void writeUserParam_(std::ostream & os, const MetaInfoInterface & meta, int indent = 4, String tag = "nameValue")
00196 {
00197 std::vector<String> keys;
00198 meta.getKeys(keys);
00199
00200 for (std::vector<String>::const_iterator it = keys.begin(); it != keys.end(); ++it)
00201 {
00202 if ((*it)[0] != '#')
00203 {
00204 os << String(indent, '\t') << "<" << tag << " name=\"" << *it << "\" value=\"" << meta.getMetaValue(*it) << "\"/>\n";
00205 }
00206 }
00207 }
00208
00210 std::vector<DataProcessing> data_processing_;
00211
00212 private:
00214 MzXMLHandler();
00215
00216 static const XMLCh * s_value_;
00217 static const XMLCh * s_count_;
00218 static const XMLCh * s_type_;
00219 static const XMLCh * s_name_;
00220 static const XMLCh * s_version_;
00221 static const XMLCh * s_filename_;
00222 static const XMLCh * s_filetype_;
00223 static const XMLCh * s_filesha1_;
00224 static const XMLCh * s_completiontime_;
00225 static const XMLCh * s_precision_;
00226 static const XMLCh * s_byteorder_;
00227 static const XMLCh * s_pairorder_;
00228 static const XMLCh * s_compressionType_;
00229 static const XMLCh * s_precursorintensity_;
00230 static const XMLCh * s_precursorcharge_;
00231 static const XMLCh * s_windowwideness_;
00232 static const XMLCh * s_mslevel_;
00233 static const XMLCh * s_peakscount_;
00234 static const XMLCh * s_polarity_;
00235 static const XMLCh * s_scantype_;
00236 static const XMLCh * s_retentiontime_;
00237 static const XMLCh * s_startmz_;
00238 static const XMLCh * s_endmz_;
00239 static const XMLCh * s_first_;
00240 static const XMLCh * s_last_;
00241 static const XMLCh * s_phone_;
00242 static const XMLCh * s_email_;
00243 static const XMLCh * s_uri_;
00244 static const XMLCh * s_num_;
00245 static const XMLCh * s_intensitycutoff_;
00246 static const XMLCh * s_centroided_;
00247 static const XMLCh * s_deisotoped_;
00248 static const XMLCh * s_chargedeconvoluted_;
00249
00250
00251 void initStaticMembers_()
00252 {
00253 static bool init(false);
00254 if (!init)
00255 {
00256 s_value_ = xercesc::XMLString::transcode("value");
00257 s_count_ = xercesc::XMLString::transcode("scanCount");
00258 s_type_ = xercesc::XMLString::transcode("type");
00259 s_name_ = xercesc::XMLString::transcode("name");
00260 s_version_ = xercesc::XMLString::transcode("version");
00261 s_filename_ = xercesc::XMLString::transcode("fileName");
00262 s_filetype_ = xercesc::XMLString::transcode("fileType");
00263 s_filesha1_ = xercesc::XMLString::transcode("fileSha1");
00264 s_completiontime_ = xercesc::XMLString::transcode("completionTime");
00265 s_precision_ = xercesc::XMLString::transcode("precision");
00266 s_byteorder_ = xercesc::XMLString::transcode("byteOrder");
00267 s_pairorder_ = xercesc::XMLString::transcode("pairOrder");
00268 s_compressionType_ = xercesc::XMLString::transcode("compressionType");
00269 s_precursorintensity_ = xercesc::XMLString::transcode("precursorIntensity");
00270 s_precursorcharge_ = xercesc::XMLString::transcode("precursorCharge");
00271 s_windowwideness_ = xercesc::XMLString::transcode("windowWideness");
00272 s_mslevel_ = xercesc::XMLString::transcode("msLevel");
00273 s_peakscount_ = xercesc::XMLString::transcode("peaksCount");
00274 s_polarity_ = xercesc::XMLString::transcode("polarity");
00275 s_scantype_ = xercesc::XMLString::transcode("scanType");
00276 s_retentiontime_ = xercesc::XMLString::transcode("retentionTime");
00277 s_startmz_ = xercesc::XMLString::transcode("startMz");
00278 s_endmz_ = xercesc::XMLString::transcode("endMz");
00279 s_first_ = xercesc::XMLString::transcode("first");
00280 s_last_ = xercesc::XMLString::transcode("last");
00281 s_phone_ = xercesc::XMLString::transcode("phone");
00282 s_email_ = xercesc::XMLString::transcode("email");
00283 s_uri_ = xercesc::XMLString::transcode("URI");
00284 s_num_ = xercesc::XMLString::transcode("num");
00285 s_intensitycutoff_ = xercesc::XMLString::transcode("intensityCutoff");
00286 s_centroided_ = xercesc::XMLString::transcode("centroided");
00287 s_deisotoped_ = xercesc::XMLString::transcode("deisotoped");
00288 s_chargedeconvoluted_ = xercesc::XMLString::transcode("chargeDeconvoluted");
00289
00290 init = true;
00291 }
00292 return;
00293 }
00294
00295 };
00296
00297
00298
00299
00300 template <typename MapType>
00301 const XMLCh * MzXMLHandler<MapType>::s_value_ = 0;
00302 template <typename MapType>
00303 const XMLCh * MzXMLHandler<MapType>::s_count_ = 0;
00304 template <typename MapType>
00305 const XMLCh * MzXMLHandler<MapType>::s_type_ = 0;
00306 template <typename MapType>
00307 const XMLCh * MzXMLHandler<MapType>::s_name_ = 0;
00308 template <typename MapType>
00309 const XMLCh * MzXMLHandler<MapType>::s_version_ = 0;
00310 template <typename MapType>
00311 const XMLCh * MzXMLHandler<MapType>::s_filename_ = 0;
00312 template <typename MapType>
00313 const XMLCh * MzXMLHandler<MapType>::s_filetype_ = 0;
00314 template <typename MapType>
00315 const XMLCh * MzXMLHandler<MapType>::s_filesha1_ = 0;
00316 template <typename MapType>
00317 const XMLCh * MzXMLHandler<MapType>::s_completiontime_ = 0;
00318 template <typename MapType>
00319 const XMLCh * MzXMLHandler<MapType>::s_precision_ = 0;
00320 template <typename MapType>
00321 const XMLCh * MzXMLHandler<MapType>::s_byteorder_ = 0;
00322 template <typename MapType>
00323 const XMLCh * MzXMLHandler<MapType>::s_pairorder_ = 0;
00324 template <typename MapType>
00325 const XMLCh * MzXMLHandler<MapType>::s_compressionType_ = 0;
00326 template <typename MapType>
00327 const XMLCh * MzXMLHandler<MapType>::s_precursorintensity_ = 0;
00328 template <typename MapType>
00329 const XMLCh * MzXMLHandler<MapType>::s_precursorcharge_ = 0;
00330 template <typename MapType>
00331 const XMLCh * MzXMLHandler<MapType>::s_windowwideness_ = 0;
00332 template <typename MapType>
00333 const XMLCh * MzXMLHandler<MapType>::s_mslevel_ = 0;
00334 template <typename MapType>
00335 const XMLCh * MzXMLHandler<MapType>::s_peakscount_ = 0;
00336 template <typename MapType>
00337 const XMLCh * MzXMLHandler<MapType>::s_polarity_ = 0;
00338 template <typename MapType>
00339 const XMLCh * MzXMLHandler<MapType>::s_scantype_ = 0;
00340 template <typename MapType>
00341 const XMLCh * MzXMLHandler<MapType>::s_retentiontime_ = 0;
00342 template <typename MapType>
00343 const XMLCh * MzXMLHandler<MapType>::s_startmz_ = 0;
00344 template <typename MapType>
00345 const XMLCh * MzXMLHandler<MapType>::s_endmz_ = 0;
00346 template <typename MapType>
00347 const XMLCh * MzXMLHandler<MapType>::s_first_ = 0;
00348 template <typename MapType>
00349 const XMLCh * MzXMLHandler<MapType>::s_last_ = 0;
00350 template <typename MapType>
00351 const XMLCh * MzXMLHandler<MapType>::s_phone_ = 0;
00352 template <typename MapType>
00353 const XMLCh * MzXMLHandler<MapType>::s_email_ = 0;
00354 template <typename MapType>
00355 const XMLCh * MzXMLHandler<MapType>::s_uri_ = 0;
00356 template <typename MapType>
00357 const XMLCh * MzXMLHandler<MapType>::s_num_ = 0;
00358 template <typename MapType>
00359 const XMLCh * MzXMLHandler<MapType>::s_intensitycutoff_ = 0;
00360 template <typename MapType>
00361 const XMLCh * MzXMLHandler<MapType>::s_centroided_ = 0;
00362 template <typename MapType>
00363 const XMLCh * MzXMLHandler<MapType>::s_deisotoped_ = 0;
00364 template <typename MapType>
00365 const XMLCh * MzXMLHandler<MapType>::s_chargedeconvoluted_ = 0;
00366
00367 template <typename MapType>
00368 void MzXMLHandler<MapType>::startElement(const XMLCh * const , const XMLCh * const , const XMLCh * const qname, const xercesc::Attributes & attributes)
00369 {
00370 static bool init_static_members(false);
00371 if (!init_static_members)
00372 {
00373 initStaticMembers_();
00374 }
00375
00376 static UInt scan_count = 0;
00377
00378 String tag = sm_.convert(qname);
00379 open_tags_.push_back(tag);
00380
00381
00382
00383 if (skip_spectrum_ && tag != "scan")
00384 return;
00385
00386 if (tag == "msRun")
00387 {
00388 Int count = 0;
00389 optionalAttributeAsInt_(count, attributes, s_count_);
00390 exp_->reserve(count);
00391 logger_.startProgress(0, count, "loading mzXML file");
00392 scan_count = 0;
00393 data_processing_.clear();
00394
00395 }
00396 else if (tag == "parentFile")
00397 {
00398 SourceFile sf;
00399 sf.setNameOfFile(attributeAsString_(attributes, s_filename_));
00400 sf.setFileType(attributeAsString_(attributes, s_filetype_));
00401 sf.setChecksum(attributeAsString_(attributes, s_filesha1_), SourceFile::SHA1);
00402 exp_->getSourceFiles().push_back(sf);
00403 }
00404 else if (tag == "software")
00405 {
00406 String & parent_tag = *(open_tags_.end() - 2);
00407 if (parent_tag == "dataProcessing")
00408 {
00409 data_processing_.back().getSoftware().setVersion(attributeAsString_(attributes, s_version_));
00410 data_processing_.back().getSoftware().setName(attributeAsString_(attributes, s_name_));
00411 data_processing_.back().setMetaValue("#type", String(attributeAsString_(attributes, s_type_)));
00412
00413 String time;
00414 optionalAttributeAsString_(time, attributes, s_completiontime_);
00415 data_processing_.back().setCompletionTime(asDateTime_(time));
00416 }
00417 else if (parent_tag == "msInstrument")
00418 {
00419 exp_->getInstrument().getSoftware().setVersion(attributeAsString_(attributes, s_version_));
00420 exp_->getInstrument().getSoftware().setName(attributeAsString_(attributes, s_name_));
00421 }
00422 }
00423 else if (tag == "peaks")
00424 {
00425
00426 precision_ = "32";
00427 optionalAttributeAsString_(precision_, attributes, s_precision_);
00428 if (precision_ != "32" && precision_ != "64")
00429 {
00430 error(LOAD, String("Invalid precision '") + precision_ + "' in element 'peaks'");
00431 }
00432
00433 String byte_order = "network";
00434 optionalAttributeAsString_(byte_order, attributes, s_byteorder_);
00435 if (byte_order != "network")
00436 {
00437 error(LOAD, String("Invalid or missing byte order '") + byte_order + "' in element 'peaks'. Must be 'network'!");
00438 }
00439
00440 String pair_order = "m/z-int";
00441 optionalAttributeAsString_(pair_order, attributes, s_pairorder_);
00442 if (pair_order != "m/z-int")
00443 {
00444 error(LOAD, String("Invalid or missing pair order '") + pair_order + "' in element 'peaks'. Must be 'm/z-int'!");
00445 }
00446
00447 compressionType_ = "none";
00448 optionalAttributeAsString_(compressionType_, attributes, s_compressionType_);
00449 if (compressionType_ != "none" && compressionType_ != "zlib")
00450 {
00451 error(LOAD, String("Invalid compression type ") + compressionType_ + "in elements 'peaks'. Must be 'none' or 'zlib'! ");
00452 }
00453 }
00454 else if (tag == "precursorMz")
00455 {
00456
00457 exp_->back().getPrecursors().push_back(Precursor());
00458
00459 try
00460 {
00461 exp_->back().getPrecursors().back().setIntensity(attributeAsDouble_(attributes, s_precursorintensity_));
00462 }
00463 catch (Exception::ParseError & )
00464 {
00465 error(LOAD, "Mandatory attribute 'precursorIntensity' of tag 'precursorMz' not found! Setting precursor intensity to zero!");
00466 }
00467
00468 Int charge = 0;
00469 if (optionalAttributeAsInt_(charge, attributes, s_precursorcharge_))
00470 {
00471 exp_->back().getPrecursors().back().setCharge(charge);
00472 }
00473
00474 DoubleReal window = 0.0;
00475 if (optionalAttributeAsDouble_(window, attributes, s_windowwideness_))
00476 {
00477 exp_->back().getPrecursors().back().setIsolationWindowLowerOffset(window);
00478 }
00479 }
00480 else if (tag == "scan")
00481 {
00482 skip_spectrum_ = false;
00483
00484 if (options_.getMetadataOnly())
00485 throw EndParsingSoftly(__FILE__, __LINE__, __PRETTY_FUNCTION__);
00486
00487
00488 UInt ms_level = attributeAsInt_(attributes, s_mslevel_);
00489 if (ms_level == 0)
00490 {
00491 warning(LOAD, String("Invalid 'msLevel' attribute with value '0' in 'scan' element found. Assuming ms level 1!"));
00492 ms_level = 1;
00493 }
00494
00495
00496 DoubleReal retention_time = 0.0;
00497 String time_string = "";
00498 if (optionalAttributeAsString_(time_string, attributes, s_retentiontime_))
00499 {
00500 time_string = time_string.suffix('T');
00501
00502 if (time_string.has('H'))
00503 {
00504 retention_time += 3600 * asDouble_(time_string.prefix('H'));
00505 time_string = time_string.suffix('H');
00506
00507 }
00508 if (time_string.has('M'))
00509 {
00510 retention_time += 60 * asDouble_(time_string.prefix('M'));
00511 time_string = time_string.suffix('M');
00512
00513 }
00514 if (time_string.has('S'))
00515 {
00516 retention_time += asDouble_(time_string.prefix('S'));
00517 time_string = time_string.suffix('S');
00518
00519 }
00520 }
00521
00522 logger_.setProgress(scan_count);
00523
00524 if ((options_.hasRTRange() && !options_.getRTRange().encloses(DPosition<1>(retention_time)))
00525 || (options_.hasMSLevels() && !options_.containsMSLevel(ms_level)))
00526 {
00527
00528 skip_spectrum_ = true;
00529 ++scan_count;
00530 return;
00531 }
00532
00533
00534 exp_->resize(exp_->size() + 1);
00535 exp_->back().setMSLevel(ms_level);
00536 exp_->back().setRT(retention_time);
00537 exp_->back().setNativeID(String("scan=") + attributeAsString_(attributes, s_num_));
00538
00539 peak_count_ = attributeAsInt_(attributes, s_peakscount_);
00540 exp_->back().reserve(peak_count_ / 2 + 1);
00541 exp_->back().setDataProcessing(data_processing_);
00542
00543
00544
00545
00546 ScanWindow window;
00547 optionalAttributeAsDouble_(window.begin, attributes, s_startmz_);
00548 optionalAttributeAsDouble_(window.end, attributes, s_endmz_);
00549 if (window.begin != 0.0 || window.end != 0.0)
00550 {
00551 exp_->back().getInstrumentSettings().getScanWindows().push_back(window);
00552 }
00553
00554 String polarity = "any";
00555 optionalAttributeAsString_(polarity, attributes, s_polarity_);
00556 exp_->back().getInstrumentSettings().setPolarity((IonSource::Polarity) cvStringToEnum_(0, polarity, "polarity"));
00557
00558 String type = "";
00559 optionalAttributeAsString_(type, attributes, s_scantype_);
00560 if (type == "")
00561 {
00562
00563 }
00564 else if (type == "zoom")
00565 {
00566 exp_->back().getInstrumentSettings().setZoomScan(true);
00567 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00568 }
00569 else if (type == "Full")
00570 {
00571 if (ms_level > 1)
00572 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MSNSPECTRUM);
00573 else
00574 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00575 }
00576 else if (type == "SIM")
00577 {
00578 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::SIM);
00579 }
00580 else if (type == "SRM" || type == "MRM")
00581 {
00582 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::SRM);
00583 }
00584 else if (type == "CRM")
00585 {
00586 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::CRM);
00587 }
00588 else if (type == "Q1")
00589 {
00590 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00591 }
00592 else if (type == "Q3")
00593 {
00594 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00595 }
00596 else if (type == "EMS")
00597 {
00598 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00599 }
00600 else if (type == "EPI")
00601 {
00602 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00603 exp_->back().setMSLevel(2);
00604 }
00605 else if (type == "ER")
00606 {
00607 exp_->back().getInstrumentSettings().setZoomScan(true);
00608 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00609 }
00610 else
00611 {
00612 exp_->back().getInstrumentSettings().setScanMode(InstrumentSettings::MASSSPECTRUM);
00613 warning(LOAD, String("Unknown scan mode '") + type + "'. Assuming full scan");
00614 }
00615
00616 ++scan_count;
00617 }
00618 else if (tag == "operator")
00619 {
00620 exp_->getContacts().resize(1);
00621 exp_->getContacts().back().setFirstName(attributeAsString_(attributes, s_first_));
00622 exp_->getContacts().back().setLastName(attributeAsString_(attributes, s_last_));
00623
00624 String tmp = "";
00625 optionalAttributeAsString_(tmp, attributes, s_email_);
00626 exp_->getContacts().back().setEmail(tmp);
00627
00628 tmp = "";
00629 optionalAttributeAsString_(tmp, attributes, s_phone_);
00630 if (tmp != "")
00631 {
00632 exp_->getContacts().back().setMetaValue("#phone", tmp);
00633 }
00634
00635 tmp = "";
00636 optionalAttributeAsString_(tmp, attributes, s_uri_);
00637 exp_->getContacts().back().setURL(tmp);
00638 }
00639 else if (tag == "msManufacturer")
00640 {
00641 exp_->getInstrument().setVendor(attributeAsString_(attributes, s_value_));
00642 }
00643 else if (tag == "msModel")
00644 {
00645 exp_->getInstrument().setModel(attributeAsString_(attributes, s_value_));
00646 }
00647 else if (tag == "msIonisation")
00648 {
00649 exp_->getInstrument().getIonSources().resize(1);
00650 exp_->getInstrument().getIonSources()[0].setIonizationMethod((IonSource::IonizationMethod) cvStringToEnum_(2, attributeAsString_(attributes, s_value_), "msIonization"));
00651 }
00652 else if (tag == "msMassAnalyzer")
00653 {
00654 exp_->getInstrument().getMassAnalyzers().resize(1);
00655 exp_->getInstrument().getMassAnalyzers()[0].setType((MassAnalyzer::AnalyzerType) cvStringToEnum_(3, attributeAsString_(attributes, s_value_), "msMassAnalyzer"));
00656 }
00657 else if (tag == "msDetector")
00658 {
00659 exp_->getInstrument().getIonDetectors().resize(1);
00660 exp_->getInstrument().getIonDetectors()[0].setType((IonDetector::Type) cvStringToEnum_(4, attributeAsString_(attributes, s_value_), "msDetector"));
00661 }
00662 else if (tag == "msResolution")
00663 {
00664 exp_->getInstrument().getMassAnalyzers()[0].setResolutionMethod((MassAnalyzer::ResolutionMethod) cvStringToEnum_(5, attributeAsString_(attributes, s_value_), "msResolution"));
00665 }
00666 else if (tag == "dataProcessing")
00667 {
00668 data_processing_.push_back(DataProcessing());
00669
00670 String boolean = "";
00671 optionalAttributeAsString_(boolean, attributes, s_deisotoped_);
00672 if (boolean == "true" || boolean == "1")
00673 {
00674 data_processing_.back().getProcessingActions().insert(DataProcessing::DEISOTOPING);
00675 }
00676
00677 boolean = "";
00678 optionalAttributeAsString_(boolean, attributes, s_chargedeconvoluted_);
00679 if (boolean == "true" || boolean == "1")
00680 {
00681 data_processing_.back().getProcessingActions().insert(DataProcessing::CHARGE_DECONVOLUTION);
00682 }
00683
00684 DoubleReal cutoff = 0.0;
00685 optionalAttributeAsDouble_(cutoff, attributes, s_intensitycutoff_);
00686 if (cutoff != 0.0)
00687 {
00688 data_processing_.back().setMetaValue("#intensity_cutoff", cutoff);
00689 }
00690
00691 boolean = "";
00692 optionalAttributeAsString_(boolean, attributes, s_centroided_);
00693 if (boolean == "true" || boolean == "1")
00694 {
00695 data_processing_.back().getProcessingActions().insert(DataProcessing::PEAK_PICKING);
00696 }
00697 }
00698 else if (tag == "nameValue")
00699 {
00700 String name = "";
00701 optionalAttributeAsString_(name, attributes, s_name_);
00702 if (name == "")
00703 return;
00704
00705 String value = "";
00706 optionalAttributeAsString_(value, attributes, s_value_);
00707
00708 String & parent_tag = *(open_tags_.end() - 2);
00709
00710 if (parent_tag == "msInstrument")
00711 {
00712 exp_->getInstrument().setMetaValue(name, value);
00713 }
00714 else if (parent_tag == "scan")
00715 {
00716 exp_->back().setMetaValue(name, value);
00717 }
00718 else
00719 {
00720 std::cout << " Warning: Unexpected tag 'nameValue' in tag '" << parent_tag << "'" << "\n";
00721 }
00722 }
00723 else if (tag == "processingOperation")
00724 {
00725 String name = "";
00726 optionalAttributeAsString_(name, attributes, s_name_);
00727 if (name == "")
00728 return;
00729
00730 String value = "";
00731 optionalAttributeAsString_(value, attributes, s_value_);
00732
00733 data_processing_.back().setMetaValue(name, value);
00734 }
00735
00736
00737 }
00738
00739 template <typename MapType>
00740 void MzXMLHandler<MapType>::endElement(const XMLCh * const , const XMLCh * const , const XMLCh * const qname)
00741 {
00742
00743
00744 static const XMLCh * s_mzxml = xercesc::XMLString::transcode("mzXML");
00745 static const XMLCh * s_peaks = xercesc::XMLString::transcode("peaks");
00746
00747 open_tags_.pop_back();
00748
00749
00750 if (skip_spectrum_)
00751 return;
00752
00753 if (equal_(qname, s_mzxml))
00754 {
00755 logger_.endProgress();
00756 }
00757 else if (equal_(qname, s_peaks))
00758 {
00759
00760 if (char_rest_ == "")
00761 {
00762 return;
00763 }
00764
00765
00766
00767 char_rest_.removeWhitespaces();
00768
00769 if (precision_ == "64")
00770 {
00771 std::vector<DoubleReal> data;
00772 if (compressionType_ == "zlib")
00773 {
00774 decoder_.decode(char_rest_, Base64::BYTEORDER_BIGENDIAN, data, true);
00775 }
00776 else
00777 {
00778 decoder_.decode(char_rest_, Base64::BYTEORDER_BIGENDIAN, data);
00779 }
00780 char_rest_ = "";
00781 PeakType peak;
00782
00783 for (Size n = 0; n < (2 * peak_count_); n += 2)
00784 {
00785
00786 if ((!options_.hasMZRange() || options_.getMZRange().encloses(DPosition<1>(data[n])))
00787 && (!options_.hasIntensityRange() || options_.getIntensityRange().encloses(DPosition<1>(data[n + 1]))))
00788 {
00789 peak.setMZ(data[n]);
00790 peak.setIntensity(data[n + 1]);
00791 exp_->back().push_back(peak);
00792 }
00793 }
00794 }
00795 else
00796 {
00797 std::vector<Real> data;
00798 if (compressionType_ == "zlib")
00799 {
00800 decoder_.decode(char_rest_, Base64::BYTEORDER_BIGENDIAN, data, true);
00801 }
00802 else
00803 {
00804 decoder_.decode(char_rest_, Base64::BYTEORDER_BIGENDIAN, data);
00805 }
00806 char_rest_ = "";
00807 PeakType peak;
00808
00809 for (Size n = 0; n < (2 * peak_count_); n += 2)
00810 {
00811 if ((!options_.hasMZRange() || options_.getMZRange().encloses(DPosition<1>(data[n])))
00812 && (!options_.hasIntensityRange() || options_.getIntensityRange().encloses(DPosition<1>(data[n + 1]))))
00813 {
00814 peak.setMZ(data[n]);
00815 peak.setIntensity(data[n + 1]);
00816 exp_->back().push_back(peak);
00817 }
00818 }
00819 }
00820 }
00821
00822 sm_.clear();
00823 }
00824
00825 template <typename MapType>
00826 void MzXMLHandler<MapType>::characters(const XMLCh * const chars, const XMLSize_t )
00827 {
00828
00829 if (skip_spectrum_)
00830 return;
00831
00832 char * transcoded_chars = sm_.convert(chars);
00833
00834 if (open_tags_.back() == "peaks")
00835 {
00836
00837 char_rest_ += transcoded_chars;
00838 }
00839 else if (open_tags_.back() == "offset" || open_tags_.back() == "indexOffset" || open_tags_.back() == "sha1")
00840 {
00841
00842 }
00843 else if (open_tags_.back() == "precursorMz")
00844 {
00845 DoubleReal mz_pos = asDouble_(transcoded_chars);
00846
00847 exp_->back().getPrecursors().back().setMZ(mz_pos);
00848
00849 DoubleReal window_width = exp_->back().getPrecursors().back().getIsolationWindowLowerOffset();
00850 if (window_width != 0.0)
00851 {
00852 exp_->back().getPrecursors().back().setIsolationWindowLowerOffset(mz_pos - 0.5 * window_width);
00853 exp_->back().getPrecursors().back().setIsolationWindowUpperOffset(mz_pos + 0.5 * window_width);
00854 }
00855 }
00856 else if (open_tags_.back() == "comment")
00857 {
00858 String parent_tag = *(open_tags_.end() - 2);
00859
00860
00861 if (parent_tag == "msInstrument")
00862 {
00863 exp_->getInstrument().setMetaValue("#comment", String(transcoded_chars));
00864 }
00865 else if (parent_tag == "dataProcessing")
00866 {
00867
00868 }
00869 else if (parent_tag == "scan")
00870 {
00871 exp_->back().setComment(transcoded_chars);
00872 }
00873 else if (String(transcoded_chars).trim() != "")
00874 {
00875 warning(LOAD, String("Unhandled comment '") + transcoded_chars + "' in element '" + open_tags_.back() + "'");
00876 }
00877 }
00878 else if (String(transcoded_chars).trim() != "")
00879 {
00880 warning(LOAD, String("Unhandled character content '") + transcoded_chars + "' in element '" + open_tags_.back() + "'");
00881 }
00882 }
00883
00884 template <typename MapType>
00885 void MzXMLHandler<MapType>::writeTo(std::ostream & os)
00886 {
00887
00888 UInt count_tmp_ = 0;
00889 for (Size s = 0; s < cexp_->size(); s++)
00890 {
00891 const SpectrumType & spec = (*cexp_)[s];
00892 if (spec.size() != 0)
00893 ++count_tmp_;
00894 }
00895 if (count_tmp_ == 0)
00896 ++count_tmp_;
00897 logger_.startProgress(0, cexp_->size(), "storing mzXML file");
00898 os << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
00899 << "<mzXML xmlns=\"http://sashimi.sourceforge.net/schema_revision/mzXML_2.1\" "
00900 << "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
00901 << "xsi:schemaLocation=\"http://sashimi.sourceforge.net/schema_revision/mzXML_2.1 "
00902 << "http://sashimi.sourceforge.net/schema_revision/mzXML_2.1/mzXML_idx_2.1.xsd\">\n"
00903 << "\t<msRun scanCount=\"" << count_tmp_ << "\">\n";
00904
00905
00906
00907
00908 if (cexp_->getSourceFiles().empty())
00909 {
00910 os << "\t\t<parentFile fileName=\"\" fileType=\"processedData\" fileSha1=\"0000000000000000000000000000000000000000\"/>\n";
00911 }
00912 else
00913 {
00914 for (Size i = 0; i < cexp_->getSourceFiles().size(); ++i)
00915 {
00916 const SourceFile & sf = cexp_->getSourceFiles()[i];
00917 os << "\t\t<parentFile fileName=\"" << sf.getNameOfFile() << "\" fileType=\"";
00918
00919 String tmp_string = sf.getFileType();
00920 tmp_string.toLower();
00921 if (tmp_string.hasSubstring("raw"))
00922 {
00923 os << "RAWData";
00924 }
00925 else
00926 {
00927 os << "processedData";
00928 }
00929
00930 os << "\" fileSha1=\"";
00931 tmp_string = sf.getChecksum();
00932 if (sf.getChecksum().size() != 40 || sf.getChecksumType() != SourceFile::SHA1)
00933 {
00934 os << "0000000000000000000000000000000000000000";
00935 }
00936 else
00937 {
00938 os << sf.getChecksum();
00939 }
00940 os << "\"/>\n";
00941 }
00942 }
00943
00944
00945
00946
00947 if (cexp_->getInstrument() != Instrument() || cexp_->getContacts().size() != 0)
00948 {
00949 const Instrument & inst = cexp_->getInstrument();
00950 os << "\t\t<msInstrument>\n"
00951 << "\t\t\t<msManufacturer category=\"msManufacturer\" value=\"" << inst.getVendor() << "\"/>\n" << "\t\t\t<msModel category=\"msModel\" value=\"" << inst.getModel() << "\"/>\n";
00952 if (inst.getIonSources().empty() || !inst.getIonSources()[0].getIonizationMethod())
00953 {
00954 os << "\t\t\t<msIonisation category=\"msIonisation\" value=\"\"/>\n";
00955 }
00956 else
00957 {
00958 os << "\t\t\t<msIonisation category=\"msIonisation\" value=\"" << cv_terms_[2][inst.getIonSources()[0].getIonizationMethod()] << "\"/>\n";
00959 }
00960 const std::vector<MassAnalyzer> & analyzers = inst.getMassAnalyzers();
00961 if (analyzers.empty() || !analyzers[0].getResolutionMethod())
00962 {
00963 os << "\t\t\t<msMassAnalyzer category=\"msMassAnalyzer\" value=\"\"/>\n";
00964 }
00965 else
00966 {
00967 os << "\t\t\t<msMassAnalyzer category=\"msMassAnalyzer\" value=\"" << cv_terms_[3][analyzers[0].getType()] << "\"/>\n";
00968 }
00969 if (inst.getIonDetectors().empty() || !inst.getIonDetectors()[0].getType())
00970 {
00971 os << "\t\t\t<msDetector category=\"msDetector\" value=\"\"/>\n";
00972 }
00973 else
00974 {
00975 os << "\t\t\t<msDetector category=\"msDetector\" value=\"" << cv_terms_[4][inst.getIonDetectors()[0].getType()] << "\"/>\n";
00976 }
00977 os << "\t\t\t<software type=\"acquisition\" name=\"" << inst.getSoftware().getName() << "\" version=\"" << inst.getSoftware().getVersion() << "\"/>\n";
00978 if (analyzers.empty() || !analyzers[0].getResolutionMethod())
00979 {
00980 os << "\t\t\t<msResolution category=\"msResolution\" value=\"\"/>\n";
00981 }
00982 else
00983 {
00984 os << "\t\t\t<msResolution category=\"msResolution\" value=\"" << cv_terms_[5][analyzers[0].getResolutionMethod()] << "\"/>\n";
00985 }
00986
00987 if (cexp_->getContacts().size() > 0)
00988 {
00989 const ContactPerson & cont = cexp_->getContacts()[0];
00990
00991 os << "\t\t\t<operator first=\"" << cont.getFirstName() << "\" last=\"" << cont.getLastName() << "\"";
00992
00993 if (cont.getEmail() != "")
00994 {
00995 os << " email=\"" << cont.getEmail() << "\"";
00996 }
00997
00998 if (cont.getURL() != "")
00999 {
01000 os << " URI=\"" << cont.getURL() << "\"";
01001 }
01002
01003 if (cont.metaValueExists("#phone"))
01004 {
01005 os << " phone=\"" << (String)(cont.getMetaValue("#phone")) << "\"";
01006 }
01007
01008 os << "/>\n";
01009 }
01010 writeUserParam_(os, inst, 3);
01011
01012 if (inst.metaValueExists("#comment"))
01013 {
01014 os << "\t\t\t<comment>" << inst.getMetaValue("#comment") << "</comment>\n";
01015 }
01016
01017 os << "\t\t</msInstrument>\n";
01018 }
01019
01020
01021
01022
01023 if (cexp_->size() == 0 || (*cexp_)[0].getDataProcessing().empty())
01024 {
01025 os << "\t\t<dataProcessing>\n"
01026 << "\t\t\t<software type=\"processing\" name=\"\" version=\"\"/>\n"
01027 << "\t\t</dataProcessing>\n";
01028 }
01029 else
01030 {
01031 for (Size i = 0; i < (*cexp_)[0].getDataProcessing().size(); ++i)
01032 {
01033 const DataProcessing & data_processing = (*cexp_)[0].getDataProcessing()[i];
01034 os << "\t\t<dataProcessing deisotoped=\""
01035 << data_processing.getProcessingActions().count(DataProcessing::DEISOTOPING)
01036 << "\" chargeDeconvoluted=\""
01037 << data_processing.getProcessingActions().count(DataProcessing::CHARGE_DECONVOLUTION)
01038 << "\" centroided=\""
01039 << data_processing.getProcessingActions().count(DataProcessing::PEAK_PICKING)
01040 << "\"";
01041 if (data_processing.metaValueExists("#intensity_cutoff"))
01042 {
01043 os << " intensityCutoff=\"" << data_processing.getMetaValue("#intensity_cutoff").toString() << "\"";
01044 }
01045 os << ">\n"
01046 << "\t\t\t<software type=\"";
01047 if (data_processing.metaValueExists("#type"))
01048 {
01049 os << data_processing.getMetaValue("#type").toString();
01050 }
01051 else
01052 {
01053 os << "processing";
01054 }
01055
01056 os << "\" name=\"" << data_processing.getSoftware().getName()
01057 << "\" version=\"" << data_processing.getSoftware().getVersion();
01058
01059 if (data_processing.getCompletionTime() != DateTime())
01060 {
01061 os << "\" completionTime=\"" << data_processing.getCompletionTime().get().substitute(' ', 'T');
01062 }
01063 os << "\"/>\n";
01064 writeUserParam_(os, data_processing, 3, "processingOperation");
01065
01066 os << "\t\t</dataProcessing>\n";
01067 }
01068 }
01069
01070
01071
01072 bool all_numbers = true;
01073 bool all_empty = true;
01074 bool all_prefixed_numbers = true;
01075 for (Size s = 0; s < cexp_->size(); s++)
01076 {
01077 String native_id = (*cexp_)[s].getNativeID();
01078 if (!native_id.hasPrefix("scan="))
01079 {
01080 all_prefixed_numbers = false;
01081 }
01082 else
01083 {
01084 native_id = native_id.substr(5);
01085 }
01086 try
01087 {
01088 native_id.toInt();
01089 }
01090 catch (Exception::ConversionError &)
01091 {
01092 all_numbers = false;
01093 all_prefixed_numbers = false;
01094 if (native_id != "")
01095 {
01096 all_empty = false;
01097 }
01098 }
01099 }
01100
01101 if (!all_numbers && !all_empty)
01102 {
01103 warning(STORE, "Not all spectrum native IDs are numbers or correctly prefixed with 'scan='. The spectra are renumbered and the native IDs are lost!");
01104 }
01105
01106
01107 std::stack<UInt> open_scans;
01108 for (Size s = 0; s < cexp_->size(); s++)
01109 {
01110 logger_.setProgress(s);
01111 const SpectrumType & spec = (*cexp_)[s];
01112
01113 UInt ms_level = spec.getMSLevel();
01114 open_scans.push(ms_level);
01115
01116 Size spectrum_id = s + 1;
01117 if (all_prefixed_numbers)
01118 {
01119 spectrum_id = spec.getNativeID().substr(5).toInt();
01120 }
01121 else if (all_numbers)
01122 {
01123 spectrum_id = spec.getNativeID().toInt();
01124 }
01125
01126 os << String(ms_level + 1, '\t')
01127 << "<scan num=\"" << spectrum_id << "\" msLevel=\""
01128 << ms_level << "\" peaksCount=\""
01129 << spec.size() << "\" polarity=\"";
01130 if (spec.getInstrumentSettings().getPolarity() == IonSource::POSITIVE)
01131 {
01132 os << "+";
01133 }
01134 else if (spec.getInstrumentSettings().getPolarity() == IonSource::NEGATIVE)
01135 {
01136 os << "-";
01137 }
01138 else
01139 {
01140 os << "any";
01141 }
01142
01143
01144 switch (spec.getInstrumentSettings().getScanMode())
01145 {
01146 case InstrumentSettings::UNKNOWN:
01147 break;
01148
01149 case InstrumentSettings::MASSSPECTRUM:
01150 case InstrumentSettings::MS1SPECTRUM:
01151 case InstrumentSettings::MSNSPECTRUM:
01152 if (spec.getInstrumentSettings().getZoomScan())
01153 {
01154 os << "\" scanType=\"zoom";
01155 }
01156 else
01157 {
01158 os << "\" scanType=\"Full";
01159 }
01160 break;
01161
01162 case InstrumentSettings::SIM:
01163 os << "\" scanType=\"SIM";
01164 break;
01165
01166 case InstrumentSettings::SRM:
01167 os << "\" scanType=\"SRM";
01168 break;
01169
01170 case InstrumentSettings::CRM:
01171 os << "\" scanType=\"CRM";
01172 break;
01173
01174 default:
01175 os << "\" scanType=\"Full";
01176 warning(STORE, String("Scan type '") + InstrumentSettings::NamesOfScanMode[spec.getInstrumentSettings().getScanMode()] + "' not supported by mzXML. Using 'Full' scan mode!");
01177 }
01178
01179 os << "\" retentionTime=\"";
01180 if (spec.getRT() < 0)
01181 os << "-";
01182 os << "PT" << std::fabs(spec.getRT()) << "S\"";
01183 if (!spec.getInstrumentSettings().getScanWindows().empty())
01184 {
01185 os << " startMz=\"" << spec.getInstrumentSettings().getScanWindows()[0].begin << "\" endMz=\"" << spec.getInstrumentSettings().getScanWindows()[0].end << "\"";
01186 }
01187 if (spec.getInstrumentSettings().getScanWindows().size() > 1)
01188 {
01189 warning(STORE, "The MzXML format can store only one scan window for each scan. Only the first one is stored!");
01190 }
01191 os << ">\n";
01192
01193
01194 for (Size i = 0; i < spec.getPrecursors().size(); ++i)
01195 {
01196 const Precursor & precursor = spec.getPrecursors()[i];
01197
01198 os << String(ms_level + 2, '\t') << "<precursorMz precursorIntensity=\"" << precursor.getIntensity();
01199
01200 if (precursor.getCharge() != 0)
01201 os << "\" precursorCharge=\"" << precursor.getCharge();
01202
01203 if (precursor.getIsolationWindowLowerOffset() != precursor.getIsolationWindowUpperOffset())
01204 os << "\" windowWideness=\"" << (precursor.getIsolationWindowUpperOffset() - precursor.getIsolationWindowLowerOffset());
01205
01206 os << "\">" << precursor.getMZ() << "</precursorMz>\n";
01207 }
01208
01209 if (!spec.empty())
01210 {
01211 os << String(ms_level + 2, '\t') << "<peaks precision=\"32\"" << " byteOrder=\"network\" pairOrder=\"m/z-int\">";
01212
01213
01214 std::vector<Real> tmp;
01215 for (Size i = 0; i < spec.size(); i++)
01216 {
01217 tmp.push_back(spec[i].getMZ());
01218 tmp.push_back(spec[i].getIntensity());
01219 }
01220
01221 String encoded;
01222 decoder_.encode(tmp, Base64::BYTEORDER_BIGENDIAN, encoded);
01223 os << encoded << "</peaks>\n";
01224 }
01225 else
01226 {
01227 os << String(ms_level + 2, '\t') << "<peaks precision=\"32\"" << " byteOrder=\"network\" pairOrder=\"m/z-int\" xsi:nil=\"1\"/>\n";
01228 }
01229
01230 writeUserParam_(os, spec, ms_level + 2);
01231 if (spec.getComment() != "")
01232 {
01233 os << String(ms_level + 2, '\t') << "<comment>" << spec.getComment() << "</comment>\n";
01234 }
01235
01236
01237 UInt next_ms_level = 0;
01238 if (s < cexp_->size() - 1)
01239 {
01240 next_ms_level = ((*cexp_)[s + 1]).getMSLevel();
01241 }
01242
01243 if (next_ms_level <= ms_level)
01244 {
01245 for (Size i = 0; i <= ms_level - next_ms_level && !open_scans.empty(); ++i)
01246 {
01247 os << String(ms_level - i + 1, '\t') << "</scan>\n";
01248 open_scans.pop();
01249 }
01250 }
01251 }
01252
01253 os << "\t</msRun>\n"
01254 << "\t<indexOffset>0</indexOffset>\n"
01255 << "</mzXML>\n";
01256
01257 logger_.endProgress();
01258 spec_write_counter_ = 1;
01259 }
01260
01261 }
01262
01263 }
01264
01265 #endif