source: releases/Mars.2014.05.26/mfileio/MWriteRootFile.cc

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