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

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