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

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