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

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