source: trunk/Mars/mbase/MStatusArray.cc@ 20067

Last change on this file since 20067 was 19760, checked in by tbretz, 5 years ago
They are added to the ListOfCleanups. This is implemented as a THashTable. According to the root documentation, it is required to rehash the table whenever the name of an object in the table changes. This is now guranteed if name change happens via SetName
File size: 20.7 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 03/2004 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2009
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MStatusArray
28//
29// Helper class for MStatusDisplay
30//
31// If you want to read a MStatusArray (normally with name MStatusDisplay)
32// it is recommended to do it like this:
33// TFile f("myfile.root", "read");
34// MStatusArray arr;
35// arr.Read();
36//
37// If you want to use TFile::Get or TFile::GetObject you should switch off
38// addding Histograms automatically to the current directory first:
39// TFile f("myfile.root", "read");
40// TH1::AddDirectory(kFALSE);
41// f.Get("MStatusDisplay");
42//
43//////////////////////////////////////////////////////////////////////////////
44#include "MStatusArray.h"
45
46#include <TH1.h> // TH1::AddDirectoryStatus();
47#include <TFile.h> // gFile
48#include <TClass.h>
49#include <TCanvas.h>
50
51#include <TGraph.h> // For the TGraph workaround
52#include <TPaveText.h> // For the TPaveText workaround
53
54#include <THashList.h>
55
56#include "MLog.h"
57#include "MLogManip.h"
58
59#include "MParContainer.h" // MParContainer::GetClass
60#include "MStatusDisplay.h"
61
62//#define DEBUG
63
64ClassImp(MStatusArray);
65
66using namespace std;
67
68// --------------------------------------------------------------------------
69//
70// Initialize the MStatusArray from an MStatusDisplay. Note, the contents
71// still owned by MStatusDisplay and will vanish if the display changes
72// or is deleted without further notice.
73//
74MStatusArray::MStatusArray(const MStatusDisplay &d) : TObjArray()
75{
76 d.FillArray(*this);
77}
78
79#ifdef DEBUG
80static void *ptr = 0;
81#endif
82
83void MStatusArray::SetName(const char *name)
84{
85 TObjArray::SetName(name);
86
87 // From the documentation of THashList::THashList
88 //
89 // WARNING !!!
90 // If the name of an object in the HashList is modified, The hashlist
91 // must be Rehashed
92 //
93 auto table = dynamic_cast<THashList*>(gROOT->GetListOfCleanups());
94 if (table)
95 table->Rehash(THashList::kInitHashTableCapacity);
96}
97
98// --------------------------------------------------------------------------
99//
100// Remove objects matching the id (the first character of their class
101// name) recuresively
102//
103Bool_t MStatusArray::RecursiveDelete(TVirtualPad *p, const char id) const
104{
105 if (!p)
106 return kTRUE;
107
108 // It seems that it is necessary to keep the ListOfPrimitve consistent
109 // while deleting - reason unknown.
110 TIter Next(p->GetListOfPrimitives());
111 TObject *o=0;
112 while ((o=Next()))
113 {
114 if (!dynamic_cast<TVirtualPad*>(o) && (o->ClassName()[0]==id || id==0))
115 {
116 while (p->GetListOfPrimitives()->Remove(o));
117
118 // This is necessary because for unknown reasons TPaveText
119 // as a member of a class (e.g. MHRanForestGini) doesn't
120 // get removed from the pad if deleted, and therefore
121 // gets deleted twice (FIXME: To be investigated)
122 //if (dynamic_cast<TPaveText*>(o) && !o->TestBit(kCanDelete))
123 // continue;
124
125#ifdef DEBUG
126 cout << "Start Delete " << o << " " << o->GetName() << " " << o->ClassName() << endl;
127#endif
128 delete o;
129
130#ifdef DEBUG
131 cout << "Done Delete " << o<< endl;
132#endif
133 return kFALSE;
134 }
135
136 while (!RecursiveDelete(dynamic_cast<TVirtualPad*>(o), id));
137 }
138
139 return kTRUE;
140}
141
142// --------------------------------------------------------------------------
143//
144// Make sure to set the kMustCleanup for all object in our tree
145// which will later be deleted when the array is destructed.
146//
147void MStatusArray::SetCleanup(TObject *obj) const
148{
149 if (!obj)
150 return;
151
152 TVirtualPad *pad = dynamic_cast<TVirtualPad*>(obj);
153
154 // Do not set the bit for pads because it would end in
155 // endless recursions
156 if (pad && !dynamic_cast<TCanvas*>(obj))
157 obj->ResetBit(kMustCleanup);
158 else
159 {
160 // If this is no pad or TCanvas set the kMustCleanup bit
161 // to make sure that the object is not by chance deleted twice.
162 obj->SetBit(kMustCleanup);
163
164 // Unfortunately, this can still happen to the fHistogram and
165 // fFunctions in a TGraph (because for some reason if a TGraph
166 // is stored twice in a file they might later share the same
167 // objects)
168 TGraph *g = dynamic_cast<TGraph*>(obj);
169 if (g)
170 {
171#ifdef DEBUG
172 cout << "Found TGraph " << g->GetName() << " " << g << " while deleting " << ptr << endl;
173#endif
174 if (g->GetHistogram())
175 g->GetHistogram()->SetBit(kMustCleanup);
176 if (g->GetListOfFunctions())
177 g->GetListOfFunctions()->SetBit(kMustCleanup);
178 }
179 }
180
181 if (!pad)
182 return;
183
184 // If object was a pad go one layer deeper
185 TIter Next(pad->GetListOfPrimitives());
186 TObject *o=0;
187 while ((o=Next()))
188 SetCleanup(o);
189}
190
191// This is a stupid C++ trick which allos us to access the
192// protected data-mebers of TGraph
193class MyGraph : public TGraph
194{
195public:
196 void ResetListOfFunctions()
197 {
198 fFunctions = 0;
199 }
200};
201
202void MStatusArray::RecursiveRemove(TCollection *col, TObject *obj)
203{
204 // First remove the object from the collection as often as possible
205 // (The collection is always a ListOfPrimitives(). This code is
206 // based on TCollection::RecursiveRemove and TPad::RecursiveRemove)
207 while (col->Remove(obj));
208
209 TObject *object=0;
210
211 // Loop over all objects in the collection
212 TIter Next(col);
213 while ((object=Next()))
214 {
215 // Check if this object is a pad. If it is a pad go one
216 // layer deeper.
217 TPad *pad = dynamic_cast<TPad*>(object);
218 if (pad)
219 {
220#ifdef DEBUG
221 cout << "Start RecursiveRemove in " << pad->GetName() << " " << pad << endl;
222#endif
223 RecursiveRemove(pad->GetListOfPrimitives(), obj);
224#ifdef DEBUG
225 cout << "Done RecursiveRemove in " << pad->GetName() << " " << pad << endl;
226#endif
227 continue;
228 }
229
230 // If it is not a TPad check if it is a TGraph
231 TGraph *g = dynamic_cast<TGraph*>(object);
232 if (g/* && g!=ptr*/)
233 {
234#ifdef DEBUG
235 gLog << all << "MStatusArray::RecursiveRemove " << obj << " from TGraph " << g << endl;
236#endif
237
238 // If the object to be removed is the histogram from the
239 // TGraph the histogram got already deleted so we have to
240 // reset the pointer
241 if (g->GetHistogram()==obj)
242 {
243#ifdef DEBUG
244 gLog << all << " SetHist(0)" << endl;
245#endif
246 g->SetHistogram(0);
247 }
248
249 // If the object to be removed is the ListOfFunction from the
250 // TGraph the ListOfFunction got already deleted so we have to
251 // reset the pointer. This is done by a stupid C++ trick
252 // (a derived class which can access the protected data mebers)
253 if (g->GetListOfFunctions()==obj)
254 {
255#ifdef DEBUG
256 gLog << all << " SetFunc(0)" << endl;
257#endif
258 static_cast<MyGraph*>(g)->ResetListOfFunctions();
259 }
260 }
261
262#ifdef DEBUG
263 cout << " Call RecursiveRemove for " << object->GetName() << " " << object << endl;
264#endif
265
266 // Now we go on like TCollction would do calling the RecursiveRemove
267 // of the objects (which are no pads)
268 if (object->TestBit(TObject::kNotDeleted))
269 object->RecursiveRemove(obj);
270 }
271}
272
273// --------------------------------------------------------------------------
274//
275// Use our own implementation of RecursiveRemove.
276//
277void MStatusArray::RecursiveRemove(TObject *obj)
278{
279#ifdef DEBUG
280 cout << "RecursiveRemove " << obj << endl;
281#endif
282 RecursiveRemove(this, obj);
283}
284
285// --------------------------------------------------------------------------
286//
287// The problem is that our objects (e.g. MHRate) can own histograms and
288// graphs (or other objects), which are data members rather than allocated
289// objects (pointers), and which are drawn into a pad. If such a pad gets
290// read from a file we have to set the kCanDelete bits for all objects
291// in all pads to ensure that they are properly deleted (since we
292// don't know who owns them). Unfortunately, this might result in the
293// data member deleted first and then second in the destructor of the
294// owning class. One simple workaround is to delete all our own objects
295// ("M*") first without touching the root objects ("T*") and delete the
296// remaining ones later. This might not work if teh owned objects are
297// MARS classes. Fortunately, none of our classes yets owns another of
298// our class not dynamically allocated. (This case is covered by
299// the RecursiveRemove facility).
300//
301// Furthermore TGraphs which are read from a file as a data member
302// of a class and which are drawn to a pad share the same fFunctions
303// and fHistogram. To avoid double deleting (the second TGraph won't
304// be signaled if the first one is deleted) we implement our own
305// RecursiveRemove facility. (This gets a bit better in root 5.28
306// because TGraph::RecursiveRemove will than also check fHistogram)
307//
308void MStatusArray::Delete(Option_t *)
309{
310 // Add this to the list of cleanups to ensure as many cleaning
311 // operations as possible are propagated
312 gROOT->GetListOfCleanups()->Add(this);
313
314 // First make sure that all kMustCleanup bits are set, esepcially
315 // the ones of TGraph::fHistogram and TGraph::fFunctions
316 TIter Next(this);
317 TObject *o=0;
318 while ((o=Next()))
319 SetCleanup(o);
320
321#if ROOT_VERSION_CODE < ROOT_VERSION(5,26,00)
322 // Now delete the MARS object first because we have full control
323 // of them
324 TIter Next2(this);
325 while ((o=Next2()))
326 while (!RecursiveDelete(dynamic_cast<TVirtualPad*>(o), 'M'));
327
328 // Now delete all root objects
329 TIter Next3(this);
330 while ((o=Next3()))
331 while (!RecursiveDelete(dynamic_cast<TVirtualPad*>(o)));
332#endif
333
334 // And delete all the rest
335 TObjArray::Delete();
336
337 // Remove it from the list again
338 gROOT->GetListOfCleanups()->Remove(this);
339}
340
341// --------------------------------------------------------------------------
342//
343// If o==NULL a new status display is created, otherwise the one with name o
344// is searched in gROOT->GetListOfSpecials().
345// In this display the contents of the MStatusArray is displayed.
346//
347TObject *MStatusArray::DisplayIn(Option_t *o) const
348{
349 MStatusDisplay *d = 0;
350 if (TString(o).IsNull())
351 d = new MStatusDisplay;
352
353 if (!d)
354 d = (MStatusDisplay*)gROOT->GetListOfSpecials()->FindObject(o);
355
356 if (!d)
357 return 0;
358
359 if (d->Display(*this))
360 return d;
361
362 delete d;
363 return 0;
364}
365
366// --------------------------------------------------------------------------
367//
368// Display the contents of the given tab in the display given as argument.
369//
370void MStatusArray::DisplayIn(MStatusDisplay &d, const char *tab) const
371{
372 d.Display(*this, tab);
373}
374
375TObject *MStatusArray::FindObjectInPad(TVirtualPad *pad, const char *object, TClass *cls) const
376{
377 TObject *o = NULL;//pad->FindObject(object);
378// if (o && o->InheritsFrom(cls))
379// return o;
380
381 TIter Next(pad->GetListOfPrimitives());
382 while ((o=Next()))
383 {
384 if (o->GetName()==(TString)object && o->InheritsFrom(cls))
385 return o;
386
387 if (o==pad || !o->InheritsFrom(TVirtualPad::Class()))
388 continue;
389
390 if ((o = FindObjectInPad((TVirtualPad*)o, object, cls)))
391 return o;
392// if (o->InheritsFrom(cls))
393// return o;
394 }
395 return 0;
396}
397
398TCanvas *MStatusArray::FindCanvas(const char *name) const
399{
400 TObject *o = TObjArray::FindObject(name);
401 if (!o)
402 return 0;
403
404 return o->InheritsFrom(TCanvas::Class()) ? (TCanvas*)o : 0;
405}
406
407
408TObject *MStatusArray::FindObjectInCanvas(const char *object, const char *base, const char *canvas) const
409{
410 gLog << err;
411 TClass *cls = MParContainer::GetClass(base, &gLog);
412 if (!cls)
413 return 0;
414
415 TCanvas *c = canvas ? FindCanvas(canvas) : 0;
416 if (canvas)
417 {
418 if (!c)
419 {
420 gLog << warn << "Canvas '" << canvas << "' not found..." << endl;
421 return 0;
422 }
423
424 TObject *o = FindObjectInPad(c, object, cls);
425 if (!o)
426 {
427 gLog << warn << "Object '" << object << "' [" << base << "] not found in canvas '" << canvas << "'..." << endl;
428 return 0;
429 }
430
431 return o; //o->InheritsFrom(cls) ? o : 0;
432 }
433
434 TObject *o=0;
435 TIter Next(this);
436 while ((o=Next()))
437 {
438 if (!o->InheritsFrom(TVirtualPad::Class()))
439 continue;
440
441 if ((o=FindObjectInPad((TVirtualPad*)c, object, cls)))
442 return o;
443 }
444
445 gLog << warn << "Object '" << object << "' [" << base << "] not found in canvas '" << canvas << "'..." << endl;
446 return NULL;
447}
448
449TObject *MStatusArray::FindObjectInCanvas(const char *object, const char *canvas) const
450{
451 return FindObjectInCanvas(object, object, canvas);
452}
453
454TObject *MStatusArray::FindObject(const char *object, const char *base) const
455{
456 return FindObjectInCanvas(object, base, 0);
457}
458
459TObject *MStatusArray::FindObject(const char *object) const
460{
461 return FindObjectInCanvas(object, object, 0);
462}
463
464// --------------------------------------------------------------------------
465//
466// Print recursively all objects in this and sub-pads
467//
468void MStatusArray::PrintObjectsInPad(const TCollection *list, const TString &name, Int_t lvl) const
469{
470 TIter Next(list);
471 TObject *o=0;
472 while ((o=Next()))
473 {
474 const Bool_t print = name.IsNull() || name==(TString)o->GetName();
475 if (print)
476 {
477 if (lvl>0)
478 gLog << setw(lvl) << ' ';
479 gLog << " " << o->ClassName() << ": " << o->GetName() << " <" << Next.GetOption() << "> (" << o << ") " << (int)o->TestBit(kCanDelete) << endl;
480 }
481
482 if (o->InheritsFrom(TVirtualPad::Class()))
483 PrintObjectsInPad(((TVirtualPad*)o)->GetListOfPrimitives(), print?TString():name, lvl+1);
484 }
485}
486
487// --------------------------------------------------------------------------
488//
489// Print recursively all objects in this and sub-pads. If !option.IsNull()
490// only objects in the corresponding pad are printed.
491//
492void MStatusArray::Print(Option_t *option) const
493{
494 gLog << all;
495
496 PrintObjectsInPad(this, TString(option));
497}
498
499/*
500// --------------------------------------------------------------------------
501//
502// Make sure that kCanDelete is properly set for all directly contained
503// objects. Some kCanDelete bits might not be properly set or get lost when
504// the MParContainer is stored.
505//
506void MStatusArray::SetCanDelete(const TCollection *list) const
507{
508 TIter Next(list);
509 TObject *o=0;
510 while ((o=Next()))
511 {
512 if (o->InheritsFrom(TVirtualPad::Class()))
513 SetCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
514 else
515 o->SetBit(kCanDelete|kMustCleanup);
516 }
517}
518*/
519
520// --------------------------------------------------------------------------
521//
522// Set kCanDelete for all objects for which kMyCanDelete is set. This
523// is a STUPID workaruond for an ANNOYING root bug which is that
524// the streamer function of TH1 resets the KCanDelete bit after reading.
525//
526void MStatusArray::SetCanDelete(const TCollection *list) const
527{
528 TIter Next(list);
529 TObject *o=0;
530 while ((o=Next()))
531 {
532 if (o->InheritsFrom(TVirtualPad::Class()))
533 SetCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
534 else
535 {
536 if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
537 {
538 o->SetBit(kCanDelete);
539 o->ResetBit(kMyCanDelete);
540 }
541 }
542 }
543}
544
545void MStatusArray::EnableTH1Workaround(const TCollection *list) const
546{
547 TIter Next(list?list:this);
548 TObject *o=0;
549 while ((o=Next()))
550 {
551 if (o->InheritsFrom(TVirtualPad::Class()))
552 EnableTH1Workaround(((TVirtualPad*)o)->GetListOfPrimitives());
553 else
554 if (o->InheritsFrom("TH1"))
555 o->SetBit(kCanDelete);
556 }
557}
558
559// --------------------------------------------------------------------------
560//
561// Set kMyCanDelete for all objects for which kCanDelete is set. This
562// is a STUPID workaruond for an ANNOYING root bug which is that
563// the streamer function of TH1 resets the KCanDelete bit after reading.
564//
565void MStatusArray::SetMyCanDelete(const TCollection *list) const
566{
567 TIter Next(list);
568 TObject *o=0;
569 while ((o=Next()))
570 {
571 if (o->InheritsFrom(TVirtualPad::Class()))
572 SetMyCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
573 else
574 {
575 if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
576 gLog << warn << "WARNING - MStatusArray::Write - " << o->GetName() << " [" << o->ClassName() << "] has BIT(30) already set!" << endl;
577
578 if (o->TestBit(kCanDelete) && o->InheritsFrom("TH1"))
579 o->SetBit(kMyCanDelete);
580 }
581 }
582}
583
584// --------------------------------------------------------------------------
585//
586// Reset kMyCanDelete for all objects for which kMyCanDelete is set. This
587// is a STUPID workaruond for an ANNOYING root bug which is that
588// the streamer function of TH1 resets the KCanDelete bit after reading.
589//
590void MStatusArray::ResetMyCanDelete(const TCollection *list) const
591{
592 TIter Next(list);
593 TObject *o=0;
594 while ((o=Next()))
595 {
596 if (o->InheritsFrom(TVirtualPad::Class()))
597 ResetMyCanDelete(((TVirtualPad*)o)->GetListOfPrimitives());
598 else
599 {
600 if (o->TestBit(kMyCanDelete) && o->InheritsFrom("TH1"))
601 o->ResetBit(kMyCanDelete);
602 }
603 }
604}
605
606MStatusArray::~MStatusArray()
607{
608 // This is the destructor from TObjArray...
609 // It must be here, because for some reason I don't know it
610 // is otherwise not correctly executed from the Interpreter
611 // (root 5.12/00f)
612 if (IsOwner())
613 Delete();
614
615 TStorage::Dealloc(fCont);
616
617 fCont = 0;
618 fSize = 0;
619}
620
621// --------------------------------------------------------------------------
622//
623// Switch off adding histograms to current directory before reading.
624// Switch back
625//
626Int_t MStatusArray::Read(const char *name)
627{
628 // It seems that the contents are not properly deleted by TObjArray::Read
629 Delete();
630
631 SetOwner();
632
633 const TString keyname = name?name:"MStatusDisplay";
634
635 // Check if key exists (to suppress an error on the console
636 if (!gDirectory->GetListOfKeys())
637 {
638 gLog << inf << keyname << " [MStatusArray] not found (no open file?)" << endl;
639 return 0;
640 }
641
642 // Check if key exists (to suppress an error on the console
643 if (!gDirectory->GetListOfKeys()->FindObject(keyname))
644 {
645 gLog << inf << keyname << " [MStatusArray] not found." << endl;
646 return 0;
647 }
648
649 // Make sure newly read histograms are not added to the current directory
650 const Bool_t store = TH1::AddDirectoryStatus();
651 TH1::AddDirectory(kFALSE);
652 const Int_t rc = TObjArray::Read(keyname);
653 TH1::AddDirectory(store);
654
655 // All objects in the list (TNamed, TCanvas, etc) do not have
656 // the kCanDelete bit set. Make sure that it is set to make
657 // them deleted by the destructor of this list
658 TIter Next(this);
659 TObject *o=0;
660 while ((o=Next()))
661 {
662 if (o->InheritsFrom(TVirtualPad::Class()))
663 {
664 TIter Next2(((TVirtualPad*)o)->GetListOfPrimitives());
665 TObject *o2=0;
666 while ((o2=Next2()))
667 if (o2->InheritsFrom(MParContainer::Class()))
668 o2->SetBit(kCanDelete);
669 }
670 o->SetBit(kCanDelete);
671 }
672
673 // Make sure that all kCanDelete bits are properly set
674 SetCanDelete(this);
675 SetOwner();
676
677 return rc;
678}
679
680// --------------------------------------------------------------------------
681//
682// Switch off adding histograms to current directory before reading.
683// Switch back
684//
685Int_t MStatusArray::Write(const char *name, Int_t option, Int_t bufsize) const
686{
687 SetMyCanDelete(this);
688 const Int_t rc = TObjArray::Write(name, option, bufsize);
689 ResetMyCanDelete(this);
690
691 return rc;
692}
Note: See TracBrowser for help on using the repository browser.