musrfit  1.9.2
musrRootValidation.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 
3  musrRootValidation.cpp
4 
5  Author: Andreas Suter
6  e-mail: andreas.suter@psi.ch
7 
8 ***************************************************************************/
9 
10 /***************************************************************************
11  * Copyright (C) 2007-2023 by Andreas Suter *
12  * andreas.suter@psi.ch *
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * *
19  * This program is distributed in the hope that it will be useful, *
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22  * GNU General Public License for more details. *
23  * *
24  * You should have received a copy of the GNU General Public License *
25  * along with this program; if not, write to the *
26  * Free Software Foundation, Inc., *
27  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28  ***************************************************************************/
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include <iostream>
35 #include <fstream>
36 #include <vector>
37 #include <string>
38 
39 #include "TString.h"
40 #include "TFile.h"
41 #include "TFolder.h"
42 #include "TKey.h"
43 #include "TObjArray.h"
44 #include "TObjString.h"
45 #include "TSystemFile.h"
46 
47 #include <libxml/xmlreader.h>
48 #include <libxml/parser.h>
49 #include <libxml/xmlschemas.h>
50 
51 #ifdef HAVE_GIT_REV_H
52 #include "git-revision.h"
53 #endif
54 
55 #include "PMusr.h"
56 
57 //-----------------------------------------------------------------------
62 {
63  public:
64  PMusrRoot2Xml(const char *fileName, bool quiet, bool keep);
65  virtual ~PMusrRoot2Xml();
66 
67  virtual Bool_t IsValid() { return fValid; }
68  virtual TString GetXmlDumpFileName() { return fXmlDumpFileName; }
69  virtual UInt_t GetNoOfDecayHistos() { return fNoOfDecayHistos; }
71  virtual UInt_t GetNoOfHistos() { return fNoOfHistos; }
72  virtual UInt_t GetNoOfRedGreenOffsets() { return fNoOfRedGreenOffsets; }
73  virtual UInt_t GetNoOfDetectors() { return fNoOfDetectors; }
74 
75  private:
77 
78  std::vector<std::string> fXmlData;
79 
80  Bool_t fQuiet;
81  Bool_t fKeep;
82  Bool_t fValid;
83  TString fFileName;
84  TString fXmlDumpFileName;
86 
88  UInt_t fNoOfHistos;
90  UInt_t fNoOfDetectors;
91 
92  virtual void SortHistoFolders();
93  virtual void DumpFolder(TFolder *folder, UInt_t offset);
94  virtual void DumpObjArray(TObjArray *obj, UInt_t offset);
95  virtual void DumpEntry(TObject *obj, UInt_t offset);
96  virtual void CheckClass(TObject *obj, TString str, UInt_t offset);
97  virtual void GetType(TString entry, TString &type);
98  virtual void GetLabel(TString entry, TString &label);
99 };
100 
101 //-----------------------------------------------------------------------
105 PMusrRoot2Xml::PMusrRoot2Xml(const char *fileName, bool quiet, bool keep) : fQuiet(quiet), fKeep(keep), fFileName(fileName)
106 {
107  fXmlDumpFileName = "__MusrRootXmlDump.xml";
109  fValid = false;
110  fXmlData.clear();
111  fNoOfDecayHistos = 0;
112  fNoOfHistos = 0;
114  fNoOfDetectors = 0;
115 
116  // read assumed MusrRoot file
117  TFile f(fFileName.Data());
118 
119  if (f.IsZombie()) {
120  std::cerr << std::endl << "**ERROR** couldn't open file " << fFileName << std::endl;
121  return;
122  }
123 
124  fXmlData.push_back("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
125  fXmlData.push_back("<MusrRoot xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"file:MusrRoot.xsd\">");
126 
127  TIter next = f.GetListOfKeys();
128  TKey *key;
129  TFolder *folder;
130  TString str, tag;
131 
132  UInt_t offset = 2;
133 
134  while ((key = dynamic_cast<TKey*>(next())) != nullptr) {
135  if (!fQuiet) std::cout << std::endl << "name: " << key->GetName() << ", class name: " << key->GetClassName();
136  str = key->GetClassName();
137  if (str == "TFolder") {
138  folder = dynamic_cast<TFolder*>(key->ReadObj());
139  CheckClass(folder, str, offset);
140  }
141  }
142  if (!fQuiet) std::cout << std::endl;
143 
144  f.Close();
145 
146  fXmlData.push_back("</MusrRoot>");
147 
148  // the sort_histo_folders is needed since XML-Schema is not flexible enough to handle
149  // histos -|
150  // |- DecayAnaModule
151  // ... (any other analyzer module sub-folder
152  // |- SCAnaModule
153  // Hence SCAnaModule has artificially moved up, just to follow DecayAnaModule
155 
156  std::ofstream fout(fXmlDumpFileName.Data());
157 
158  for (UInt_t i=0; i<fXmlData.size(); i++)
159  fout << fXmlData[i] << std::endl;
160  fout.close();
161 
162  fValid = true;
163 }
164 
165 //-----------------------------------------------------------------------
170 {
171  if (!fKeep) {
172  TSystemFile sf(fXmlDumpFileName.Data(), "./");
173  sf.Delete();
174  }
175 }
176 
177 //-----------------------------------------------------------------------
187 {
188  std::vector<std::string> temp_xml_data;
189 
190  // first make a copy of the original fXmlData
191  for (unsigned int i=0; i<fXmlData.size(); i++)
192  temp_xml_data.push_back(fXmlData[i]);
193 
194  // remove SCAnaModule from temp_xml_data
195  unsigned int start = 0, end = 0;
196  for (unsigned int i=0; i<temp_xml_data.size(); i++) {
197  if (temp_xml_data[i].find("<SCAnaModule>") != std::string::npos)
198  start = i;
199  if (temp_xml_data[i].find("</SCAnaModule>") != std::string::npos)
200  end = i+1;
201  }
202  if ((start > 0) && (end > 0))
203  temp_xml_data.erase(temp_xml_data.begin()+start, temp_xml_data.begin()+end);
204  else // no SCAnaModule present, hence nothing to be done
205  return;
206 
207  // insert SCAnaModule just after DecayAnaModule
208  // 1st find end of DecayAnaModule
209  unsigned int pos = 0;
210  for (unsigned int i=0; i<temp_xml_data.size(); i++) {
211  if (temp_xml_data[i].find("</DecayAnaModule>") != std::string::npos) {
212  pos = i+1;
213  break;
214  }
215  }
216  if (pos == 0) // something is wrong, hence to not do anything
217  return;
218  temp_xml_data.insert(temp_xml_data.begin()+pos, fXmlData.begin()+start, fXmlData.begin()+end);
219 
220  // copy temp_xml_data back into fXmlData
221  fXmlData.clear();
222  for (unsigned int i=0; i<temp_xml_data.size(); i++)
223  fXmlData.push_back(temp_xml_data[i]);
224 
225  // clean up
226  temp_xml_data.clear();
227 }
228 
229 //-----------------------------------------------------------------------
236 void PMusrRoot2Xml::DumpFolder(TFolder *folder, UInt_t offset)
237 {
238  TString offsetStr="";
239  for (UInt_t i=0; i<offset; i++)
240  offsetStr += " ";
241 
242  TIter next = folder->GetListOfFolders();
243  TObject *obj;
244  TString str;
245  while ((obj = dynamic_cast<TObject*>(next())) != nullptr) {
246  if (!fQuiet) std::cout << std::endl << offsetStr << "name: " << obj->GetName() << ", class name: " << obj->ClassName();
247  str = obj->ClassName();
248  CheckClass(obj, str, offset);
249  }
250 }
251 
252 //-----------------------------------------------------------------------
259 void PMusrRoot2Xml::DumpObjArray(TObjArray *obj, UInt_t offset)
260 {
261  TString offsetStr="";
262  for (UInt_t i=0; i<offset; i++)
263  offsetStr += " ";
264 
265  TObjString *tstr;
266  TString str, xmlStr, type, label, xmlLabel;
267 
268  // check if the obj name is anything like DetectorXXX, where XXX is a number
269  xmlLabel = TString(obj->GetName());
270  if (xmlLabel.BeginsWith("Detector")) {
271  xmlLabel.Remove(0, 8); // remove 'Detector'
272  if (xmlLabel.IsDigit())
273  xmlLabel = "Detector";
274  else
275  xmlLabel = TString(obj->GetName());
276  }
277 
278  if (!fQuiet) std::cout << std::endl << offsetStr << obj->GetName() << " (# " << obj->GetEntries() << ")";
279  if (!strcmp(obj->GetName(), "DetectorInfo"))
280  fNoOfDetectors = obj->GetEntries();
281 
282  xmlStr = offsetStr + "<" + xmlLabel + ">";
283  fXmlData.push_back(xmlStr.Data());
284 
285  for (UInt_t i=0; i<static_cast<UInt_t>(obj->GetEntries()); i++) {
286  // check if entry is a TObjArray
287  type = obj->At(i)->ClassName();
288  if (type == "TObjArray") {
289  DumpObjArray(dynamic_cast<TObjArray*>(obj->At(i)), offset+2);
290  } else { // not a TObjArray
291  tstr = static_cast<TObjString*>(obj->At(i));
292  str = tstr->GetString();
293  str.Remove(TString::kTrailing, '\n');
294 
295  GetType(str, type);
296  GetLabel(str, label);
297 
298  if (!fQuiet) std::cout << std::endl << offsetStr << i << ": " << str;
299 
300  // filter out the number of histograms according to the RunInfo
301  if (str.Contains("- No of Histos: ")) {
302  TString histoStr = str;
303  Ssiz_t pos = histoStr.Last(':');
304  histoStr.Remove(0, pos+1);
305  pos = histoStr.Last('-');
306  histoStr.Remove(pos);
307  fNoOfHistos = histoStr.Atoi();
308  }
309 
310  // filter out the number of Red/Green offsets
311  if (str.Contains("- RedGreen Offsets: ")) {
312  TString redGreenStr = str;
313  Ssiz_t pos = redGreenStr.Last(':');
314  redGreenStr.Remove(0, pos+1);
315  pos = redGreenStr.Last('-');
316  redGreenStr.Remove(pos);
317  TObjArray *tokens = redGreenStr.Tokenize(";");
318  if (tokens)
319  fNoOfRedGreenOffsets = tokens->GetEntries();
320  if (tokens) delete tokens;
321  }
322 
323  xmlStr = offsetStr + " " + "<" + label + ">" + type + "</" + label + ">" ;
324  fXmlData.push_back(xmlStr.Data());
325  }
326  }
327 
328  xmlStr = offsetStr + "</" + xmlLabel + ">";
329  fXmlData.push_back(xmlStr.Data());
330 }
331 
332 //-----------------------------------------------------------------------
339 void PMusrRoot2Xml::DumpEntry(TObject *obj, UInt_t offset)
340 {
341  TString offsetStr="";
342  for (UInt_t i=0; i<offset; i++)
343  offsetStr += " ";
344 
345  TString nameTag(""), typeTag("");
346  switch (fFolderTag) {
347  case eDecayAnaModule:
348  nameTag = "HistoName";
349  typeTag = "HistoType";
350  break;
352  nameTag = "SlowControlName";
353  typeTag = "SlowControlType";
354  break;
355  case eUnkown:
356  default:
357  nameTag = "Name";
358  typeTag = "Type";
359  break;
360  }
361 
364 
365  TString str;
366 
367  str = offsetStr + "<" + nameTag + ">";
368  str += obj->GetName();
369  str += "</" + nameTag + ">";
370  fXmlData.push_back(str.Data());
371 
372  str = offsetStr + "<" + typeTag + ">";
373  str += obj->ClassName();
374  str += "</" + typeTag + ">";
375  fXmlData.push_back(str.Data());
376 }
377 
378 //-----------------------------------------------------------------------
386 void PMusrRoot2Xml::CheckClass(TObject *obj, TString str, UInt_t offset)
387 {
388  TString offsetStr="";
389  for (UInt_t i=0; i<offset; i++)
390  offsetStr += " ";
391 
392  if (str == "TFolder") {
393  TString xmlTagName(TString(obj->GetName()));
394 
395  // set folder tag
396  if (!xmlTagName.CompareTo("DecayAnaModule"))
398  else if (!xmlTagName.CompareTo("SCAnaModule"))
400  else if (!xmlTagName.CompareTo("SCAnaModule"))
402  else
404 
405  offset += 2;
406  str = offsetStr + "<" + xmlTagName + ">";
407  fXmlData.push_back(str.Data());
408 
409  DumpFolder(dynamic_cast<TFolder*>(obj), offset);
410 
411  str = offsetStr + "</" + xmlTagName + ">";
412  fXmlData.push_back(str.Data());
413  } else if (str == "TObjArray") {
414  offset += 2;
415  DumpObjArray(dynamic_cast<TObjArray*>(obj), offset);
416  } else {
417  // filter out the proper entry tag
418  TString entryTag("");
419  switch (fFolderTag) {
420  case eDecayAnaModule:
421  entryTag = TString("DecayHistoEntry");
422  break;
424  entryTag = TString("SlowControlHistoEntry");
425  break;
426  case eUnkown:
427  default:
428  entryTag = TString("Entry");
429  break;
430  }
431 
432  offset += 2;
433  str = offsetStr + "<" + entryTag + ">";
434  fXmlData.push_back(str.Data());
435  DumpEntry(dynamic_cast<TObject*>(obj), offset);
436  str = offsetStr + "</" + entryTag + ">";
437  fXmlData.push_back(str.Data());
438  }
439 }
440 
441 //-----------------------------------------------------------------------
448 void PMusrRoot2Xml::GetType(TString entry, TString &type)
449 {
450  if (entry.Contains("-@0")) {
451  type = "TString";
452  } else if (entry.Contains("-@1")) {
453  type = "Int_t";
454  } else if (entry.Contains("-@2")) {
455  type = "Double_t";
456  } else if (entry.Contains("-@3")) {
457  type = "TMusrRunPhysicalQuantity";
458  } else if (entry.Contains("-@4")) {
459  type = "TStringVector";
460  } else if (entry.Contains("-@5")) {
461  type = "TIntVector";
462  } else if (entry.Contains("-@6")) {
463  type = "TDoubleVector";
464  } else {
465  type = "TString";
466  }
467 }
468 
469 //-----------------------------------------------------------------------
476 void PMusrRoot2Xml::GetLabel(TString entry, TString &label)
477 {
478  label="no_idea";
479 
480  Ssiz_t start = entry.First('-');
481  Ssiz_t end = entry.First(':');
482 
483  if ((start == -1) || (end == -1))
484  return;
485 
486  if (end - start < 2)
487  return;
488 
489  // check that '-@' is present in the string, otherwise it is NOT a known label
490  Ssiz_t pos = entry.First('@');
491  if (pos < 1)
492  return;
493  if (entry(pos-1) != '-')
494  return;
495 
496  // cut out value
497  label = entry;
498  label.Remove(0, start+2);
499  label.Remove(end-start-2, label.Length());
500 
501  label.ReplaceAll(' ', '_'); // replace spaces through underscores
502  label.ReplaceAll('(', '_'); // replace '(' through underscores
503  label.ReplaceAll(')', '_'); // replace ')' through underscores
504  label.ReplaceAll('[', '_'); // replace '[' through underscores
505  label.ReplaceAll(']', '_'); // replace ']' through underscores
506  label.ReplaceAll('{', '_'); // replace '[' through underscores
507  label.ReplaceAll('}', '_'); // replace ']' through underscores
508 }
509 
510 //-----------------------------------------------------------------------
515 {
516  std::cout << std::endl << "usage: musrRootValidation <musrRootFile> <musrRootSchema> [--quiet] [--keep] | --help | --version";
517  std::cout << std::endl << " --quiet: do not dump the MusrRoot file info while validating";
518  std::cout << std::endl << " --keep: do NOT delete the intermediate XML-file";
519  std::cout << std::endl << " --help: shows this help";
520  std::cout << std::endl << " --version: shows the current version";
521  std::cout << std::endl << std::endl;
522 }
523 
524 //-----------------------------------------------------------------------
531 int is_valid(const xmlDocPtr doc, const char *schema_filename)
532 {
533  xmlDocPtr schema_doc = xmlReadFile(schema_filename, nullptr, XML_PARSE_NONET);
534  if (schema_doc == nullptr) {
535  std::cerr << std::endl << "**ERROR** the schema (" << schema_filename << ") cannot be loaded or is not well-formed" << std::endl << std::endl;
536  return -1;
537  }
538  xmlSchemaParserCtxtPtr parser_ctxt = xmlSchemaNewDocParserCtxt(schema_doc);
539  if (parser_ctxt == nullptr) {
540  std::cerr << std::endl << "**ERROR** unable to create a parser context for the schema." << std::endl << std::endl;
541  xmlFreeDoc(schema_doc);
542  return -2;
543  }
544  xmlSchemaPtr schema = xmlSchemaParse(parser_ctxt);
545  if (schema == nullptr) {
546  std::cerr << std::endl << "**ERROR** the schema itself is not valid." << std::endl << std::endl;
547  xmlSchemaFreeParserCtxt(parser_ctxt);
548  xmlFreeDoc(schema_doc);
549  return -3;
550  }
551  xmlSchemaValidCtxtPtr valid_ctxt = xmlSchemaNewValidCtxt(schema);
552  if (valid_ctxt == nullptr) {
553  std::cerr << std::endl << "**ERROR** unable to create a validation context for the schema." << std::endl << std::endl;
554  xmlSchemaFree(schema);
555  xmlSchemaFreeParserCtxt(parser_ctxt);
556  xmlFreeDoc(schema_doc);
557  return -4;
558  }
559  int is_valid = (xmlSchemaValidateDoc(valid_ctxt, doc) == 0);
560  xmlSchemaFreeValidCtxt(valid_ctxt);
561  xmlSchemaFree(schema);
562  xmlSchemaFreeParserCtxt(parser_ctxt);
563  xmlFreeDoc(schema_doc);
564  // force the return value to be non-negative on success
565  return is_valid ? 1 : 0;
566 }
567 
568 //-----------------------------------------------------------------------
572 int main(int argc, char *argv[])
573 {
574  if (argc==1) {
575  mrv_syntax();
576  return -1;
577  } else if (argc==2) {
578  if (!strcmp(argv[1], "--version")) {
579 #ifdef HAVE_CONFIG_H
580 #ifdef HAVE_GIT_REV_H
581  std::cout << std::endl << "musrRootValidation version: " << PACKAGE_VERSION << ", git-branch: " << GIT_BRANCH << ", git-rev: " << GIT_CURRENT_SHA1 << " (" << BUILD_TYPE << "), ROOT version: " << ROOT_VERSION_USED << std::endl << std::endl;
582 #else
583  std::cout << std::endl << "musrRootValidation version: " << PACKAGE_VERSION << " (" << BUILD_TYPE << "), ROOT version: " << ROOT_VERSION_USED << std::endl << std::endl;
584 #endif
585 #else
586 #ifdef HAVE_GIT_REV_H
587  std::cout << std::endl << "musrRootValidation git-branch: " << GIT_BRANCH << ", git-rev: " << GIT_CURRENT_SHA1 << std::endl << std::endl;
588 #else
589  std::cout << std::endl << "musrRootValidation version: unknown." << std::endl << std::endl;
590 #endif
591 #endif
592  return 0;
593  } else {
594  mrv_syntax();
595  return -1;
596  }
597  }
598 
599  // filter out possible options
600  bool quiet=false, keep=false;
601  for (int i=1; i<argc; i++) {
602  if (!strcmp(argv[i], "--quiet"))
603  quiet = true;
604  if (!strcmp(argv[i], "--keep"))
605  keep = true;
606  }
607 
608  PMusrRoot2Xml dump(argv[1], quiet, keep);
609  if (!dump.IsValid()) {
610  std::cerr << std::endl << "**ERROR** " << argv[1] << " is not a valid MusrRoot file." << std::endl << std::endl;
611  return -1;
612  }
613 
614  xmlDocPtr doc = xmlParseFile(dump.GetXmlDumpFileName().Data());
615  if (doc == nullptr) {
616  std::cerr << std::endl << "**ERROR** couldn't get xmlDocPtr for xml-file " << argv[1] << "." << std::endl << std::endl;
617  return -1;
618  }
619 
620  if (is_valid(doc, argv[2])) {
621  std::cout << std::endl << "xml-file " << argv[1] << " validates against xml-schema " << argv[2] << std::endl << std::endl;
622  } else {
623  std::cerr << std::endl << "**ERROR** xml-file " << argv[1] << " fails to validate against xml-schema " << argv[2] << std::endl << std::endl;
624  }
625 
626  // do some further consistency checks
627  if (dump.GetNoOfDecayHistos() != dump.GetNoOfExpectedHistos()) {
628  std::cerr << std::endl << "**ERROR** number of histogram found in the DecayAnaModule is inconsistent";
629  std::cerr << std::endl << " with the header; found " << dump.GetNoOfDecayHistos() << " histograms in the DecayAnaModule,";
630  std::cerr << std::endl << " but in the header: No of Histos = " << dump.GetNoOfHistos() << "; # RedGreen Offsets = " << dump.GetNoOfRedGreenOffsets();
631  std::cerr << std::endl << std::endl;
632  }
633 
634  if (dump.GetNoOfDecayHistos() != dump.GetNoOfDetectors()) {
635  std::cerr << std::endl << "**ERROR** number of histogram found in the DecayAnaModule is inconsistent";
636  std::cerr << std::endl << " with number of Detector entries in the RunHeader.";
637  std::cerr << std::endl << " Found " << dump.GetNoOfDecayHistos() << " histograms in the DecayAnaModule,";
638  std::cerr << std::endl << " but " << dump.GetNoOfDetectors() << " number of Detector entries.";
639  std::cerr << std::endl << std::endl;
640  }
641 
642  return 0;
643 }
void mrv_syntax()
Bool_t fKeep
true = keep the XML dump file
virtual void DumpObjArray(TObjArray *obj, UInt_t offset)
virtual UInt_t GetNoOfExpectedHistos()
UInt_t fNoOfDetectors
number of detector entries in the header
virtual void DumpFolder(TFolder *folder, UInt_t offset)
virtual void DumpEntry(TObject *obj, UInt_t offset)
Bool_t fValid
true if the conversion was fine
virtual Bool_t IsValid()
int is_valid(const xmlDocPtr doc, const char *schema_filename)
virtual void GetType(TString entry, TString &type)
PMusrRoot2Xml(const char *fileName, bool quiet, bool keep)
Bool_t fQuiet
true = suppress output while converting
virtual UInt_t GetNoOfRedGreenOffsets()
virtual void CheckClass(TObject *obj, TString str, UInt_t offset)
std::vector< std::string > fXmlData
keeps the XML structure dump of the ROOT file
UInt_t fNoOfHistos
number of histos from run header
EFolderTag fFolderTag
switch indicating which kind of TFolder object is found
UInt_t fNoOfRedGreenOffsets
number of RedGreen offsets
virtual TString GetXmlDumpFileName()
virtual void GetLabel(TString entry, TString &label)
virtual void SortHistoFolders()
UInt_t fNoOfDecayHistos
number of decay histos in the DecayAnaModule
virtual UInt_t GetNoOfDecayHistos()
TString fFileName
file name of the ROOT file
virtual UInt_t GetNoOfHistos()
int main(int argc, char *argv[])
virtual UInt_t GetNoOfDetectors()
TString fXmlDumpFileName
file name of the XML dump file