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

Last change on this file since 5138 was 4826, checked in by tbretz, 20 years ago
*** empty log message ***
File size: 26.3 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-2004
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/////////////////////////////////////////////////////////////////////////////
40#include "MWriteRootFile.h"
41
42#include <fstream>
43
44#include <TFile.h>
45#include <TTree.h>
46#include <TRegexp.h>
47
48#include "MLog.h"
49#include "MLogManip.h"
50
51#include "MRead.h"
52#include "MParList.h"
53
54ClassImp(MRootFileBranch);
55ClassImp(MWriteRootFile);
56
57using namespace std;
58
59const TString MWriteRootFile::gsDefName = "MWriteRootFile";
60const TString MWriteRootFile::gsDefTitle = "Task which writes a root-output file";
61
62void MWriteRootFile::Init(const char *name, const char *title)
63{
64 fName = name ? name : gsDefName.Data();
65 fTitle = title ? title : gsDefTitle.Data();
66
67 //
68 // Set the Arrays the owner of its entries. This means, that the
69 // destructor of the arrays will delete all its entries.
70 //
71 fBranches.SetOwner();
72
73 //
74 // Believing the root user guide, TTree instanced are owned by the
75 // directory (file) in which they are. This means we don't have to
76 // care about their destruction.
77 //
78 //fTrees.SetOwner();
79}
80
81// --------------------------------------------------------------------------
82//
83// Default constructor. It is there to support some root stuff.
84// Don't use it.
85//
86MWriteRootFile::MWriteRootFile() : fOut(NULL)
87{
88 Init();
89
90 //
91 // Set the Arrays the owner of its entries. This means, that the
92 // destructor of the arrays will delete all its entries.
93 //
94 fBranches.SetOwner();
95}
96
97// --------------------------------------------------------------------------
98//
99// Use this constructor to run in a special mode.
100//
101// In this mode for each input file a new output file is written. This
102// happens in ReInit.
103//
104// comp: Compression Level (see TFile, TBranch)
105// rule: Rule to create output file name (see GetNewFileName())
106// overwrite: Allow newly created file to overwrite old files ("RECREATE")
107// ftitle: File title stored in the file (see TFile)
108// name, title: Name and title of this object
109//
110MWriteRootFile::MWriteRootFile(const Int_t comp,
111 const char *rule,
112 const Bool_t overwrite,
113 const char *ftitle,
114 const char *name,
115 const char *title) : fSplitRule(rule)
116{
117 Init(name, title);
118
119 //
120 // Open a TFile in dummy mode! This is necessary to be able to create
121 // the trees and branches, which are then (in ReInit) moved to
122 // a valid file. (Stupid workaround - but does a good job)
123 //
124 fOut = new TFile("/dev/null", overwrite?"RECREATE":"NEW", ftitle, comp);
125}
126
127// --------------------------------------------------------------------------
128//
129// Specify the name of the root file. You can also give an option ("UPDATE"
130// and "RECREATE" would make sense only) as well as the file title and
131// compression factor. To a more detaild description of the options see
132// TFile.
133//
134MWriteRootFile::MWriteRootFile(const char *fname,
135 const Option_t *opt,
136 const char *ftitle,
137 const Int_t comp,
138 const char *name,
139 const char *title)
140{
141 Init(name, title);
142
143 //
144 // If no name is given we open the TFile in some kind of dummy mode...
145 //
146 if (!fname)
147 {
148 fOut = new TFile("/dev/null", "READ", ftitle, comp);
149 return;
150 }
151
152 TString str(fname);
153 if (!str.EndsWith(".root", TString::kIgnoreCase))
154 str += ".root";
155
156 //
157 // Open the rootfile
158 //
159 fOut = new TFile(str, opt, ftitle, comp);
160}
161
162// --------------------------------------------------------------------------
163//
164// Prints some statistics about the file to the screen. And closes the file
165// properly.
166//
167void MWriteRootFile::Close()
168{
169 //
170 // Print some statistics to the looging out.
171 //
172 Print();
173
174 //
175 // If the file is still open (no error) write the keys. This is necessary
176 // for appearance of the all trees and branches.
177 //
178 if (IsFileOpen())
179 fOut->Write();
180
181 //
182 // Delete the file. This'll also close the file (if open)
183 //
184 delete fOut;
185 fOut = 0;
186
187 //
188 // Remark:
189 // - Trees are automatically deleted by the the file
190 // (unless file.SetDirectory(0) was called)
191 // - Branches are automatically deleted by the tree destructor
192 //
193
194 *fLog << inf << "Output File closed and object deleted." << endl;
195}
196
197// --------------------------------------------------------------------------
198//
199// call Close()
200//
201MWriteRootFile::~MWriteRootFile()
202{
203 Close();
204}
205
206// --------------------------------------------------------------------------
207//
208// Prints all trees with the actually number of written entries to log-out.
209//
210void MWriteRootFile::Print(Option_t *) const
211{
212 *fLog << all << underline << "File: " << GetFileName() << endl;
213
214 if (fTrees.GetEntries()==0)
215 {
216 *fLog << " No contents." << endl;
217 return;
218 }
219
220 TObject *obj;
221 TIter NextBranch(&fBranches);
222 while ((obj=NextBranch()))
223 {
224 MRootFileBranch *b = (MRootFileBranch*)obj;
225
226 if (!b->GetTree() || b->GetTree()->TestBit(kIsNewTree))
227 continue;
228
229 TBranch *branch = b->GetBranch();
230
231 TString name = b->GetTree()->GetName();
232 name += '.';
233 name += branch->GetName();
234
235 *fLog << " " << name.Strip(TString::kTrailing, '.') << ": \t" << (ULong_t)branch->GetEntries() << " entries." << endl;
236 }
237
238 TTree *t = NULL;
239 TIter NextTree(&fTrees);
240 while ((t=(TTree*)NextTree()))
241 if (t->TestBit(kIsNewTree))
242 *fLog << " " << t->GetName() << ": \t" << (ULong_t)t->GetEntries() << " entries." << endl;
243 *fLog << endl;
244}
245
246// --------------------------------------------------------------------------
247//
248// Add a new Container to list of containers which should be written to the
249// file. Give the name of the container which will identify the container
250// in the parameterlist. tname is the name of the tree to which the
251// container should be written (Remark: one tree can hold more than one
252// container). The default is the same name as the container name.
253// You can slso specify a title for the tree. This is only
254// used the first time this tree in 'mentioned'. As default the title
255// is the name of the tree.
256//
257void MWriteRootFile::AddContainer(const char *cname, const char *tname, Bool_t must)
258{
259 TIter Next(&fBranches);
260 TObject *o=0;
261 while ((o=Next()))
262 if (TString(o->GetName())==TString(tname) && TString(o->GetTitle())==TString(cname))
263 {
264 *fLog << "WARNING - Container '" << cname << "' in Tree '" << tname << "' already scheduled... ignored." << endl;
265 return;
266 }
267
268 //
269 // create a new entry in the list of branches to write and
270 // add the entry to the list.
271 //
272 MRootFileBranch *entry = new MRootFileBranch(AddSerialNumber(cname), tname, must);
273 fBranches.AddLast(entry);
274
275 if (tname && tname[0])
276 AddToBranchList(Form("%s.%s", (const char*)AddSerialNumber(cname), tname));
277}
278
279// --------------------------------------------------------------------------
280//
281// Add a new Container to list of containers which should be written to the
282// file. Give the pointer to the container. tname is the name of the tree to
283// which the container should be written (Remark: one tree can hold more than
284// one container). The default is the same name as the container name.
285// You can slso specify a title for the tree. This is only
286// used the first time this tree in 'mentioned'. As default the title
287// is the name of the tree.
288//
289void MWriteRootFile::AddContainer(MParContainer *cont, const char *tname,
290 Bool_t must)
291{
292 TIter Next(&fBranches);
293 TObject *o=0;
294 while ((o=Next()))
295 if (TString(o->GetName())==TString(tname) &&
296 static_cast<MRootFileBranch*>(o)->GetContainer()==cont)
297 {
298 *fLog << "WARNING - Container " << cont << " in Tree '" << tname << "' already scheduled... ignored." << endl;
299 return;
300 }
301
302 //
303 // create a new entry in the list of branches to write and
304 // add the entry to the list.
305 //
306 MRootFileBranch *entry = new MRootFileBranch(cont, tname, must);
307 fBranches.AddLast(entry);
308}
309
310// --------------------------------------------------------------------------
311//
312// Add a new Container to list of containers which should be written to the
313// file. Give the pointer to the container. tname is the name of the tree to
314// which the container should be written (Remark: one tree can hold more than
315// one container). The default is the same name as the container name.
316// You can slso specify a title for the tree. This is only
317// used the first time this tree in 'mentioned'. As default the title
318// is the name of the tree.
319//
320Bool_t MWriteRootFile::GetContainer(MParList *pList)
321{
322 //
323 // loop over all branches which are 'marked' as branches to get written.
324 //
325 MRootFileBranch *entry;
326
327 TIter Next(&fBranches);
328 while ((entry=(MRootFileBranch*)Next()))
329 {
330 //
331 // Get the pointer to the container. If the pointer is NULL it seems,
332 // that the user identified the container by name.
333 //
334 MParContainer *cont = entry->GetContainer();
335 if (!cont)
336 {
337 //
338 // Get the name and try to find a container with this name
339 // in the parameter list.
340 //
341 const char *cname = entry->GetContName();
342 cont = (MParContainer*)pList->FindObject(cname);
343 if (!cont)
344 {
345 //
346 // No corresponding container is found
347 //
348 if (entry->MustHave())
349 {
350 *fLog << err << "Cannot find parameter container '" << cname << "'." << endl;
351 return kFALSE;
352 }
353
354 *fLog << inf << "Unnecessary parameter container '" << cname << "' not found..." << endl;
355 delete fBranches.Remove(entry);
356 continue;
357 }
358
359 //
360 // The container is found. Put the pointer into the entry.
361 //
362 entry->SetContainer(cont);
363 }
364
365 //
366 // Get container name, tree name and tree title of this entry.
367 //
368 const char *cname = cont->GetName();
369 const char *tname = entry->GetName();
370 const TString ttitle(Form("Tree containing %s", cont->GetDescriptor()));
371
372 //
373 // if the tree name is NULL this idetifies it to use the default:
374 // the container name.
375 //
376 if (tname[0] == '\0')
377 tname = cname;
378
379 //
380 // Check if the tree is already existing (part of the file)
381 //
382 TTree *tree = (TTree*)fOut->Get(tname);
383 if (!tree)
384 {
385 //
386 // if the tree doesn't exist create a new tree. Use the tree
387 // name as title if title is NULL.
388 // And add the tree to the list of trees
389 //
390 TDirectory *save = gDirectory;
391 fOut->cd();
392
393 tree = new TTree(tname, ttitle);
394 fTrees.AddLast(tree);
395
396 //
397 // If the tree does not already exist in the file mark this
398 // tree as a branch created by MWriteRootFile
399 //
400 tree->SetBit(kIsNewTree);
401
402 gDirectory = save;
403
404 *fLog << inf << "Tree " << tname << " created." << endl;
405 }
406
407 //
408 // In case the file is opened as 'UPDATE' the tree may still not
409 // be in the list. Because it neither was created previously,
410 // nor this time, so the corresponding entries is marked as a
411 // single branch to be filled. --> Add it to the list of trees.
412 //
413 if (!fTrees.FindObject(tree))
414 fTrees.AddLast(tree);
415
416 //
417 // Now we have a valid tree. Search the list of trees for this tree
418 // Add a pointer to the entry in the tree list to this branch-entry
419 //
420 entry->SetTree(tree);
421
422 TString branchname(cname);
423 branchname.Append(".");
424
425 //
426 // Try to get the branch from the file.
427 // If the branch already exists the user specified one branch twice.
428 //
429 TBranch *branch = tree->GetBranch(branchname);
430 if (branch)
431 {
432 *fLog << inf << "Branch '" << cname << "' already existing... updating." << endl;
433 branch->SetAddress(entry->GetAddress());
434
435 if (!fSplitRule.IsNull())
436 {
437 *fLog << warn << endl;
438 *fLog << "WARNING: You are updating an existing branch. For this case" << endl;
439 *fLog << " file-splitting mode is not allowed... disabled!" << endl;
440 *fLog << endl;
441 fSplitRule = "";
442 }
443 }
444 else
445 {
446 //
447 // Create a new branch in the actual tree. The branch has the name
448 // container name. The type of the container is given by the
449 // ClassName entry in the container. The Address is the address of a
450 // pointer to the container (gotten from the branch entry). As
451 // Basket size we specify a (more or less) common default value.
452 // The containers should be written in Splitlevel=1
453 //
454 *fLog << inf << "Creating Branch " << cname << " of " << cont->ClassName();
455 *fLog << " in tree " << tree->GetName() << "... " << flush;
456
457 branch = tree->Branch(branchname, cont->ClassName(), entry->GetAddress());
458
459 //
460 // If the branch couldn't be created we have a problem.
461 //
462 if (!branch)
463 {
464 *fLog << endl;
465 *fLog << err << "Unable to create branch '" << cname << "'." << endl;
466 return kFALSE;
467 }
468
469 *fLog << "done." << endl;
470
471 if (!tree->TestBit(kIsNewTree) && !fSplitRule.IsNull())
472 {
473 *fLog << warn << endl;
474 *fLog << "WARNING: You have created a new branch in an existing tree." << endl;
475 *fLog << " For this case file-splitting mode is not allowed... disabled!" << endl;
476 *fLog << endl;
477 fSplitRule= "";
478 }
479 }
480
481 //
482 // Tell the entry also which branch belongs to it (this is necessary
483 // for branches belonging to already existing tree, UPDATE-mode)
484 //
485 entry->SetBranch(branch);
486 }
487
488 return kTRUE;
489}
490
491// --------------------------------------------------------------------------
492//
493// Checks all given containers (branch entries) for the write flag.
494// If the write flag is set the corresponding Tree is marked to get filled.
495// All Trees which are marked to be filled are filled with all their
496// branches.
497// In case of a file opened in 'UPDATE' mode, single branches can be
498// filled, too. WARNING - for the moment there is no check whether
499// you filled the correct number of events into the branch, so that
500// each of the other branches in the tree has the correct corresponding
501// number of new entries in the new branch!
502// Be carefull: If only one container (corresponding to a branch) of a tree
503// has the write flag, all containers in this tree are filled!
504//
505Bool_t MWriteRootFile::CheckAndWrite()
506{
507 TObject *obj;
508
509 //
510 // Loop over all branch entries
511 //
512 TIter NextBranch(&fBranches);
513 while ((obj=NextBranch()))
514 {
515 MRootFileBranch *b = (MRootFileBranch*)obj;
516
517 //
518 // Check for the Write flag
519 //
520 if (!b->GetContainer()->IsReadyToSave())
521 continue;
522
523 //
524 // If the write flag of the branch entry is set, set the write flag of
525 // the corresponding tree entry.
526 //
527 if (b->GetTree()->TestBit(kIsNewTree))
528 b->GetTree()->SetBit(kFillTree);
529 else
530 {
531 if (!b->GetBranch()->Fill())
532 {
533 *fLog << err << "ERROR - Zero bytes written to branch '" << b->GetBranch()->GetName() << "'... abort." << endl;
534 return kFALSE;
535 }
536 }
537 }
538
539 //
540 // Loop over all tree entries
541 //
542 const Int_t n = fTrees.GetEntriesFast();
543
544 for (int idx=0; idx<n; idx++)
545 {
546 TTree *t = (TTree*)fTrees[idx];
547
548 //
549 // Check the write flag of the tree
550 //
551 if (!t->TestBit(kFillTree))
552 continue;
553
554 //
555 // If the write flag is set, fill the tree (with the corresponding
556 // branches/containers), delete the write flag and increase the number
557 // of written/filled entries.
558 //
559 t->ResetBit(kFillTree);
560
561 if (!t->Fill())
562 {
563 *fLog << err << "ERROR - Zero bytes written to tree '" << t->GetName() << "'... abort." << endl;
564 return kFALSE;
565 }
566 }
567
568 //
569 // For more information see TTree:ChangeFile()
570 //
571 TTree *t0 = (TTree*)fTrees[0];
572 if (!t0 || fOut==t0->GetCurrentFile())
573 return kTRUE;
574
575 *fLog << warn << endl;
576 *fLog << "WARNING - MWriteRootFile: Root's TTree/TFile has opened a new file" << endl;
577 *fLog << " automatically. You can change this behaviour using TTree::SetMaxTreeSize." << endl;
578 *fLog << " You won't be able to read splitted files correctly with MReadMarsFile if" << endl;
579 *fLog << " they have more than one entry in 'RunHeaders' or you try to read more than" << endl;
580 *fLog << " one of such sequences at once." << endl;
581 *fLog << endl;
582
583 return kTRUE;
584}
585
586// --------------------------------------------------------------------------
587//
588// Open a new file with the ame fname. Move all trees and branches from the
589// old file to the new file.
590//
591Bool_t MWriteRootFile::ChangeFile(const char *fname)
592{
593 //
594 // The following code is more or less a copy of TTree::ChangeFile
595 //
596 const Int_t compr = fOut->GetCompressionLevel();
597 const TString title = fOut->GetTitle();
598
599 *fLog << inf << "MWriteRootFile - Open new file " << fname << " (Title=" << title << ", Compression=" << compr << ")" << endl;
600
601 // Open new file with old setup
602 TFile *newfile = TFile::Open(fname, "RECREATE", title, compr);
603 if (!newfile)
604 {
605 *fLog << err << "ERROR - Cannot open new file " << fname << endl;
606 return kFALSE;
607 }
608
609 // Print statistics of old file
610 const TString n = GetFileName();
611 if (!n.IsNull() && n!=TString("/dev/null"))
612 Print();
613
614 if (fOut->IsOpen())
615 fOut->Write();
616
617 // Move all trees from the old file to the new file
618 TObject *obj=0;
619 while ((obj = fOut->GetList()->First()))
620 {
621 // Remove obj from old file (otherwise deleting
622 // the old file will delete the objs)
623 fOut->GetList()->Remove(obj);
624
625 // If this is not a tree do nothing.
626 if (!obj->InheritsFrom("TTree"))
627 continue;
628
629 // process all trees in the old file
630 TTree *t = (TTree*)obj;
631
632 // reset and move to new file (this is done implicitly for all branches)
633 t->Reset();
634 t->SetDirectory(newfile);
635 }
636
637 // Close/delete the old file (keys already written above)
638 delete fOut;
639
640 // Replace current with new file
641 fOut = newfile;
642
643 // Change current directory to new file
644 gFile = fOut;
645
646 return kTRUE;
647}
648
649// --------------------------------------------------------------------------
650//
651// A rule looks like:
652// "outputpath{s/source/destination}"
653//
654// outpath: the output path into which the files are written
655// {s/source/destination} a substitution rule for the filename
656// while source can be a regular expression everything which matches this
657// regular expression (see TRegexp) will be replaced by destination.
658// Warning: The algorithm is recursive you may create endless loops!
659//
660// Example:
661// inputfile: /data/MAGIC/Period016/rootdata/20040621_23210_D_Mkn421_E.root
662// rule: /outpath/{s/_D_/_Y_}
663// outfile: /outpath/20040621_23210_Y_Mkn421_E.root
664//
665// If you need more difficult rules please send me an eMail...
666//
667TString MWriteRootFile::GetNewFileName(const char *inname) const
668{
669 // Remove the path from the filename
670 TString fname(inname);
671 if (fname.Last('/')>=0)
672 fname.Remove(0, fname.Last('/')+1);
673
674 // Make a copy of the rule
675 TString rule(fSplitRule);
676
677 // [characte class], ^ do not, [^{}] do not match { and }, [^{}]+ match at least not one { or }
678 const TRegexp subst0("{s/[^{}/]+/[^{}/]+}");
679
680 TString path;
681 Bool_t first = kTRUE;
682
683 Ssiz_t idx=0;
684 while (1)
685 {
686 // Find a substitution exprsssion
687 Ssiz_t len = 0;
688 idx = subst0.Index(rule, &len);
689 if (idx<0)
690 break;
691
692 // If the first substitution expression is found in the rule
693 // determin the path from everything before
694 if (first)
695 {
696 path=rule(0, idx);
697 first = kFALSE;
698 }
699
700 // Extract a substitution expression
701 TString expr = rule(idx, len);
702 rule.Remove(idx, len);
703
704 expr.Remove(0,3);
705 expr.Remove(expr.Length()-1);
706
707 // In all cases this is well defined (see Regexp)
708 const Ssiz_t pos = expr.First('/');
709
710 // Split substitution rule into source and destination
711 const TString src = expr(0, pos);
712 const TString dest = expr(pos+1, expr.Length());
713
714 // Replace source by destination
715 const TRegexp regexp(src);
716 while (1)
717 {
718 TString sub = fname(idx+dest.Length());
719 idx = regexp.Index(fname, &len);
720 if (idx<0)
721 break;
722
723 fname.Replace(idx, len, dest);
724 }
725 }
726
727 // Check if we have a trailing '/'
728 if (!path.IsNull() && path[path.Length()-1]!='/')
729 path.Append("/");
730
731 // Create full qualified pathname
732 path += fname;
733 return path;
734}
735
736// --------------------------------------------------------------------------
737//
738// ReInit. If file splitting is not allowed call MWriteFile::ReInit.
739//
740// In other cases get MRead from the TaskList (splitting is switched of if
741// this is impossible).
742//
743// Convert the input- into a new output file-name.
744//
745// Open a new file, change all trees to the new file (calling ChangeFile()),
746// and close the old one.
747//
748// Call MWriteFile::ReInit()
749//
750Bool_t MWriteRootFile::ReInit(MParList *pList)
751{
752 if (fSplitRule.IsNull())
753 return MWriteFile::ReInit(pList);
754
755 MRead *read = (MRead*)pList->FindTask("MRead");
756 if (!read)
757 {
758 *fLog << warn;
759 *fLog << "WARNING: No Task 'MRead' found in the tasklist. This task is" << endl;
760 *fLog << " necessary to get the filename. Without a filename file" << endl;
761 *fLog << " file splitting is not allowed... disabled!" << endl;
762 *fLog << endl;
763 fSplitRule = "";
764 return kTRUE;
765 }
766
767 const TString fname = GetNewFileName(read->GetFileName());
768 if (!ChangeFile(fname))
769 return kFALSE;
770
771 return MWriteFile::ReInit(pList);
772}
773
774// --------------------------------------------------------------------------
775//
776// return open state of the root file.
777//
778Bool_t MWriteRootFile::IsFileOpen() const
779{
780 const char *n = fOut->GetName();
781 return n==0 || *n==0 ? kTRUE : fOut->IsOpen();
782}
783
784// --------------------------------------------------------------------------
785//
786// return name of the root-file
787//
788const char *MWriteRootFile::GetFileName() const
789{
790 const char *n = fOut->GetName();
791 return n==0 || *n==0 ? "<dummy>" : n;
792}
793
794// --------------------------------------------------------------------------
795//
796// cd into file. See TFile::cd()
797//
798Bool_t MWriteRootFile::cd(const char *path)
799{
800 return fOut->cd(path);
801}
802
803// --------------------------------------------------------------------------
804//
805// Implementation of SavePrimitive. Used to write the call to a constructor
806// to a macro. In the original root implementation it is used to write
807// gui elements to a macro-file.
808//
809void MWriteRootFile::StreamPrimitive(ofstream &out) const
810{
811 out << " MWriteRootFile " << GetUniqueName() << "(\"";
812 out << fOut->GetName() << "\", \"";
813 out << fOut->GetOption() << "\", \"";
814 out << fOut->GetTitle() << "\", ";
815 out << fOut->GetCompressionLevel();
816
817 if (fName!=gsDefName || fTitle!=gsDefTitle)
818 {
819 out << ", \"" << fName << "\"";
820 if (fTitle!=gsDefTitle)
821 out << ", \"" << fTitle << "\"";
822 }
823 out << ");" << endl;
824
825
826 MRootFileBranch *entry;
827 TIter Next(&fBranches);
828 while ((entry=(MRootFileBranch*)Next()))
829 {
830 out << " " << GetUniqueName() << ".AddContainer(";
831
832 if (entry->GetContainer())
833 {
834 entry->GetContainer()->SavePrimitive(out);
835 out << "&" << entry->GetContainer()->GetUniqueName();
836 }
837 else
838 out << "\"" << entry->GetContName() << "\"";
839
840 out << ", \"" << entry->GetName() << "\"";
841 if (!entry->MustHave())
842 out << ", kFALSE";
843
844 out <<");" << endl;
845 }
846}
Note: See TracBrowser for help on using the repository browser.