/* ======================================================================== *\ ! ! * ! * This file is part of MARS, the MAGIC Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appear in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz 12/2000 ! ! Copyright: MAGIC Software Development, 2000-2004 ! ! \* ======================================================================== */ ////////////////////////////////////////////////////////////////////////////// // // MParContainer // // The MParContainer class is the base class for all MARS parameter // containers. At the moment it is almost the same than ROOT's TNamed. // A TNamed contains the essential elements (name, title) // to identify a derived object in lists like our MParList or MTaskList. // The main difference is that the name and title isn't stored and read // to and from root files ("//!") // // MParContainer has several enhancements compared to TNamed: // - GetDescriptor(): returns name and class type // - GetUniqueName(): returns a unique name (used in StreamPrimitive) // - SetLogStream(MLog *lg): Set a logging stream to which loggingis stored // - Reset(): Reset content of class in an eventloop // - IsReadyToSave(): The contents are ready to be saved to a file // - IsSavedAsPrimitive(): A unique name for this instance is already // existing // - SetVariables(): Can be overloaded if the containers stores // coefficients (to be used in fits) // - SetDisplay(): Set a display for redirecting graphical output // - GetNames(): Get Name/Title from instance and store it in // a TObjArray (used to store the names of the // conteiners in a file // - SetNames(): vice versa // - ReadEnv(), WriteEnv(): Function which is used for automatical setup // IsEnvDefined() from a TEnv file // ////////////////////////////////////////////////////////////////////////////// #include "MParContainer.h" #include // isdigit #include // ofstream, AsciiWrite #include // Env::Lookup #include // IsA #include // TObjArray #include // GetClassPointer #include // TMethodCall, AsciiWrite #include // TDataMember, AsciiWrite #include // gPad #include "MString.h" #include "MLog.h" #include "MLogManip.h" TList *gListOfPrimitives; // forard declaration in MParContainer.h #undef DEBUG //#define DEBUG ClassImp(MParContainer); using namespace std; MParContainer::MParContainer(const char *name, const char *title) : fName(name), fTitle(title), fLog(&gLog), fDisplay(NULL), fReadyToSave(kFALSE) { } MParContainer::MParContainer(const TString &name, const TString &title) : fName(name), fTitle(title), fLog(&gLog), fDisplay(NULL), fReadyToSave(kFALSE) { } // -------------------------------------------------------------------------- // // MParContainer copy ctor // MParContainer::MParContainer(const MParContainer &named) { fName = named.fName; fTitle = named.fTitle; fLog = named.fLog; fReadyToSave = named.fReadyToSave; fDisplay = named.fDisplay; } MParContainer::~MParContainer() { #ifdef DEBUG *fLog << all << "Deleting " << GetDescriptor() << endl; #endif } // -------------------------------------------------------------------------- // // MParContainer assignment operator. // MParContainer& MParContainer::operator=(const MParContainer& rhs) { if (this == &rhs) return *this; TObject::operator=(rhs); fName = rhs.fName; fTitle = rhs.fTitle; fLog = rhs.fLog; fReadyToSave = rhs.fReadyToSave; return *this; } // -------------------------------------------------------------------------- // // Make a clone of an object using the Streamer facility. // If newname is specified, this will be the name of the new object // TObject *MParContainer::Clone(const char *newname) const { MParContainer *named = (MParContainer*)TObject::Clone(); if (newname && strlen(newname)) named->SetName(newname); return named; } // -------------------------------------------------------------------------- // // Compare two MParContainer objects. Returns 0 when equal, -1 when this is // smaller and +1 when bigger (like strcmp). // Int_t MParContainer::Compare(const TObject *obj) const { if (this == obj) return 0; return fName.CompareTo(obj->GetName()); } // -------------------------------------------------------------------------- // // Copy this to obj. // void MParContainer::Copy(TObject &obj) #if ROOT_VERSION_CODE > ROOT_VERSION(3,04,01) const #endif { MParContainer &cont = (MParContainer&)obj; TObject::Copy(obj); cont.fName = fName; cont.fTitle = fTitle; cont.fLog = fLog; cont.fReadyToSave = fReadyToSave; } // -------------------------------------------------------------------------- // // Encode MParContainer into output buffer. // void MParContainer::FillBuffer(char *&buffer) { fName.FillBuffer(buffer); fTitle.FillBuffer(buffer); } // -------------------------------------------------------------------------- // // Returns the name of the object. If the name of the object is not the // class name it returns the object name and in []-brackets the class name. // const char *MParContainer::GetDescriptor() const { // // Because it returns a (const char*) we cannot return a casted // local TString. The pointer would - immediatly after return - // point to a random memory segment, because the TString has gone. // MString desc; desc.Print("%s [%s]", fName.Data(), ClassName()); return fName==ClassName() ? ClassName() : desc.Data(); } // -------------------------------------------------------------------------- // // Return a unique name for this container. It is created from // the container name and the unique Id. (This is mostly used // in the StreamPrimitive member functions) // const TString MParContainer::GetUniqueName() const { TString ret = ToLower(fName); if (isdigit(ret[ret.Length()-1])) ret+="_"; ret+=GetUniqueID(); return ret; } // -------------------------------------------------------------------------- // // List MParContainer name and title. // void MParContainer::ls(Option_t *) const { TROOT::IndentLevel(); *fLog << all << GetDescriptor() << " " << GetTitle() << ": kCanDelete="; *fLog << Int_t(TestBit(kCanDelete)) << endl; } // -------------------------------------------------------------------------- // // Print MParContainer name and title. // void MParContainer::Print(Option_t *) const { *fLog << all << GetDescriptor() << " " << GetTitle() << endl; } // -------------------------------------------------------------------------- // // Change (i.e. set) the name of the MParContainer. // WARNING !! // If the object is a member of a THashTable, THashList container // The HashTable must be Rehashed after SetName // For example the list of objects in the current directory is a THashList // void MParContainer::SetName(const char *name) { fName = name; ResetBit(kIsSavedAsPrimitive); if (gPad && TestBit(kMustCleanup)) gPad->Modified(); } // -------------------------------------------------------------------------- // // Change (i.e. set) all the MParContainer parameters (name and title). // See also WARNING in SetName // void MParContainer::SetObject(const char *name, const char *title) { fName = name; fTitle = title; ResetBit(kIsSavedAsPrimitive); if (gPad && TestBit(kMustCleanup)) gPad->Modified(); } // -------------------------------------------------------------------------- // // Change (i.e. set) the title of the MParContainer. // void MParContainer::SetTitle(const char *title) { fTitle = title; ResetBit(kIsSavedAsPrimitive); if (gPad && TestBit(kMustCleanup)) gPad->Modified(); } // -------------------------------------------------------------------------- // // Return size of the MParContainer part of the TObject. // Int_t MParContainer::Sizeof() const { Int_t nbytes = fName.Sizeof() + fTitle.Sizeof(); return nbytes; } // -------------------------------------------------------------------------- // // If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a // container, overload this function. // void MParContainer::AsciiRead(ifstream &fin) { *fLog << warn << "To use the the ascii input of " << GetName(); *fLog << " you have to overload " << ClassName() << "::AsciiRead." << endl; } // -------------------------------------------------------------------------- // // Write out a data member given as a TDataMember object to an output stream. // Bool_t MParContainer::WriteDataMember(ostream &out, const TDataMember *member, Double_t scale) const { if (!member) return kFALSE; if (!member->IsPersistent() || member->Property()&kIsStatic) return kFALSE; /*const*/ TMethodCall *call = ((TDataMember*)member)->GetterMethod(); //FIXME: Root if (!call) { *fLog << warn << "Sorry, no getter method found for " << member->GetName() << endl; return kFALSE; } // For debugging: out << member->GetName() << ":"; switch (call->ReturnType()) { case TMethodCall::kLong: Long_t l; call->Execute((void*)this, l); // FIXME: const, root out << l << " "; return kTRUE; case TMethodCall::kDouble: Double_t d; call->Execute((void*)this, d); // FIXME: const, root out << (scale*d) << " "; return kTRUE; default: //case TMethodCall::kString: //case TMethodCall::kOther: /* someone may want to enhance this? */ return kFALSE; } } // -------------------------------------------------------------------------- // // Write out a data member given by name to an output stream. // Bool_t MParContainer::WriteDataMember(ostream &out, const char *member, Double_t scale) const { /*const*/ TClass *cls = IsA()->GetBaseDataMember(member); if (!cls) return kFALSE; return WriteDataMember(out, cls->GetDataMember(member), scale); } // -------------------------------------------------------------------------- // // Write out a data member from a given TList of TDataMembers. // returns kTRUE when at least one member was successfully written // Bool_t MParContainer::WriteDataMember(ostream &out, const TList *list) const { Bool_t rc = kFALSE; TDataMember *data = NULL; TIter Next(list); while ((data=(TDataMember*)Next())) rc |= WriteDataMember(out, data); return rc; } // -------------------------------------------------------------------------- // // If you want to use Ascii-Input/-Output (eg. MWriteAsciiFile) of a // container, you may overload this function. If you don't overload it // the data member of a class are written to the file in the order of // appearance in the class header (be more specfic: root dictionary) // Only data members which are of integer (Bool_t, Int_t, ...) or // floating point (Float_t, Double_t, ...) type are written. // returns kTRUE when at least one member was successfully written // Bool_t MParContainer::AsciiWrite(ostream &out) const { // *fLog << warn << "To use the the ascii output of " << GetName(); // *fLog << " you have to overload " << ClassName() << "::AsciiWrite." << endl; Bool_t rc = WriteDataMember(out, IsA()->GetListOfDataMembers()); TIter NextBaseClass(IsA()->GetListOfBases()); TBaseClass *base; while ((base = (TBaseClass*) NextBaseClass())) { /*const*/ TClass *cls = base->GetClassPointer(); if (!cls) continue; if (cls->GetClassVersion()) rc |= WriteDataMember(out, cls->GetListOfDataMembers()); } return rc; } TMethodCall *MParContainer::GetterMethod(const char *name) const { const TString n(name); const Int_t pos1 = n.First('.'); const TString part1 = pos1<0 ? n : n(0, pos1); const TString part2 = pos1<0 ? TString("") : n(pos1+1, n.Length()); TClass *cls = IsA()->GetBaseDataMember(part1); if (cls) { TDataMember *member = cls->GetDataMember(part1); if (!member) { *fLog << err << "Datamember '" << part1 << "' not in " << GetDescriptor() << endl; return NULL; } // This handles returning references of contained objects, eg // class X { TObject fO; TObject &GetO() { return fO; } }; if (!member->IsBasic() && !part2.IsNull()) { cls = gROOT->GetClass(member->GetTypeName()); if (!cls) { *fLog << err << "Datamember " << part1 << " ["; *fLog << member->GetTypeName() << "] not in dictionary." << endl; return NULL; } if (!cls->InheritsFrom(MParContainer::Class())) { *fLog << err << "Datamember " << part1 << " ["; *fLog << member->GetTypeName() << "] does not inherit from "; *fLog << "MParContainer." << endl; return NULL; } const MParContainer *sub = (MParContainer*)((ULong_t)this+member->GetOffset()); return sub->GetterMethod(part2); } if (member->IsaPointer()) { *fLog << warn << "Data-member " << part1 << " is a pointer..." << endl; *fLog << dbginf << "Not yet implemented!" << endl; //TClass *test = gROOT->GetClass(member->GetTypeName()); return 0; } TMethodCall *call = member->GetterMethod(); if (call) return call; } *fLog << warn << "No standard access for '" << part1 << "' in "; *fLog << GetDescriptor() << " or one of its base classes." << endl; TMethodCall *call = NULL; *fLog << warn << "Trying to find MethodCall '" << ClassName(); *fLog << "::Get" << part1 << "' instead " << endl; call = new TMethodCall(IsA(), (TString)"Get"+part1, ""); if (call->GetMethod()) return call; delete call; *fLog << warn << "Trying to find MethodCall '" << ClassName(); *fLog << "::" << part1 << "' instead " << endl; call = new TMethodCall(IsA(), part1, ""); if (call->GetMethod()) return call; delete call; *fLog << err << "Sorry, no getter method found for " << part1 << endl; return NULL; } // -------------------------------------------------------------------------- // // Implementation of SavePrimitive. Used to write the call to a constructor // to a macro. In the original root implementation it is used to write // gui elements to a macro-file. // void MParContainer::SavePrimitive(ofstream &out, Option_t *o) { static UInt_t uid = 0; if (IsSavedAsPrimitive()) return; SetUniqueID(uid++); SetBit(kIsSavedAsPrimitive); if (gListOfPrimitives && !gListOfPrimitives->FindObject(this)) gListOfPrimitives->Add(this); StreamPrimitive(out); } // -------------------------------------------------------------------------- // // Creates the string written by SavePrimitive and returns it. // void MParContainer::StreamPrimitive(ofstream &out) const { out << " // Using MParContainer::StreamPrimitive" << endl; out << " " << ClassName() << " " << GetUniqueName() << "(\""; out << fName << "\", \"" << fTitle << "\");" << endl; } void MParContainer::GetNames(TObjArray &arr) const { arr.AddLast(new TNamed(fName, fTitle)); } void MParContainer::SetNames(TObjArray &arr) { TNamed *name = (TNamed*)arr.First(); fName = name->GetName(); fTitle = name->GetTitle(); delete arr.Remove(name); arr.Compress(); } // -------------------------------------------------------------------------- // // Creates a new instance of this class. The idea is to create a clone of // this class in its initial state. // MParContainer *MParContainer::New() const { return (MParContainer*)IsA()->New(); } // -------------------------------------------------------------------------- // // Read the contents/setup of a parameter container/task from a TEnv // instance (steering card/setup file). // The key to search for in the file should be of the syntax: // prefix.vname // While vname is a name which is specific for a single setup date // (variable) of this container and prefix is something like: // evtloopname.name // While name is the name of the containers/tasks in the parlist/tasklist // // eg. Job4.MImgCleanStd.CleaningLevel1: 3.0 // Job4.MImgCleanStd.CleaningLevel2: 2.5 // // If this cannot be found the next step is to search for // MImgCleanStd.CleaningLevel1: 3.0 // And if this doesn't exist, too, we should search for: // CleaningLevel1: 3.0 // // Warning: The programmer is responsible for the names to be unique in // all Mars classes. // Bool_t MParContainer::ReadEnv(const TEnv &env, TString prefix, Bool_t print) { if (!IsEnvDefined(env, prefix, "", print)) return kFALSE; *fLog << warn << "WARNING - Resource " << prefix+fName << " found, but no " << ClassName() << "::ReadEnv." << endl; return kTRUE; } // -------------------------------------------------------------------------- // // Write the contents/setup of a parameter container/task to a TEnv // instance (steering card/setup file). // The key to search for in the file should be of the syntax: // prefix.vname // While vname is a name which is specific for a single setup date // (variable) of this container and prefix is something like: // evtloopname.name // While name is the name of the containers/tasks in the parlist/tasklist // // eg. Job4.MImgCleanStd.CleaningLevel1: 3.0 // Job4.MImgCleanStd.CleaningLevel2: 2.5 // // If this cannot be found the next step is to search for // MImgCleanStd.CleaningLevel1: 3.0 // And if this doesn't exist, too, we should search for: // CleaningLevel1: 3.0 // // Warning: The programmer is responsible for the names to be unique in // all Mars classes. // Bool_t MParContainer::WriteEnv(TEnv &env, TString prefix, Bool_t print) const { if (!IsEnvDefined(env, prefix, "", print)) return kFALSE; *fLog << warn << "WARNING - Resource " << prefix+fName << " found, but " << ClassName() << "::WriteEnv not overloaded." << endl; return kTRUE; } Bool_t MParContainer::IsEnvDefined(const TEnv &env, TString prefix, TString postfix, Bool_t print) const { if (!postfix.IsNull()) postfix.Insert(0, "."); return IsEnvDefined(env, prefix+postfix, print); } Bool_t MParContainer::IsEnvDefined(const TEnv &env, TString name, Bool_t print) const { if (print) *fLog << all << GetDescriptor() << " - " << name << "... " << flush; if (!((TEnv&)env).Defined(name)) { if (print) *fLog << "not found." << endl; return kFALSE; } if (print) *fLog << "found." << endl; return kTRUE; } Int_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, Int_t dflt) const { return GetEnvValue(env, prefix+"."+postfix, dflt); } Double_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, Double_t dflt) const { return GetEnvValue(env, prefix+"."+postfix, dflt); } const char *MParContainer::GetEnvValue(const TEnv &env, TString prefix, TString postfix, const char *dflt) const { return GetEnvValue(env, prefix+"."+postfix, dflt); } Int_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, Int_t dflt) const { return ((TEnv&)env).GetValue(prefix, dflt); } Double_t MParContainer::GetEnvValue(const TEnv &env, TString prefix, Double_t dflt) const { return ((TEnv&)env).GetValue(prefix, dflt); } const char *MParContainer::GetEnvValue(const TEnv &env, TString prefix, const char *dflt) const { return ((TEnv&)env).GetValue(prefix, dflt); }