source: trunk/MagicSoft/Mars/mfileio/MWriteRootFile.cc@ 9436

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