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

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