Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TFileMerger.cxx
Go to the documentation of this file.
1// @(#)root/io:$Id$
2// Author: Andreas Peters + Fons Rademakers + Rene Brun 26/5/2005
3
4/*************************************************************************
5 * Copyright (C) 1995-2005, 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\class TFileMerger TFileMerger.cxx
14\ingroup IO
15
16This class provides file copy and merging services.
17
18It can be used to copy files (not only ROOT files), using TFile or
19any of its remote file access plugins. It is therefore useful in
20a Grid environment where the files might be accessible only remotely.
21The merging interface allows files containing histograms and trees
22to be merged, like the standalone hadd program.
23*/
24
25#include "TFileMerger.h"
26#include "TDirectory.h"
27#include "TError.h"
28#include "TUrl.h"
29#include "TFile.h"
30#include "TUUID.h"
31#include "TSystem.h"
32#include "TKey.h"
33#include "THashList.h"
34#include "TObjString.h"
35#include "TObjArray.h"
36#include "TClass.h"
37#include "TFileMergeInfo.h"
38#include "TClassRef.h"
39#include "TROOT.h"
40#include "TMemFile.h"
41#include "TVirtualMutex.h"
42
43#ifdef WIN32
44// For _getmaxstdio
45#include <cstdio>
46#else
47// For getrlimit
48#include <sys/time.h>
49#include <sys/resource.h>
50#endif
51
52#include <cstring>
53
55
58
59static const Int_t kCpProgress = BIT(14);
60static const Int_t kCintFileNumber = 100;
61////////////////////////////////////////////////////////////////////////////////
62/// Return the maximum number of allowed opened files minus some wiggle room
63/// for CINT or at least of the standard library (stdio).
64
66{
67 int maxfiles;
68#ifdef WIN32
69 maxfiles = _getmaxstdio();
70#else
71 rlimit filelimit;
72 if (getrlimit(RLIMIT_NOFILE,&filelimit)==0) {
73 maxfiles = filelimit.rlim_cur;
74 } else {
75 // We could not get the value from getrlimit, let's return a reasonable default.
76 maxfiles = 512;
77 }
78#endif
79 if (maxfiles > kCintFileNumber) {
80 return maxfiles - kCintFileNumber;
81 } else if (maxfiles > 5) {
82 return maxfiles - 5;
83 } else {
84 return maxfiles;
85 }
86}
87
88////////////////////////////////////////////////////////////////////////////////
89/// Create file merger object.
90
92 : fMaxOpenedFiles( R__GetSystemMaxOpenedFiles() ),
93 fLocal(isLocal), fHistoOneGo(histoOneGo)
94{
97
99 gROOT->GetListOfCleanups()->Add(this);
100}
101
102////////////////////////////////////////////////////////////////////////////////
103/// Cleanup.
104
106{
107 {
109 gROOT->GetListOfCleanups()->Remove(this);
110 }
112}
113
114////////////////////////////////////////////////////////////////////////////////
115/// Reset merger file list.
116
118{
123}
124
125////////////////////////////////////////////////////////////////////////////////
126/// Add file to file merger.
127
128Bool_t TFileMerger::AddFile(const char *url, Bool_t cpProgress)
129{
130 if (fPrintLevel > 0) {
131 Printf("%s Source file %d: %s", fMsgPrefix.Data(), fFileList.GetEntries() + fExcessFiles.GetEntries() + 1, url);
132 }
133
134 TFile *newfile = 0;
135 TString localcopy;
136
137 if (fFileList.GetEntries() >= (fMaxOpenedFiles-1)) {
138
139 TObjString *urlObj = new TObjString(url);
140 fMergeList.Add(urlObj);
141
142 urlObj = new TObjString(url);
143 urlObj->SetBit(kCpProgress);
144 fExcessFiles.Add(urlObj);
145 return kTRUE;
146 }
147
148 // We want gDirectory untouched by anything going on here
150
151 if (fLocal) {
152 TUUID uuid;
153 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
154 if (!TFile::Cp(url, localcopy, cpProgress)) {
155 Error("AddFile", "cannot get a local copy of file %s", url);
156 return kFALSE;
157 }
158 newfile = TFile::Open(localcopy, "READ");
159 } else {
160 newfile = TFile::Open(url, "READ");
161 }
162
163 // Zombie files should also be skipped
164 if (newfile && newfile->IsZombie()) {
165 delete newfile;
166 newfile = 0;
167 }
168
169 if (!newfile) {
170 if (fLocal)
171 Error("AddFile", "cannot open local copy %s of URL %s",
172 localcopy.Data(), url);
173 else
174 Error("AddFile", "cannot open file %s", url);
175 return kFALSE;
176 } else {
178
179 newfile->SetBit(kCanDelete);
180 fFileList.Add(newfile);
181
182 TObjString *urlObj = new TObjString(url);
183 fMergeList.Add(urlObj);
184
185 return kTRUE;
186 }
187}
188
189////////////////////////////////////////////////////////////////////////////////
190/// Add the TFile to this file merger and *do not* give ownership of the TFile to this
191/// object.
192///
193/// Return kTRUE if the addition was successful.
194
196{
197 return AddFile(source,kFALSE,cpProgress);
198}
199
200////////////////////////////////////////////////////////////////////////////////
201/// Add the TFile to this file merger and give ownership of the TFile to this
202/// object (unless kFALSE is returned).
203///
204/// Return kTRUE if the addition was successful.
205
207{
208 return AddFile(source,kTRUE,cpProgress);
209}
210
211////////////////////////////////////////////////////////////////////////////////
212/// Add the TFile to this file merger and give ownership of the TFile to this
213/// object (unless kFALSE is returned).
214///
215/// Return kTRUE if the addition was successful.
216
218{
219 if (source == 0 || source->IsZombie()) {
220 return kFALSE;
221 }
222
223 if (fPrintLevel > 0) {
224 Printf("%s Source file %d: %s",fMsgPrefix.Data(),fFileList.GetEntries()+1,source->GetName());
225 }
226
227 TFile *newfile = 0;
228 TString localcopy;
229
230 // We want gDirectory untouched by anything going on here
232 if (fLocal && !source->InheritsFrom(TMemFile::Class())) {
233 TUUID uuid;
234 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
235 if (!source->Cp(localcopy, cpProgress)) {
236 Error("AddFile", "cannot get a local copy of file %s", source->GetName());
237 return kFALSE;
238 }
239 newfile = TFile::Open(localcopy, "READ");
240 // Zombie files should also be skipped
241 if (newfile && newfile->IsZombie()) {
242 delete newfile;
243 newfile = 0;
244 }
245 } else {
246 newfile = source;
247 }
248
249 if (!newfile) {
250 if (fLocal)
251 Error("AddFile", "cannot open local copy %s of URL %s",
252 localcopy.Data(), source->GetName());
253 else
254 Error("AddFile", "cannot open file %s", source->GetName());
255 return kFALSE;
256 } else {
258
259 if (own || newfile != source) {
260 newfile->SetBit(kCanDelete);
261 } else {
262 newfile->ResetBit(kCanDelete);
263 }
264 fFileList.Add(newfile);
265
266 TObjString *urlObj = new TObjString(source->GetName());
267 fMergeList.Add(urlObj);
268
269 if (newfile != source && own) {
270 delete source;
271 }
272 return kTRUE;
273 }
274}
275
276////////////////////////////////////////////////////////////////////////////////
277/// Open merger output file.
278
279Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force, Int_t compressionLevel)
280{
281 return OutputFile(outputfile,(force?"RECREATE":"CREATE"),compressionLevel);
282}
283
284////////////////////////////////////////////////////////////////////////////////
285/// Open merger output file.
286
287Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force)
288{
289 Bool_t res = OutputFile(outputfile,(force?"RECREATE":"CREATE"),1); // 1 is the same as the default from the TFile constructor.
291 return res;
292}
293
294////////////////////////////////////////////////////////////////////////////////
295/// Open merger output file.
296///
297/// The 'mode' parameter is passed to the TFile constructor as the option, it
298/// should be one of 'NEW','CREATE','RECREATE','UPDATE'
299/// 'UPDATE' is usually used in conjunction with IncrementalMerge.
300
301Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode, Int_t compressionLevel)
302{
303 // We want gDirectory untouched by anything going on here
305 if (TFile *outputFile = TFile::Open(outputfile, mode, "", compressionLevel))
306 return OutputFile(std::unique_ptr<TFile>(outputFile));
307
308 Error("OutputFile", "cannot open the MERGER output file %s", fOutputFilename.Data());
309 return kFALSE;
310}
311
312////////////////////////////////////////////////////////////////////////////////
313/// Set an output file opened externally by the users
314
315Bool_t TFileMerger::OutputFile(std::unique_ptr<TFile> outputfile)
316{
317 if (!outputfile || outputfile->IsZombie()) {
318 Error("OutputFile", "cannot open the MERGER output file %s", (outputfile) ? outputfile->GetName() : "");
319 return kFALSE;
320 }
321
322 if (!outputfile->IsWritable()) {
323 Error("OutputFile", "output file %s is not writable", outputfile->GetName());
324 return kFALSE;
325 }
326
328
329 TFile *oldfile = fOutputFile;
330 fOutputFile = 0; // This avoids the complaint from RecursiveRemove about the file being deleted which is here
331 // spurrious. (see RecursiveRemove).
332 SafeDelete(oldfile);
333
334 fOutputFilename = outputfile->GetName();
335 // We want gDirectory untouched by anything going on here
337 fOutputFile = outputfile.release(); // Transfer the ownership of the file.
338
339 return kTRUE;
340}
341
342////////////////////////////////////////////////////////////////////////////////
343/// Open merger output file. 'mode' is passed to the TFile constructor as the option, it should
344/// be one of 'NEW','CREATE','RECREATE','UPDATE'
345/// 'UPDATE' is usually used in conjunction with IncrementalMerge.
346
347Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode /* = "RECREATE" */)
348{
349 Bool_t res = OutputFile(outputfile,mode,1); // 1 is the same as the default from the TFile constructor.
351 return res;
352}
353
354////////////////////////////////////////////////////////////////////////////////
355/// Print list of files being merged.
356
358{
359 fFileList.Print(options);
360 fExcessFiles.Print(options);
361}
362
363////////////////////////////////////////////////////////////////////////////////
364/// Merge the files.
365///
366/// If no output file was specified it will write into
367/// the file "FileMerger.root" in the working directory. Returns true
368/// on success, false in case of error.
369
371{
372 return PartialMerge(kAll | kRegular);
373}
374
375namespace {
376
377/// Merge a list of RNTuples
378Long64_t MergeRNTuples(TClass* rntupleHandle, const TString& /* target */, const TList& /* sources */) {
379 if (!rntupleHandle) {
380 return Long64_t(-1);
381 }
382 // todo(max) implement rntuple merger
383 // [ ] build complete list of sources (some sources may actually be a directory with RNTuples inside)
384 // [ ] merge them
385 ROOT::MergeFunc_t func = rntupleHandle->GetMerge();
386 return func(static_cast<void*>(rntupleHandle), nullptr, nullptr);
387}
388
389Bool_t IsMergeable(TClass *cl)
390{
391 return (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) ||
392 (cl->IsTObject() && !cl->IsLoaded() &&
393 /* If it has a dictionary and GetMerge() is nullptr then we already know the answer
394 to the next question is 'no, if we were to ask we would useless trigger
395 auto-parsing */
396 (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ||
397 cl->GetMethodWithPrototype("Merge", "TCollection*"))));
398};
399
400Bool_t WriteOneAndDelete(const TString &name, TClass *cl, TObject *obj, bool canBeMerged, Bool_t ownobj, TDirectory *target)
401{
402 Bool_t status = kTRUE;
403 if (cl->InheritsFrom(TCollection::Class())) {
404 // Don't overwrite, if the object were not merged.
405 if (obj->Write(name, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= 0) {
406 status = kFALSE;
407 }
408 ((TCollection *)obj)->SetOwner();
409 if (ownobj)
410 delete obj;
411 } else {
412 // Don't overwrite, if the object were not merged.
413 // NOTE: this is probably wrong for emulated objects.
414 if (cl->IsTObject()) {
415 if (obj->Write(name, canBeMerged ? TObject::kOverwrite : 0) <= 0) {
416 status = kFALSE;
417 }
419 } else {
420 if (target->WriteObjectAny((void *)obj, cl, name, canBeMerged ? "OverWrite" : "") <= 0) {
421 status = kFALSE;
422 }
423 }
424 if (ownobj)
425 cl->Destructor(obj); // just in case the class is not loaded.
426 }
427 return status;
428}
429
430Bool_t WriteCycleInOrder(const TString &name, TIter &nextkey, TIter &peeknextkey, TDirectory *target)
431{
432 // Recurse until we find a different name or type appear.
433 TKey *key = (TKey*)peeknextkey();
434 if (!key || name != key->GetName()) {
435 return kTRUE;
436 }
438 if (IsMergeable(cl))
439 return kTRUE;
440 // Now we can advance the real iterator
441 (void)nextkey();
442 Bool_t result = WriteCycleInOrder(name, nextkey, peeknextkey, target);
443 TObject *obj = key->ReadObj();
444
445 return WriteOneAndDelete(name, cl, obj, kFALSE, kTRUE, target) && result;
446};
447
448} // anonymous namespace
449
451 TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed,
452 const TString &path, TDirectory *current_sourcedir, TFile *current_file, TKey *key,
453 TObject *obj, TIter &nextkey)
454{
455 const char *keyname = obj ? obj->GetName() : key->GetName();
456 const char *keyclassname = obj ? obj->IsA()->GetName() : key->GetClassName();
457 const char *keytitle = obj ? obj->GetTitle() : key->GetTitle();
458
459 // Keep only the highest cycle number for each key for mergeable objects. They are stored
460 // in the (hash) list consecutively and in decreasing order of cycles, so we can continue
461 // until the name changes. We flag the case here and we act consequently later.
462 Bool_t alreadyseen = (oldkeyname == keyname) ? kTRUE : kFALSE;
463 Bool_t ownobj = kFALSE;
464
465 // Read in but do not copy directly the processIds.
466 if (strcmp(keyclassname, "TProcessID") == 0 && key) {
467 key->ReadObj();
468 return kTRUE;
469 }
470
471 // If we have already seen this object [name], we already processed
472 // the whole list of files for this objects and we can just skip it
473 // and any related cycles.
474 if (allNames.FindObject(keyname)) {
475 oldkeyname = keyname;
476 return kTRUE;
477 }
478
479 TClass *cl = TClass::GetClass(keyclassname);
480 if (!cl) {
481 Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s",
482 keyclassname, keyname, keytitle);
483 return kTRUE;
484 }
485 // For mergeable objects we add the names in a local hashlist handling them
486 // again (see above)
487 if (IsMergeable(cl))
488 allNames.Add(new TObjString(keyname));
489
491 // Skip the TTree objects and any related cycles.
492 oldkeyname = keyname;
493 return kTRUE;
494 }
495 // Check if only the listed objects are to be merged
496 if (type & kOnlyListed) {
497 onlyListed = kFALSE;
498 oldkeyname = keyname;
499 oldkeyname += " ";
500 onlyListed = fObjectNames.Contains(oldkeyname);
501 oldkeyname = keyname;
502 if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) return kTRUE;
503 }
504
505 if (!(type&kResetable && type&kNonResetable)) {
506 // If neither or both are requested at the same time, we merger both types.
507 if (!(type&kResetable)) {
508 if (cl->GetResetAfterMerge()) {
509 // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects)
510 oldkeyname = keyname;
511 return kTRUE;
512 }
513 }
514 if (!(type&kNonResetable)) {
515 if (!cl->GetResetAfterMerge()) {
516 // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects)
517 oldkeyname = keyname;
518 return kTRUE;
519 }
520 }
521 }
522 // read object from first source file
523 if (type & kIncremental) {
524 if (!obj)
525 obj = current_sourcedir->GetList()->FindObject(keyname);
526 if (!obj && key) {
527 obj = key->ReadObj();
528 ownobj = kTRUE;
529 } else if (obj && info.fIsFirst && current_sourcedir != target
530 && !cl->InheritsFrom( TDirectory::Class() )) {
531 R__ASSERT(cl->IsTObject());
532 TDirectory::TContext ctxt(current_sourcedir);
533 obj = obj->Clone();
534 ownobj = kTRUE;
535 }
536 } else if (key) {
537 obj = key->ReadObj();
538 ownobj = kTRUE;
539 }
540 if (!obj) {
541 Info("MergeRecursive", "could not read object for key {%s, %s}",
542 keyname, keytitle);
543 return kTRUE;
544 }
545 Bool_t canBeFound = (type & kIncremental) && (current_sourcedir->GetList()->FindObject(keyname) != nullptr);
546
547 // if (cl->IsTObject())
548 // obj->ResetBit(kMustCleanup);
549 if (cl->IsTObject() && cl != obj->IsA()) {
550 Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s). Continuing with %s.",
551 keyclassname, obj->IsA()->GetName(), obj->IsA()->GetName());
552 cl = obj->IsA();
553 }
554 Bool_t canBeMerged = kTRUE;
555
556 TList dirtodelete;
557 auto getDirectory = [&dirtodelete](TDirectory *parent, const char *name, const TString &pathname)
558 {
559 TDirectory *result = dynamic_cast<TDirectory*>(parent->GetList()->FindObject(name));
560 if (!result) {
561 result = parent->GetDirectory(pathname);
562 if (result && result != parent)
563 dirtodelete.Add(result);
564 }
565
566 return result;
567 };
568
569 if ( cl->InheritsFrom( TDirectory::Class() ) ) {
570 // it's a subdirectory
571
572 target->cd();
573 TDirectory *newdir;
574
575 // For incremental or already seen we may have already a directory created
576 if (type & kIncremental || alreadyseen) {
577 newdir = target->GetDirectory(obj->GetName());
578 if (!newdir) {
579 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
580 // newdir->ResetBit(kMustCleanup);
581 }
582 } else {
583 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
584 // newdir->ResetBit(kMustCleanup);
585 }
586
587 // newdir is now the starting point of another round of merging
588 // newdir still knows its depth within the target file via
589 // GetPath(), so we can still figure out where we are in the recursion
590
591 // If this folder is a onlyListed object, merge everything inside.
592 if (onlyListed) type &= ~kOnlyListed;
593 status = MergeRecursive(newdir, sourcelist, type);
594 if (onlyListed) type |= kOnlyListed;
595 if (!status) return kFALSE;
596 } else if (!cl->IsTObject() && cl->GetMerge()) {
597 // merge objects that don't derive from TObject
598 if (std::string(keyclassname) == "ROOT::Experimental::RNTuple") {
599 Warning("MergeRecursive", "merging RNTuples is experimental");
600 // todo(max): check if this works when a TDirectory is passed as the first
601 // input argument
602 Long64_t mergeResult = MergeRNTuples(cl, *path, *sourcelist);
603 if (mergeResult < 0) {
604 Error("MergeRecursive", "error merging RNTuples");
605 return kFALSE;
606 }
607 } else {
608 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
609 Error("MergeRecursive", "Merging objects that don't inherit from TObject is unimplemented (key: %s of type %s in file %s)",
610 keyname, keyclassname, nextsource->GetName());
611 canBeMerged = kFALSE;
612 }
613 } else if (cl->IsTObject() && cl->GetMerge()) {
614 // Check if already treated
615 if (alreadyseen) return kTRUE;
616
617 TList inputs;
618 TList todelete;
620
621 // Loop over all source files and merge same-name object
622 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
623 if (nextsource == 0) {
624 // There is only one file in the list
625 ROOT::MergeFunc_t func = cl->GetMerge();
626 func(obj, &inputs, &info);
627 info.fIsFirst = kFALSE;
628 } else {
629 do {
630 // make sure we are at the correct directory level by cd'ing to path
631 TDirectory *ndir = getDirectory(nextsource, target->GetName(), path);
632 if (ndir) {
633 // For consistency (and persformance), we reset the MustCleanup be also for those
634 // 'key' retrieved indirectly.
635 // ndir->ResetBit(kMustCleanup);
636 ndir->cd();
637 TObject *hobj = ndir->GetList()->FindObject(keyname);
638 if (!hobj) {
639 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname);
640 if (key2) {
641 hobj = key2->ReadObj();
642 if (!hobj) {
643 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
644 keyname, keytitle, nextsource->GetName());
645 nextsource = (TFile*)sourcelist->After(nextsource);
646 return kTRUE;
647 }
648 todelete.Add(hobj);
649 }
650 }
651 if (hobj) {
652 // Set ownership for collections
653 if (hobj->InheritsFrom(TCollection::Class())) {
654 ((TCollection*)hobj)->SetOwner();
655 }
656 hobj->ResetBit(kMustCleanup);
657 inputs.Add(hobj);
658 if (!oneGo) {
659 ROOT::MergeFunc_t func = cl->GetMerge();
660 Long64_t result = func(obj, &inputs, &info);
661 info.fIsFirst = kFALSE;
662 if (result < 0) {
663 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
664 keyname, nextsource->GetName());
665 }
666 inputs.Clear();
667 todelete.Delete();
668 }
669 }
670 }
671 nextsource = (TFile*)sourcelist->After( nextsource );
672 } while (nextsource);
673 // Merge the list, if still to be done
674 if (oneGo || info.fIsFirst) {
675 ROOT::MergeFunc_t func = cl->GetMerge();
676 func(obj, &inputs, &info);
677 info.fIsFirst = kFALSE;
678 inputs.Clear();
679 todelete.Delete();
680 }
681 }
682 } else if (cl->IsTObject()) {
683 // try synthesizing the Merge method call according to the TObject
684 TList listH;
685 TString listHargs;
686 if (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*")) {
687 listHargs.Form("(TCollection*)0x%zx,(TFileMergeInfo*)0x%zx",
688 (size_t)&listH, (size_t)&info);
689 } else if (cl->GetMethodWithPrototype("Merge", "TCollection*")) {
690 listHargs.Form("((TCollection*)0x%zx)", (size_t)&listH);
691 } else {
692 // pass unmergeable objects through to the output file
693 canBeMerged = kFALSE;
694 }
695 if (canBeMerged) {
696 if (alreadyseen) {
697 // skip already seen mergeable objects, don't skip unmergeable objects
698 return kTRUE;
699 }
700 // Loop over all source files and merge same-name object
701 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
702 if (nextsource == 0) {
703 // There is only one file in the list
704 Int_t error = 0;
705 obj->Execute("Merge", listHargs.Data(), &error);
706 info.fIsFirst = kFALSE;
707 if (error) {
708 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
709 obj->GetName(), keyname);
710 }
711 } else {
712 while (nextsource) {
713 // make sure we are at the correct directory level by cd'ing to path
714 TDirectory *ndir = getDirectory(nextsource, target->GetName(), path);
715 if (ndir) {
716 ndir->cd();
717 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname);
718 if (key2) {
719 TObject *hobj = key2->ReadObj();
720 if (!hobj) {
721 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
722 keyname, keytitle, nextsource->GetName());
723 nextsource = (TFile*)sourcelist->After(nextsource);
724 return kTRUE;
725 }
726 // Set ownership for collections
727 if (hobj->InheritsFrom(TCollection::Class())) {
728 ((TCollection*)hobj)->SetOwner();
729 }
730 hobj->ResetBit(kMustCleanup);
731 listH.Add(hobj);
732 Int_t error = 0;
733 obj->Execute("Merge", listHargs.Data(), &error);
734 info.fIsFirst = kFALSE;
735 if (error) {
736 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
737 obj->GetName(), nextsource->GetName());
738 }
739 listH.Delete();
740 }
741 }
742 nextsource = (TFile*)sourcelist->After( nextsource );
743 }
744 // Merge the list, if still to be done
745 if (info.fIsFirst) {
746 Int_t error = 0;
747 obj->Execute("Merge", listHargs.Data(), &error);
748 info.fIsFirst = kFALSE;
749 listH.Delete();
750 }
751 }
752 }
753 } else {
754 // Object is of no type that we can merge
755 canBeMerged = kFALSE;
756 }
757
758 // now write the merged histogram (which is "in" obj) to the target file
759 // note that this will just store obj in the current directory level,
760 // which is not persistent until the complete directory itself is stored
761 // by "target->SaveSelf()" below
762 target->cd();
763
764 oldkeyname = keyname;
765 //!!if the object is a tree, it is stored in globChain...
766 if (cl->InheritsFrom(TDirectory::Class())) {
767 // printf("cas d'une directory\n");
768
769 auto dirobj = dynamic_cast<TDirectory *>(obj);
770 TString dirpath(dirobj->GetPath());
771 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
772 dirpath.Remove(0, std::strlen(dirobj->GetFile()->GetPath()));
773
774 // Do not delete the directory if it is part of the output
775 // and we are in incremental mode (because it will be reused
776 // and has not been written to disk (for performance reason).
777 // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed.
778 if (ownobj && (!(type & kIncremental) || dirobj->GetFile() != target)) {
779 dirobj->ResetBit(kMustCleanup);
780 delete dirobj;
781 }
782 // Let's also delete the directory from the other source (thanks to the 'allNames'
783 // mechanism above we will not process the directories when tranversing the next
784 // files).
785 TIter deliter(&dirtodelete);
786 while(TObject *ndir = deliter()) {
787 // For consistency (and performance), we reset the MustCleanup be also for those
788 // 'key' retrieved indirectly.
789 ndir->ResetBit(kMustCleanup);
790 delete ndir;
791 }
792 } else if (!canBeFound) { // Don't write the partial result for TTree and TH1
793
794 if (!canBeMerged) {
795 TIter peeknextkey(nextkey);
796 status = WriteCycleInOrder(oldkeyname, nextkey, peeknextkey, target) && status;
797 status = WriteOneAndDelete(oldkeyname, cl, obj, kFALSE, ownobj, target) && status;
798 } else {
799 status = WriteOneAndDelete(oldkeyname, cl, obj, kTRUE, ownobj, target) && status;
800 }
801 }
802 info.Reset();
803 dirtodelete.Clear("nodelete"); // If needed the delete is done explicitly above.
804 return kTRUE;
805}
806
807////////////////////////////////////////////////////////////////////////////////
808/// Merge all objects in a directory
809///
810/// The type is defined by the bit values in TFileMerger::EPartialMergeType.
811
812Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type /* = kRegular | kAll */)
813{
814 Bool_t status = kTRUE;
815 Bool_t onlyListed = kFALSE;
816 if (fPrintLevel > 0) {
817 Printf("%s Target path: %s",fMsgPrefix.Data(),target->GetPath());
818 }
819
820 // Get the dir name
821 TString path(target->GetPath());
822 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
823 path.Remove(0, std::strlen(target->GetFile()->GetPath()));
824
825 Int_t nguess = sourcelist->GetSize()+1000;
826 THashList allNames(nguess);
827 allNames.SetOwner(kTRUE);
828 // If the mode is set to skipping list objects, add names to the allNames list
829 if (type & kSkipListed) {
830 TObjArray *arr = fObjectNames.Tokenize(" ");
831 arr->SetOwner(kFALSE);
832 for (Int_t iname=0; iname<arr->GetEntriesFast(); iname++)
833 allNames.Add(arr->At(iname));
834 delete arr;
835 }
836 ((THashList*)target->GetList())->Rehash(nguess);
837 ((THashList*)target->GetListOfKeys())->Rehash(nguess);
838
839 TFileMergeInfo info(target);
841 info.fOptions = fMergeOptions;
843 info.fOptions.Append(" fast");
844 }
845
846 TFile *current_file;
847 TDirectory *current_sourcedir;
848 if (type & kIncremental) {
849 current_file = 0;
850 current_sourcedir = target;
851 } else {
852 current_file = (TFile*)sourcelist->First();
853 current_sourcedir = current_file->GetDirectory(path);
854 }
855 while (current_file || current_sourcedir) {
856 // When current_sourcedir != 0 and current_file == 0 we are going over the target
857 // for an incremental merge.
858 if (current_sourcedir && (current_file == 0 || current_sourcedir != target)) {
859 TString oldkeyname;
860
861 // Loop over live objects
862 TIter nextobj( current_sourcedir->GetList() );
863 TObject *obj;
864 while ( (obj = (TKey*)nextobj())) {
865 auto result = MergeOne(target, sourcelist, type,
866 info, oldkeyname, allNames, status, onlyListed, path,
867 current_sourcedir, current_file,
868 nullptr, obj, nextobj);
869 if (!result)
870 return kFALSE; // Stop completely in case of error.
871 } // while ( (obj = (TKey*)nextobj()))
872
873 // loop over all keys in this directory
874 TIter nextkey( current_sourcedir->GetListOfKeys() );
875 TKey *key;
876
877 while ( (key = (TKey*)nextkey())) {
878 auto result = MergeOne(target, sourcelist, type,
879 info, oldkeyname, allNames, status, onlyListed, path,
880 current_sourcedir, current_file,
881 key, nullptr, nextkey);
882 if (!result)
883 return kFALSE; // Stop completely in case of error.
884 } // while ( ( TKey *key = (TKey*)nextkey() ) )
885 }
886 current_file = current_file ? (TFile*)sourcelist->After(current_file) : (TFile*)sourcelist->First();
887 if (current_file) {
888 current_sourcedir = current_file->GetDirectory(path);
889 } else {
890 current_sourcedir = 0;
891 }
892 }
893 // save modifications to the target directory.
894 if (!(type&kIncremental)) {
895 // In case of incremental build, we will call Write on the top directory/file, so we do not need
896 // to call SaveSelf explicilty.
897 target->SaveSelf(kTRUE);
898 }
899
900 return status;
901}
902
903////////////////////////////////////////////////////////////////////////////////
904/// Merge the files. If no output file was specified it will write into
905/// the file "FileMerger.root" in the working directory. Returns true
906/// on success, false in case of error.
907/// The type is defined by the bit values in EPartialMergeType:
908/// kRegular : normal merge, overwritting the output file
909/// kIncremental : merge the input file with the content of the output file (if already exising) (default)
910/// kAll : merge all type of objects (default)
911/// kResetable : merge only the objects with a MergeAfterReset member function.
912/// kNonResetable : merge only the objects without a MergeAfterReset member function.
913///
914/// If the type is set to kIncremental the output file is done deleted at the end of
915/// this operation. If the type is not set to kIncremental, the output file is closed.
916
918{
919 if (!fOutputFile) {
921 if (outf.IsNull()) {
922 outf.Form("file:%s/FileMerger.root", gSystem->TempDirectory());
923 Info("PartialMerge", "will merge the results to the file %s\n"
924 "since you didn't specify a merge filename",
925 TUrl(outf).GetFile());
926 }
927 if (!OutputFile(outf.Data())) {
928 return kFALSE;
929 }
930 }
931
932 // Special treament for the single file case ...
933 if ((fFileList.GetEntries() == 1) && !fExcessFiles.GetEntries() &&
937
938 TFile *file = (TFile *) fFileList.First();
939 if (!file || (file && file->IsZombie())) {
940 Error("PartialMerge", "one-file case: problem attaching to file");
941 return kFALSE;
942 }
943 Bool_t result = kTRUE;
944 if (!(result = file->Cp(fOutputFilename))) {
945 Error("PartialMerge", "one-file case: could not copy '%s' to '%s'",
946 file->GetPath(), fOutputFilename.Data());
947 return kFALSE;
948 }
949 if (file->TestBit(kCanDelete)) file->Close();
950
951 // Remove the temporary file
952 if (fLocal && !file->InheritsFrom(TMemFile::Class())) {
953 TUrl u(file->GetPath(), kTRUE);
954 if (gSystem->Unlink(u.GetFile()) != 0)
955 Warning("PartialMerge", "problems removing temporary local file '%s'", u.GetFile());
956 }
958 return result;
959 }
960
962
964
965 Bool_t result = kTRUE;
966 Int_t type = in_type;
967 while (result && fFileList.GetEntries()>0) {
969
970 // Remove local copies if there are any
971 TIter next(&fFileList);
972 TFile *file;
973 while ((file = (TFile*) next())) {
974 // close the files
975 if (file->TestBit(kCanDelete)) file->Close();
976 // remove the temporary files
977 if(fLocal && !file->InheritsFrom(TMemFile::Class())) {
978 TString p(file->GetPath());
979 // coverity[unchecked_value] Index is return a value with range or NPos to select the whole name.
980 p = p(0, p.Index(':',0));
981 gSystem->Unlink(p);
982 }
983 }
985 if (result && fExcessFiles.GetEntries() > 0) {
986 // We merge the first set of files in the output,
987 // we now need to open the next set and make
988 // sure we accumulate into the output, so we
989 // switch to incremental merging (if not already set)
991 result = OpenExcessFiles();
992 }
993 }
994 if (!result) {
995 Error("Merge", "error during merge of your ROOT files");
996 } else {
997 // Close or write is required so the file is complete.
998 if (in_type & kIncremental) {
999 // In the case of 'kDelayWrite' the caller want to avoid having to
1000 // write the output objects once for every input file and instead
1001 // write it only once at the end of the process.
1002 if (!(in_type & kDelayWrite))
1004 } else {
1005 // If in_type is not incremental but type is incremental we are now in
1006 // the case where the user "explicitly" request a non-incremental merge
1007 // but we still have internally an incremental merge. Because the user
1008 // did not request the incremental merge they also probably do not to a
1009 // final Write of the file and thus not doing the write here would lead
1010 // to data loss ...
1011 if (type & kIncremental)
1013 gROOT->GetListOfFiles()->Remove(fOutputFile);
1014 fOutputFile->Close();
1015 }
1016 }
1017
1018 // Cleanup
1019 if (in_type & kIncremental) {
1020 Clear();
1021 } else {
1024 }
1025 return result;
1026}
1027
1028////////////////////////////////////////////////////////////////////////////////
1029/// Open up to fMaxOpenedFiles of the excess files.
1030
1032{
1033 if (fPrintLevel > 0) {
1034 Printf("%s Opening the next %d files", fMsgPrefix.Data(), TMath::Min(fExcessFiles.GetEntries(), fMaxOpenedFiles - 1));
1035 }
1036 Int_t nfiles = 0;
1037 TIter next(&fExcessFiles);
1038 TObjString *url = 0;
1039 TString localcopy;
1040 // We want gDirectory untouched by anything going on here
1042 while( nfiles < (fMaxOpenedFiles-1) && ( url = (TObjString*)next() ) ) {
1043 TFile *newfile = 0;
1044 if (fLocal) {
1045 TUUID uuid;
1046 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
1047 if (!TFile::Cp(url->GetName(), localcopy, url->TestBit(kCpProgress))) {
1048 Error("OpenExcessFiles", "cannot get a local copy of file %s", url->GetName());
1049 return kFALSE;
1050 }
1051 newfile = TFile::Open(localcopy, "READ");
1052 } else {
1053 newfile = TFile::Open(url->GetName(), "READ");
1054 }
1055
1056 if (!newfile) {
1057 if (fLocal)
1058 Error("OpenExcessFiles", "cannot open local copy %s of URL %s",
1059 localcopy.Data(), url->GetName());
1060 else
1061 Error("OpenExcessFiles", "cannot open file %s", url->GetName());
1062 return kFALSE;
1063 } else {
1065
1066 newfile->SetBit(kCanDelete);
1067 fFileList.Add(newfile);
1068 ++nfiles;
1069 fExcessFiles.Remove(url);
1070 }
1071 }
1072 return kTRUE;
1073}
1074
1075////////////////////////////////////////////////////////////////////////////////
1076/// Intercept the case where the output TFile is deleted!
1077
1079{
1080 if (obj == fOutputFile) {
1081 Fatal("RecursiveRemove","Output file of the TFile Merger (targeting %s) has been deleted (likely due to a TTree larger than 100Gb)", fOutputFilename.Data());
1082 }
1083
1084}
1085
1086////////////////////////////////////////////////////////////////////////////////
1087/// Set a limit to the number of files that TFileMerger will open simultaneously.
1088///
1089/// If the request is higher than the system limit, we reset it to the system limit.
1090/// If the request is less than two, we reset it to 2 (one for the output file and one for the input file).
1091
1093{
1095 if (newmax < sysmax) {
1096 fMaxOpenedFiles = newmax;
1097 } else {
1098 fMaxOpenedFiles = sysmax;
1099 }
1100 if (fMaxOpenedFiles < 2) {
1101 fMaxOpenedFiles = 2;
1102 }
1103}
1104
1105////////////////////////////////////////////////////////////////////////////////
1106/// Set the prefix to be used when printing informational message.
1107
1108void TFileMerger::SetMsgPrefix(const char *prefix)
1109{
1110 fMsgPrefix = prefix;
1111}
1112
typedef void(GLAPIENTRYP _GLUfuncptr)(void)
#define SafeDelete(p)
Definition RConfig.hxx:537
int Int_t
Definition RtypesCore.h:45
const Bool_t kFALSE
Definition RtypesCore.h:101
bool Bool_t
Definition RtypesCore.h:63
long long Long64_t
Definition RtypesCore.h:80
const Bool_t kTRUE
Definition RtypesCore.h:100
const char Option_t
Definition RtypesCore.h:66
#define BIT(n)
Definition Rtypes.h:85
#define ClassImp(name)
Definition Rtypes.h:364
#define R__ASSERT(e)
Definition TError.h:118
TClassRef R__TH1_Class("TH1")
static Int_t R__GetSystemMaxOpenedFiles()
Return the maximum number of allowed opened files minus some wiggle room for CINT or at least of the ...
TClassRef R__TTree_Class("TTree")
static const Int_t kCpProgress
static const Int_t kCintFileNumber
char name[80]
Definition TGX11.cxx:110
int type
Definition TGX11.cxx:121
@ kMustCleanup
Definition TObject.h:370
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:404
void Printf(const char *fmt,...)
R__EXTERN TSystem * gSystem
Definition TSystem.h:559
#define R__LOCKGUARD(mutex)
TClassRef is used to implement a permanent reference to a TClass object.
Definition TClassRef.h:28
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:80
TMethod * GetMethodWithPrototype(const char *method, const char *proto, Bool_t objectIsConst=kFALSE, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch)
Find the method with a given prototype.
Definition TClass.cxx:4442
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5386
ROOT::ResetAfterMergeFunc_t GetResetAfterMerge() const
Return the wrapper around Merge.
Definition TClass.cxx:7425
Bool_t IsLoaded() const
Return true if the shared library of this class is currently in the a process's memory.
Definition TClass.cxx:5898
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition TClass.cxx:5924
ROOT::MergeFunc_t GetMerge() const
Return the wrapper around Merge.
Definition TClass.cxx:7417
Bool_t InheritsFrom(const char *cl) const
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4860
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2966
Collection abstract base class.
Definition TCollection.h:65
virtual void Print(Option_t *option="") const
Default print for collections, calls Print(option, 1).
virtual Int_t GetEntries() const
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
TDirectory * GetDirectory(const char *apath, Bool_t printError=false, const char *funcname="GetDirectory") override
Find a directory named "apath".
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
virtual TList * GetList() const
Definition TDirectory.h:222
virtual TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
virtual const char * GetPath() const
Returns the full path of the directory.
virtual Int_t WriteObjectAny(const void *, const char *, const char *, Option_t *="", Int_t=0)
Definition TDirectory.h:300
virtual TFile * GetFile() const
Definition TDirectory.h:220
virtual Bool_t cd()
Change current directory to "this" directory.
virtual void SaveSelf(Bool_t=kFALSE)
Definition TDirectory.h:255
virtual TDirectory * mkdir(const char *name, const char *title="", Bool_t returnExistingDirectory=kFALSE)
Create a sub-directory "a" or a hierarchy of sub-directories "a/b/c/...".
virtual TList * GetListOfKeys() const
Definition TDirectory.h:223
TIOFeatures * fIOFeatures
This class provides file copy and merging services.
Definition TFileMerger.h:30
TString fObjectNames
List of object names to be either merged exclusively or skipped.
Definition TFileMerger.h:54
virtual Bool_t OutputFile(const char *url, Bool_t force)
Open merger output file.
TList fMergeList
list of TObjString containing the name of the files need to be merged
Definition TFileMerger.h:55
virtual Bool_t AddFile(TFile *source, Bool_t own, Bool_t cpProgress)
Add the TFile to this file merger and give ownership of the TFile to this object (unless kFALSE is re...
virtual void PrintFiles(Option_t *options)
Print list of files being merged.
Bool_t fHistoOneGo
Merger histos in one go (default is kTRUE)
Definition TFileMerger.h:53
virtual Bool_t MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type=kRegular|kAll)
Merge all objects in a directory.
TList fFileList
A list the file (TFile*) which shall be merged.
Definition TFileMerger.h:39
virtual Bool_t Merge(Bool_t=kTRUE)
Merge the files.
virtual Bool_t MergeOne(TDirectory *target, TList *sourcelist, Int_t type, TFileMergeInfo &info, TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed, const TString &path, TDirectory *current_sourcedir, TFile *current_file, TKey *key, TObject *obj, TIter &nextkey)
TString fOutputFilename
The name of the outputfile for merging.
Definition TFileMerger.h:41
virtual void RecursiveRemove(TObject *obj)
Intercept the case where the output TFile is deleted!
TString fMsgPrefix
Prefix to be used when printing informational message (default TFileMerger)
Definition TFileMerger.h:49
TIOFeatures * fIOFeatures
IO features to use in the output file.
Definition TFileMerger.h:48
TFileMerger(const TFileMerger &)=delete
void SetMsgPrefix(const char *prefix)
Set the prefix to be used when printing informational message.
Bool_t fNoTrees
True if Trees should not be merged (default is kFALSE)
Definition TFileMerger.h:43
@ kAll
Merge all type of objects (default)
Definition TFileMerger.h:76
@ kIncremental
Merge the input file with the content of the output file (if already existing).
Definition TFileMerger.h:71
@ kKeepCompression
Keep compression level unchanged for each input files.
Definition TFileMerger.h:81
@ kSkipListed
Skip objects specified in fObjectNames list.
Definition TFileMerger.h:80
@ kNonResetable
Only the objects without a MergeAfterReset member function.
Definition TFileMerger.h:73
@ kResetable
Only the objects with a MergeAfterReset member function.
Definition TFileMerger.h:72
@ kOnlyListed
Only the objects specified in fObjectNames list.
Definition TFileMerger.h:79
@ kRegular
Normal merge, overwriting the output file.
Definition TFileMerger.h:70
@ kDelayWrite
Delay the TFile write (to reduce the number of write when reusing the file)
Definition TFileMerger.h:74
Bool_t fExplicitCompLevel
True if the user explicitly requested a compression level change (default kFALSE)
Definition TFileMerger.h:44
Bool_t fCompressionChange
True if the output and input have different compression level (default kFALSE)
Definition TFileMerger.h:45
virtual ~TFileMerger()
Cleanup.
Int_t fPrintLevel
How much information to print out at run time.
Definition TFileMerger.h:46
void SetMaxOpenedFiles(Int_t newmax)
Set a limit to the number of files that TFileMerger will open simultaneously.
TString fMergeOptions
Options (in string format) to be passed down to the Merge functions.
Definition TFileMerger.h:47
Bool_t OpenExcessFiles()
Open up to fMaxOpenedFiles of the excess files.
TList fExcessFiles
! List of TObjString containing the name of the files not yet added to fFileList due to user or syste...
Definition TFileMerger.h:56
TFile * fOutputFile
The outputfile for merging.
Definition TFileMerger.h:40
virtual Bool_t PartialMerge(Int_t type=kAll|kIncremental)
Merge the files.
Bool_t fLocal
Makes local copies of merging files if True (default is kTRUE)
Definition TFileMerger.h:52
virtual void Reset()
Reset merger file list.
Int_t fMaxOpenedFiles
Maximum number of files opened at the same time by the TFileMerger.
Definition TFileMerger.h:51
virtual Bool_t AddAdoptFile(TFile *source, Bool_t cpProgress=kTRUE)
Add the TFile to this file merger and give ownership of the TFile to this object (unless kFALSE is re...
Bool_t fFastMethod
True if using Fast merging algorithm (default)
Definition TFileMerger.h:42
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format.
Definition TFile.h:54
Int_t GetCompressionSettings() const
Definition TFile.h:401
Int_t GetCompressionLevel() const
Definition TFile.h:395
virtual Bool_t Cp(const char *dst, Bool_t progressbar=kTRUE, UInt_t buffersize=1000000)
Allows to copy this file to the dst URL.
Definition TFile.cxx:4922
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
Definition TFile.cxx:4025
Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsiz=0) override
Write memory objects to this file.
Definition TFile.cxx:2374
void Close(Option_t *option="") override
Close a file.
Definition TFile.cxx:899
THashList implements a hybrid collection class consisting of a hash table and a list to store TObject...
Definition THashList.h:34
TObject * FindObject(const char *name) const
Find object using its name.
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
virtual const char * GetClassName() const
Definition TKey.h:76
virtual const char * GetTitle() const
Returns title (title can contain 32x32 xpm thumbnail/icon).
Definition TKey.cxx:1532
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition TKey.cxx:750
A doubly linked list.
Definition TList.h:38
virtual void Add(TObject *obj)
Definition TList.h:81
virtual TObject * After(const TObject *obj) const
Returns the object after object obj.
Definition TList.cxx:330
virtual TObject * Remove(TObject *obj)
Remove object from the list.
Definition TList.cxx:822
virtual TObject * FindObject(const char *name) const
Find an object in this list using its name.
Definition TList.cxx:578
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
Definition TList.cxx:470
virtual TObject * First() const
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:659
virtual void Clear(Option_t *option="")
Remove all objects from the list.
Definition TList.cxx:402
virtual const char * GetName() const
Returns name of object.
Definition TNamed.h:47
An array of TObjects.
Definition TObjArray.h:31
Int_t GetEntriesFast() const
Definition TObjArray.h:58
TObject * At(Int_t idx) const
Definition TObjArray.h:164
Collectable string class.
Definition TObjString.h:28
const char * GetName() const
Returns name of object.
Definition TObjString.h:38
Mother of all ROOT objects.
Definition TObject.h:41
virtual void Clear(Option_t *="")
Definition TObject.h:119
virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0)
Write this object to the current directory.
Definition TObject.cxx:868
@ kOverwrite
overwrite existing object with same name
Definition TObject.h:92
@ kSingleKey
write collection with single key
Definition TObject.h:91
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:429
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:201
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:216
virtual void Execute(const char *method, const char *params, Int_t *error=0)
Execute method on this object with the given parameter string, e.g.
Definition TObject.cxx:349
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:949
R__ALWAYS_INLINE Bool_t IsZombie() const
Definition TObject.h:153
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:766
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:515
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:963
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:991
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:473
void ResetBit(UInt_t f)
Definition TObject.h:200
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition TObject.h:64
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:937
Basic string class.
Definition TString.h:136
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1201
const char * Data() const
Definition TString.h:369
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition TString.cxx:2222
Bool_t IsNull() const
Definition TString.h:407
TString & Remove(Ssiz_t pos)
Definition TString.h:673
TString & Append(const char *cs)
Definition TString.h:564
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2314
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:624
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:639
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1381
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1482
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition TUUID.cxx:570
This class represents a WWW compatible URL.
Definition TUrl.h:33
const char * GetFile() const
Definition TUrl.h:69
Long64_t(* MergeFunc_t)(void *, TCollection *, TFileMergeInfo *)
Definition Rtypes.h:114
Short_t Min(Short_t a, Short_t b)
Definition TMathBase.h:176
Definition file.py:1