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

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