00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
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
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
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 }
00069 }
00070 while (*p == '*') ++p;
00071 return (!*p);
00072
00073 starCheck:
00074 if (!star) return false;
00075 str++;
00076 goto loopStart;
00077 }
00078
00079
00080 RootConnectionSetup::RootConnectionSetup() : refCount(1), m_msgSvc(0)
00081 {
00082 }
00083
00084
00085 RootConnectionSetup::~RootConnectionSetup() {
00086 deletePtr(m_msgSvc);
00087 }
00088
00089
00090 void RootConnectionSetup::addRef() {
00091 ++refCount;
00092 }
00093
00094
00095 void RootConnectionSetup::release() {
00096 int tmp = --refCount;
00097 if ( tmp <= 0 ) {
00098 delete this;
00099 }
00100 }
00101
00102
00103 void RootConnectionSetup::setMessageSvc(MsgStream* m) {
00104 MsgStream* tmp = m_msgSvc;
00105 m_msgSvc = m;
00106 deletePtr(tmp);
00107 }
00108
00109
00110 RootDataConnection::RootDataConnection(const IInterface* owner, CSTR fname, RootConnectionSetup* setup)
00111 : IDataConnection(owner,fname), m_setup(setup), m_statistics(0), m_tool(0)
00112 {
00113
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
00125 RootDataConnection::~RootDataConnection() {
00126 m_setup->release();
00127 releasePtr(m_tool);
00128 }
00129
00130
00131 void RootDataConnection::addClient(const IInterface* client) {
00132 m_clients.insert(client);
00133 }
00134
00135
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
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
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
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
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
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
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
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
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
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
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
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
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
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
00412 CSTR RootDataConnection::empty() const {
00413 return s_empty;
00414 }
00415
00416
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
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
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
00479
00480 nb = 1;
00481 }
00482 else if ( vsn>1000000 && (vsn%1000000)<52400 ) {
00483
00484
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
00500 pair<const RootRef*,const RootDataConnection::ContainerSection*>
00501 RootDataConnection::getMergeSection(const string& container, int entry) const {
00502
00503
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
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
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