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

Last change on this file since 19344 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.