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

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