| # Gaudi Framework, version v25r0 |

Home |  | Generated: Mon Feb 17 2014

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
NTupleSvc.cpp
Go to the documentation of this file.
1 //====================================================================
2 // NTupleSvc.cpp
3 //--------------------------------------------------------------------
4 //
5 // Package : GaudiSvc/NTupleSvc ( The LHCb Offline System)
6 //
7 // Description: implementation of the NTuple service
8 //
9 // Author : M.Frank
10 // History :
11 // +---------+----------------------------------------------+---------
12 // | Date | Comment | Who
13 // +---------+----------------------------------------------+---------
14 // | 29/10/98| Initial version | MF
15 // | 29/09/99| Added access to ICnvManager for missing |
16 // | | converters | MF
17 // | 20/09/00| Connect dynamically to conversion service |
18 // | | for N-tuple persistency | MF
19 // +---------+----------------------------------------------+---------
20 //
21 //====================================================================
22 #define GAUDISVC_NTUPLESVC_CPP
23
24 // Framework include files
25 #include "GaudiKernel/xtoa.h"
26 #include "GaudiKernel/SmartIF.h"
27 #include "GaudiKernel/Tokenizer.h"
28 #include "GaudiKernel/DataObject.h"
29 #include "GaudiKernel/ObjectFactory.h"
30 #include "GaudiKernel/GenericAddress.h"
31
32 #include "GaudiKernel/IProperty.h"
33 #include "GaudiKernel/ISvcLocator.h"
34 #include "GaudiKernel/IDataSelector.h"
35
36 #include "GaudiKernel/Property.h"
37 #include "GaudiKernel/Selector.h"
38 #include "GaudiKernel/MsgStream.h"
39 #include "GaudiKernel/ConversionSvc.h"
40 #include "GaudiKernel/DataSelectionAgent.h"
41 #include "GaudiKernel/NTupleImplementation.h"
42
43 #include "NTupleSvc.h"
44
45 // Instantiation of a static factory class used by clients to create
46 // instances of this service
47 DECLARE_COMPONENT(NTupleSvc)
48
49
50 DECLARE_NAMESPACE_OBJECT_FACTORY(NTuple,Selector)
51
52 #include <sstream>
53
54 namespace {
55 inline std::string toStr(long id) {
56 std::ostringstream s; s << id;
57 return s.str();
58 }
59 }
60
62 NTupleSvc::NTupleSvc(const std::string& name, ISvcLocator* svc)
63 : base_class(name, svc)
64 {
65 declareProperty("Input", m_input);
66 declareProperty("Output", m_output);
67 m_rootName = "/NTUPLES";
68 m_rootCLID = CLID_DataObject;
69 }
70
72 NTupleSvc::~NTupleSvc() {
73 }
74
76 StatusCode NTupleSvc::initialize() {
77 StatusCode status = DataSvc::initialize();
78 if ( status.isSuccess() ) {
79 status = setProperties();
80 if ( status.isSuccess() ) {
81 StatusCode iret(StatusCode::SUCCESS,true);
82 DataObject* root = new NTuple::Directory();
83 status = setRoot(m_rootName, root);
84 for ( DBaseEntries::iterator i = m_output.begin(); i != m_output.end(); ++i ) {
85 iret = connect(*i);
86 if ( !iret.isSuccess() ) {
87 status = iret;
88 }
89 }
90 for ( DBaseEntries::iterator j = m_input.begin(); j != m_input.end(); ++j ) {
91 iret = connect(*j);
92 if ( !iret.isSuccess() ) {
93 status = iret;
94 }
95 }
96 }
97 }
98 return status;
99 }
100
102 StatusCode NTupleSvc::reinitialize() {
103 return StatusCode::SUCCESS;
104 }
105
106 // Check if a datasource is connected
107 bool NTupleSvc::isConnected(const std::string& identifier) const {
108 Connections::const_iterator i = m_connections.find(identifier);
109 return !(i==m_connections.end());
110 }
111
113 IConversionSvc* NTupleSvc::getDataLoader(IRegistry* pRegistry) {
114 if ( 0 != pRegistry ) {
115 std::string full = pRegistry->identifier();
116 size_t len = m_rootName.length();
117 size_t idx = full.find(SEPARATOR,len+1);
118 std::string path = (idx==std::string::npos) ? full : full.substr(0, idx);
119 Connections::iterator i = m_connections.find(path);
120 if ( i != m_connections.end() ) {
121 return (*i).second.service;
122 }
123 }
124 return 0;
125 }
126
127 StatusCode NTupleSvc::updateDirectories() {
128 typedef std::vector<IRegistry*> Leaves;
129 long need_update = 0;
130 DataObject* pO = 0;
131 StatusCode iret = findObject(m_rootName, pO);
132 MsgStream log ( msgSvc(), name() );
133 // log << MSG::DEBUG << "in finalize()" << endmsg;
134 if ( iret.isSuccess() ) {
135 Leaves leaves;
136 iret = objectLeaves(pO, leaves);
137 if ( iret.isSuccess() ) {
138 // Only traverse the tree below the files
139 for ( Leaves::iterator d = leaves.begin(); d != leaves.end(); d++ ) {
140 if ( (*d)->object() ) {
141 IOpaqueAddress* pA = (*d)->address();
142 if ( pA ) {
143 unsigned long typ = pA->ipar()[1];
144 if ( typ == 'R' || typ == 'N' || typ == 'U' ) {
145 // ...starting from the file entries: first save the directories/ntuples
146 IConversionSvc* svc = getDataLoader(*d);
147 if ( 0 != svc ) {
148 StatusCode status;
149 DataSelectionAgent agent;
150 IDataSelector* sel = agent.selectedObjects();
151 traverseSubTree ( (*d)->object(), &agent ).ignore();
152 for(int i = sel->size()-1; i >= 0; i-- ) {
153 DataObject* o = (*sel)[i];
154 IRegistry* r = o->registry();
155 status = svc->updateRep(r->address(), o);
156 if ( !status.isSuccess() ) {
157 iret = status;
158 }
159 }
160 for(int j = sel->size()-1; j >= 0; j-- ) {
161 DataObject* o = (*sel)[j];
162 IRegistry* r = o->registry();
163 status = svc->updateRepRefs(r->address(), o);
164 if ( !status.isSuccess() ) {
165 iret = status;
166 }
167 }
168 if ( iret.isSuccess() ) need_update += sel->size();
169 }
170 }
171 }
172 }
173 }
174 }
175 }
176 if ( !iret.isSuccess() ) {
177 log << MSG::ERROR << "ERROR while saving NTuples" << endmsg;
178 return iret;
179 }
180 else if ( need_update > 0 ) {
181 log << MSG::INFO << "NTuples saved successfully" << endmsg;
182 }
183 return iret;
184 }
185
186 // Finalize single service
187 void NTupleSvc::releaseConnection(Connection& c) {
188 SmartIF<IService> isvc( c.service );
189 if ( isvc.isValid( ) ) {
190 isvc->finalize().ignore();
191 }
192 c.service->release();
193 c.service = 0;
194 }
195
196 // Close all open connections
197 StatusCode NTupleSvc::disconnect(const std::string& nam) {
198 Connections::iterator i = m_connections.find(nam);
199 if ( i != m_connections.end() ) {
200 releaseConnection((*i).second);
201 m_connections.erase(i);
202 return StatusCode::SUCCESS;
203 }
204 return StatusCode::FAILURE;
205 }
206
207 // Close all open connections
208 StatusCode NTupleSvc::disconnectAll() {
209 for(Connections::iterator i = m_connections.begin(); i != m_connections.end(); ++i) {
210 releaseConnection((*i).second);
211 }
212 m_connections.erase(m_connections.begin(), m_connections.end());
213 return StatusCode::SUCCESS;
214 }
215
217 StatusCode NTupleSvc::finalize() {
218 StatusCode status = updateDirectories();
219 status = clearStore();
220 status = DataSvc::finalize();
221 status = disconnectAll();
222 return status;
223 }
224
225 StatusCode NTupleSvc::connect(const std::string& ident) {
226 std::string logName;
227 return connect(ident, logName);
228 }
229
230 StatusCode NTupleSvc::connect(const std::string& ident, std::string& logname) {
231 MsgStream log ( msgSvc(), name() );
232 DataObject* pO = 0;
233 StatusCode status = findObject(m_rootName, pO);
234 if ( status.isSuccess() ) {
235 char typ=0;
236 Tokenizer tok(true);
237 std::vector<Prop> props;
238 long loc = ident.find(" ");
239 std::string filename, auth, svc = "", db_typ = "";
240 logname = ident.substr(0,loc);
241 tok.analyse(ident.substr(loc+1,ident.length()), " ", "", "", "=", "'", "'");
242 for ( Tokenizer::Items::iterator i = tok.items().begin(); i != tok.items().end(); ++i) {
243 const std::string& tag = (*i).tag();
244 switch( ::toupper(tag[0]) ) {
245 case 'A':
246 break;
247 case 'F': // FILE='<file name>'
248 case 'D': // DATAFILE='<file name>'
249 filename = (*i).value();
250 break;
251 case 'O': // OPT='<NEW<CREATE,WRITE>, UPDATE, READ>'
252 switch( ::toupper((*i).value()[0]) ) {
253 case 'C':
254 case 'N':
255 case 'W':
256 typ = 'N';
257 break;
258 case 'U':
259 typ = 'U';
260 break;
261 case 'O':
262 case 'R':
263 typ = 'O';
264 break;
265 default:
266 typ = 0;
267 break;
268 }
269 break;
270 case 'T': // TYP='<HBOOK,ROOT,OBJY,...>'
271 db_typ = (*i).value();
272 break;
273 default:
274 props.push_back( Prop((*i).tag(), (*i).value()));
275 break;
276 }
277 }
278 if ( 0 != typ ) {
279 IConversionSvc* pSvc = 0;
280 status = createService(name()+'.'+logname, db_typ, props, pSvc);
281 if ( status.isSuccess() ) {
282 status = attachTuple(filename, logname, typ, pSvc->repSvcType());
283 if ( status.isSuccess() ) {
284 m_connections.insert(Connections::value_type(m_rootName+'/'+logname,Connection(pSvc)));
285 return StatusCode::SUCCESS;
286 }
287 }
288 }
289 }
290 log << MSG::ERROR << "Cannot add " << ident << " invalid filename!" << endmsg;
291 return StatusCode::FAILURE;
292 }
293
294 StatusCode NTupleSvc::createService(const std::string& /* nam */,
295 const std::string& typ,
296 const std::vector<Prop>& /* props */,
297 IConversionSvc*& pSvc)
298 {
299 MsgStream log ( msgSvc(), name() );
301 // Get the value of the Stat persistancy mechanism from the AppMgr
302 IProperty* appPropMgr = 0;
303 StatusCode sts = serviceLocator()->queryInterface(IProperty::interfaceID(), pp_cast<void>(&appPropMgr) );
304 if( !sts.isSuccess() ) {
305 // Report an error and return the FAILURE status code
306 log << MSG::ERROR << "Could not get PropMgr" << endmsg;
307 return sts;
308 }
309
310 StringProperty sp("HistogramPersistency","");
311 sts = appPropMgr->getProperty( &sp );
312 if ( !sts.isSuccess() ) {
313 log << MSG::ERROR << "Could not get NTuple Persistency format"
314 << " from ApplicationMgr properties" << endmsg;
315 return sts;
316 }
317
318 long storage_typ = TEST_StorageType;
319 if ( sp.value() == "HBOOK" ) {
320 storage_typ = HBOOK_StorageType;
321 }
322 else if ( sp.value() == "ROOT" ) {
323 storage_typ = ROOT_StorageType;
324 }
325 else {
326 appPropMgr->release();
327 log << MSG::ERROR << "Unknown NTuple Persistency format: " << sp.value() << endmsg;
328 return StatusCode::FAILURE;
329 }
330 // Clean up
331 appPropMgr->release();
332
333 if ( typ.length() > 0 && typ != sp.value() ) {
334 log << MSG::WARNING << "NTuple persistency type is "
335 << sp.value() << "." << endmsg
336 << "Type given by job option "
337 << "NTupleSvc.Input/Output ignored!" << endmsg;
338 }
339
340 // log << MSG::DEBUG << "storage type: " << m_storageType << endmsg;
341
342 // FIXME: (MCl) why NTupleSvc has to directly create a ConversionSvc?
343 IService* pService = 0;
344 IInterface* iface = new ConversionSvc(name()+"Conversions", serviceLocator(), storage_typ);
345 StatusCode status = iface->queryInterface(IService::interfaceID(), pp_cast<void>(&pService));
346 if ( status.isSuccess() ) {
347 status = iface->queryInterface(IConversionSvc::interfaceID(), pp_cast<void>(&pSvc));
348 if ( !status.isSuccess() ) {
349 pService->release();
350 return status;
351 }
352 }
353 status = pService->sysInitialize();
354 if ( !status.isSuccess() ) {
355 return status;
356 }
357 pService->release();
358 status = pSvc->setDataProvider(this);
359 if ( !status.isSuccess() ) {
360 return status;
361 }
362 return status;
363 }
364
366 StatusCode NTupleSvc::create(const CLID& typ, const std::string& title, NTuple::Tuple*& refpTuple) {
367 NTuple::TupleImp* pTuple = 0;
368 StatusCode status = StatusCode::FAILURE;
369 if ( typ == CLID_ColumnWiseTuple ) {
370 pTuple = new NTuple::ColumnWiseTuple( title );
371 }
372 else if ( typ == CLID_RowWiseTuple ) {
373 pTuple = new NTuple::RowWiseTuple( title );
374 }
375 else {
377 }
378 if ( 0 != pTuple ) {
379 pTuple->setTupleService(this);
380 status = StatusCode::SUCCESS;
381 }
382 refpTuple = pTuple;
383 return status;
384 }
385
387 NTuple::Tuple* NTupleSvc::book (const std::string& fullPath, const CLID& type, const std::string& title) {
388 DataObject* pObj = 0;
389 std::string path = fullPath;
390 MsgStream log(msgSvc(), name());
391 if ( path[0] != SEPARATOR ) {
392 path = m_rootName;
393 path += SEPARATOR;
394 path += fullPath;
395 }
396 StatusCode status = retrieveObject(path, pObj);
397 if ( !status.isSuccess() ) {
398 int sep = path.rfind(SEPARATOR);
399 if ( sep > 0 ) {
400 std::string p_path (path, 0, sep);
401 std::string o_path (path, sep, path.length());
402 DataObject* dir = createDirectory(p_path);
403 if ( 0 != dir ) {
404 NTuple::Tuple* tup = book( dir, o_path, type, title);
405 if ( 0 == tup ) {
406 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Unknown reason)" << endmsg;
407 }
408 return tup;
409 }
410 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Invalid parent directory)" << endmsg;
411 return 0;
412 }
413 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Invalid path)" << endmsg;
414 return 0;
415 }
416 log << MSG::ERROR << "Cannot book N-tuple " << path << " (Exists already)" << endmsg;
417 return 0;
418 }
419
421 NTuple::Tuple* NTupleSvc::book (const std::string& dirPath, const std::string& relPath, const CLID& type, const std::string& title) {
422 std::string full = dirPath;
423 if (relPath[0] != SEPARATOR) full += SEPARATOR;
424 full += relPath;
425 return book(full, type, title);
426 }
427
429 NTuple::Tuple* NTupleSvc::book (const std::string& dirPath, long id, const CLID& type, const std::string& title) {
430 return book( dirPath, toStr(id), type, title);
431 }
432
434 NTuple::Tuple* NTupleSvc::book (DataObject* pParent, const std::string& relPath, const CLID& type, const std::string& title) {
435 NTuple::Tuple* pObj = 0;
436 // Check if object is already present
437 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj));
438 // No ? Then create it!
439 if ( !status.isSuccess() ) {
440 status = create( type, title, pObj);
441 if ( status.isSuccess() ) {
442 // Finally register the created N tuple with the store
443 status = registerObject(pParent, relPath, pObj);
444 if ( status.isSuccess() ) {
445 return pObj;
446 }
447 pObj->release();
448 }
449 }
450 return 0;
451 }
452
454 NTuple::Tuple* NTupleSvc::book (DataObject* pParent,
455 long id,
456 const CLID& type,
457 const std::string& title) {
458 return book( pParent, toStr(id), type, title);
459 }
460
462 NTuple::Directory* NTupleSvc::createDirectory (DataObject* pParent,
463 const std::string& relPath) {
464 if ( 0 != pParent ) {
465 IRegistry* pDir = pParent->registry();
466 if ( 0 != pDir ) {
467 std::string full = pDir->identifier();
468 full += (relPath[0]=='/') ? "" : "/";
469 full += relPath;
470 return createDirectory(full);
471 }
472 }
473 return 0;
474 }
475
477 NTuple::Directory* NTupleSvc::createDirectory (DataObject* pParent, long id) {
478 return createDirectory( pParent, toStr(id) );
479 }
480
482 NTuple::Directory* NTupleSvc::createDirectory (const std::string& dirPath, long id) {
483 return createDirectory( dirPath, toStr(id) );
484 }
485
487 NTuple::Directory* NTupleSvc::createDirectory (const std::string& dirPath, const std::string& relPath ) {
488 std::string full = dirPath;
489 full += (relPath[0]=='/') ? "" : "/";
490 full += relPath;
491 return createDirectory(full);
492 }
493
494 StatusCode NTupleSvc::attachTuple(const std::string& filename, const std::string& logname, const char typ, const long t) {
495 MsgStream log(msgSvc(), name());
496 DataObject* p;
497 // First get the root object
498 StatusCode status = retrieveObject(m_rootName, p);
499 if ( status.isSuccess() ) {
500 // Now add the registry entry to the store
501 std::string entryname = m_rootName;
502 entryname += '/';
503 entryname += logname;
504 GenericAddress* pA =
505 new GenericAddress(t, CLID_NTupleFile, filename, entryname, 0, typ);
506 status = registerAddress(p, logname, pA);
507 if ( status.isSuccess() ) {
508 log << MSG::INFO << "Added stream file:" << filename << " as " << logname << endmsg;
509 return status;
510 }
511 pA->release();
512 }
513 log << MSG::ERROR << "Cannot add file:" << filename << " as " << logname << endmsg;
514 return status;
515 }
516
518 NTuple::Directory* NTupleSvc::createDirectory (const std::string& fullPath) {
519 NTuple::Directory* p = 0;
520 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&p));
521 if ( !status.isSuccess() ) {
522 int sep2 = fullPath.rfind(SEPARATOR);
523 if ( sep2 > 0 ) {
524 std::string relPath = fullPath.substr(0, sep2);
525 p = createDirectory(relPath);
526 if ( 0 != p ) {
527 p = new NTuple::Directory();
528 // Finally register the created N tuple with the store
529 status = registerObject(fullPath, p);
530 if ( status.isSuccess() ) {
531 // ...starting from the file entries
532 IConversionSvc* svc = getDataLoader(p->registry());
533 if ( 0 != svc ) {
534 IOpaqueAddress* pAddr = 0;
535 status = svc->createRep (p, pAddr);
536 if ( status.isSuccess() ) {
537 p->registry()->setAddress(pAddr);
538 status = svc->fillRepRefs (pAddr, p);
539 if ( status.isSuccess() ) {
540 return p;
541 }
542 }
543 }
544 unregisterObject(p);
545 }
546 p->release();
547 p = 0;
548 }
549 }
550 }
551 try {
552 p = dynamic_cast<NTuple::Directory*>(p);
553 return p;
554 }
555 catch (...) {
556 }
557 return 0;
558 }
559
561 NTuple::Tuple* NTupleSvc::access(const std::string&, const std::string&) {
562 MsgStream log ( msgSvc(), name() );
563 return 0;
564 }
565
567 StatusCode NTupleSvc::save(const std::string& fullPath) {
568 MsgStream log ( msgSvc(), name() );
569 NTuple::Tuple* pObj = 0;
570 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
571 if ( status.isSuccess() ) {
572 return save ( pObj );
573 }
574 return INVALID_OBJ_PATH;
575 }
576
578 StatusCode NTupleSvc::save(NTuple::Tuple* n_tuple) {
579 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
580 if ( 0 != tuple ) {
581 try {
582 IConversionSvc* pSvc = tuple->conversionService();
583 IRegistry* pReg = tuple->registry();
584 if ( 0 != pSvc && 0 != pReg ) {
585 IOpaqueAddress* pAddr = pReg->address();
586 StatusCode status = pSvc->updateRep(pAddr, n_tuple);
587 if ( status.isSuccess() ) {
588 status = pSvc->updateRepRefs(pAddr, n_tuple);
589 }
590 return status;
591 }
592 return IDataProviderSvc::NO_DATA_LOADER;
593 }
594 catch(...) {
595 }
596 }
597 return INVALID_OBJECT;
598 }
599
601 StatusCode NTupleSvc::save(DataObject* pParent, const std::string& relPath) {
602 NTuple::Tuple* pObj = 0;
603 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
604 if ( status.isSuccess() ) {
605 return save ( pObj );
606 }
607 return INVALID_OBJ_PATH;
608 }
609
611 StatusCode NTupleSvc::writeRecord( NTuple::Tuple* n_tuple ) {
612 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
613 if ( 0 != tuple ) {
614 try {
615 IConversionSvc* pSvc = tuple->conversionService();
616 if ( 0 == pSvc ) {
617 pSvc = getDataLoader(n_tuple->registry());
618 tuple->setConversionService(pSvc);
619 }
620 if ( 0 != pSvc ) {
621 IRegistry* pReg = n_tuple->registry();
622 IOpaqueAddress* pAddr = pReg->address();
623 StatusCode status = pSvc->createRep(n_tuple, pAddr);
624 if ( status.isSuccess() ) {
625 pReg->setAddress(pAddr);
626 status = pSvc->fillRepRefs(pAddr, n_tuple);
627 }
628 return status;
629 }
630 return IDataProviderSvc::NO_DATA_LOADER;
631 }
632 catch(...) {
633 }
634 }
635 return INVALID_OBJECT;
636 }
637
639 StatusCode NTupleSvc::writeRecord(const std::string& fullPath ) {
640 NTuple::Tuple* pObj = 0;
641 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
642 if ( status.isSuccess() ) {
643 return writeRecord ( pObj );
644 }
645 return INVALID_OBJ_PATH;
646 }
647
649 StatusCode NTupleSvc::writeRecord( DataObject* pParent, const std::string& relPath) {
650 NTuple::Tuple* pObj = 0;
651 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
652 if ( status.isSuccess() ) {
653 return writeRecord ( pObj );
654 }
655 return INVALID_OBJ_PATH;
656 }
657
659 StatusCode NTupleSvc::readRecord( NTuple::Tuple* n_tuple ) {
660 StatusCode status = INVALID_OBJECT;
661 NTuple::TupleImp* tuple = (NTuple::TupleImp*)n_tuple;
662 if ( 0 != tuple ) {
663 try {
664 IConversionSvc* pSvc = tuple->conversionService();
665 if ( 0 == pSvc ) {
666 pSvc = getDataLoader(n_tuple->registry());
667 tuple->setConversionService(pSvc);
668 }
669 if ( 0 != pSvc ) {
670 IRegistry* pReg = n_tuple->registry();
671 IOpaqueAddress* pAddr = pReg->address();
672 status = pSvc->updateObj(pAddr, n_tuple);
673 if ( status.isSuccess() ) {
674 status = pSvc->updateObjRefs(pAddr, n_tuple);
675 }
676 return status;
677 }
678 status = IDataProviderSvc::NO_DATA_LOADER;
679 }
680 catch(...) {
681 status = INVALID_OBJECT;
682 }
683 }
684 return status;
685 }
686
688 StatusCode NTupleSvc::readRecord(const std::string& fullPath) {
689 NTuple::Tuple* pObj = 0;
690 StatusCode status = findObject(fullPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
691 if ( status.isSuccess() ) {
692 return readRecord ( pObj );
693 }
694 return INVALID_OBJ_PATH;
695 }
696
698 StatusCode NTupleSvc::readRecord(DataObject* pParent, const std::string& relPath) {
699 NTuple::Tuple* pObj = 0;
700 StatusCode status = findObject(pParent, relPath, *pp_cast<DataObject>(&pObj)); // Check if object is present
701 if ( status.isSuccess() ) {
702 return readRecord ( pObj );
703 }
704 return INVALID_OBJ_PATH;
705 }