source: trunk/MagicSoft/Mars/mbase/MParContainer.cc@ 5620

Last change on this file since 5620 was 5557, checked in by tbretz, 20 years ago
*** empty log message ***
File size: 22.5 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@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2004
21!
22!
23\* ======================================================================== */
24
25//////////////////////////////////////////////////////////////////////////////
26//
27// MParContainer
28//
29// The MParContainer class is the base class for all MARS parameter
30// containers. At the moment it is almost the same than ROOT's TNamed.
31// A TNamed contains the essential elements (name, title)
32// to identify a derived object in lists like our MParList or MTaskList.
33// The main difference is that the name and title isn't stored and read
34// to and from root files ("//!")
35//
36// MParContainer has several enhancements compared to TNamed:
37// - GetDescriptor(): returns name and class type
38// - GetUniqueName(): returns a unique name (used in StreamPrimitive)
39// - SetLogStream(MLog *lg): Set a logging stream to which loggingis stored
40// - Reset(): Reset content of class in an eventloop
41// - IsReadyToSave(): The contents are ready to be saved to a file
42// - IsSavedAsPrimitive(): A unique name for this instance is already
43// existing
44// - SetVariables(): Can be overloaded if the containers stores
45// coefficients (to be used in fits)
46// - SetDisplay(): Set a display for redirecting graphical output
47// - GetNames(): Get Name/Title from instance and store it in
48// a TObjArray (used to store the names of the
49// conteiners in a file
50// - SetNames(): vice versa
51// - ReadEnv(), WriteEnv(): Function which is used for automatical setup
52// IsEnvDefined() from a TEnv file
53//
54//////////////////////////////////////////////////////////////////////////////
55#include "MParContainer.h"
56
57#include <ctype.h> // isdigit
58#include <fstream> // ofstream
59
60#include <TEnv.h> // Env::Lookup
61#include <TClass.h> // IsA
62#include <TObjArray.h> // TObjArray
63#include <TBaseClass.h> // GetClassPointer
64#include <TMethodCall.h> // TMethodCall, AsciiWrite
65#include <TDataMember.h> // TDataMember, AsciiWrite
66#include <TVirtualPad.h> // gPad
67
68#include "MString.h"
69
70#include "MLog.h"
71#include "MLogManip.h"
72
73TList *gListOfPrimitives; // forard declaration in MParContainer.h
74
75#undef DEBUG
76//#define DEBUG
77
78ClassImp(MParContainer);
79
80using namespace std;
81
82MParContainer::MParContainer(const char *name, const char *title) :
83 fName(name), fTitle(title), fLog(&gLog), fDisplay(NULL), fReadyToSave(kFALSE)
84{
85}
86
87MParContainer::MParContainer(const TString &name, const TString &title) :
88 fName(name), fTitle(title), fLog(&gLog), fDisplay(NULL), fReadyToSave(kFALSE)
89{
90}
91
92// --------------------------------------------------------------------------
93//
94// MParContainer copy ctor
95//
96MParContainer::MParContainer(const MParContainer &named)
97{
98 fName = named.fName;
99 fTitle = named.fTitle;
100
101 fLog = named.fLog;
102
103 fReadyToSave = named.fReadyToSave;
104
105 fDisplay = named.fDisplay;
106}
107
108MParContainer::~MParContainer()
109{
110#ifdef DEBUG
111 *fLog << all << "Deleting " << GetDescriptor() << endl;
112#endif
113}
114
115// --------------------------------------------------------------------------
116//
117// MParContainer assignment operator.
118//
119MParContainer& MParContainer::operator=(const MParContainer& rhs)
120{
121 if (this == &rhs)
122 return *this;
123
124 TObject::operator=(rhs);
125
126 fName = rhs.fName;
127 fTitle = rhs.fTitle;
128
129 fLog = rhs.fLog;
130 fReadyToSave = rhs.fReadyToSave;
131
132 return *this;
133}
134
135// --------------------------------------------------------------------------
136//
137// Make a clone of an object using the Streamer facility.
138// If newname is specified, this will be the name of the new object
139//
140TObject *MParContainer::Clone(const char *newname) const
141{
142
143 MParContainer *named = (MParContainer*)TObject::Clone();
144 if (newname && strlen(newname)) named->SetName(newname);
145 return named;
146}
147
148// --------------------------------------------------------------------------
149//
150// Compare two MParContainer objects. Returns 0 when equal, -1 when this is
151// smaller and +1 when bigger (like strcmp).
152//
153Int_t MParContainer::Compare(const TObject *obj) const
154{
155 if (this == obj) return 0;
156 return fName.CompareTo(obj->GetName());
157}
158
159// --------------------------------------------------------------------------
160//
161// Copy this to obj.
162//
163void MParContainer::Copy(TObject &obj)
164#if ROOT_VERSION_CODE > ROOT_VERSION(3,04,01)
165const
166#endif
167{
168 MParContainer &cont = (MParContainer&)obj;
169
170 TObject::Copy(obj);
171
172 cont.fName = fName;
173 cont.fTitle = fTitle;
174
175 cont.fLog = fLog;
176 cont.fReadyToSave = fReadyToSave;
177}
178
179// --------------------------------------------------------------------------
180//
181// Encode MParContainer into output buffer.
182//
183void MParContainer::FillBuffer(char *&buffer)
184{
185 fName.FillBuffer(buffer);
186 fTitle.FillBuffer(buffer);
187}
188
189// --------------------------------------------------------------------------
190//
191// Returns the name of the object. If the name of the object is not the
192// class name it returns the object name and in []-brackets the class name.
193//
194const char *MParContainer::GetDescriptor() const
195{
196 return GetDescriptor(*this);
197}
198
199// --------------------------------------------------------------------------
200//
201// Returns the name of the object. If the name of the object is not the
202// class name it returns the object name and in []-brackets the class name.
203//
204const char *MParContainer::GetDescriptor(const TObject &o)
205{
206 //
207 // Because it returns a (const char*) we cannot return a casted
208 // local TString. The pointer would - immediatly after return -
209 // point to a random memory segment, because the TString has gone.
210 //
211 MString desc;
212 desc.Print("%s [%s]", o.GetName(), o.ClassName());
213 return (TString)o.GetName()==o.ClassName() ? o.ClassName() : desc.Data();
214}
215
216// --------------------------------------------------------------------------
217//
218// Return a unique name for this container. It is created from
219// the container name and the unique Id. (This is mostly used
220// in the StreamPrimitive member functions)
221//
222const TString MParContainer::GetUniqueName() const
223{
224 TString ret = ToLower(fName);
225
226 if (isdigit(ret[ret.Length()-1]))
227 ret+="_";
228
229 ret+=GetUniqueID();
230
231 return ret;
232}
233
234// --------------------------------------------------------------------------
235//
236// List MParContainer name and title.
237//
238void MParContainer::ls(Option_t *) const
239{
240 TROOT::IndentLevel();
241 *fLog << all << GetDescriptor() << " " << GetTitle() << ": kCanDelete=";
242 *fLog << Int_t(TestBit(kCanDelete)) << endl;
243}
244
245// --------------------------------------------------------------------------
246//
247// Print MParContainer name and title.
248//
249void MParContainer::Print(Option_t *) const
250{
251 *fLog << all << GetDescriptor() << " " << GetTitle() << endl;
252}
253
254// --------------------------------------------------------------------------
255//
256// Change (i.e. set) the name of the MParContainer.
257// WARNING !!
258// If the object is a member of a THashTable, THashList container
259// The HashTable must be Rehashed after SetName
260// For example the list of objects in the current directory is a THashList
261//
262void MParContainer::SetName(const char *name)
263{
264 fName = name;
265 ResetBit(kIsSavedAsPrimitive);
266 if (gPad && TestBit(kMustCleanup)) gPad->Modified();
267}
268
269// --------------------------------------------------------------------------
270//
271// Change (i.e. set) all the MParContainer parameters (name and title).
272// See also WARNING in SetName
273//
274void MParContainer::SetObject(const char *name, const char *title)
275{
276 fName = name;
277 fTitle = title;
278 ResetBit(kIsSavedAsPrimitive);
279 if (gPad && TestBit(kMustCleanup)) gPad->Modified();
280}
281
282// --------------------------------------------------------------------------
283//
284// Change (i.e. set) the title of the MParContainer.
285//
286void MParContainer::SetTitle(const char *title)
287{
288 fTitle = title;
289 ResetBit(kIsSavedAsPrimitive);
290 if (gPad && TestBit(kMustCleanup)) gPad->Modified();
291}
292
293// --------------------------------------------------------------------------
294//
295// Return size of the MParContainer part of the TObject.
296//
297Int_t MParContainer::Sizeof() const
298{
299 Int_t nbytes = fName.Sizeof() + fTitle.Sizeof();
300 return nbytes;
301}
302
303// --------------------------------------------------------------------------
304//
305// If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a
306// container, overload this function.
307//
308void MParContainer::AsciiRead(istream &fin)
309{
310 *fLog << warn << "To use the the ascii input of " << GetName();
311 *fLog << " you have to overload " << ClassName() << "::AsciiRead." << endl;
312}
313
314// --------------------------------------------------------------------------
315//
316// Write out a data member given as a TDataMember object to an output stream.
317//
318Bool_t MParContainer::WriteDataMember(ostream &out, const TDataMember *member, Double_t scale) const
319{
320 if (!member)
321 return kFALSE;
322
323 if (!member->IsPersistent() || member->Property()&kIsStatic)
324 return kFALSE;
325
326 /*const*/ TMethodCall *call = ((TDataMember*)member)->GetterMethod(); //FIXME: Root
327 if (!call)
328 {
329 *fLog << warn << "Sorry, no getter method found for " << member->GetName() << endl;
330 return kFALSE;
331 }
332
333 // For debugging: out << member->GetName() << ":";
334
335 switch (call->ReturnType())
336 {
337 case TMethodCall::kLong:
338 Long_t l;
339 call->Execute((void*)this, l); // FIXME: const, root
340 out << l << " ";
341 return kTRUE;
342
343 case TMethodCall::kDouble:
344 Double_t d;
345 call->Execute((void*)this, d); // FIXME: const, root
346 out << (scale*d) << " ";
347 return kTRUE;
348
349 default:
350 //case TMethodCall::kString:
351 //case TMethodCall::kOther:
352 /* someone may want to enhance this? */
353 return kFALSE;
354 }
355}
356
357// --------------------------------------------------------------------------
358//
359// Write out a data member given by name to an output stream.
360//
361Bool_t MParContainer::WriteDataMember(ostream &out, const char *member, Double_t scale) const
362{
363 /*const*/ TClass *cls = IsA()->GetBaseDataMember(member);
364 if (!cls)
365 return kFALSE;
366
367 return WriteDataMember(out, cls->GetDataMember(member), scale);
368}
369
370// --------------------------------------------------------------------------
371//
372// Write out a data member from a given TList of TDataMembers.
373// returns kTRUE when at least one member was successfully written
374//
375Bool_t MParContainer::WriteDataMember(ostream &out, const TList *list) const
376{
377 Bool_t rc = kFALSE;
378
379 TDataMember *data = NULL;
380
381 TIter Next(list);
382 while ((data=(TDataMember*)Next()))
383 rc |= WriteDataMember(out, data);
384
385 return rc;
386}
387
388// --------------------------------------------------------------------------
389//
390// If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a
391// container, you may overload this function. If you don't overload it
392// the data member of a class are written to the file in the order of
393// appearance in the class header (be more specfic: root dictionary)
394// Only data members which are of integer (Bool_t, Int_t, ...) or
395// floating point (Float_t, Double_t, ...) type are written.
396// returns kTRUE when at least one member was successfully written
397//
398Bool_t MParContainer::AsciiWrite(ostream &out) const
399{
400 // *fLog << warn << "To use the the ascii output of " << GetName();
401 // *fLog << " you have to overload " << ClassName() << "::AsciiWrite." << endl;
402
403 Bool_t rc = WriteDataMember(out, IsA()->GetListOfDataMembers());
404
405 TIter NextBaseClass(IsA()->GetListOfBases());
406 TBaseClass *base;
407 while ((base = (TBaseClass*) NextBaseClass()))
408 {
409 /*const*/ TClass *cls = base->GetClassPointer();
410
411 if (!cls)
412 continue;
413
414 if (cls->GetClassVersion())
415 rc |= WriteDataMember(out, cls->GetListOfDataMembers());
416 }
417
418 return rc;
419}
420
421TMethodCall *MParContainer::GetterMethod(const char *name) const
422{
423 const TString n(name);
424 const Int_t pos1 = n.First('.');
425
426 const TString part1 = pos1<0 ? n : n(0, pos1);
427 const TString part2 = pos1<0 ? TString("") : n(pos1+1, n.Length());
428
429 TClass *cls = IsA()->GetBaseDataMember(part1);
430 if (cls)
431 {
432 TDataMember *member = cls->GetDataMember(part1);
433 if (!member)
434 {
435 *fLog << err << "Datamember '" << part1 << "' not in " << GetDescriptor() << endl;
436 return NULL;
437 }
438
439 // This handles returning references of contained objects, eg
440 // class X { TObject fO; TObject &GetO() { return fO; } };
441 if (!member->IsBasic() && !part2.IsNull())
442 {
443 cls = gROOT->GetClass(member->GetTypeName());
444 if (!cls)
445 {
446 *fLog << err << "Datamember " << part1 << " [";
447 *fLog << member->GetTypeName() << "] not in dictionary." << endl;
448 return NULL;
449 }
450 if (!cls->InheritsFrom(MParContainer::Class()))
451 {
452 *fLog << err << "Datamember " << part1 << " [";
453 *fLog << member->GetTypeName() << "] does not inherit from ";
454 *fLog << "MParContainer." << endl;
455 return NULL;
456 }
457
458 const MParContainer *sub = (MParContainer*)((ULong_t)this+member->GetOffset());
459 return sub->GetterMethod(part2);
460 }
461
462 if (member->IsaPointer())
463 {
464 *fLog << warn << "Data-member " << part1 << " is a pointer..." << endl;
465 *fLog << dbginf << "Not yet implemented!" << endl;
466 //TClass *test = gROOT->GetClass(member->GetTypeName());
467 return 0;
468 }
469
470 TMethodCall *call = member->GetterMethod();
471 if (call)
472 return call;
473 }
474
475 *fLog << warn << "No standard access for '" << part1 << "' in ";
476 *fLog << GetDescriptor() << " or one of its base classes." << endl;
477
478 TMethodCall *call = NULL;
479
480 *fLog << warn << "Trying to find MethodCall '" << ClassName();
481 *fLog << "::Get" << part1 << "' instead <LEAKS MEMORY>" << endl;
482 call = new TMethodCall(IsA(), (TString)"Get"+part1, "");
483 if (call->GetMethod())
484 return call;
485
486 delete call;
487
488 *fLog << warn << "Trying to find MethodCall '" << ClassName();
489 *fLog << "::" << part1 << "' instead <LEAKS MEMORY>" << endl;
490 call = new TMethodCall(IsA(), part1, "");
491 if (call->GetMethod())
492 return call;
493
494 delete call;
495
496 *fLog << err << "Sorry, no getter method found for " << part1 << endl;
497 return NULL;
498}
499
500// --------------------------------------------------------------------------
501//
502// Implementation of SavePrimitive. Used to write the call to a constructor
503// to a macro. In the original root implementation it is used to write
504// gui elements to a macro-file.
505//
506void MParContainer::SavePrimitive(ofstream &out, Option_t *o)
507{
508 static UInt_t uid = 0;
509
510 if (IsSavedAsPrimitive())
511 return;
512
513 SetUniqueID(uid++);
514 SetBit(kIsSavedAsPrimitive);
515
516 if (gListOfPrimitives && !gListOfPrimitives->FindObject(this))
517 gListOfPrimitives->Add(this);
518
519 StreamPrimitive(out);
520}
521
522// --------------------------------------------------------------------------
523//
524// Creates the string written by SavePrimitive and returns it.
525//
526void MParContainer::StreamPrimitive(ofstream &out) const
527{
528 out << " // Using MParContainer::StreamPrimitive" << endl;
529 out << " " << ClassName() << " " << GetUniqueName() << "(\"";
530 out << fName << "\", \"" << fTitle << "\");" << endl;
531}
532
533void MParContainer::GetNames(TObjArray &arr) const
534{
535 arr.AddLast(new TNamed(fName, fTitle));
536}
537
538void MParContainer::SetNames(TObjArray &arr)
539{
540 TNamed *name = (TNamed*)arr.First();
541
542 fName = name->GetName();
543 fTitle = name->GetTitle();
544
545 delete arr.Remove(name);
546 arr.Compress();
547}
548
549// --------------------------------------------------------------------------
550//
551// Creates a new instance of this class. The idea is to create a clone of
552// this class in its initial state.
553//
554MParContainer *MParContainer::New() const
555{
556 return (MParContainer*)IsA()->New();
557}
558
559// --------------------------------------------------------------------------
560//
561// Read the contents/setup of a parameter container/task from a TEnv
562// instance (steering card/setup file).
563// The key to search for in the file should be of the syntax:
564// prefix.vname
565// While vname is a name which is specific for a single setup date
566// (variable) of this container and prefix is something like:
567// evtloopname.name
568// While name is the name of the containers/tasks in the parlist/tasklist
569//
570// eg. Job4.MImgCleanStd.CleaningLevel1: 3.0
571// Job4.MImgCleanStd.CleaningLevel2: 2.5
572//
573// If this cannot be found the next step is to search for
574// MImgCleanStd.CleaningLevel1: 3.0
575// And if this doesn't exist, too, we should search for:
576// CleaningLevel1: 3.0
577//
578// Warning: The programmer is responsible for the names to be unique in
579// all Mars classes.
580//
581// Return values:
582// kTRUE: Environment string found
583// kFALSE: Environment string not found
584// kERROR: Error occured, eg. environment invalid
585//
586// Overload this if you don't want to control the level of setup-string. In
587// this case ReadEnv gets called with the different possibilities, see TestEnv.
588//
589Int_t MParContainer::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
590{
591 if (!IsEnvDefined(env, prefix, "", print))
592 return kFALSE;
593
594 *fLog << warn << "WARNING - Resource " << prefix+fName << " found, but no " << ClassName() << "::ReadEnv." << endl;
595 return kTRUE;
596}
597
598// --------------------------------------------------------------------------
599//
600// Write the contents/setup of a parameter container/task to a TEnv
601// instance (steering card/setup file).
602// The key to search for in the file should be of the syntax:
603// prefix.vname
604// While vname is a name which is specific for a single setup date
605// (variable) of this container and prefix is something like:
606// evtloopname.name
607// While name is the name of the containers/tasks in the parlist/tasklist
608//
609// eg. Job4.MImgCleanStd.CleaningLevel1: 3.0
610// Job4.MImgCleanStd.CleaningLevel2: 2.5
611//
612// If this cannot be found the next step is to search for
613// MImgCleanStd.CleaningLevel1: 3.0
614// And if this doesn't exist, too, we should search for:
615// CleaningLevel1: 3.0
616//
617// Warning: The programmer is responsible for the names to be unique in
618// all Mars classes.
619//
620Bool_t MParContainer::WriteEnv(TEnv &env, TString prefix, Bool_t print) const
621{
622 if (!IsEnvDefined(env, prefix, "", print))
623 return kFALSE;
624
625 *fLog << warn << "WARNING - Resource " << prefix+fName << " found, but " << ClassName() << "::WriteEnv not overloaded." << endl;
626 return kTRUE;
627}
628
629// --------------------------------------------------------------------------
630//
631// Take the prefix and call ReadEnv for:
632// prefix.containername
633// prefix.classname
634// containername
635// classname
636//
637// The existance of an environment variable is done in this order. If
638// ReadEnv return kTRUE the existance of the container setup is assumed and
639// the other tests are skipped. If kFALSE is assumed the sequence is
640// continued. In case of kERROR failing of the setup from a file is assumed.
641//
642// Overload this if you want to control the handling of level of setup-string
643// mentioned above. In this case ReadEnv gets never called if you don't call
644// it explicitly.
645//
646Int_t MParContainer::TestEnv(const TEnv &env, TString prefix, Bool_t print)
647{
648 if (print)
649 *fLog << all << "Testing Prefix+ContName: " << prefix+GetName() << endl;
650 Int_t rc = ReadEnv(env, prefix+GetName(), print);
651 if (rc==kERROR || rc==kTRUE)
652 return rc;
653
654 // Check For: Job4.MClassName.Varname
655 if (print)
656 *fLog << all << "Testing Prefix+ClasName: " << prefix+ClassName() << endl;
657 rc = ReadEnv(env, prefix+ClassName(), print);
658 if (rc==kERROR || rc==kTRUE)
659 return rc;
660
661 // Check For: ContainerName.Varname
662 if (print)
663 *fLog << all << "Testing ContName: " << GetName() << endl;
664 rc = ReadEnv(env, GetName(), print);
665 if (rc==kERROR || rc==kTRUE)
666 return rc;
667
668 // Check For: MClassName.Varname
669 if (print)
670 *fLog << all << "Testing ClassName: " << ClassName() << endl;
671 rc = ReadEnv(env, ClassName(), print);
672 if (rc==kERROR || rc==kTRUE)
673 return rc;
674
675 // Not found
676 return kFALSE;
677}
678
679Bool_t MParContainer::IsEnvDefined(const TEnv &env, TString prefix, TString postfix, Bool_t print) const
680{
681 if (!postfix.IsNull())
682 postfix.Insert(0, ".");
683
684 return IsEnvDefined(env, prefix+postfix, print);
685}
686
687Bool_t MParContainer::IsEnvDefined(const TEnv &env, TString name, Bool_t print) const
688{
689 if (print)
690 *fLog << all << GetDescriptor() << " - " << name << "... " << flush;
691
692 if (!((TEnv&)env).Defined(name))
693 {
694 if (print)
695 *fLog << "not found." << endl;
696 return kFALSE;
697 }
698
699 if (print)
700 *fLog << "found." << endl;
701
702 return kTRUE;
703}
704
705Int_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, Int_t dflt) const
706{
707 return GetEnvValue(env, prefix+"."+postfix, dflt);
708}
709
710Double_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, Double_t dflt) const
711{
712 return GetEnvValue(env, prefix+"."+postfix, dflt);
713}
714
715const char *MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, const char *dflt) const
716{
717 return GetEnvValue(env, prefix+"."+postfix, dflt);
718}
719
720Int_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, Int_t dflt) const
721{
722 return ((TEnv&)env).GetValue(prefix, dflt);
723}
724
725Double_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, Double_t dflt) const
726{
727 return ((TEnv&)env).GetValue(prefix, dflt);
728}
729
730const char *MParContainer::GetEnvValue(const TEnv &env, TString prefix, const char *dflt) const
731{
732 return ((TEnv&)env).GetValue(prefix, dflt);
733}
Note: See TracBrowser for help on using the repository browser.