Gaudi Framework, version v23r0

Home   Generated: Mon Jan 30 2012

RootDataConnection.cpp

Go to the documentation of this file.
00001 // $Id: RootDataConnection.cpp,v 1.16 2010-09-27 15:43:53 frankb Exp $
00002 //====================================================================
00003 //      RootDataConnection.cpp
00004 //--------------------------------------------------------------------
00005 //
00006 //      Author     : M.Frank
00007 //====================================================================
00008 // $Header: /afs/cern.ch/project/cvs/reps/lhcb/Online/RootCnv/src/RootDataConnection.cpp,v 1.16 2010-09-27 15:43:53 frankb Exp $
00009 
00010 // Framework include files
00011 #include "RootCnv/RootDataConnection.h"
00012 #include "RootUtils.h"
00013 #include "GaudiKernel/IOpaqueAddress.h"
00014 #include "GaudiKernel/LinkManager.h"
00015 #include "GaudiKernel/DataObject.h"
00016 #include "GaudiKernel/IRegistry.h"
00017 #include "GaudiKernel/MsgStream.h"
00018 // ROOT include files
00019 #include "TROOT.h"
00020 #include "TFile.h"
00021 #include "TTree.h"
00022 #include "TLeaf.h"
00023 #include "TClass.h"
00024 #include "TBranch.h"
00025 #include "TTreePerfStats.h"
00026 
00027 using namespace Gaudi;
00028 using namespace std;
00029 typedef const string& CSTR;
00030 
00031 static string s_empty;
00032 static string s_local = "<localDB>";
00033 
00034 #ifdef __POOL_COMPATIBILITY
00035 #include "PoolTool.h"
00036 #endif
00037 #include "RootTool.h"
00038 
00039 
00040 static bool match_wild(const char *str, const char *pat)    {
00041   //
00042   // Credits: Code from Alessandro Felice Cantatore.
00043   // 
00044   static char table[256];
00045   static bool first = true;
00046   const char *s, *p;
00047   bool star = false;
00048   if ( first ) {
00049     for (int i = 0; i < 256; ++i) table[i] = char(i);
00050     first = false;
00051   }
00052 loopStart:
00053   for (s = str, p = pat; *s; ++s, ++p) {
00054     switch (*p) {
00055     case '?':
00056       if (*s == '.') goto starCheck;
00057       break;
00058     case '*':
00059       star = true;
00060       str = s, pat = p;
00061       do { ++pat; } while (*pat == '*');
00062       if (!*pat) return true;
00063       goto loopStart;
00064     default:
00065       if ( *(table+*s) != *(table+*p) )
00066         goto starCheck;
00067       break;
00068     } /* endswitch */
00069   } /* endfor */
00070   while (*p == '*') ++p;
00071   return (!*p);
00072 
00073 starCheck:
00074   if (!star) return false;
00075   str++;
00076   goto loopStart;
00077 }
00078 
00079 // Standard constructor
00080 RootConnectionSetup::RootConnectionSetup() : refCount(1), m_msgSvc(0)
00081 {
00082 }
00083 
00084 // Standard destructor      
00085 RootConnectionSetup::~RootConnectionSetup() {
00086   deletePtr(m_msgSvc);
00087 }
00088 
00089 // Increase reference count
00090 void RootConnectionSetup::addRef() {
00091   ++refCount;
00092 }
00093 
00094 // Decrease reference count
00095 void RootConnectionSetup::release() {
00096   int tmp = --refCount;
00097   if ( tmp <= 0 ) {
00098     delete this;
00099   }
00100 }
00101 
00102 // Set message service reference
00103 void RootConnectionSetup::setMessageSvc(MsgStream* m) {
00104   MsgStream* tmp = m_msgSvc;
00105   m_msgSvc = m;
00106   deletePtr(tmp);
00107 }
00108 
00109 // Standard constructor
00110 RootDataConnection::RootDataConnection(const IInterface* owner, CSTR fname, RootConnectionSetup* setup)
00111   : IDataConnection(owner,fname), m_setup(setup), m_statistics(0), m_tool(0)
00112 { //               01234567890123456789012345678901234567890
00113   // Check if FID: A82A3BD8-7ECB-DC11-8DC0-000423D950B0
00114   if ( fname.length() == 36 && fname[8]=='-'&&fname[13]=='-'&&fname[18]=='-'&&fname[23]=='-' ) {
00115     m_name = "FID:"+fname;
00116   }
00117   m_setup->addRef();
00118   m_age  = 0;
00119   m_file = 0;
00120   m_refs = 0;
00121   addClient(owner);
00122 }
00123 
00124 // Standard destructor      
00125 RootDataConnection::~RootDataConnection()   {
00126   m_setup->release();
00127   releasePtr(m_tool);
00128 }
00129 
00130 // Add new client to this data source
00131 void RootDataConnection::addClient(const IInterface* client) {
00132   m_clients.insert(client);
00133 }
00134 
00135 // Remove client from this data source
00136 size_t RootDataConnection::removeClient(const IInterface* client) {
00137   Clients::iterator i=m_clients.find(client);
00138   if ( i != m_clients.end() ) m_clients.erase(i);
00139   return m_clients.size();
00140 }
00141 
00142 // Lookup client for this data source
00143 bool RootDataConnection::lookupClient(const IInterface* client)   const {
00144   Clients::const_iterator i=m_clients.find(client);
00145   return i != m_clients.end();
00146 }
00147 
00148 // Save TTree access statistics if required
00149 void RootDataConnection::saveStatistics(CSTR statisticsFile) {
00150   if ( m_statistics ) {
00151     m_statistics->Print();
00152     if ( !statisticsFile.empty() )
00153       m_statistics->SaveAs(statisticsFile.c_str());
00154     deletePtr(m_statistics);
00155   }
00156 }
00157 
00158 // Enable TTreePerStats
00159 void RootDataConnection::enableStatistics(CSTR section) {
00160   if ( 0 == m_statistics ) {
00161     TTree* t=getSection(section,false);
00162     if ( t ) {
00163       m_statistics = new TTreePerfStats((section+"_ioperf").c_str(),t);
00164       return;
00165     }
00166     msgSvc() << MSG::WARNING << "Failed to enable perfstats for tree:" << section << endmsg;
00167     return;
00168   }
00169   msgSvc() << MSG::INFO << "Perfstats are ALREADY ENABLED." << endmsg;
00170 }
00171 
00172 // Create file access tool to encapsulate POOL compatibiliy
00173 RootDataConnection::Tool* RootDataConnection::makeTool()   {
00174   releasePtr(m_tool);
00175   if ( !m_refs ) m_refs = (TTree*)m_file->Get("Refs");
00176   if ( m_refs )
00177     m_tool = new RootTool(this);
00178 #ifdef __POOL_COMPATIBILITY
00179   else if ( m_file->Get("##Links") != 0 )
00180     m_tool = new PoolTool(this);
00181 #endif
00182   return m_tool;
00183 }
00184 
00185 // Connect the file in READ mode
00186 StatusCode RootDataConnection::connectRead()  {
00187   m_file = TFile::Open(m_pfn.c_str());
00188   if ( m_file && !m_file->IsZombie() )   {
00189     StatusCode sc = StatusCode::FAILURE;
00190     msgSvc() << MSG::DEBUG << "Opened file " << m_pfn << " in mode READ. [" << m_fid << "]" << endmsg << MSG::DEBUG;    
00191     if ( msgSvc().isActive() ) m_file->ls();
00192     msgSvc() << MSG::VERBOSE;
00193     if ( msgSvc().isActive() ) m_file->Print();
00194     if ( makeTool() ) sc = m_tool->readRefs();
00195     if ( sc.isSuccess() ) {
00196       bool need_fid = m_fid == m_pfn;
00197       string fid = m_fid;
00198       m_mergeFIDs.clear();
00199       for(size_t i=0, n=m_params.size(); i<n; ++i) {
00200         if ( m_params[i].first == "FID" )  {
00201           m_mergeFIDs.push_back(m_params[i].second);
00202           if ( m_params[i].second != m_fid )    {
00203             msgSvc() << MSG::DEBUG << "Check FID param:" << m_params[i].second << endmsg;
00204             //if ( m_fid == m_pfn ) {
00205             m_fid = m_params[i].second;
00206             //}
00207           }
00208         }
00209       }
00210       if ( !need_fid && fid != m_fid ) {
00211         msgSvc() << MSG::ERROR << "FID mismatch:" << fid << "(Catalog) != " << m_fid << "(file)" << endmsg
00212           << "for PFN:" << m_pfn << endmsg;
00213         return StatusCode::FAILURE;
00214       }
00215       msgSvc() << MSG::DEBUG << "Using FID " << m_fid << " from params table...." << endmsg
00216         << "for PFN:" << m_pfn << endmsg;
00217       return sc;
00218     }
00219   }
00220   else if ( m_file ) {
00221     deletePtr(m_file);
00222   }
00223   return StatusCode::FAILURE;
00224 }
00225 
00226 // Open data stream in write mode
00227 StatusCode RootDataConnection::connectWrite(IoType typ)  {
00228   msgSvc() << MSG::DEBUG;
00229   switch(typ)  {
00230   case CREATE:
00231     resetAge();
00232     m_file = TFile::Open(m_pfn.c_str(),"CREATE","Root event data");
00233     m_refs = new TTree("Refs","Root reference data");
00234     msgSvc() << "Opened file " << m_pfn << " in mode CREATE. [" << m_fid << "]" << endmsg;
00235     m_params.push_back(make_pair("PFN",m_pfn));
00236     if ( m_fid != m_pfn ) {
00237       m_params.push_back(make_pair("FID",m_fid));
00238     }
00239     makeTool();
00240     break;
00241   case RECREATE:
00242     resetAge();
00243     m_file = TFile::Open(m_pfn.c_str(),"RECREATE","Root event data");
00244     msgSvc() << "Opened file " << m_pfn << " in mode RECREATE. [" << m_fid << "]" << endmsg;
00245     m_refs = new TTree("Refs","Root reference data");
00246     m_params.push_back(make_pair("PFN",m_pfn));
00247     if ( m_fid != m_pfn ) {
00248       m_params.push_back(make_pair("FID",m_fid));
00249     }
00250     makeTool();
00251     break;
00252   case UPDATE:
00253     resetAge();
00254     m_file = TFile::Open(m_pfn.c_str(),"UPDATE","Root event data");
00255     msgSvc() << "Opened file " << m_pfn << " in mode UPDATE. [" << m_fid << "]" << endmsg;
00256     if ( m_file && !m_file->IsZombie() )  {
00257       if ( makeTool() ) return m_tool->readRefs();
00258       TDirectory::TContext ctxt(m_file);
00259       m_refs = new TTree("Refs","Root reference data");
00260       makeTool();
00261       return StatusCode::SUCCESS;
00262     }
00263     break;
00264   default:
00265     m_refs = 0;
00266     m_file = 0;
00267     return StatusCode::FAILURE;
00268   }
00269   return 0==m_file ? StatusCode::FAILURE : StatusCode::SUCCESS;
00270 }
00271 
00272 // Release data stream and release implementation dependent resources
00273 StatusCode RootDataConnection::disconnect()    {
00274   if ( m_file ) {
00275     if ( !m_file->IsZombie() )   {
00276       if ( m_file->IsWritable() ) {
00277         msgSvc() << MSG::DEBUG;
00278         TDirectory::TContext ctxt(m_file);
00279         if ( m_refs ) {
00280           m_tool->saveRefs().ignore();
00281           m_refs->Write();
00282         }
00283         for(Sections::iterator i=m_sections.begin(); i!= m_sections.end();++i) {
00284           if ( (*i).second ) {
00285             (*i).second->Write();
00286             msgSvc() << "Disconnect section " << (*i).first << " " << (*i).second->GetName() << endmsg;
00287           }
00288         }
00289         m_sections.clear();
00290       }
00291       msgSvc() << MSG::DEBUG;
00292       if ( msgSvc().isActive() ) m_file->ls();
00293       msgSvc() << MSG::VERBOSE;
00294       if ( msgSvc().isActive() ) m_file->Print();
00295       m_file->Close();
00296     }
00297     msgSvc() << MSG::DEBUG << "Disconnected file " << m_pfn << " " << m_file->GetName() << endmsg;
00298     deletePtr(m_file);
00299     releasePtr(m_tool);
00300   }
00301   return StatusCode::SUCCESS;
00302 }
00303 
00304 // Access TTree section from section name. The section is created if required.
00305 TTree* RootDataConnection::getSection(CSTR section, bool create) {
00306   TTree* t = m_sections[section];
00307   if ( !t ) {
00308     t = (TTree*)m_file->Get(section.c_str());
00309     if ( !t && create ) {
00310       TDirectory::TContext ctxt(m_file);
00311       t = new TTree(section.c_str(),"Root data for Gaudi");
00312     }
00313     if ( t ) {
00314       int cacheSize = m_setup->cacheSize;
00315       if ( create ) {
00316         //t->SetAutoFlush(100);
00317       }
00318       if ( section == m_setup->loadSection && cacheSize>-2 )  {
00319         MsgStream& msg = msgSvc();
00320         int learnEntries = m_setup->learnEntries;
00321         t->SetCacheSize(cacheSize);
00322         t->SetCacheLearnEntries(learnEntries);
00323         msg << MSG::DEBUG;
00324         if ( create ) {
00325           msg << "Tree:" << section << "Setting up tree cache:" << cacheSize << endmsg;
00326         }
00327         else {
00328           const StringVec& vB = m_setup->vetoBranches;
00329           const StringVec& cB = m_setup->cacheBranches;
00330           msg << "Tree:" << section << " Setting up tree cache:" << cacheSize << " Add all branches." << endmsg;
00331           msg << "Tree:" << section << " Learn for " << learnEntries << " entries." << endmsg;
00332 
00333           if ( cB.size()==0 && vB.size()== 0 ) {
00334             msg << "Adding (default) all branches to tree cache." << endmsg;
00335             t->AddBranchToCache("*",kTRUE);
00336           }
00337           if ( cB.size()==1 && cB[0]=="*" ) {
00338             msg << "Adding all branches to tree cache according to option \"CacheBranches\"." << endmsg;
00339             t->AddBranchToCache("*",kTRUE);
00340           }
00341           else {
00342             StringVec::const_iterator i;
00343             for(TIter it(t->GetListOfBranches()); it.Next(); )  {
00344               const char* n = ((TNamed*)(*it))->GetName();
00345               bool add = false, veto = false;
00346               for(i=cB.begin(); i != cB.end();++i) {
00347                 if ( !match_wild(n,(*i).c_str()) ) continue;
00348                 add = true;
00349                 break;
00350               }
00351               for(i=vB.begin(); !add && i!=vB.end();++i) {
00352                 if ( !match_wild(n,(*i).c_str()) ) continue;
00353                 veto = true;
00354                 break;
00355               }
00356               if ( add && !veto ) {
00357                 msg << "Add " << n << " to branch cache." << endmsg;
00358                 t->AddBranchToCache(n,kTRUE);
00359               }
00360               else {
00361                 msg << "Do not cache branch " << n << endmsg;
00362               }
00363             }
00364           }
00365         }
00366       }
00367       m_sections[section] = t;
00368     }
00369   }
00370   return t;
00371 }
00372 
00373 // Access data branch by name: Get existing branch in write mode
00374 TBranch* RootDataConnection::getBranch(CSTR section, CSTR branch_name, TClass* cl, void* ptr) {
00375   string n = branch_name+".";
00376   for(int i=0, m=n.length()-1; i<m; ++i) if ( !isalnum(n[i]) ) n[i]='_';
00377   TTree* t = getSection(section,true);
00378   TBranch* b = t->GetBranch(n.c_str());
00379   if ( !b && cl && m_file->IsWritable() ) {
00380     b = t->Branch(n.c_str(),cl->GetName(),(void*)(ptr ? &ptr : 0));
00381   }
00382   if ( !b ) {
00383     b = t->GetBranch(branch_name.c_str());
00384   }
00385   if ( b )   {
00386     b->SetAutoDelete(kFALSE);
00387   }
00388   return b;
00389 }
00390 
00391 // Convert path string to path index
00392 int RootDataConnection::makeLink(CSTR p) {
00393   int cnt = 0;
00394   StringVec::iterator ip;
00395   for(ip=m_links.begin();ip!=m_links.end();++ip,++cnt) {
00396     if( (*ip) == p ) return cnt;
00397   }
00398   m_links.push_back(p);
00399   return m_links.size()-1;
00400 }
00401 
00402 // Access database/file name from saved index
00403 CSTR RootDataConnection::getDb(int which) const {
00404   if ( (which>=0) && (size_t(which)<m_dbs.size()) )  {
00405     if ( *(m_dbs.begin()+which) == s_local ) return m_fid;
00406     return *(m_dbs.begin()+which);
00407   }
00408   return s_empty;
00409 }
00410 
00411 // Empty string reference
00412 CSTR RootDataConnection::empty() const { 
00413   return s_empty;
00414 }
00415 
00416 // Save object of a given class to section and container
00417 pair<int,unsigned long> 
00418 RootDataConnection::saveObj(CSTR section, CSTR cnt, TClass* cl, DataObject* pObj,bool fill) {
00419   DataObjectPush push(pObj);
00420   return save(section,cnt,cl,pObj,fill);
00421 }
00422 
00423 // Save object of a given class to section and container
00424 pair<int,unsigned long> 
00425 RootDataConnection::save(CSTR section, CSTR cnt, TClass* cl, void* pObj, bool fill_missing) {
00426   TBranch* b = getBranch(section, cnt, cl, (void*)(pObj ? &pObj : 0));
00427   if ( b ) {
00428     Long64_t evt = b->GetEntries();
00429     if ( fill_missing ) {
00430       Long64_t num, nevt = b->GetTree()->GetEntries();
00431       if ( nevt > evt )   {
00432         b->SetAddress(0);
00433         num = nevt - evt;
00434         while( num > 0 ) { b->Fill(); --num; }
00435         msgSvc() << MSG::DEBUG << "Added " << long(nevt-evt) 
00436                  << " / Tree: " << nevt << " / Branch: " << b->GetEntries()+1
00437                  << " NULL entries to:" << cnt << endmsg;
00438         evt = b->GetEntries();
00439       }
00440     }
00441     b->SetAddress(&pObj);
00442     return make_pair(b->Fill(),(unsigned long)evt);
00443   }
00444   else if ( 0 != pObj ) {
00445     msgSvc() << MSG::ERROR << "Failed to access branch " << m_name << "/" << cnt << endmsg;
00446   }
00447   return make_pair(-1,~0);
00448 }
00449 
00450 // Load object
00451 int RootDataConnection::loadObj(CSTR section, CSTR cnt, unsigned long entry, DataObject*& pObj) {
00452   TBranch* b = getBranch(section,cnt);
00453   if ( b ) {
00454     TClass* cl = gROOT->GetClass(b->GetClassName(),kTRUE);
00455     if ( cl ) {
00456       int nb = -1;
00457       pObj = (DataObject*)cl->New();
00458       {
00459         DataObjectPush push(pObj);
00460         b->SetAddress(&pObj);
00461         if ( section == m_setup->loadSection ) {
00462           TTree* t = b->GetTree();
00463           if ( Long64_t(entry) != t->GetReadEntry() ) {
00464             t->LoadTree(Long64_t(entry));
00465           }
00466         }
00467         nb = b->GetEntry(entry);
00468         msgSvc() << MSG::VERBOSE;
00469         if ( msgSvc().isActive() ) {
00470           msgSvc() << "Load [" << entry << "] --> " << section 
00471                    << ":" << cnt << "  " << nb << " bytes." 
00472                    << endmsg;
00473         }
00474         if ( nb == 0 && pObj->clID() == CLID_DataObject) {
00475           TFile* f = b->GetFile();
00476           int vsn = f->GetVersion();
00477           if ( vsn < 52400 ) {
00478             // For Gaudi v21r5 (ROOT 5.24.00b) DataObject::m_version was not written!
00479             // Still this call be well be successful.
00480             nb = 1;
00481           }
00482           else if ( vsn>1000000 && (vsn%1000000)<52400 ) {
00483             // dto. Some POOL files have for unknown reasons a version
00484             // not according to ROOT standards. Hack this explicitly.
00485             nb = 1;
00486           }
00487         }
00488         if ( nb < 0 ) {
00489           delete pObj;
00490           pObj = 0;
00491         }
00492       }
00493       return nb;
00494     }
00495   }
00496   return -1;
00497 }
00498 
00499 // Access link section for single container and entry
00500 pair<const RootRef*,const RootDataConnection::ContainerSection*> 
00501 RootDataConnection::getMergeSection(const string& container, int entry) const {
00502   //size_t idx = cont.find('/',1);
00503   //string container = cont[0]=='/' ? cont.substr(1,idx==string::npos?idx:idx-1) : cont;
00504   MergeSections::const_iterator i=m_mergeSects.find(container);
00505   if ( i != m_mergeSects.end() ) {
00506     size_t cnt = 0;
00507     const ContainerSections& s = (*i).second;
00508     for(ContainerSections::const_iterator j=s.begin(); j != s.end(); ++j,++cnt) {
00509       const ContainerSection& c = *j;
00510       if ( entry >= c.start && entry < (c.start+c.length) ) {
00511         if ( m_linkSects.size() > cnt ) {
00512           if ( msgSvc().isActive() ) {
00513             msgSvc() << MSG::VERBOSE << "MergeSection for:" << container 
00514               << "  [" << entry << "]" << endmsg
00515               << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
00516           }
00517           return make_pair(&(m_linkSects[cnt]), &c);
00518         }
00519       }
00520     }
00521   }
00522   msgSvc() << MSG::DEBUG << "Return INVALID MergeSection for:" << container 
00523     << "  [" << entry << "]" << endmsg
00524     << "FID:" << m_fid << " -> PFN:" << m_pfn << endmsg;
00525   return make_pair((const RootRef*)0,(const ContainerSection*)0);
00526 }
00527 
00528 // Create reference object from registry entry
00529 void RootDataConnection::makeRef(IRegistry* pR, RootRef& ref) {
00530   IOpaqueAddress* pA = pR->address();
00531   makeRef(pR->name(),pA->clID(),pA->svcType(),pA->par()[0],pA->par()[1],-1,ref);
00532 }
00533 
00534 // Create reference object from values
00535 void RootDataConnection::makeRef(CSTR name, long clid, int tech, CSTR dbase, CSTR cnt, int entry, RootRef& ref) {
00536   string db(dbase);
00537   int cdb=-1, ccnt=-1, clnk=-1;
00538   StringVec::iterator idb, icnt, ilnk;
00539   if ( db == m_fid ) {
00540     db = s_local;
00541   }
00542   ref.entry = entry;
00543   if ( !db.empty() ) {
00544     for(cdb=0,idb=m_dbs.begin(); idb!=m_dbs.end();++idb,++cdb)
00545       if( (*idb) == db ) break;
00546     if ( idb == m_dbs.end() ) {
00547       cdb = m_dbs.size();
00548       m_dbs.push_back(db);
00549     }
00550   }
00551   if ( !cnt.empty() ) {
00552     for(ccnt=0,icnt=m_conts.begin(); icnt!=m_conts.end();++icnt,++ccnt)
00553       if( (*icnt) == cnt ) break;
00554     if ( icnt == m_conts.end() ) {
00555       ccnt = m_conts.size();
00556       m_conts.push_back(cnt);
00557     }
00558   }
00559   if ( !name.empty() ) {
00560     for(clnk=0,ilnk=m_links.begin(); ilnk!=m_links.end();++ilnk,++clnk)
00561       if( (*ilnk) == name ) break;
00562     if ( ilnk == m_links.end() ) {
00563       clnk = m_links.size();
00564       m_links.push_back(name);
00565     }
00566   }
00567   ref.dbase     = cdb;
00568   ref.container = ccnt;
00569   ref.link      = clnk;
00570   ref.clid      = clid;
00571   ref.svc       = tech;
00572   if ( ref.svc == POOL_ROOT_StorageType || 
00573     ref.svc == POOL_ROOTKEY_StorageType || 
00574     ref.svc == POOL_ROOTTREE_StorageType ) {
00575       ref.svc = ROOT_StorageType;
00576     }
00577 }
00578 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines

Generated at Mon Jan 30 2012 13:53:04 for Gaudi Framework, version v23r0 by Doxygen version 1.7.2 written by Dimitri van Heesch, © 1997-2004