Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TXMLFile.cxx
Go to the documentation of this file.
1 // @(#)root/xml:$Id: c6d85738bc844c3af55b6d85902df8fc3a014be2 $
2 // Author: Sergey Linev, Rene Brun 10.05.2004
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //________________________________________________________________________
13 //
14 // The main motivation for the XML format is to facilitate the
15 // communication with other non ROOT applications. Currently
16 // writing and reading XML files is limited to ROOT applications.
17 // It is our intention to develop a simple reader independent
18 // of the ROOT libraries that could be used as an example for
19 // real applications. One of possible approach with code generation
20 // is implemented in TXMLPlayer class.
21 //
22 // The XML format should be used only for small data volumes,
23 // typically histogram files, pictures, geometries, calibrations.
24 // The XML file is built in memory before being dumped to disk.
25 //
26 // Like for normal ROOT files, XML files use the same I/O mechanism
27 // exploiting the ROOT/CINT dictionary. Any class having a dictionary
28 // can be saved in XML format.
29 //
30 // This first implementation does not support subdirectories
31 // or Trees.
32 //
33 // The shared library libRXML.so may be loaded dynamically
34 // via gSystem->Load("libRXML"). This library is automatically
35 // loaded by the plugin manager as soon as a XML file is created
36 // via, eg
37 // TFile::Open("file.xml","recreate");
38 // TFile::Open returns a TXMLFile object. When a XML file is open in write mode,
39 // one can use the normal TObject::Write to write an object in the file.
40 // Alternatively one can use the new functions TDirectoryFile::WriteObject and
41 // TDirectoryFile::WriteObjectAny to write a TObject* or any class not deriving
42 // from TObject.
43 //
44 // example of a session saving a histogram to a XML file
45 // =====================================================
46 // TFile *f = TFile::Open("Example.xml","recreate");
47 // TH1F *h = new TH1F("h","test",1000,-2,2);
48 // h->FillRandom("gaus");
49 // h->Write();
50 // delete f;
51 //
52 // example of a session reading the histogram from the file
53 // ========================================================
54 // TFile *f = TFile::Open("Example.xml");
55 // TH1F *h = (TH1F*)f->Get("h");
56 // h->Draw();
57 //
58 // A new option in the canvas "File" menu is available to save
59 // a TCanvas as a XML file. One can also do
60 // canvas->Print("Example.xml");
61 //
62 // Configuring ROOT with the option "xml"
63 // ======================================
64 // The XML package is enabled by default
65 //
66 // documentation
67 // =============
68 // See also classes TBufferXML, TKeyXML, TXMLEngine, TXMLSetup and TXMLPlayer.
69 // An example of XML file corresponding to the small example below
70 // can be found at http://root.cern.ch/root/Example.xml
71 //
72 //______________________________________________________________________________
73 
74 #include "TXMLFile.h"
75 
76 #include "TROOT.h"
77 #include "TSystem.h"
78 #include "TList.h"
79 #include "TObjArray.h"
80 #include "TKeyXML.h"
81 #include "TObjArray.h"
82 #include "TArrayC.h"
83 #include "TStreamerInfo.h"
84 #include "TStreamerElement.h"
85 #include "TProcessID.h"
86 #include "TError.h"
87 #include "TClass.h"
88 #include "TVirtualMutex.h"
89 #include <ROOT/RMakeUnique.hxx>
90 
91 ClassImp(TXMLFile);
92 
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 /// Open or creates local XML file with name filename.
96 /// It is recommended to specify filename as "<file>.xml". The suffix ".xml"
97 /// will be used by object browsers to automatically identify the file as
98 /// a XML file. If the constructor fails in any way IsZombie() will
99 /// return true. Use IsOpen() to check if the file is (still) open.
100 ///
101 /// If option = NEW or CREATE create a new file and open it for writing,
102 /// if the file already exists the file is
103 /// not opened.
104 /// = RECREATE create a new file, if the file already
105 /// exists it will be overwritten.
106 /// = 2xoo create a new file with specified xml settings
107 /// for more details see TXMLSetup class
108 /// = UPDATE open an existing file for writing.
109 /// if no file exists, it is created.
110 /// = READ open an existing file for reading.
111 ///
112 /// For more details see comments for TFile::TFile() constructor
113 ///
114 /// TXMLFile does not support TTree objects
115 
116 TXMLFile::TXMLFile(const char *filename, Option_t *option, const char *title, Int_t compression)
117 {
118  if (!gROOT)
119  ::Fatal("TFile::TFile", "ROOT system not initialized");
120 
121  fXML = std::make_unique<TXMLEngine>();
122 
123  if (filename && !strncmp(filename, "xml:", 4))
124  filename += 4;
125 
126  gDirectory = nullptr;
127  SetName(filename);
128  SetTitle(title);
129  TDirectoryFile::Build(this, 0);
130 
131  fD = -1;
132  fFile = this;
133  fFree = nullptr;
134  fVersion = gROOT->GetVersionInt(); // ROOT version in integer format
135  fUnits = 4;
136  fOption = option;
137  SetCompressionSettings(compression);
138  fWritten = 0;
139  fSumBuffer = 0;
140  fSum2Buffer = 0;
141  fBytesRead = 0;
142  fBytesWrite = 0;
143  fClassIndex = 0;
144  fSeekInfo = 0;
145  fNbytesInfo = 0;
146  fProcessIDs = nullptr;
147  fNProcessIDs = 0;
148  fIOVersion = TXMLFile::Class_Version();
149  SetBit(kBinaryFile, kFALSE);
150 
151  fOption = option;
152  fOption.ToUpper();
153 
154  if (fOption == "NEW")
155  fOption = "CREATE";
156 
157  Bool_t create = (fOption == "CREATE") ? kTRUE : kFALSE;
158  Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
159  Bool_t update = (fOption == "UPDATE") ? kTRUE : kFALSE;
160  Bool_t read = (fOption == "READ") ? kTRUE : kFALSE;
161  Bool_t xmlsetup = IsValidXmlSetup(option);
162  if (xmlsetup)
163  recreate = kTRUE;
164 
165  if (!create && !recreate && !update && !read) {
166  read = kTRUE;
167  fOption = "READ";
168  }
169 
170  Bool_t devnull = kFALSE;
171  const char *fname = nullptr;
172 
173  if (!filename || !filename[0]) {
174  Error("TXMLFile", "file name is not specified");
175  goto zombie;
176  }
177 
178  // support dumping to /dev/null on UNIX
179  if (!strcmp(filename, "/dev/null") && !gSystem->AccessPathName(filename, kWritePermission)) {
180  devnull = kTRUE;
181  create = kTRUE;
182  recreate = kFALSE;
183  update = kFALSE;
184  read = kFALSE;
185  fOption = "CREATE";
186  SetBit(TFile::kDevNull);
187  }
188 
189  gROOT->cd();
190 
191  fname = gSystem->ExpandPathName(filename);
192  if (fname) {
193  SetName(fname);
194  delete[](char *) fname;
195  fname = GetName();
196  } else {
197  Error("TXMLFile", "error expanding path %s", filename);
198  goto zombie;
199  }
200 
201  if (recreate) {
202  if (!gSystem->AccessPathName(fname, kFileExists))
203  gSystem->Unlink(fname);
204  recreate = kFALSE;
205  create = kTRUE;
206  fOption = "CREATE";
207  }
208 
209  if (create && !devnull && !gSystem->AccessPathName(fname, kFileExists)) {
210  Error("TXMLFile", "file %s already exists", fname);
211  goto zombie;
212  }
213 
214  if (update) {
215  if (gSystem->AccessPathName(fname, kFileExists)) {
216  update = kFALSE;
217  create = kTRUE;
218  }
219  if (update && gSystem->AccessPathName(fname, kWritePermission)) {
220  Error("TXMLFile", "no write permission, could not open file %s", fname);
221  goto zombie;
222  }
223  }
224 
225  if (read) {
226  if (gSystem->AccessPathName(fname, kFileExists)) {
227  Error("TXMLFile", "file %s does not exist", fname);
228  goto zombie;
229  }
230  if (gSystem->AccessPathName(fname, kReadPermission)) {
231  Error("TXMLFile", "no read permission, could not open file %s", fname);
232  goto zombie;
233  }
234  }
235 
236  fRealName = fname;
237 
238  if (create || update)
239  SetWritable(kTRUE);
240  else
241  SetWritable(kFALSE);
242 
243  if (create) {
244  if (xmlsetup)
245  ReadSetupFromStr(option);
246  else
247  ReadSetupFromStr(TXMLSetup::DefaultXmlSetup());
248  }
249 
250  InitXmlFile(create);
251 
252  return;
253 
254 zombie:
255  MakeZombie();
256  gDirectory = gROOT;
257 }
258 
259 ////////////////////////////////////////////////////////////////////////////////
260 /// initialize xml file and correspondent structures
261 /// identical to TFile::Init() function
262 
263 void TXMLFile::InitXmlFile(Bool_t create)
264 {
265  Int_t len = gROOT->GetListOfStreamerInfo()->GetSize() + 1;
266  if (len < 5000)
267  len = 5000;
268  fClassIndex = new TArrayC(len);
269  fClassIndex->Reset(0);
270 
271  if (create) {
272  fDoc = fXML->NewDoc();
273  XMLNodePointer_t fRootNode = fXML->NewChild(nullptr, nullptr, xmlio::Root);
274  fXML->DocSetRootElement(fDoc, fRootNode);
275  } else {
276  ReadFromFile();
277  }
278 
279  {
280  R__LOCKGUARD(gROOTMutex);
281  gROOT->GetListOfFiles()->Add(this);
282  }
283  cd();
284 
285  fNProcessIDs = 0;
286  TKey *key = nullptr;
287  TIter iter(fKeys);
288  while ((key = (TKey *)iter()) != nullptr) {
289  if (!strcmp(key->GetClassName(), "TProcessID"))
290  fNProcessIDs++;
291  }
292 
293  fProcessIDs = new TObjArray(fNProcessIDs + 1);
294 }
295 
296 ////////////////////////////////////////////////////////////////////////////////
297 /// Close a XML file
298 /// For more comments see TFile::Close() function
299 
300 void TXMLFile::Close(Option_t *option)
301 {
302  if (!IsOpen())
303  return;
304 
305  TString opt = option;
306  if (opt.Length() > 0)
307  opt.ToLower();
308 
309  if (IsWritable())
310  SaveToFile();
311 
312  fWritable = kFALSE;
313 
314  if (fDoc) {
315  fXML->FreeDoc(fDoc);
316  fDoc = nullptr;
317  }
318 
319  if (fClassIndex) {
320  delete fClassIndex;
321  fClassIndex = nullptr;
322  }
323 
324  if (fStreamerInfoNode) {
325  fXML->FreeNode(fStreamerInfoNode);
326  fStreamerInfoNode = nullptr;
327  }
328 
329  {
330  TDirectory::TContext ctxt(this);
331  // Delete all supported directories structures from memory
332  TDirectoryFile::Close();
333  }
334 
335  // delete the TProcessIDs
336  TList pidDeleted;
337  TIter next(fProcessIDs);
338  TProcessID *pid;
339  while ((pid = (TProcessID *)next())) {
340  if (!pid->DecrementCount()) {
341  if (pid != TProcessID::GetSessionProcessID())
342  pidDeleted.Add(pid);
343  } else if (opt.Contains("r")) {
344  pid->Clear();
345  }
346  }
347  pidDeleted.Delete();
348 
349  R__LOCKGUARD(gROOTMutex);
350  gROOT->GetListOfFiles()->Remove(this);
351 }
352 
353 ////////////////////////////////////////////////////////////////////////////////
354 /// destructor of TXMLFile object
355 
356 TXMLFile::~TXMLFile()
357 {
358  Close();
359 }
360 
361 ////////////////////////////////////////////////////////////////////////////////
362 /// return kTRUE if file is opened and can be accessed
363 
364 Bool_t TXMLFile::IsOpen() const
365 {
366  return fDoc != nullptr;
367 }
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 /// Reopen a file with a different access mode, like from READ to
371 /// See TFile::Open() for details
372 
373 Int_t TXMLFile::ReOpen(Option_t *mode)
374 {
375  cd();
376 
377  TString opt = mode;
378  opt.ToUpper();
379 
380  if (opt != "READ" && opt != "UPDATE") {
381  Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
382  return 1;
383  }
384 
385  if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
386  return 1;
387 
388  if (opt == "READ") {
389  // switch to READ mode
390 
391  if (IsOpen() && IsWritable())
392  SaveToFile();
393  fOption = opt;
394 
395  SetWritable(kFALSE);
396 
397  } else {
398  fOption = opt;
399 
400  SetWritable(kTRUE);
401  }
402 
403  return 0;
404 }
405 
406 ////////////////////////////////////////////////////////////////////////////////
407 /// create XML key, which will store object in xml structures
408 
409 TKey *TXMLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t)
410 {
411  return new TKeyXML(mother, ++fKeyCounter, obj, name);
412 }
413 
414 ////////////////////////////////////////////////////////////////////////////////
415 /// create XML key, which will store object in xml structures
416 
417 TKey *TXMLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t)
418 {
419  return new TKeyXML(mother, ++fKeyCounter, obj, cl, name);
420 }
421 
422 ////////////////////////////////////////////////////////////////////////////////
423 /// function produces pair of xml and dtd file names
424 
425 void TXMLFile::ProduceFileNames(const char *filename, TString &fname, TString &dtdname)
426 {
427  fname = filename;
428  dtdname = filename;
429 
430  Bool_t hasxmlext = kFALSE;
431 
432  if (fname.Length() > 4) {
433  TString last = fname(fname.Length() - 4, 4);
434  last.ToLower();
435  hasxmlext = (last == ".xml");
436  }
437 
438  if (hasxmlext) {
439  dtdname.Replace(dtdname.Length() - 4, 4, ".dtd");
440  } else {
441  fname += ".xml";
442  dtdname += ".dtd";
443  }
444 }
445 
446 ////////////////////////////////////////////////////////////////////////////////
447 /// Saves xml structures to the file
448 /// xml elements are kept in list of TKeyXML objects
449 /// When saving, all this elements are linked to root xml node
450 /// At the end StreamerInfo structures are added
451 /// After xml document is saved, all nodes will be unlinked from root node
452 /// and kept in memory.
453 /// Only Close() or destructor release memory, used by xml structures
454 
455 void TXMLFile::SaveToFile()
456 {
457  if (!fDoc)
458  return;
459 
460  if (gDebug > 1)
461  Info("SaveToFile", "File: %s", fRealName.Data());
462 
463  XMLNodePointer_t fRootNode = fXML->DocGetRootElement(fDoc);
464 
465  fXML->FreeAttr(fRootNode, xmlio::Setup);
466  fXML->NewAttr(fRootNode, nullptr, xmlio::Setup, GetSetupAsString());
467 
468  fXML->FreeAttr(fRootNode, xmlio::Ref);
469  fXML->NewAttr(fRootNode, nullptr, xmlio::Ref, xmlio::Null);
470 
471  if (GetIOVersion() > 1) {
472 
473  fXML->FreeAttr(fRootNode, xmlio::CreateTm);
474  if (TestBit(TFile::kReproducible))
475  fXML->NewAttr(fRootNode, nullptr, xmlio::CreateTm, TDatime((UInt_t) 1).AsSQLString());
476  else
477  fXML->NewAttr(fRootNode, nullptr, xmlio::CreateTm, fDatimeC.AsSQLString());
478 
479  fXML->FreeAttr(fRootNode, xmlio::ModifyTm);
480  if (TestBit(TFile::kReproducible))
481  fXML->NewAttr(fRootNode, nullptr, xmlio::ModifyTm, TDatime((UInt_t) 1).AsSQLString());
482  else
483  fXML->NewAttr(fRootNode, nullptr, xmlio::ModifyTm, fDatimeM.AsSQLString());
484 
485  fXML->FreeAttr(fRootNode, xmlio::ObjectUUID);
486  if (TestBit(TFile::kReproducible))
487  fXML->NewAttr(fRootNode, nullptr, xmlio::ObjectUUID, TUUID("00000000-0000-0000-0000-000000000000").AsString());
488  else
489  fXML->NewAttr(fRootNode, nullptr, xmlio::ObjectUUID, fUUID.AsString());
490 
491  fXML->FreeAttr(fRootNode, xmlio::Title);
492  if (strlen(GetTitle()) > 0)
493  fXML->NewAttr(fRootNode, nullptr, xmlio::Title, GetTitle());
494 
495  fXML->FreeAttr(fRootNode, xmlio::IOVersion);
496  fXML->NewIntAttr(fRootNode, xmlio::IOVersion, GetIOVersion());
497 
498  fXML->FreeAttr(fRootNode, "file_version");
499  fXML->NewIntAttr(fRootNode, "file_version", fVersion);
500  }
501 
502  TString fname, dtdname;
503  ProduceFileNames(fRealName, fname, dtdname);
504 
505  /*
506  TIter iter(GetListOfKeys());
507  TKeyXML* key = nullptr;
508  while ((key=(TKeyXML*)iter()) != nullptr)
509  fXML->AddChild(fRootNode, key->KeyNode());
510  */
511 
512  CombineNodesTree(this, fRootNode, kTRUE);
513 
514  WriteStreamerInfo();
515 
516  if (fStreamerInfoNode)
517  fXML->AddChild(fRootNode, fStreamerInfoNode);
518 
519  Int_t layout = GetCompressionLevel() > 5 ? 0 : 1;
520 
521  fXML->SaveDoc(fDoc, fname, layout);
522 
523  /* iter.Reset();
524  while ((key=(TKeyXML*)iter()) != nullptr)
525  fXML->UnlinkNode(key->KeyNode());
526  */
527  CombineNodesTree(this, fRootNode, kFALSE);
528 
529  if (fStreamerInfoNode)
530  fXML->UnlinkNode(fStreamerInfoNode);
531 }
532 
533 ////////////////////////////////////////////////////////////////////////////////
534 /// Connect/disconnect all file nodes to single tree before/after saving
535 
536 void TXMLFile::CombineNodesTree(TDirectory *dir, XMLNodePointer_t topnode, Bool_t dolink)
537 {
538  if (!dir)
539  return;
540 
541  TIter iter(dir->GetListOfKeys());
542  TKeyXML *key = nullptr;
543 
544  while ((key = (TKeyXML *)iter()) != nullptr) {
545  if (dolink)
546  fXML->AddChild(topnode, key->KeyNode());
547  else
548  fXML->UnlinkNode(key->KeyNode());
549  if (key->IsSubdir())
550  CombineNodesTree(FindKeyDir(dir, key->GetKeyId()), key->KeyNode(), dolink);
551  }
552 }
553 
554 ////////////////////////////////////////////////////////////////////////////////
555 /// read document from file
556 /// Now full content of document reads into the memory
557 /// Then document decomposed to separate keys and streamer info structures
558 /// All irrelevant data will be cleaned
559 
560 Bool_t TXMLFile::ReadFromFile()
561 {
562  fDoc = fXML->ParseFile(fRealName);
563  if (!fDoc)
564  return kFALSE;
565 
566  XMLNodePointer_t fRootNode = fXML->DocGetRootElement(fDoc);
567 
568  if (!fRootNode || !fXML->ValidateVersion(fDoc)) {
569  fXML->FreeDoc(fDoc);
570  fDoc = nullptr;
571  return kFALSE;
572  }
573 
574  ReadSetupFromStr(fXML->GetAttr(fRootNode, xmlio::Setup));
575 
576  if (fXML->HasAttr(fRootNode, xmlio::CreateTm)) {
577  TDatime tm(fXML->GetAttr(fRootNode, xmlio::CreateTm));
578  fDatimeC = tm;
579  }
580 
581  if (fXML->HasAttr(fRootNode, xmlio::ModifyTm)) {
582  TDatime tm(fXML->GetAttr(fRootNode, xmlio::ModifyTm));
583  fDatimeM = tm;
584  }
585 
586  if (fXML->HasAttr(fRootNode, xmlio::ObjectUUID)) {
587  TUUID id(fXML->GetAttr(fRootNode, xmlio::ObjectUUID));
588  fUUID = id;
589  }
590 
591  if (fXML->HasAttr(fRootNode, xmlio::Title))
592  SetTitle(fXML->GetAttr(fRootNode, xmlio::Title));
593 
594  if (fXML->HasAttr(fRootNode, xmlio::IOVersion))
595  fIOVersion = fXML->GetIntAttr(fRootNode, xmlio::IOVersion);
596  else
597  fIOVersion = 1;
598 
599  if (fXML->HasAttr(fRootNode, "file_version"))
600  fVersion = fXML->GetIntAttr(fRootNode, "file_version");
601 
602  fStreamerInfoNode = fXML->GetChild(fRootNode);
603  fXML->SkipEmpty(fStreamerInfoNode);
604  while (fStreamerInfoNode) {
605  if (strcmp(xmlio::SInfos, fXML->GetNodeName(fStreamerInfoNode)) == 0)
606  break;
607  fXML->ShiftToNext(fStreamerInfoNode);
608  }
609  fXML->UnlinkNode(fStreamerInfoNode);
610 
611  if (fStreamerInfoNode)
612  ReadStreamerInfo();
613 
614  if (IsUseDtd())
615  if (!fXML->ValidateDocument(fDoc, gDebug > 0)) {
616  fXML->FreeDoc(fDoc);
617  fDoc = nullptr;
618  return kFALSE;
619  }
620 
621  ReadKeysList(this, fRootNode);
622 
623  fXML->CleanNode(fRootNode);
624 
625  return kTRUE;
626 }
627 
628 ////////////////////////////////////////////////////////////////////////////////
629 /// Read list of keys for directory
630 
631 Int_t TXMLFile::ReadKeysList(TDirectory *dir, XMLNodePointer_t topnode)
632 {
633  if (!dir || !topnode)
634  return 0;
635 
636  Int_t nkeys = 0;
637 
638  XMLNodePointer_t keynode = fXML->GetChild(topnode);
639  fXML->SkipEmpty(keynode);
640  while (keynode) {
641  XMLNodePointer_t next = fXML->GetNext(keynode);
642 
643  if (strcmp(xmlio::Xmlkey, fXML->GetNodeName(keynode)) == 0) {
644  fXML->UnlinkNode(keynode);
645 
646  TKeyXML *key = new TKeyXML(dir, ++fKeyCounter, keynode);
647  dir->AppendKey(key);
648 
649  if (gDebug > 2)
650  Info("ReadKeysList", "Add key %s from node %s", key->GetName(), fXML->GetNodeName(keynode));
651 
652  nkeys++;
653  }
654 
655  keynode = next;
656  fXML->SkipEmpty(keynode);
657  }
658 
659  return nkeys;
660 }
661 
662 ////////////////////////////////////////////////////////////////////////////////
663 /// convert all TStreamerInfo, used in file, to xml format
664 
665 void TXMLFile::WriteStreamerInfo()
666 {
667  if (fStreamerInfoNode) {
668  fXML->FreeNode(fStreamerInfoNode);
669  fStreamerInfoNode = nullptr;
670  }
671 
672  if (!IsStoreStreamerInfos())
673  return;
674 
675  TObjArray list;
676 
677  TIter iter(gROOT->GetListOfStreamerInfo());
678 
679  TStreamerInfo *info = nullptr;
680 
681  while ((info = (TStreamerInfo *)iter()) != nullptr) {
682  Int_t uid = info->GetNumber();
683  if (fClassIndex->fArray[uid])
684  list.Add(info);
685  }
686 
687  if (list.GetSize() == 0)
688  return;
689 
690  fStreamerInfoNode = fXML->NewChild(nullptr, nullptr, xmlio::SInfos);
691  for (int n = 0; n <= list.GetLast(); n++) {
692  info = (TStreamerInfo *)list.At(n);
693 
694  XMLNodePointer_t infonode = fXML->NewChild(fStreamerInfoNode, nullptr, "TStreamerInfo");
695 
696  fXML->NewAttr(infonode, nullptr, "name", info->GetName());
697  fXML->NewAttr(infonode, nullptr, "title", info->GetTitle());
698 
699  fXML->NewIntAttr(infonode, "v", info->IsA()->GetClassVersion());
700  fXML->NewIntAttr(infonode, "classversion", info->GetClassVersion());
701  fXML->NewAttr(infonode, nullptr, "canoptimize",
702  (info->TestBit(TStreamerInfo::kCannotOptimize) ? xmlio::False : xmlio::True));
703  fXML->NewIntAttr(infonode, "checksum", info->GetCheckSum());
704 
705  TIter iter2(info->GetElements());
706  TStreamerElement *elem = nullptr;
707  while ((elem = (TStreamerElement *)iter2()) != nullptr)
708  StoreStreamerElement(infonode, elem);
709  }
710 }
711 
712 ////////////////////////////////////////////////////////////////////////////////
713 /// Read streamerinfo structures from xml format and provide them in the list
714 /// It is user responsibility to destroy this list
715 
716 TFile::InfoListRet TXMLFile::GetStreamerInfoListImpl(bool /* lookupSICache */)
717 {
718  ROOT::Internal::RConcurrentHashColl::HashValue hash;
719 
720  if (!fStreamerInfoNode)
721  return {nullptr, 1, hash};
722 
723  TList *list = new TList();
724 
725  XMLNodePointer_t sinfonode = fXML->GetChild(fStreamerInfoNode);
726  fXML->SkipEmpty(sinfonode);
727 
728  while (sinfonode) {
729  if (strcmp("TStreamerInfo", fXML->GetNodeName(sinfonode)) == 0) {
730  TString fname = fXML->GetAttr(sinfonode, "name");
731  TString ftitle = fXML->GetAttr(sinfonode, "title");
732 
733  TStreamerInfo *info = new TStreamerInfo(TClass::GetClass(fname));
734  info->SetTitle(ftitle);
735 
736  list->Add(info);
737 
738  Int_t clversion = AtoI(fXML->GetAttr(sinfonode, "classversion"));
739  info->SetClassVersion(clversion);
740  info->SetOnFileClassVersion(clversion);
741  Int_t checksum = AtoI(fXML->GetAttr(sinfonode, "checksum"));
742  info->SetCheckSum(checksum);
743 
744  const char *canoptimize = fXML->GetAttr(sinfonode, "canoptimize");
745  if (!canoptimize || (strcmp(canoptimize, xmlio::False) == 0))
746  info->SetBit(TStreamerInfo::kCannotOptimize);
747  else
748  info->ResetBit(TStreamerInfo::kCannotOptimize);
749 
750  XMLNodePointer_t node = fXML->GetChild(sinfonode);
751  fXML->SkipEmpty(node);
752  while (node) {
753  ReadStreamerElement(node, info);
754  fXML->ShiftToNext(node);
755  }
756  }
757  fXML->ShiftToNext(sinfonode);
758  }
759 
760  list->SetOwner();
761 
762  return {list, 0, hash};
763 }
764 
765 ////////////////////////////////////////////////////////////////////////////////
766 /// store data of single TStreamerElement in streamer node
767 
768 void TXMLFile::StoreStreamerElement(XMLNodePointer_t infonode, TStreamerElement *elem)
769 {
770  TClass *cl = elem->IsA();
771 
772  XMLNodePointer_t node = fXML->NewChild(infonode, nullptr, cl->GetName());
773 
774  char sbuf[100], namebuf[100];
775 
776  fXML->NewAttr(node, nullptr, "name", elem->GetName());
777  if (strlen(elem->GetTitle()) > 0)
778  fXML->NewAttr(node, nullptr, "title", elem->GetTitle());
779 
780  fXML->NewIntAttr(node, "v", cl->GetClassVersion());
781 
782  fXML->NewIntAttr(node, "type", elem->GetType());
783 
784  if (strlen(elem->GetTypeName()) > 0)
785  fXML->NewAttr(node, nullptr, "typename", elem->GetTypeName());
786 
787  fXML->NewIntAttr(node, "size", elem->GetSize());
788 
789  if (elem->GetArrayDim() > 0) {
790  fXML->NewIntAttr(node, "numdim", elem->GetArrayDim());
791 
792  for (int ndim = 0; ndim < elem->GetArrayDim(); ndim++) {
793  sprintf(namebuf, "dim%d", ndim);
794  fXML->NewIntAttr(node, namebuf, elem->GetMaxIndex(ndim));
795  }
796  }
797 
798  if (cl == TStreamerBase::Class()) {
799  TStreamerBase *base = (TStreamerBase *)elem;
800  sprintf(sbuf, "%d", base->GetBaseVersion());
801  fXML->NewAttr(node, nullptr, "baseversion", sbuf);
802  sprintf(sbuf, "%d", base->GetBaseCheckSum());
803  fXML->NewAttr(node, nullptr, "basechecksum", sbuf);
804  } else if (cl == TStreamerBasicPointer::Class()) {
805  TStreamerBasicPointer *bptr = (TStreamerBasicPointer *)elem;
806  fXML->NewIntAttr(node, "countversion", bptr->GetCountVersion());
807  fXML->NewAttr(node, nullptr, "countname", bptr->GetCountName());
808  fXML->NewAttr(node, nullptr, "countclass", bptr->GetCountClass());
809  } else if (cl == TStreamerLoop::Class()) {
810  TStreamerLoop *loop = (TStreamerLoop *)elem;
811  fXML->NewIntAttr(node, "countversion", loop->GetCountVersion());
812  fXML->NewAttr(node, nullptr, "countname", loop->GetCountName());
813  fXML->NewAttr(node, nullptr, "countclass", loop->GetCountClass());
814  } else if ((cl == TStreamerSTL::Class()) || (cl == TStreamerSTLstring::Class())) {
815  TStreamerSTL *stl = (TStreamerSTL *)elem;
816  fXML->NewIntAttr(node, "STLtype", stl->GetSTLtype());
817  fXML->NewIntAttr(node, "Ctype", stl->GetCtype());
818  }
819 }
820 
821 ////////////////////////////////////////////////////////////////////////////////
822 /// read and reconstruct single TStreamerElement from xml node
823 
824 void TXMLFile::ReadStreamerElement(XMLNodePointer_t node, TStreamerInfo *info)
825 {
826  TClass *cl = TClass::GetClass(fXML->GetNodeName(node));
827  if (!cl || !cl->InheritsFrom(TStreamerElement::Class()))
828  return;
829 
830  TStreamerElement *elem = (TStreamerElement *)cl->New();
831 
832  int elem_type = fXML->GetIntAttr(node, "type");
833 
834  elem->SetName(fXML->GetAttr(node, "name"));
835  elem->SetTitle(fXML->GetAttr(node, "title"));
836  elem->SetType(elem_type);
837  elem->SetTypeName(fXML->GetAttr(node, "typename"));
838  elem->SetSize(fXML->GetIntAttr(node, "size"));
839 
840  if (cl == TStreamerBase::Class()) {
841  int basever = fXML->GetIntAttr(node, "baseversion");
842  ((TStreamerBase *)elem)->SetBaseVersion(basever);
843  Int_t baseCheckSum = fXML->GetIntAttr(node, "basechecksum");
844  ((TStreamerBase *)elem)->SetBaseCheckSum(baseCheckSum);
845  } else if (cl == TStreamerBasicPointer::Class()) {
846  TString countname = fXML->GetAttr(node, "countname");
847  TString countclass = fXML->GetAttr(node, "countclass");
848  Int_t countversion = fXML->GetIntAttr(node, "countversion");
849 
850  ((TStreamerBasicPointer *)elem)->SetCountVersion(countversion);
851  ((TStreamerBasicPointer *)elem)->SetCountName(countname);
852  ((TStreamerBasicPointer *)elem)->SetCountClass(countclass);
853  } else if (cl == TStreamerLoop::Class()) {
854  TString countname = fXML->GetAttr(node, "countname");
855  TString countclass = fXML->GetAttr(node, "countclass");
856  Int_t countversion = fXML->GetIntAttr(node, "countversion");
857  ((TStreamerLoop *)elem)->SetCountVersion(countversion);
858  ((TStreamerLoop *)elem)->SetCountName(countname);
859  ((TStreamerLoop *)elem)->SetCountClass(countclass);
860  } else if ((cl == TStreamerSTL::Class()) || (cl == TStreamerSTLstring::Class())) {
861  int fSTLtype = fXML->GetIntAttr(node, "STLtype");
862  int fCtype = fXML->GetIntAttr(node, "Ctype");
863  ((TStreamerSTL *)elem)->SetSTLtype(fSTLtype);
864  ((TStreamerSTL *)elem)->SetCtype(fCtype);
865  }
866 
867  char namebuf[100];
868 
869  if (fXML->HasAttr(node, "numdim")) {
870  int numdim = fXML->GetIntAttr(node, "numdim");
871  elem->SetArrayDim(numdim);
872  for (int ndim = 0; ndim < numdim; ndim++) {
873  sprintf(namebuf, "dim%d", ndim);
874  int maxi = fXML->GetIntAttr(node, namebuf);
875  elem->SetMaxIndex(ndim, maxi);
876  }
877  }
878 
879  elem->SetType(elem_type);
880  elem->SetNewType(elem_type);
881 
882  info->GetElements()->Add(elem);
883 }
884 
885 ////////////////////////////////////////////////////////////////////////////////
886 /// Change layout of objects in xml file
887 /// Can be changed only for newly created file.
888 ///
889 /// Currently there are two supported layouts:
890 ///
891 /// TXMLSetup::kSpecialized = 2
892 /// This is default layout of the file, when xml nodes names class names and data member
893 /// names are used. For instance:
894 /// <TAttLine version="1">
895 /// <fLineColor v="1"/>
896 /// <fLineStyle v="1"/>
897 /// <fLineWidth v="1"/>
898 /// </TAttLine>
899 ///
900 /// TXMLSetup::kGeneralized = 3
901 /// For this layout all nodes name does not depend from class definitions.
902 /// The same class looks like
903 /// <Class name="TAttLine" version="1">
904 /// <Member name="fLineColor" v="1"/>
905 /// <Member name="fLineStyle" v="1"/>
906 /// <Member name="fLineWidth" v="1"/>
907 /// </Member>
908 ///
909 
910 void TXMLFile::SetXmlLayout(EXMLLayout layout)
911 {
912  if (IsWritable() && (GetListOfKeys()->GetSize() == 0))
913  TXMLSetup::SetXmlLayout(layout);
914 }
915 
916 ////////////////////////////////////////////////////////////////////////////////
917 /// If true, all correspondent to file TStreamerInfo objects will be stored in file
918 /// this allows to apply schema evolution later for this file
919 /// may be useful, when file used outside ROOT and TStreamerInfo objects does not required
920 /// Can be changed only for newly created file.
921 
922 void TXMLFile::SetStoreStreamerInfos(Bool_t iConvert)
923 {
924  if (IsWritable() && (GetListOfKeys()->GetSize() == 0))
925  TXMLSetup::SetStoreStreamerInfos(iConvert);
926 }
927 
928 ////////////////////////////////////////////////////////////////////////////////
929 /// Specify usage of DTD for this file.
930 /// Currently this option not available (always false).
931 /// Can be changed only for newly created file.
932 
933 void TXMLFile::SetUsedDtd(Bool_t use)
934 {
935  if (IsWritable() && (GetListOfKeys()->GetSize() == 0))
936  TXMLSetup::SetUsedDtd(use);
937 }
938 
939 ////////////////////////////////////////////////////////////////////////////////
940 /// Specify usage of namespaces in xml file
941 /// In current implementation every instrumented class in file gets its unique namespace,
942 /// which is equal to name of class and refer to root documentation page like
943 /// <TAttPad xmlns:TAttPad="http://root.cern.ch/root/htmldoc/TAttPad.html" version="3">
944 /// And xml node for class member gets its name as combination of class name and member name
945 /// <TAttPad:fLeftMargin v="0.100000"/>
946 /// <TAttPad:fRightMargin v="0.100000"/>
947 /// <TAttPad:fBottomMargin v="0.100000"/>
948 /// and so on
949 /// Usage of namespace increase size of xml file, but makes file more readable
950 /// and allows to produce DTD in the case, when in several classes data member has same name
951 /// Can be changed only for newly created file.
952 
953 void TXMLFile::SetUseNamespaces(Bool_t iUseNamespaces)
954 {
955  if (IsWritable() && (GetListOfKeys()->GetSize() == 0))
956  TXMLSetup::SetUseNamespaces(iUseNamespaces);
957 }
958 
959 ////////////////////////////////////////////////////////////////////////////////
960 /// Add comment line on the top of the xml document
961 /// This line can only be seen in xml editor and cannot be accessed later
962 /// with TXMLFile methods
963 
964 Bool_t TXMLFile::AddXmlComment(const char *comment)
965 {
966  if (!IsWritable())
967  return kFALSE;
968 
969  return fXML->AddDocComment(fDoc, comment);
970 }
971 
972 ////////////////////////////////////////////////////////////////////////////////
973 /// Adds style sheet definition on the top of xml document
974 /// Creates <?xml-stylesheet alternate="yes" title="compact" href="small-base.css" type="text/css"?>
975 /// Attributes href and type must be supplied,
976 /// other attributes: title, alternate, media, charset are optional
977 /// if alternate==0, attribute alternate="no" will be created,
978 /// if alternate>0, attribute alternate="yes"
979 /// if alternate<0, attribute will not be created
980 /// This style sheet definition cannot be later access with TXMLFile methods.
981 
982 Bool_t TXMLFile::AddXmlStyleSheet(const char *href, const char *type, const char *title, int alternate,
983  const char *media, const char *charset)
984 {
985  if (!IsWritable())
986  return kFALSE;
987 
988  return fXML->AddDocStyleSheet(fDoc, href, type, title, alternate, media, charset);
989 }
990 
991 ////////////////////////////////////////////////////////////////////////////////
992 /// Add just one line on the top of xml document
993 /// For instance, line can contain special xml processing instructions
994 /// Line should has correct xml syntax that later it can be decoded by xml parser
995 /// To be parsed later by TXMLFile again, this line should contain either
996 /// xml comments or xml processing instruction
997 
998 Bool_t TXMLFile::AddXmlLine(const char *line)
999 {
1000  if (!IsWritable())
1001  return kFALSE;
1002 
1003  return fXML->AddDocRawLine(fDoc, line);
1004 }
1005 
1006 ////////////////////////////////////////////////////////////////////////////////
1007 /// Create key for directory entry in the key
1008 
1009 Long64_t TXMLFile::DirCreateEntry(TDirectory *dir)
1010 {
1011  TDirectory *mother = dir->GetMotherDir();
1012  if (!mother)
1013  mother = this;
1014 
1015  TKeyXML *key = new TKeyXML(mother, ++fKeyCounter, dir, dir->GetName(), dir->GetTitle());
1016 
1017  key->SetSubir();
1018 
1019  return key->GetKeyId();
1020 }
1021 
1022 ////////////////////////////////////////////////////////////////////////////////
1023 /// Search for key which correspond to directory dir
1024 
1025 TKeyXML *TXMLFile::FindDirKey(TDirectory *dir)
1026 {
1027  TDirectory *motherdir = dir->GetMotherDir();
1028  if (!motherdir)
1029  motherdir = this;
1030 
1031  TIter next(motherdir->GetListOfKeys());
1032  TObject *obj = nullptr;
1033 
1034  while ((obj = next()) != nullptr) {
1035  TKeyXML *key = dynamic_cast<TKeyXML *>(obj);
1036 
1037  if (key)
1038  if (key->GetKeyId() == dir->GetSeekDir())
1039  return key;
1040  }
1041 
1042  return nullptr;
1043 }
1044 
1045 ////////////////////////////////////////////////////////////////////////////////
1046 /// Find a directory in motherdir with a seek equal to keyid
1047 
1048 TDirectory *TXMLFile::FindKeyDir(TDirectory *motherdir, Long64_t keyid)
1049 {
1050  if (!motherdir)
1051  motherdir = this;
1052 
1053  TIter next(motherdir->GetList());
1054  TObject *obj = nullptr;
1055 
1056  while ((obj = next()) != nullptr) {
1057  TDirectory *dir = dynamic_cast<TDirectory *>(obj);
1058  if (dir)
1059  if (dir->GetSeekDir() == keyid)
1060  return dir;
1061  }
1062 
1063  return nullptr;
1064 }
1065 
1066 ////////////////////////////////////////////////////////////////////////////////
1067 /// Read keys for directory
1068 /// Make sense only once, while next time no new subnodes will be created
1069 
1070 Int_t TXMLFile::DirReadKeys(TDirectory *dir)
1071 {
1072  TKeyXML *key = FindDirKey(dir);
1073  if (!key)
1074  return 0;
1075 
1076  return ReadKeysList(dir, key->KeyNode());
1077 }
1078 
1079 ////////////////////////////////////////////////////////////////////////////////
1080 /// Update key attributes
1081 
1082 void TXMLFile::DirWriteKeys(TDirectory *)
1083 {
1084  TIter next(GetListOfKeys());
1085  TObject *obj = nullptr;
1086 
1087  while ((obj = next()) != nullptr) {
1088  TKeyXML *key = dynamic_cast<TKeyXML *>(obj);
1089  if (key)
1090  key->UpdateAttributes();
1091  }
1092 }
1093 
1094 ////////////////////////////////////////////////////////////////////////////////
1095 /// Write the directory header
1096 
1097 void TXMLFile::DirWriteHeader(TDirectory *dir)
1098 {
1099  TKeyXML *key = FindDirKey(dir);
1100  if (key)
1101  key->UpdateObject(dir);
1102 }