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

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