source: trunk/MagicSoft/Mars/mfileio/MReadTree.cc@ 1471

Last change on this file since 1471 was 1471, checked in by tbretz, 22 years ago
*** empty log message ***
File size: 24.9 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 12/2000 <mailto:tbretz@uni-sw.gwdg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2002
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26// //
27// MReadTree //
28// //
29// This tasks opens all branches in a specified tree and creates the //
30// corresponding parameter containers if not already existing in the //
31// parameter list. //
32// //
33// The Process function reads one events from the tree. To go through the //
34// events of one tree make sure that the event number is increased from //
35// outside. It makes also possible to go back by decreasing the number. //
36// //
37// If you don't want to start reading the first event you have to call //
38// MReadTree::SetEventNum after instantiating your MReadTree-object. //
39// //
40// To make reading much faster (up to a factor of 10 to 20) you can //
41// ensure that only the data you are really processing is enabled by //
42// calling MReadTree::UseLeaf. //
43// //
44// If the chain switches from one file to another file all //
45// TObject::Notify() functions are called of TObject objects which were //
46// added to the Notifier list view MReadTree::AddNotify. If MReadTree //
47// is the owner (viw MReadTree::SetOwner) all this objects are deleted //
48// by the destructor of MReadTree //
49// //
50/////////////////////////////////////////////////////////////////////////////
51#include "MReadTree.h"
52
53#include <fstream.h>
54
55#include <TFile.h> // TFile::GetName
56#include <TChain.h>
57#include <TSystem.h> // gSystem->ExpandPath
58#include <TGProgressBar.h>
59#include <TChainElement.h>
60#include <TOrdCollection.h>
61
62#include "MLog.h"
63#include "MLogManip.h"
64
65#include "MTime.h"
66#include "MFilter.h"
67#include "MParList.h"
68#include "MTaskList.h"
69
70ClassImp(MReadTree);
71
72class MChain : public TChain
73{
74private:
75 Bool_t fNotified;
76
77public:
78 MChain() : TChain(), fNotified(kFALSE) {}
79 MChain(const char *name, const char *title="") : TChain(name, title), fNotified(kFALSE) {}
80
81 void ResetTree() { fTree = 0; }
82
83 virtual Bool_t Notify() { fNotified = kTRUE; return kTRUE; }
84 virtual void SetNotify(TObject *obj) { fNotify = obj; fNotified = kFALSE; }
85
86 Int_t LoadTree(Int_t entry)
87 {
88 //
89 // This is the code from TChain::LoadTree but skips the
90 // notification in LoadTree. If LoadTree raises the notification
91 // a flag is set and the notification is done by hand. This
92 // is done to be able to catch the return value from Notify. If
93 // it has not been successfull -15 is returned.
94 // This is to support return values from Notify()/Reinit()
95 //
96 TObject *notify = GetNotify();
97
98 SetNotify(this);
99
100 Int_t rc = TChain::LoadTree(entry);
101
102 if (rc >= 0 && fNotified && notify)
103 if (!notify->Notify())
104 rc = -15;
105
106 SetNotify(notify);
107
108 return rc;
109 }
110};
111
112// --------------------------------------------------------------------------
113//
114// Default constructor. It creates an TChain instance which represents the
115// the Tree you want to read and adds the given file (if you gave one).
116// More files can be added using MReadTree::AddFile.
117// Also an empty veto list is created. This list is used if you want to
118// veto (disable or "don't enable") a branch in the tree, it vetos also
119// the creation of the corresponding object.
120// An empty list of TObjects are also created. This objects are called
121// at any time the TChain starts to read from another file.
122//
123MReadTree::MReadTree(const char *tname, const char *fname,
124 const char *name, const char *title)
125 : fNumEntry(0), fBranchChoosing(kFALSE), fAutoEnable(kTRUE), fProgress(NULL)
126{
127 fName = name ? name : "MReadTree";
128 fTitle = title ? title : "Task to loop over all events in one single tree";
129
130 fVetoList = new TList;
131 fVetoList->SetOwner();
132
133 fNotify = new TList;
134
135 //
136 // open the input stream
137 //
138 fChain = new MChain(tname);
139
140 // root 3.02:
141 // In TChain::Addfile remove the limitation that the file name must contain
142 // the string ".root". ".root" is necessary only in case one wants to specify
143 // a Tree in a subdirectory of a Root file with eg, the format:
144
145 if (fname)
146 fChain->Add(fname);
147}
148
149// --------------------------------------------------------------------------
150//
151// Destructor. It deletes the TChain and veto list object
152//
153MReadTree::~MReadTree()
154{
155 //
156 // Delete all the pointers to pointers to the objects where the
157 // branche data gets stored.
158 //
159 TIter Next(fChain->GetStatus());
160
161 TChainElement *element = NULL;
162 while ((element=(TChainElement*)Next()))
163 delete (MParContainer**)element->GetBaddress();
164
165 //
166 // Delete the chain and the veto list
167 //
168#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,00)
169 if (fChain->GetFile())
170 delete fChain->GetFile();
171#endif
172 delete fChain;
173
174 delete fNotify;
175 delete fVetoList;
176}
177
178// --------------------------------------------------------------------------
179//
180// If the owner flag is set all TObjects which are scheduled via
181// AddNotify are deleted by the destructor of MReadTree
182//
183void MReadTree::SetOwner(Bool_t flag)
184{
185 flag ? fNotify->SetBit(kIsOwner) : fNotify->ResetBit(kIsOwner);
186}
187
188// --------------------------------------------------------------------------
189//
190// This function is called each time MReadTree changes the file to read
191// from. It calls all TObject::Notify() functions which are scheduled
192// via AddNotify.
193//
194Bool_t MReadTree::Notify()
195{
196 *fLog << inf << GetDescriptor() << ": Notify '" << fChain->GetName();
197 *fLog << "' (before processing event #" << GetEventNum()-1 << ")" << endl;
198
199 //fNotify->Notify();
200
201 return kTRUE;
202}
203
204// --------------------------------------------------------------------------
205//
206// If you want to read the given tree over several files you must add
207// the files here before PreProcess is called. Be careful: If the tree
208// doesn't have the same contents (branches) it may confuse your
209// program (trees which are are not existing in later files are not read
210// anymore, tree wich are not existing in the first file are never read)
211//
212// Name may use the wildcarding notation, eg "xxx*.root" means all files
213// starting with xxx in the current file system directory.
214//
215// AddFile returns the number of files added to the chain.
216//
217Int_t MReadTree::AddFile(const char *fname)
218{
219 //
220 // FIXME! A check is missing whether the file already exists or not.
221 //
222 //
223 // returns the number of file which were added
224 //
225 return fChain->Add(fname);
226}
227
228// --------------------------------------------------------------------------
229//
230// This function is called if Branch choosing method should get enabled.
231// Branch choosing means, that only the enabled branches are read into
232// memory. To use an enableing scheme we have to disable all branches first.
233// This is done, if this function is called the first time.
234//
235void MReadTree::EnableBranchChoosing()
236{
237 if (fBranchChoosing)
238 return;
239
240 *fLog << inf << GetDescriptor() << ": Branch choosing method enabled (only enabled branches are read)." << endl;
241 fChain->SetBranchStatus("*", kFALSE);
242 fBranchChoosing = kTRUE;
243}
244
245// --------------------------------------------------------------------------
246//
247// The first time this function is called all branches are disabled.
248// The given branch is enabled. By enabling only the branches you
249// are processing you can speed up your calculation many times (up to
250// a factor of 10 or 20)
251//
252void MReadTree::EnableBranch(const char *name)
253{
254 EnableBranchChoosing();
255
256 TNamed branch(name, "");
257 SetBranchStatus(&branch, kTRUE);
258}
259
260// --------------------------------------------------------------------------
261//
262// Set branch status of branch name
263//
264void MReadTree::SetBranchStatus(const char *name, Bool_t status)
265{
266 fChain->SetBranchStatus(name, status);
267
268 *fLog << inf << (status ? "Enabled" : "Disabled");
269 *fLog << " subbranch '" << name << "'." << endl;
270}
271
272// --------------------------------------------------------------------------
273//
274// Checks whether a branch with the given name exists in the chain
275// and sets the branch status of this branch corresponding to status.
276//
277void MReadTree::SetBranchStatus(TObject *branch, Bool_t status)
278{
279 //
280 // Get branch name
281 //
282 const char *name = branch->GetName();
283
284 //
285 // Check whether this branch really exists
286 //
287 if (fChain->GetBranch(name))
288 SetBranchStatus(name, status);
289
290 //
291 // Remove trailing '.' if one and try to enable the subbranch without
292 // the master branch name. This is to be compatible with older mars
293 // and camera files.
294 //
295 const char *dot = strrchr(name, '.');
296 if (!dot)
297 return;
298
299 if (fChain->GetBranch(dot+1))
300 SetBranchStatus(dot+1, status);
301}
302
303// --------------------------------------------------------------------------
304//
305// Set the status of all branches in the list to status.
306//
307void MReadTree::SetBranchStatus(const TList *list, Bool_t status)
308{
309 //
310 // Loop over all subbranches in this master branch
311 //
312 TIter Next(list);
313
314 TObject *obj;
315 while ((obj=Next()))
316 SetBranchStatus(obj, status);
317}
318
319// --------------------------------------------------------------------------
320//
321// This is the implementation of the Auto Enabling Scheme.
322// For more information see MTask::AddBranchToList.
323// This function loops over all tasks and its filters in the tasklist
324// and enables all branches which are requested by the tasks and its
325// filters.
326//
327// To enable 'unknown' branches which are not in the branchlist of
328// the tasks you can call EnableBranch
329//
330void MReadTree::EnableBranches(MParList *plist)
331{
332 //
333 // check whether branch choosing must be switched on
334 //
335 EnableBranchChoosing();
336
337 //
338 // request the tasklist from the parameter list.
339 // FIXME: Tasklist can have a different name
340 //
341 const MTaskList *tlist = (MTaskList*)plist->FindObject("MTaskList");
342 if (!tlist)
343 {
344 *fLog << warn << GetDescriptor() << "Cannot use auto enabeling scheme for branches. 'MTaskList' not found." << endl;
345 return;
346 }
347
348 //
349 // This loop is not necessary. We could do it like in the commented
350 // loop below. But this loop makes sure, that we don't try to enable
351 // one branch several times. This would not harm, but we would get
352 // an output for each attempt. To have several outputs for one subbranch
353 // may confuse the user, this we don't want.
354 // This loop creates a new list of subbranches and for each branch
355 // which is added we check before whether it already exists or not.
356 //
357 TList list;
358
359 MTask *task;
360 TIter NextTask(tlist->GetList());
361 while ((task=(MTask*)NextTask()))
362 {
363 TObject *obj;
364
365 TIter NextTBranch(task->GetListOfBranches());
366 while ((obj=NextTBranch()))
367 if (!list.FindObject(obj->GetName()))
368 list.Add(obj);
369
370 const MFilter *filter = task->GetFilter();
371
372 if (!filter)
373 continue;
374
375 TIter NextFBranch(filter->GetListOfBranches());
376 while ((obj=NextFBranch()))
377 if (!list.FindObject(obj->GetName()))
378 list.Add(obj);
379 }
380
381 SetBranchStatus(&list, kTRUE);
382/*
383 //
384 // Loop over all tasks iand its filters n the task list.
385 //
386 MTask *task;
387 TIter NextTask(tlist->GetList());
388 while ((task=(MTask*)NextTask()))
389 {
390 SetBranchStatus(task->GetListOfBranches(), kTRUE);
391
392 const MFilter *filter = task->GetFilter();
393 if (!filter)
394 continue;
395
396 SetBranchStatus(filter->GetListOfBranches(), kTRUE);
397
398 }
399*/
400}
401
402// --------------------------------------------------------------------------
403//
404// The disables all subbranches of the given master branch.
405//
406void MReadTree::DisableSubBranches(TBranch *branch)
407{
408 //
409 // This is not necessary, it would work without. But the output
410 // may confuse the user...
411 //
412 if (fAutoEnable || fBranchChoosing)
413 return;
414
415 SetBranchStatus(branch->GetListOfBranches(), kFALSE);
416}
417
418// --------------------------------------------------------------------------
419//
420// The PreProcess loops (till now) over the branches in the given tree.
421// It checks if the corresponding containers (containers with the same
422// name than the branch name) are existing in the Parameter Container List.
423// If not, a container of objec type 'branch-name' is created (everything
424// after the last semicolon in the branch name is stripped). Only
425// branches which don't have a veto (see VetoBranch) are enabled If the
426// object isn't found in the root dictionary (a list of classes known by the
427// root environment) the branch is skipped and an error message is printed
428// out.
429//
430Bool_t MReadTree::PreProcess(MParList *pList)
431{
432 //
433 // Make sure, that all the following calls doesn't result in
434 // Notifications. This may be dangerous, because the notified
435 // tasks are not preprocessed.
436 //
437 fChain->SetNotify(NULL);
438
439 //
440 // get number of events in this tree
441 //
442 fNumEntries = (UInt_t)fChain->GetEntries();
443
444 if (!fNumEntries)
445 {
446 *fLog << warn << dbginf << "No entries found in file(s)" << endl;
447 return kFALSE;
448 }
449
450 //
451 // output logging information
452 //
453 *fLog << inf << fNumEntries << " entries found in file(s)." << endl;
454
455 //
456 // Get all branches of this tree and
457 // create the Iterator to loop over all branches
458 //
459 TIter Next(fChain->GetListOfBranches());
460 TBranch *branch=NULL;
461
462 Int_t num=0;
463 //
464 // loop over all tasks for processing
465 //
466 while ( (branch=(TBranch*)Next()) )
467 {
468 //
469 // Get Name of Branch and Object
470 //
471 const char *bname = branch->GetName();
472
473 TString oname(bname);
474 if (oname.EndsWith("."))
475 oname.Remove(oname.Length()-1);
476
477 //
478 // Check if enabeling the branch is allowed
479 //
480 if (fVetoList->FindObject(oname))
481 {
482 *fLog << inf << "Master branch " << bname << " has veto... skipped." << endl;
483 DisableSubBranches(branch);
484 continue;
485 }
486
487 //
488 // Create a pointer to the pointer to the object in which the
489 // branch data is stored. The pointers are stored in the TChain
490 // object and we get the pointers from there to delete it.
491 //
492 MParContainer **pcont= new MParContainer*;
493
494#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
495 const char *classname = oname;
496#else
497 const char *classname = branch->GetClassName();
498#endif
499
500 //
501 // check if object is existing in the list
502 //
503 *pcont=pList->FindCreateObj(classname, oname);
504
505 if (!*pcont)
506 {
507 //
508 // if class is not existing in the (root) environment
509 // we cannot proceed reading this branch
510 //
511 *fLog << warn << dbginf << "Warning: Class '" << classname;
512 *fLog << "' for " << oname << " not existing in dictionary. Branch skipped." << endl;
513 DisableSubBranches(branch);
514 continue;
515 }
516
517 //
518 // Check whether a Pointer to a pointer already exists, if
519 // we created one already delete it.
520 //
521 TChainElement *element = (TChainElement*)fChain->GetStatus()->FindObject(bname);
522 if (element)
523 delete (MParContainer**)element->GetBaddress();
524
525 //
526 // here pcont is a pointer the to container in which the data from
527 // the actual branch should be stored - enable branch.
528 //
529 fChain->SetBranchAddress(bname, pcont);
530
531 *fLog << inf << "Master branch address " << bname << " [";
532 *fLog << classname << "] setup for reading." << endl;
533
534 //*fLog << "Branch " << bname << " autodel: " << (int)branch->IsAutoDelete() << endl;
535 //branch->SetAutoDelete();
536
537 num++;
538 }
539
540 *fLog << inf << GetDescriptor() << " setup " << num << " master branches addresses." << endl;
541
542 //
543 // If auto enabling scheme isn't disabled, do auto enabling
544 //
545 if (fAutoEnable)
546 EnableBranches(pList);
547
548 //
549 // If a progress bar is given set its range.
550 //
551 if (fProgress)
552 fProgress->SetRange(0, fNumEntries);
553
554 //
555 // Now we can start notifying. Reset tree makes sure, that TChain thinks
556 // that the correct file is not yet initialized and reinitilizes it
557 // as soon as the first event is read. This is necessary to call
558 // the notifiers when the first event is read, but after the
559 // PreProcess-function.
560 //
561 fChain->ResetTree();
562 fChain->SetNotify(this);
563
564 return kTRUE;
565}
566
567// --------------------------------------------------------------------------
568//
569// Set the ready to save flag of all containers which branchaddresses are
570// set for. This is necessary to copy data.
571//
572void MReadTree::SetReadyToSave(Bool_t flag)
573{
574 TIter Next(fChain->GetStatus());
575
576 TChainElement *element = NULL;
577 while ((element=(TChainElement*)Next()))
578 {
579 //
580 // Check whether the branch is enabled
581 //
582 if (!element->GetStatus())
583 continue;
584
585 //
586 // Get the pointer to the pointer of the corresponding container
587 //
588 MParContainer **pcont = (MParContainer**)element->GetBaddress();
589
590 //
591 // Check whether the pointer is not NULL
592 //
593 if (!pcont || !*pcont)
594 continue;
595
596 //
597 // Set the ready to save status of the container.
598 //
599 (*pcont)->SetReadyToSave(flag);
600 }
601
602 //
603 // Set the ready to save status of this task (used?), too
604 //
605 MTask::SetReadyToSave(flag);
606}
607
608// --------------------------------------------------------------------------
609//
610// The Process-function reads one event from the tree (this contains all
611// enabled branches) and increases the position in the file by one event.
612// (Remark: The position can also be set by some member functions
613// If the end of the file is reached the Eventloop is stopped.
614//
615#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
616#include "MRawEvtData.h"
617#endif
618Bool_t MReadTree::Process()
619{
620 //
621 // This is necessary due to a bug in TChain::LoadTree in root.
622 // will be fixed in 3.03
623 //
624#if ROOT_VERSION_CODE < ROOT_VERSION(3,03,01)
625 if (fNumEntry >= fNumEntries)
626 return kFALSE;
627#endif
628
629#if ROOT_VERSION_CODE < ROOT_VERSION(3,02,06)
630 //
631 // This fixes 99.9% of a memory leak using a root version prior
632 // to 3.02/??
633 //
634 TChainElement *element=NULL;
635 TIter Next(fChain->GetStatus());
636 while ((element=(TChainElement*)Next()))
637 {
638 MParContainer **c = (MParContainer**)element->GetBaddress();
639 if (!c) continue;
640 if ((*c)->InheritsFrom(MRawEvtData::Class()))
641 ((MRawEvtData*)(*c))->DeletePixels(kFALSE);
642
643 }
644#endif
645
646 Bool_t rc = fChain->GetEntry(fNumEntry++) != 0;
647
648 if (rc)
649 SetReadyToSave();
650
651 return rc;
652}
653
654// --------------------------------------------------------------------------
655//
656// Get the Event with the current EventNumber fNumEntry
657//
658Bool_t MReadTree::GetEvent()
659{
660 Bool_t rc = fChain->GetEntry(fNumEntry) != 0;
661
662 if (rc)
663 SetReadyToSave();
664
665 return rc;
666}
667
668// --------------------------------------------------------------------------
669//
670// Decrease the number of the event which is read by Process() next
671// by one or more
672//
673Bool_t MReadTree::DecEventNum(UInt_t dec)
674{
675 if (fNumEntry-dec >= fNumEntries)
676 {
677 *fLog << warn << GetDescriptor() << ": DecEventNum, WARNING - Event " << fNumEntry << "-";
678 *fLog << dec << "=" << (Int_t)fNumEntry-dec << " out of Range." << endl;
679 return kFALSE;
680 }
681
682 fNumEntry -= dec;
683 return kTRUE;
684}
685
686// --------------------------------------------------------------------------
687//
688// Increase the number of the event which is read by Process() next
689// by one or more
690//
691Bool_t MReadTree::IncEventNum(UInt_t inc)
692{
693 if (fNumEntry+inc >= fNumEntries)
694 {
695 *fLog << warn << GetDescriptor() << ": IncEventNum, WARNING - Event " << fNumEntry << "+";
696 *fLog << inc << "=" << (Int_t)fNumEntry+inc << " out of Range." << endl;
697 return kFALSE;
698 }
699
700 fNumEntry += inc;
701 return kTRUE;
702}
703
704// --------------------------------------------------------------------------
705//
706// This function makes Process() read event number nr next
707//
708// Remark: You can use this function after instatiating you MReadTree-object
709// to set the event number from which you want to start reading.
710//
711Bool_t MReadTree::SetEventNum(UInt_t nr)
712{
713 if (nr >= fNumEntries)
714 {
715 *fLog << warn << GetDescriptor() << ": SetEventNum, WARNING - " << nr << " out of Range." << endl;
716 return kFALSE;
717 }
718
719 fNumEntry = nr;
720 return kTRUE;
721}
722
723// --------------------------------------------------------------------------
724//
725// For the branch with the given name:
726// 1) no object is automatically created
727// 2) the branch address for this branch is not set
728// (because we lack the object, see 1)
729// 3) The whole branch (exactly: all its subbranches) are disabled
730// this means are not read in memory by TTree:GetEntry
731//
732void MReadTree::VetoBranch(const char *name)
733{
734 fVetoList->Add(new TNamed(name, ""));
735}
736
737// --------------------------------------------------------------------------
738//
739// Return the name of the file we are actually reading from.
740//
741TString MReadTree::GetFileName() const
742{
743 const TFile *file = fChain->GetFile();
744
745 if (!file)
746 return TString("<unknown>");
747
748 TString name(file->GetName());
749 name.Remove(0, name.Last('/')+1);
750 return name;
751}
752
753// --------------------------------------------------------------------------
754//
755// Return the number of the file in the chain, -1 in case of an error
756//
757Int_t MReadTree::GetFileIndex() const
758{
759 return fChain->GetTreeNumber();
760 /*
761 const TString filename = fChain->GetFile()->GetName();
762
763 int i=0;
764 TObject *file = NULL;
765
766 TIter Next(fChain->GetListOfFiles());
767 while ((file=Next()))
768 {
769 if (filename==gSystem->ExpandPathName(file->GetTitle()))
770 return i;
771 i++;
772 }
773 return -1;
774 */
775}
776
777// --------------------------------------------------------------------------
778//
779// This schedules a TObject which Notify(9 function is called in case
780// of MReadTree (TChain) switches from one file in the chain to another
781// one.
782//
783void MReadTree::AddNotify(TObject *obj)
784{
785 fNotify->Add(obj);
786}
787
788void MReadTree::Print(Option_t *o) const
789{
790 *fLog << all << GetDescriptor() << dec << endl;
791 *fLog << setfill('-') << setw(strlen(GetDescriptor())) << "" << endl;
792 *fLog << " Files [Tree]:" << endl;
793
794 int i = 0;
795 TIter Next(fChain->GetListOfFiles());
796 TObject *obj = NULL;
797 while ((obj=Next()))
798 *fLog << " " << i++ << ") " << obj->GetTitle() << " [" << obj->GetName() << "]" << endl;
799
800 *fLog << " Total Number of Entries: " << fNumEntries << endl;
801 *fLog << " Next Entry to read: " << fNumEntry << endl;
802}
803
804void MReadTree::SavePrimitive(ofstream &out, Option_t *o="")
805{
806 TString name = ToLower(fName);
807
808 out << " " << ClassName() << " " << name << "(";
809 out << fChain->GetName() << ", \"" << fName << "\", \"" << fTitle << "\");" << endl;
810
811 TIter Next(fChain->GetListOfFiles());
812 TObject *obj = NULL;
813 while ((obj=Next()))
814 out << " " << name << ".AddFile(\"" << obj->GetTitle() << "\");" << endl;
815
816 if (!fAutoEnable)
817 out << " " << name << ".DisableAutoScheme();" << endl;
818
819 if (fNumEntry!=0)
820 out << " " << name << ".SetEventNum(" << fNumEntry << ");" << endl;
821
822
823}
Note: See TracBrowser for help on using the repository browser.