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

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