source: trunk/Mars/mfileio/MWriteRootFile.cc@ 20063

Last change on this file since 20063 was 19760, checked in by tbretz, 5 years ago
They are added to the ListOfCleanups. This is implemented as a THashTable. According to the root documentation, it is required to rehash the table whenever the name of an object in the table changes. This is now guranteed if name change happens via SetName
File size: 38.1 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 6/2001 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: Software Development, 2000-2009
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MWriteRootFile
28//
29// This is a writer to store several containers to a root file.
30// The containers are added with AddContainer.
31// To understand how it works, see base class MWriteFile
32//
33// Warning: Look at the Warning in MTaskList.
34//
35// There is a special mode of operation which opens a new file for each new
36// file read by the reading task (opening the new file is initiated by
37// ReInit()) For more details see the corresponding constructor.
38//
39// Memory based trees
40// ------------------
41// It is possible to store the data into memory (TTrees) instead of
42// writing the data into a file. To do this either call the default
43// constructor or specify 'memory' as option in the constructor.
44//
45// Afterwards the tree can be found using
46// gROOT->GetListOfFiles()->FindObject("treename")
47//
48// Currently(!) the tree is not deleted at all! Please make sure to
49// delete it if it is not used anymore - otherwise you could wast a LOT
50// of memory. Please consider that this behaviour might change in the
51// future.
52//
53// Such trees are usefull if you want to use more basic root-tools
54// like TMultiLayerPerceptron or TEventList.
55//
56// If you want to process such memory based Trees using Mars make sure,
57// that you don't need data from the RunHeader tree because you can
58// only use MReadTree but not MReadMarsFile with such a tree.
59//
60/////////////////////////////////////////////////////////////////////////////
61#include "MWriteRootFile.h"
62
63#include <fstream>
64
65#include <TFile.h>
66#include <TTree.h>
67#include <TPRegexp.h>
68#include <THashList.h>
69
70#include "MLog.h"
71#include "MLogManip.h"
72
73#include "MRead.h"
74#include "MParList.h"
75#include "MStatusDisplay.h"
76
77ClassImp(MRootFileBranch);
78ClassImp(MWriteRootFile);
79
80using namespace std;
81
82const TString MWriteRootFile::gsDefName = "MWriteRootFile";
83const TString MWriteRootFile::gsDefTitle = "Task which writes a root-output file";
84
85void MWriteRootFile::Init(const char *name, const char *title)
86{
87 fName = name ? name : gsDefName.Data();
88 fTitle = title ? title : gsDefTitle.Data();
89
90 //
91 // Set the Arrays the owner of its entries. This means, that the
92 // destructor of the arrays will delete all its entries.
93 //
94 fBranches.SetOwner();
95 fCopies.SetOwner();
96
97 //
98 // Believing the root user guide, TTree instances are owned by the
99 // directory (file) in which they are. This means we don't have to
100 // care about their destruction.
101 //
102 //fTrees.SetOwner();
103
104 gROOT->GetListOfCleanups()->Add(this); // To remove fOut if deleted
105 SetBit(kMustCleanup);
106}
107
108// --------------------------------------------------------------------------
109//
110// Try to get the file from gROOT->GetListOfFiles. (In case the file name
111// is /dev/null we look for a file with name /dev/null and the given title)
112// If it is found fOut is set to it and returned.
113// Otherwise a new file is opened and returned.
114//
115TFile *MWriteRootFile::OpenFile(const char *name, Option_t *option, const char *title, Int_t comp)
116{
117 TFile *file = 0;
118
119 if (TString(name)=="/dev/null")
120 {
121 TIter Next(gROOT->GetListOfFiles());
122 TObject *obj = 0;
123 while ((obj=Next()))
124 if (TString(obj->GetName())=="/dev/null" && TString(obj->GetTitle())==title)
125 {
126 *fLog << inf2 << "Found open file '/dev/null' <Title=" << title << ">... re-using." << endl;
127 file = dynamic_cast<TFile*>(obj);
128 break;
129 }
130 }
131 else
132 {
133 file = dynamic_cast<TFile*>(gROOT->GetListOfFiles()->FindObject(name));
134
135 // If the file was not found with its name try its expanded name
136 if (!file)
137 {
138 TString fqp(name);
139 gSystem->ExpandPathName(fqp);
140 file = dynamic_cast<TFile*>(gROOT->GetListOfFiles()->FindObject(fqp));
141 }
142
143 if (file)
144 {
145 *fLog << inf2;
146 *fLog << "Found open file '" << name << "'... re-using." << endl;
147 *fLog << inf3;
148 *fLog << "Make sure that you do NOT write to trees which are" << endl;
149 *fLog << "scheduled already by a different MWriteRootFile..." << endl;
150 }
151 }
152
153 if (!file)
154 {
155 file = new TFile(name, option, title, comp);
156 if (!file->IsOpen())
157 {
158 delete file;
159 return NULL;
160 }
161
162 *fLog << inf3 << "File '" << name << "' ";
163 if (!TString(title).IsNull())
164 *fLog << "<Title=" << title << "> ";
165 *fLog << "opened [" << option << "]" << endl;
166
167 file->SetOption(option); // IMPORTANT!
168 file->SetBit(kMustCleanup);
169 ResetBit(kIsNotOwner);
170 return file;
171 }
172
173 fOut = file;
174 fOut->SetBit(kMustCleanup);
175 SetBit(kIsNotOwner);
176
177 return fOut;
178}
179
180// --------------------------------------------------------------------------
181//
182// Default constructor. It is there to support some root stuff.
183// Don't use it.
184//
185MWriteRootFile::MWriteRootFile() : fOut(NULL)
186{
187 Init();
188}
189
190// --------------------------------------------------------------------------
191//
192// Use this constructor to run in a special mode.
193//
194// In this mode for each input file a new output file is written. This
195// happens in ReInit.
196//
197// comp: Compression Level (see TFile, TBranch)
198// rule: Rule to create output file name (see SubstituteName())
199// overwrite: Allow newly created file to overwrite old files ("RECREATE")
200// ftitle: File title stored in the file (see TFile)
201// name, title: Name and title of this object
202//
203// Until the first file is opened a dummy file with name /dev/null is
204// opened to allow creation of trees and branches in the file.
205// To distinguish between different /dev/null-files the given title is used.
206//
207MWriteRootFile::MWriteRootFile(const Int_t comp,
208 const char *rule,
209 const Option_t *option,
210 const char *ftitle,
211 const char *name,
212 const char *title) : fSplitRule(rule)
213{
214 Init(name, title);
215
216 const Bool_t hasrule = fSplitRule.BeginsWith("s/") && fSplitRule.EndsWith("/");
217 if (!hasrule)
218 fSplitRule = "";
219
220 //
221 // Open a TFile in dummy mode! This is necessary to be able to create
222 // the trees and branches, which are then (in ReInit) moved to
223 // a valid file. (Stupid workaround - but does a good job)
224 //
225 const TString fname = hasrule ? "/dev/null" : rule;
226 fOut = OpenFile(fname, option, ftitle, comp);
227}
228
229// --------------------------------------------------------------------------
230//
231// Specify the name of the root file. You can also give an option ("UPDATE"
232// and "RECREATE" would make sense only) as well as the file title and
233// compression factor. To a more detaild description of the options see
234// TFile.
235//
236// To create a memory based TTree use
237// fname = name of TTree
238// option = "memory"
239// Make sure you do not read from a tree with the same name!
240//
241MWriteRootFile::MWriteRootFile(const char *fname,
242 const Option_t *option,
243 const char *ftitle,
244 const Int_t comp,
245 const char *name,
246 const char *title) : fOut(NULL)
247{
248 Init(name, title);
249
250 TString opt(option);
251 opt.ToLower();
252
253 //
254 // Check if we are writing to memory
255 //
256 if (opt=="memory")
257 {
258 fSplitRule = fname;
259 return;
260 }
261
262 //
263 // If no name is given we open the TFile in some kind of dummy mode...
264 //
265 TString str(fname);
266 if (str.IsNull())
267 {
268 fOut = new TFile("/dev/null", "READ", ftitle, comp);
269 fOut->SetBit(kMustCleanup);
270 return;
271 }
272
273 if (!str.EndsWith(".root", TString::kIgnoreCase))
274 str += ".root";
275
276 //
277 // Open the rootfile
278 //
279 fOut = OpenFile(str, opt, ftitle, comp);
280}
281
282// --------------------------------------------------------------------------
283//
284// Prints some statistics about the file to the screen. And closes the file
285// properly.
286//
287void MWriteRootFile::Close()
288{
289 //
290 // Print some statistics to the looging out.
291 //
292 if (fOut && !TestBit(kIsNotOwner))
293 {
294 Print();
295
296 //
297 // If the file is still open (no error) write the keys. This is necessary
298 // for appearance of the all trees and branches.
299 //
300 if (IsFileOpen())
301 fOut->Write();
302
303 //
304 // Delete the file. This'll also close the file (if open)
305 //
306 *fLog << inf3 << "Closing file " << fOut->GetName() << "." << endl;
307 delete fOut;
308
309 //
310 // Remark:
311 // - Trees are automatically deleted by the the file
312 // (unless file.SetDirectory(0) was called)
313 // - Branches are automatically deleted by the tree destructor
314 //
315 }
316
317 fOut = 0;
318}
319
320// --------------------------------------------------------------------------
321//
322// call Close()
323//
324MWriteRootFile::~MWriteRootFile()
325{
326 Close();
327}
328
329void MWriteRootFile::SetName(const char *name)
330{
331 MWriteFile::SetName(name);
332
333 // From the documentation of THashList::THashList
334 //
335 // WARNING !!!
336 // If the name of an object in the HashList is modified, The hashlist
337 // must be Rehashed
338 //
339 auto table = dynamic_cast<THashList*>(gROOT->GetListOfCleanups());
340 if (table)
341 table->Rehash(THashList::kInitHashTableCapacity);
342}
343
344// --------------------------------------------------------------------------
345//
346// Prints all trees with the actually number of written entries to log-out.
347//
348void MWriteRootFile::Print(Option_t *) const
349{
350 if (!fOut)
351 return;
352
353 *fLog << all << underline << "File: " << GetFileName() << dec << endl;
354
355 Bool_t cont = kFALSE;
356
357 TObject *obj;
358 TIter NextBranch(&fBranches);
359 while ((obj=NextBranch()))
360 {
361 MRootFileBranch *b = (MRootFileBranch*)obj;
362
363 if (!b->GetTree() || b->GetTree()->TestBit(kIsNewTree))
364 continue;
365
366 TBranch *branch = b->GetBranch();
367
368 TString name = b->GetTree()->GetName();
369 name += '.';
370 name += branch->GetName();
371
372 *fLog << " + " << name.Strip(TString::kTrailing, '.') << ": \t" << (ULong_t)branch->GetEntries() << " entries." << endl;
373 cont = kTRUE;
374 }
375
376 TTree *t = NULL;
377 TIter NextTree(&fTrees);
378 while ((t=(TTree*)NextTree()))
379 if (t->TestBit(kIsNewTree))
380 {
381 *fLog << " + " << t->GetName() << ": \t" << (ULong_t)t->GetEntries() << " entries." << endl;
382 cont = kTRUE;
383 }
384
385 TIter NextKey(fOut->GetList());
386
387 while ((obj=NextKey()))
388 {
389 if (!obj->InheritsFrom(TTree::Class()))
390 continue;
391
392 if (fTrees.FindObject(obj) && obj->TestBit(kIsNewTree))
393 continue;
394
395 *fLog << " - " << obj->GetName() << ": \t" << (ULong_t)((TTree*)obj)->GetEntries() << " entries." << endl;
396 cont = kTRUE;
397 }
398
399 if (!cont)
400 *fLog << " No contents." << endl;
401
402 *fLog << endl;
403}
404
405// --------------------------------------------------------------------------
406//
407// Add a new Container to list of containers which should be written to the
408// file. Give the name of the container which will identify the container
409// in the parameterlist. tname is the name of the tree to which the
410// container should be written (Remark: one tree can hold more than one
411// container). The default is the same name as the container name.
412// You can slso specify a title for the tree. This is only
413// used the first time this tree in 'mentioned'. As default the title
414// is the name of the tree.
415//
416void MWriteRootFile::AddContainer(const char *cname, const char *tname, Bool_t must, Long64_t max)
417{
418 if (!fOut && !tname)
419 tname = fSplitRule;
420
421 TIter Next(&fBranches);
422 TObject *o=0;
423 while ((o=Next()))
424 if (TString(o->GetName())==TString(tname) && TString(o->GetTitle())==TString(cname))
425 {
426 *fLog << warn;
427 *fLog << "WARNING - Container '" << cname << "' in Tree '" << tname << "' already scheduled... ignored." << endl;
428 return;
429 }
430
431 //
432 // create a new entry in the list of branches to write and
433 // add the entry to the list.
434 //
435 MRootFileBranch *entry = new MRootFileBranch(AddSerialNumber(cname), tname, must, max);
436 fBranches.AddLast(entry);
437
438 if (tname && tname[0])
439 AddToBranchList(Form("%s.%s", (const char*)AddSerialNumber(cname), tname));
440}
441
442// --------------------------------------------------------------------------
443//
444// Add a new Container to list of containers which should be written to the
445// file. Give the pointer to the container. tname is the name of the tree to
446// which the container should be written (Remark: one tree can hold more than
447// one container). The default is the same name as the container name.
448// You can slso specify a title for the tree. This is only
449// used the first time this tree in 'mentioned'. As default the title
450// is the name of the tree.
451//
452void MWriteRootFile::AddContainer(MParContainer *cont, const char *tname, Bool_t must, Long64_t max)
453{
454 if (!fOut && !tname)
455 tname = fSplitRule;
456
457 TIter Next(&fBranches);
458 TObject *o=0;
459 while ((o=Next()))
460 if (TString(o->GetName())==TString(tname) &&
461 static_cast<MRootFileBranch*>(o)->GetContainer()==cont)
462 {
463 *fLog << warn;
464 *fLog << "WARNING - Container " << cont << " in Tree '" << tname << "' already scheduled... ignored." << endl;
465 return;
466 }
467
468 //
469 // create a new entry in the list of branches to write and
470 // add the entry to the list.
471 //
472 MRootFileBranch *entry = new MRootFileBranch(cont, tname, must, max);
473 fBranches.AddLast(entry);
474}
475
476// --------------------------------------------------------------------------
477//
478// If you want to copy a full tree (or some branches of some trees)
479// completely from one file to another one you can use this
480//
481void MWriteRootFile::AddCopySource(const char *tname, const char *bname, Bool_t force)
482{
483 TObject *obj = new TNamed(tname, bname?bname:"*");
484 if (force)
485 obj->SetBit(kForced);
486
487 fCopies.Add(obj);
488 fCopies.Sort();
489}
490
491// --------------------------------------------------------------------------
492//
493// Add a new Container to list of containers which should be written to the
494// file. Give the pointer to the container. tname is the name of the tree to
495// which the container should be written (Remark: one tree can hold more than
496// one container). The default is the same name as the container name.
497// You can slso specify a title for the tree. This is only
498// used the first time this tree in 'mentioned'. As default the title
499// is the name of the tree.
500//
501Bool_t MWriteRootFile::GetContainer(MParList *pList)
502{
503 //
504 // loop over all branches which are 'marked' as branches to get written.
505 //
506 MRootFileBranch *entry;
507
508 TIter Next(&fBranches);
509 while ((entry=(MRootFileBranch*)Next()))
510 {
511 //
512 // Get the pointer to the container. If the pointer is NULL it seems,
513 // that the user identified the container by name.
514 //
515 MParContainer *cont = entry->GetContainer();
516 if (!cont)
517 {
518 //
519 // Get the name and try to find a container with this name
520 // in the parameter list.
521 //
522 const char *cname = entry->GetContName();
523 cont = (MParContainer*)pList->FindObject(cname);
524 if (!cont)
525 {
526 //
527 // No corresponding container is found
528 //
529 if (entry->MustHave())
530 {
531 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
532 return kFALSE;
533 }
534
535 *fLog << inf2 << "Unnecessary parameter container '" << cname << "' not found..." << endl;
536 delete fBranches.Remove(entry);
537 continue;
538 }
539
540 //
541 // The container is found. Put the pointer into the entry.
542 //
543 entry->SetContainer(cont);
544 }
545
546 //
547 // Get container name, tree name and tree title of this entry.
548 //
549 const char *cname = cont->GetName();
550 const char *tname = entry->GetName();
551 const TString ttitle(Form("Tree containing %s", cont->GetDescriptor().Data()));
552
553 //
554 // if the tree name is NULL this idetifies it to use the default:
555 // the container name.
556 //
557 if (tname[0] == '\0')
558 tname = cname;
559
560 //
561 // Check if the tree is already existing (part of the file or memory)
562 //
563 TTree *tree = fOut ? (TTree*)fOut->Get(tname) : dynamic_cast<TTree*>(gROOT->FindObject(tname));
564 if (!fOut && tree)
565 {
566 if (tree->GetCurrentFile())
567 {
568 *fLog << err;
569 *fLog << "ERROR - You are trying to write data into a memory stored root tree," << endl;
570 *fLog << " because you either called the default constructor or have" << endl;
571 *fLog << " instantiated MWriteRootFile using the write option 'memory'." << endl;
572 *fLog << " This tree '" << tname << "' is already existing in" << endl;
573 *fLog << " memory (gROOT->FindObject) and is already belonging to a" << endl;
574 *fLog << " file (" << tree->GetCurrentFile()->GetName() << ")." << endl;
575 *fLog << " This can - for example - happen if you are reading from a" << endl;
576 *fLog << " tree with the same name. The easiest solution in this case" << endl;
577 *fLog << " is to change the name of the tree you want to write to." << endl;
578 *fLog << endl;
579 return kFALSE;
580 }
581 *fLog << inf << "Tree '" << tname << "' found already in Memory... using." << endl;
582 }
583
584 if (!tree)
585 {
586 //
587 // if the tree doesn't exist create a new tree. Use the tree
588 // name as title if title is NULL.
589 // And add the tree to the list of trees
590 //
591 TDirectory *save = gDirectory;
592 if (fOut)
593 fOut->cd();
594 else
595 gROOT->cd();
596
597 tree = new TTree(tname, ttitle, fOut ? 99 : 1);
598 fTrees.AddLast(tree);
599
600 //
601 // If the tree does not already exist in the file mark this
602 // tree as a branch created by MWriteRootFile
603 //
604 tree->SetBit(kIsNewTree);
605
606 *fLog << inf << "Tree " << tname << " created in " << gDirectory->GetName() << endl;
607
608 gDirectory = save;
609 }
610
611 //
612 // In case the file is opened as 'UPDATE' the tree may still not
613 // be in the list. Because it neither was created previously,
614 // nor this time, so the corresponding entries is marked as a
615 // single branch to be filled. --> Add it to the list of trees.
616 //
617 if (!fTrees.FindObject(tree))
618 fTrees.AddLast(tree);
619
620 //
621 // Now we have a valid tree. Search the list of trees for this tree
622 // Add a pointer to the entry in the tree list to this branch-entry
623 //
624 entry->SetTree(tree);
625
626 TString branchname(cname);
627 branchname.Append(".");
628
629 //
630 // Try to get the branch from the file.
631 // If the branch already exists the user specified one branch twice.
632 //
633 TBranch *branch = tree->GetBranch(branchname);
634 if (branch)
635 {
636 *fLog << inf << "Branch '" << cname << "' already existing... updating." << endl;
637 branch->SetAddress(entry->GetAddress());
638
639 if (!fSplitRule.IsNull() && fOut)
640 {
641 *fLog << warn << endl;
642 *fLog << "WARNING: You are updating an existing branch. For this case" << endl;
643 *fLog << " file-splitting mode is not allowed... disabled!" << endl;
644 *fLog << endl;
645 fSplitRule = "";
646 }
647 }
648 else
649 {
650 //
651 // Create a new branch in the actual tree. The branch has the name
652 // container name. The type of the container is given by the
653 // ClassName entry in the container. The Address is the address of a
654 // pointer to the container (gotten from the branch entry). As
655 // Basket size we specify a (more or less) common default value.
656 // The containers should be written in Splitlevel=1
657 //
658 *fLog << inf << "Creating Branch '" << cname << "' ";
659 if ((TString)cname!=(TString)cont->ClassName())
660 *fLog << "[" << cont->ClassName() << "] ";
661 *fLog << "in tree " << tree->GetName() << "... " << flush;
662
663 branch = tree->Branch(branchname, cont->ClassName(), entry->GetAddress());
664
665 //
666 // If the branch couldn't be created we have a problem.
667 //
668 if (!branch)
669 {
670 *fLog << endl;
671 *fLog << err << "Unable to create branch '" << cname << "'." << endl;
672 return kFALSE;
673 }
674
675 *fLog << "done." << endl;
676
677 if (!tree->TestBit(kIsNewTree) && !fSplitRule.IsNull())
678 {
679 *fLog << warn << endl;
680 *fLog << "WARNING: You have created a new branch in an existing tree." << endl;
681 *fLog << " For this case file-splitting mode is not allowed... disabled!" << endl;
682 *fLog << endl;
683 fSplitRule= "";
684 }
685 }
686
687 //
688 // Tell the entry also which branch belongs to it (this is necessary
689 // for branches belonging to already existing tree, UPDATE-mode)
690 //
691 entry->SetBranch(branch);
692 }
693
694 return kTRUE;
695}
696
697// --------------------------------------------------------------------------
698//
699// Checks all given containers (branch entries) for the write flag.
700// If the write flag is set the corresponding Tree is marked to get filled.
701// All Trees which are marked to be filled are filled with all their
702// branches.
703// In case of a file opened in 'UPDATE' mode, single branches can be
704// filled, too. WARNING - for the moment there is no check whether
705// you filled the correct number of events into the branch, so that
706// each of the other branches in the tree has the correct corresponding
707// number of new entries in the new branch!
708// Be carefull: If only one container (corresponding to a branch) of a tree
709// has the write flag, all containers in this tree are filled!
710//
711Bool_t MWriteRootFile::CheckAndWrite()
712{
713 // This is the special case if we only copy a tre but do not
714 // write containers to it
715 const Int_t n = fTrees.GetEntriesFast();
716 if (n==0)
717 return kTRUE;
718
719 TObject *obj;
720
721 //
722 // Loop over all branch entries
723 //
724 TIter NextBranch(&fBranches);
725 while ((obj=NextBranch()))
726 {
727 MRootFileBranch *b = (MRootFileBranch*)obj;
728
729 //
730 // Check for the Write flag
731 //
732 if (!b->GetContainer()->IsReadyToSave())
733 continue;
734
735 if (b->GetMaxEntries()==b->GetTree()->GetEntries())
736 continue;
737
738 //
739 // If the write flag of the branch entry is set, set the write flag of
740 // the corresponding tree entry.
741 //
742 if (b->GetTree()->TestBit(kIsNewTree))
743 b->GetTree()->SetBit(kFillTree);
744 else
745 {
746 if (!b->GetBranch()->Fill())
747 {
748 *fLog << err << "ERROR - MWriteRootFile: Zero bytes written to branch '" << b->GetBranch()->GetName() << "'... abort." << endl;
749 return kFALSE;
750 }
751 }
752 }
753
754 //
755 // Loop over all tree entries
756 //
757 for (int idx=0; idx<n; idx++)
758 {
759 TTree *t = (TTree*)fTrees[idx];
760 if (!t)
761 {
762 *fLog << err << "ERROR - MWriteRootFile: The Tree with index " << idx << endl;
763 *fLog << " in fTrees is NULL. This should never happen. Please send" << endl;
764 *fLog << " a bug report." << endl;
765 return kFALSE;
766 }
767
768 //
769 // Check the write flag of the tree
770 //
771 if (!t->TestBit(kFillTree))
772 continue;
773
774 //
775 // If the write flag is set, fill the tree (with the corresponding
776 // branches/containers), delete the write flag and increase the number
777 // of written/filled entries.
778 //
779 t->ResetBit(kFillTree);
780
781 if (!t->Fill())
782 {
783 *fLog << err << "ERROR - MWriteRootFiole: Zero bytes written to tree '" << t->GetName() << "'... abort." << endl;
784 return kFALSE;
785 }
786 }
787
788 //
789 // If we are writing into memory we don't split into seperate files
790 //
791 if (!fOut || TestBit(kIsNotOwner))
792 return kTRUE;
793
794 //
795 // For more information see TTree:ChangeFile()
796 //
797 TTree *t0 = (TTree*)fTrees[0];
798 if (!t0 || fOut==t0->GetCurrentFile())
799 return kTRUE;
800
801 // FIXME: THIS IS EMITTED FOR ALL CONSEQUTIVE EVENTS!
802 *fLog << warn << endl;
803 *fLog << "WARNING - MWriteRootFile: Root's TTree/TFile has opened a new file" << endl;
804 *fLog << " automatically. You can change this behaviour using TTree::SetMaxTreeSize." << endl;
805 *fLog << " You won't be able to read splitted files correctly with MReadMarsFile if" << endl;
806 *fLog << " they have more than one entry in 'RunHeaders' or you try to read more than" << endl;
807 *fLog << " one of such sequences at once." << endl;
808 *fLog << endl;
809
810 return kTRUE;
811}
812
813// --------------------------------------------------------------------------
814//
815// Open a new file with the name fname. Move all trees and branches from the
816// old file to the new file.
817//
818Bool_t MWriteRootFile::ChangeFile(const char *fname)
819{
820 const Int_t compr = fOut ? fOut->GetCompressionLevel() : 0;
821 const TString title = fOut ? fOut->GetTitle() : "";
822 const TString opt = fOut ? fOut->GetOption() : "";
823
824 // Open new file with old setup
825 TFile *newfile = OpenFile(fname, opt, title, compr);
826 if (newfile && newfile==fOut)
827 {
828 *fLog << inf << "Found open file '" << fname << "'... using." << endl;
829 return kTRUE;
830 }
831 if (!newfile)
832 {
833 *fLog << err << "ERROR - Cannot open new file '" << fname << "'" << endl;
834 return kFALSE;
835 }
836
837 if (!fOut)
838 {
839 *fLog << err << "ERROR - MWriteRootFile::ChangeFile... something went terribly wrong!" << endl;
840 *fLog << " fname: " << fname << endl;
841 *fLog << " Please start debugging!" << endl;
842 return kFALSE;
843 }
844
845 *fLog << inf << "Open new file " << fname << " (Title=" << title << ", Option=" << opt << ", Compression=" << compr << ")" << endl;
846
847 // Print statistics of old file
848 const TString n = GetFileName();
849 if (!n.IsNull() && n!=TString("/dev/null"))
850 Print();
851
852 if (fOut->IsOpen())
853 fOut->Write();
854
855 // Move all trees from the old file to the new file
856 TObject *obj=0;
857 while ((obj = fOut->GetList()->First()))
858 {
859 // Remove obj from old file (otherwise deleting
860 // the old file will delete the objs)
861 fOut->GetList()->Remove(obj);
862
863 // If this is not a tree do nothing.
864 if (!obj->InheritsFrom(TTree::Class()))
865 continue;
866
867 // process all trees in the old file
868 TTree *t = (TTree*)obj;
869
870 // reset and move to new file (this is done implicitly for all branches)
871 t->Reset();
872 t->SetDirectory(newfile);
873 }
874
875 // Close/delete the old file (keys already written above)
876 *fLog << inf3 << "Closing file " << fOut->GetName() << "." << endl;
877 delete fOut;
878
879 // Replace current with new file
880 fOut = newfile;
881
882 // Change current directory to new file
883 gFile = fOut;
884
885 return kTRUE;
886}
887
888// --------------------------------------------------------------------------
889//
890// A rule looks like:
891// "s/source/destination/"
892//
893// For more details on regular expression see a proper documentation,
894// for example, "man 7 regex" if installed, or TPRegexp.
895//
896// Here is an example:
897//
898// Example:
899// inputfile: /data/MAGIC/Period016/rootdata/20040621_23210_D_Mkn421_E.root
900// rule: /([0-9]+_[0-9]+)_D_(.*[.]root)/\\/outpath\\/$1_Y_$2/
901// outfile: /outpath/20040621_23210_Y_Mkn421_E.root
902//
903// Please make sure that all / in your rules are correctly escaped, i.e.
904// in the string stored in memory it must look like \/ and in the string
905// your set in your program it must look \\/.
906//
907// Note, that this function has been made static to allow your to
908// test your split rules, i.e. regular expressions.
909//
910TString MWriteRootFile::SubstituteName(const char *regexp, TString fname)
911{
912 // Remove the path from the filename
913 if (fname.Last('/')>=0)
914 fname.Remove(0, fname.Last('/')+1);
915
916 // Regular expression to split the rule into its contents
917 static const TString sed("s/((\\\\/|[^/])*)/((\\\\/|[^/])*)/([gimosxd]*)");
918
919 // Do splitting
920 TObjArray *subStrL = TPRegexp(sed).MatchS(regexp);
921 if (subStrL->GetEntries()!=6)
922 {
923 gLog << err << "ERROR - SubstituteName: Evaluating regexp " << regexp << " failed." << endl;
924 subStrL->Print();
925 delete subStrL;
926 return "";
927 }
928
929 /*const*/ TString reg = (*subStrL)[1]->GetName(); // Regular expression to search for
930 /*const*/ TString tar = (*subStrL)[3]->GetName(); // Regular expression for replacing
931 const TString mod = (*subStrL)[5]->GetName(); // Possible modifiers (e.g. 'a')
932
933 delete subStrL;
934
935 // Unescpae slashes in paths
936 reg.ReplaceAll("\\/", "/");
937 tar.ReplaceAll("\\/", "/");
938
939 // Do substitution
940 const Int_t nrSub = TPRegexp(reg).Substitute(fname, tar, mod);
941 if (nrSub==0)
942 {
943 gLog << err << "ERROR - Substituting due to SplitRule failed." << endl;
944 gLog << " Source FileName: " << fname << endl;
945 gLog << " Search Rexexp: " << reg << endl;
946 gLog << " Replace Rexexp: " << tar << endl;
947 gLog << " Modifiers: " << mod << endl;
948 return "";
949 }
950
951 return fname;
952}
953
954// --------------------------------------------------------------------------
955//
956// Writes a copy of the TTree t to the currently open file using
957// TTree::CloneTree()
958//
959void MWriteRootFile::CopyTree(TTree &t) const
960{
961 TString out = "Copy of tree ";
962 out += t.GetName();
963 out += " in progress...";
964
965 if (fDisplay)
966 fDisplay->SetStatusLine2(out);
967
968 *fLog << inf << out << flush;
969
970 // When a new file has been opened the old clone (if existing) has
971 // been moved to the new file. We could now use CopyTree but then
972 // we would have to unpack all data and repack it. Instead
973 // we delete the moved old tree.
974 // FIXME: In priciple we could delete a "wrong" tree with the same name.
975 // Should we flag the clones and delete them in ChangeFile?
976 TObject *old = fOut->GetList()->Remove(fOut->GetList()->FindObject(t.GetName()));
977 delete old;
978
979 // Now we clone the tree without unpacking and repacking.
980 // When it has not been moved it will be deleted in the TFile destructor
981 /*TTree *clone =*/ t.CloneTree(-1, "fast");
982 //clone->Write();
983 //delete clone;
984
985 *fLog << "done." << endl;
986
987 if (fDisplay)
988 {
989 out += " done.";
990 fDisplay->SetStatusLine2(out);
991 }
992}
993
994// --------------------------------------------------------------------------
995//
996// Make all copies requested from the currently open file into the new
997// file.
998//
999Bool_t MWriteRootFile::MakeCopies(const char *fname) const
1000{
1001 if (fCopies.GetEntries()==0)
1002 return kTRUE;
1003
1004 TFile *file = dynamic_cast<TFile*>(gROOT->GetListOfFiles()->FindObject(fname));
1005 if (!file)
1006 {
1007 *fLog << err << "ERROR - MakeCopies: File " << fname << " not found in gROOT->GetListOfFiles()... abort." << endl;
1008 return kFALSE;
1009 }
1010
1011 TIter Next(&fCopies);
1012 TObject *o=0;
1013 TTree *t=0;
1014
1015 fOut->cd();
1016 while ((o=Next()))
1017 {
1018 TTree *gettree = dynamic_cast<TTree*>(file->Get(o->GetName()));
1019 if (!gettree)
1020 {
1021 const Bool_t force = o->TestBit(kForced);
1022 if (force)
1023 *fLog << err << "ERROR - ";
1024 else
1025 *fLog << inf3;
1026
1027 *fLog << "MakeCopies: Tree " << o->GetName() << " not found in file " << fname << ".";
1028 if (force)
1029 *fLog << ".. abort.";
1030 *fLog << endl;
1031
1032 if (force)
1033 return kFALSE;
1034
1035 continue;
1036 }
1037
1038 gettree->SetBranchStatus(o->GetTitle(), 1);
1039
1040 // First Execution
1041 if (t==gettree)
1042 continue;
1043
1044 // Check if its the first call
1045 if (t)
1046 CopyTree(*t);
1047 t = gettree;
1048 }
1049
1050 if (t)
1051 CopyTree(*t);
1052
1053 return kTRUE;
1054}
1055
1056// --------------------------------------------------------------------------
1057//
1058// ReInit. If file splitting is not allowed call MWriteFile::ReInit.
1059//
1060// In other cases get MRead from the TaskList (splitting is switched of if
1061// this is impossible).
1062//
1063// Convert the input- into a new output file-name.
1064//
1065// Open a new file, change all trees to the new file (calling ChangeFile()),
1066// and close the old one.
1067//
1068// Call MWriteFile::ReInit()
1069//
1070Bool_t MWriteRootFile::ReInit(MParList *pList)
1071{
1072 MRead *read = (MRead*)pList->FindTask("MRead");
1073 if (fSplitRule.IsNull() && fCopies.GetEntries()>0 && fOut)
1074 {
1075 if (!read)
1076 {
1077 *fLog << err;
1078 *fLog << "ERROR: No Task 'MRead' found in the tasklist. This task is" << endl;
1079 *fLog << " necessary to get the filename. Without a filename file" << endl;
1080 *fLog << " AddCopySource cannot be used... abort." << endl;
1081 *fLog << endl;
1082 return kFALSE;
1083 }
1084 if (!MakeCopies(read->GetFullFileName()))
1085 return kFALSE;
1086
1087 }
1088
1089 if (fSplitRule.IsNull() || !(fOut || TestBit(kIsNotOwner)))
1090 return MWriteFile::ReInit(pList);
1091
1092 if (!read)
1093 {
1094 *fLog << warn;
1095 *fLog << "WARNING: No Task 'MRead' found in the tasklist. This task is" << endl;
1096 *fLog << " necessary to get the filename. Without a filename file" << endl;
1097 *fLog << " file splitting is not allowed... disabled!" << endl;
1098 *fLog << endl;
1099 fSplitRule = "";
1100 return kTRUE;
1101 }
1102
1103
1104 const TString oldname = read->GetFullFileName();
1105 const TString newname = SubstituteName(fSplitRule, oldname);
1106 if (!ChangeFile(newname))
1107 return kFALSE;
1108
1109 if (!MakeCopies(oldname))
1110 return kFALSE;
1111
1112 return MWriteFile::ReInit(pList);
1113}
1114
1115// --------------------------------------------------------------------------
1116//
1117// return open state of the root file. If the file is 'memory' kTRUE is
1118// returned.
1119//
1120Bool_t MWriteRootFile::IsFileOpen() const
1121{
1122 if (!fOut)
1123 return kTRUE;
1124
1125 const char *n = fOut->GetName();
1126 return n==0 || *n==0 ? kTRUE : fOut->IsOpen();
1127}
1128
1129// --------------------------------------------------------------------------
1130//
1131// return name of the root-file. If the file is "memory" "<memory>" is
1132// returned.
1133//
1134const char *MWriteRootFile::GetFileName() const
1135{
1136 if (!fOut)
1137 return "<memory>";
1138
1139 const char *n = fOut->GetName();
1140 return n==0 || *n==0 ? "<dummy>" : n;
1141}
1142
1143// --------------------------------------------------------------------------
1144//
1145// cd into file. See TFile::cd(). If the file is "memory" kTRUE is returned.
1146//
1147Bool_t MWriteRootFile::cd(const char *path)
1148{
1149 return fOut ? fOut->cd(path) : kTRUE;
1150}
1151
1152// --------------------------------------------------------------------------
1153//
1154// If the output file is deleted set fOut to NULL.
1155// Call MTask::RecursiveRemove
1156//
1157void MWriteRootFile::RecursiveRemove(TObject *obj)
1158{
1159 if (obj==fOut)
1160 fOut=NULL;
1161
1162 MWriteFile::RecursiveRemove(obj);
1163}
1164
1165// --------------------------------------------------------------------------
1166//
1167// Implementation of SavePrimitive. Used to write the call to a constructor
1168// to a macro. In the original root implementation it is used to write
1169// gui elements to a macro-file.
1170//
1171void MWriteRootFile::StreamPrimitive(ostream &out) const
1172{
1173 out << " MWriteRootFile " << GetUniqueName();
1174 if (fOut)
1175 {
1176 out << "(\"";
1177 out << fOut->GetName() << "\", \"";
1178 out << fOut->GetOption() << "\", \"";
1179 out << fOut->GetTitle() << "\", ";
1180 out << fOut->GetCompressionLevel();
1181 out << ")";
1182 }
1183 out << ";" << endl;
1184
1185 if (fName!=gsDefName)
1186 out << " " << GetUniqueName() << ".SetName(\"" << fName << "\");" << endl;
1187 if (fTitle!=gsDefTitle)
1188 out << " " << GetUniqueName() << ".SetTitle(\"" << fTitle << "\");" << endl;
1189
1190 MRootFileBranch *entry;
1191 TIter Next(&fBranches);
1192 while ((entry=(MRootFileBranch*)Next()))
1193 {
1194 out << " " << GetUniqueName() << ".AddContainer(";
1195
1196 if (entry->GetContainer())
1197 {
1198 entry->GetContainer()->SavePrimitive(out);
1199 out << "&" << entry->GetContainer()->GetUniqueName();
1200 }
1201 else
1202 out << "\"" << entry->GetContName() << "\"";
1203
1204 out << ", \"" << entry->GetName() << "\"";
1205 if (!entry->MustHave())
1206 out << ", kFALSE";
1207
1208 out <<");" << endl;
1209 }
1210}
1211
Note: See TracBrowser for help on using the repository browser.