Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TTreeReaderValue.cxx
Go to the documentation of this file.
1 // @(#)root/treeplayer:$Id$
2 // Author: Axel Naumann, 2011-09-28
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers and al. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 #include "TTreeReaderValue.h"
13 
14 #include "TTreeReader.h"
15 #include "TBranchClones.h"
16 #include "TBranchElement.h"
17 #include "TBranchRef.h"
18 #include "TBranchSTL.h"
19 #include "TBranchProxyDirector.h"
20 #include "TClassEdit.h"
21 #include "TFriendElement.h"
22 #include "TFriendProxy.h"
23 #include "TLeaf.h"
24 #include "TTreeProxyGenerator.h"
25 #include "TTreeReaderValue.h"
26 #include "TRegexp.h"
27 #include "TStreamerInfo.h"
28 #include "TStreamerElement.h"
29 #include "TNtuple.h"
30 #include "TROOT.h"
31 #include <vector>
32 
33 // clang-format off
34 /**
35  * \class TTreeReaderValue
36  * \ingroup treeplayer
37  * \brief An interface for reading values stored in ROOT columnar datasets
38  *
39  * The TTreeReaderValue is a type-safe tool to be used in association with a TTreeReader
40  * to access the values stored in TTree, TNtuple and TChain datasets.
41  * TTreeReaderValue can be also used to access collections such as `std::vector`s or TClonesArray
42  * stored in columnar datasets but it is recommended to use TTreeReaderArray instead as it offers
43  * several advantages.
44  *
45  * See the documentation of TTreeReader for more details and examples.
46 */
47 // clang-format on
48 
49 ClassImp(ROOT::Internal::TTreeReaderValueBase);
50 
51 ////////////////////////////////////////////////////////////////////////////////
52 /// Construct a tree value reader and register it with the reader object.
53 
54 ROOT::Internal::TTreeReaderValueBase::TTreeReaderValueBase(TTreeReader* reader /*= 0*/,
55  const char* branchname /*= 0*/,
56  TDictionary* dict /*= 0*/):
57  fHaveLeaf(0),
58  fHaveStaticClassOffsets(0),
59  fReadStatus(kReadNothingYet),
60  fBranchName(branchname),
61  fTreeReader(reader),
62  fDict(dict)
63 {
64  RegisterWithTreeReader();
65 }
66 
67 ////////////////////////////////////////////////////////////////////////////////
68 /// Copy-construct.
69 
70 ROOT::Internal::TTreeReaderValueBase::TTreeReaderValueBase(const TTreeReaderValueBase& rhs):
71  fHaveLeaf(rhs.fHaveLeaf),
72  fHaveStaticClassOffsets(rhs.fHaveStaticClassOffsets),
73  fReadStatus(rhs.fReadStatus),
74  fSetupStatus(rhs.fSetupStatus),
75  fBranchName(rhs.fBranchName),
76  fLeafName(rhs.fLeafName),
77  fTreeReader(rhs.fTreeReader),
78  fDict(rhs.fDict),
79  fProxy(rhs.fProxy),
80  fLeaf(rhs.fLeaf),
81  fStaticClassOffsets(rhs.fStaticClassOffsets)
82 {
83  RegisterWithTreeReader();
84 }
85 
86 ////////////////////////////////////////////////////////////////////////////////
87 /// Copy-assign.
88 
89 ROOT::Internal::TTreeReaderValueBase&
90 ROOT::Internal::TTreeReaderValueBase::operator=(const TTreeReaderValueBase& rhs) {
91  if (&rhs != this) {
92  fHaveLeaf = rhs.fHaveLeaf;
93  fHaveStaticClassOffsets = rhs.fHaveStaticClassOffsets;
94  fBranchName = rhs.fBranchName;
95  fLeafName = rhs.fLeafName;
96  if (fTreeReader != rhs.fTreeReader) {
97  if (fTreeReader)
98  fTreeReader->DeregisterValueReader(this);
99  fTreeReader = rhs.fTreeReader;
100  RegisterWithTreeReader();
101  }
102  fDict = rhs.fDict;
103  fProxy = rhs.fProxy;
104  fLeaf = rhs.fLeaf;
105  fSetupStatus = rhs.fSetupStatus;
106  fReadStatus = rhs.fReadStatus;
107  fStaticClassOffsets = rhs.fStaticClassOffsets;
108  }
109  return *this;
110 }
111 
112 ////////////////////////////////////////////////////////////////////////////////
113 /// Unregister from tree reader, cleanup.
114 
115 ROOT::Internal::TTreeReaderValueBase::~TTreeReaderValueBase()
116 {
117  if (fTreeReader) fTreeReader->DeregisterValueReader(this);
118  R__ASSERT((fLeafName.Length() == 0 ) == !fHaveLeaf
119  && "leafness disagreement");
120  R__ASSERT(fStaticClassOffsets.empty() == !fHaveStaticClassOffsets
121  && "static class offset disagreement");
122 }
123 
124 ////////////////////////////////////////////////////////////////////////////////
125 /// Register with tree reader.
126 
127 void ROOT::Internal::TTreeReaderValueBase::RegisterWithTreeReader() {
128  if (fTreeReader) {
129  if (!fTreeReader->RegisterValueReader(this)) {
130  fTreeReader = nullptr;
131  }
132  }
133 }
134 
135 ////////////////////////////////////////////////////////////////////////////////
136 /// Try to read the value from the TBranchProxy, returns
137 /// the status of the read.
138 
139 
140 
141 template <ROOT::Internal::TTreeReaderValueBase::BranchProxyRead_t Func>
142 ROOT::Internal::TTreeReaderValueBase::EReadStatus ROOT::Internal::TTreeReaderValueBase::ProxyReadTemplate()
143 {
144  if ((fProxy->*Func)()) {
145  fReadStatus = kReadSuccess;
146  } else {
147  fReadStatus = kReadError;
148  }
149  return fReadStatus;
150 }
151 
152 ROOT::Internal::TTreeReaderValueBase::EReadStatus
153 ROOT::Internal::TTreeReaderValueBase::ProxyReadDefaultImpl() {
154  if (!fProxy) return kReadNothingYet;
155  if (fProxy->IsInitialized() || fProxy->Setup()) {
156 
157  using EReadType = ROOT::Detail::TBranchProxy::EReadType;
158  using TBranchPoxy = ROOT::Detail::TBranchProxy;
159 
160  EReadType readtype = EReadType::kNoDirector;
161  if (fProxy) readtype = fProxy->GetReadType();
162 
163  switch (readtype) {
164  case EReadType::kNoDirector:
165  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoDirector>;
166  break;
167  case EReadType::kReadParentNoCollection:
168  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadParentNoCollection>;
169  break;
170  case EReadType::kReadParentCollectionNoPointer:
171  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadParentCollectionNoPointer>;
172  break;
173  case EReadType::kReadParentCollectionPointer:
174  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadParentCollectionPointer>;
175  break;
176  case EReadType::kReadNoParentNoBranchCountCollectionPointer:
177  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentNoBranchCountCollectionPointer>;
178  break;
179  case EReadType::kReadNoParentNoBranchCountCollectionNoPointer:
180  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentNoBranchCountCollectionNoPointer>;
181  break;
182  case EReadType::kReadNoParentNoBranchCountNoCollection:
183  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentNoBranchCountNoCollection>;
184  break;
185  case EReadType::kReadNoParentBranchCountCollectionPointer:
186  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentBranchCountCollectionPointer>;
187  break;
188  case EReadType::kReadNoParentBranchCountCollectionNoPointer:
189  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentBranchCountCollectionNoPointer>;
190  break;
191  case EReadType::kReadNoParentBranchCountNoCollection:
192  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadTemplate<&TBranchPoxy::ReadNoParentBranchCountNoCollection>;
193  break;
194  case EReadType::kDefault:
195  // intentional fall through.
196  default: fProxyReadFunc = &TTreeReaderValueBase::ProxyReadDefaultImpl;
197  }
198  return (this->*fProxyReadFunc)();
199  }
200 
201  // If somehow the Setup fails call the original Read to
202  // have the proper error handling (message only if the Setup fails
203  // and the current proxy entry is different than the TTree's current entry)
204  if (fProxy->Read()) {
205  fReadStatus = kReadSuccess;
206  } else {
207  fReadStatus = kReadError;
208  }
209  return fReadStatus;
210 }
211 
212 ////////////////////////////////////////////////////////////////////////////////
213 /// Stringify the template argument.
214 std::string ROOT::Internal::TTreeReaderValueBase::GetElementTypeName(const std::type_info& ti) {
215  int err;
216  char* buf = TClassEdit::DemangleTypeIdName(ti, err);
217  std::string ret = buf;
218  free(buf);
219  return ret;
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// The TTreeReader has switched to a new TTree. Update the leaf.
224 
225 void ROOT::Internal::TTreeReaderValueBase::NotifyNewTree(TTree* newTree) {
226  // Since the TTree structure might have change, let's make sure we
227  // use the right reading function.
228  fProxyReadFunc = &TTreeReaderValueBase::ProxyReadDefaultImpl;
229 
230  if (!fHaveLeaf || !newTree) {
231  fLeaf = nullptr;
232  return;
233  }
234 
235  TBranch *myBranch = newTree->GetBranch(fBranchName);
236 
237  if (!myBranch) {
238  fReadStatus = kReadError;
239  Error("TTreeReaderValueBase::GetLeaf()", "Unable to get the branch from the tree");
240  return;
241  }
242 
243  fLeaf = myBranch->GetLeaf(fLeafName);
244  if (!fLeaf) {
245  Error("TTreeReaderValueBase::GetLeaf()", "Failed to get the leaf from the branch");
246  }
247 }
248 
249 ////////////////////////////////////////////////////////////////////////////////
250 /// Returns the memory address of the object being read.
251 
252 void* ROOT::Internal::TTreeReaderValueBase::GetAddress() {
253  if (ProxyRead() != kReadSuccess) return 0;
254 
255  if (fHaveLeaf){
256  if (GetLeaf()){
257  return fLeaf->GetValuePointer();
258  }
259  else {
260  fReadStatus = kReadError;
261  Error("TTreeReaderValueBase::GetAddress()", "Unable to get the leaf");
262  return 0;
263  }
264  }
265  if (fHaveStaticClassOffsets){ // Follow all the pointers
266  Byte_t *address = (Byte_t*)fProxy->GetWhere();
267 
268  for (unsigned int i = 0; i < fStaticClassOffsets.size() - 1; ++i){
269  address = *(Byte_t**)(address + fStaticClassOffsets[i]);
270  }
271 
272  return address + fStaticClassOffsets.back();
273  }
274  return (Byte_t*)fProxy->GetWhere();
275 }
276 
277 ////////////////////////////////////////////////////////////////////////////////
278 /// \brief Search a branch the name of which contains a "."
279 /// \param[out] myLeaf The leaf identified by the name if found (can be untouched).
280 /// \param[out] branchActualType Dictionary associated to the type of the leaf (can be untouched).
281 /// \param[out] errMsg The error message (can be untouched).
282 /// \return The address of the branch if found, nullptr otherwise
283 /// This method allows to efficiently search for branches which have names which
284 /// contain "dots", for example "w.v.a" or "v.a".
285 /// Therefore, it allows to support names such as v.a where the branch was
286 /// created with this syntax:
287 /// ```{.cpp}
288 /// myTree->Branch("v", &v, "a/I:b:/I")
289 /// ```
290 /// The method has some side effects, namely it can modify fSetupStatus, fProxy
291 /// and fStaticClassOffsets/fHaveStaticClassOffsets.
292 TBranch *ROOT::Internal::TTreeReaderValueBase::SearchBranchWithCompositeName(TLeaf *&myLeaf, TDictionary *&branchActualType, std::string &errMsg)
293 {
294  TRegexp leafNameExpression ("\\.[a-zA-Z0-9_]+$");
295  TString leafName (fBranchName(leafNameExpression));
296  TString branchName = fBranchName(0, fBranchName.Length() - leafName.Length());
297  auto branch = fTreeReader->GetTree()->GetBranch(branchName);
298  if (!branch){
299  std::vector<TString> nameStack;
300  nameStack.push_back(TString()); //Trust me
301  nameStack.push_back(leafName.Strip(TString::kBoth, '.'));
302  leafName = branchName(leafNameExpression);
303  branchName = branchName(0, branchName.Length() - leafName.Length());
304 
305  branch = fTreeReader->GetTree()->GetBranch(branchName);
306  if (!branch) branch = fTreeReader->GetTree()->GetBranch(branchName + ".");
307  if (leafName.Length()) nameStack.push_back(leafName.Strip(TString::kBoth, '.'));
308 
309  while (!branch && branchName.Contains(".")){
310  leafName = branchName(leafNameExpression);
311  branchName = branchName(0, branchName.Length() - leafName.Length());
312  branch = fTreeReader->GetTree()->GetBranch(branchName);
313  if (!branch) branch = fTreeReader->GetTree()->GetBranch(branchName + ".");
314  nameStack.push_back(leafName.Strip(TString::kBoth, '.'));
315  }
316 
317  if (branch && branch->IsA() == TBranchElement::Class()){
318  TBranchElement *myBranchElement = (TBranchElement*)branch;
319 
320  TString traversingBranch = nameStack.back();
321  nameStack.pop_back();
322 
323  bool found = true;
324 
325  TDataType *finalDataType = 0;
326 
327  std::vector<Long64_t> offsets;
328  Long64_t offset = 0;
329  TClass *elementClass = 0;
330 
331  TObjArray *myObjArray = myBranchElement->GetInfo()->GetElements();
332  TVirtualStreamerInfo *myInfo = myBranchElement->GetInfo();
333 
334  while (nameStack.size() && found){
335  found = false;
336 
337  for (int i = 0; i < myObjArray->GetEntries(); ++i){
338 
339  TStreamerElement *tempStreamerElement = (TStreamerElement*)myObjArray->At(i);
340 
341  if (!strcmp(tempStreamerElement->GetName(), traversingBranch.Data())){
342  offset += myInfo->GetElementOffset(i);
343 
344  traversingBranch = nameStack.back();
345  nameStack.pop_back();
346 
347  elementClass = tempStreamerElement->GetClass();
348  if (elementClass) {
349  myInfo = elementClass->GetStreamerInfo(0);
350  myObjArray = myInfo->GetElements();
351  // FIXME: this is odd, why is 'i' not also reset????
352  }
353  else {
354  finalDataType = TDataType::GetDataType((EDataType)tempStreamerElement->GetType());
355  if (!finalDataType) {
356  TDictionary* seType = TDictionary::GetDictionary(tempStreamerElement->GetTypeName());
357  if (seType && seType->IsA() == TDataType::Class()) {
358  finalDataType = TDataType::GetDataType((EDataType)((TDataType*)seType)->GetType());
359  }
360  }
361  }
362 
363  if (tempStreamerElement->IsaPointer()){
364  offsets.push_back(offset);
365  offset = 0;
366  }
367 
368  found = true;
369  break;
370  }
371  }
372  }
373 
374  offsets.push_back(offset);
375 
376  if (found){
377  fStaticClassOffsets = offsets;
378  fHaveStaticClassOffsets = 1;
379 
380  if (fDict != finalDataType && fDict != elementClass){
381  errMsg = "Wrong data type ";
382  errMsg += finalDataType ? finalDataType->GetName() : elementClass ? elementClass->GetName() : "UNKNOWN";
383  fSetupStatus = kSetupMismatch;
384  fProxy = 0;
385  return nullptr;
386  }
387  }
388  }
389 
390 
391  if (!fHaveStaticClassOffsets) {
392  errMsg = "The tree does not have a branch called ";
393  errMsg += fBranchName;
394  errMsg += ". You could check with TTree::Print() for available branches.";
395  fSetupStatus = kSetupMissingBranch;
396  fProxy = 0;
397  return nullptr;
398  }
399  }
400  else {
401  myLeaf = branch->GetLeaf(TString(leafName(1, leafName.Length())));
402  if (!myLeaf){
403  errMsg = "The tree does not have a branch, nor a sub-branch called ";
404  errMsg += fBranchName;
405  errMsg += ". You could check with TTree::Print() for available branches.";
406  fSetupStatus = kSetupMissingBranch;
407  fProxy = 0;
408  return nullptr;
409  }
410  else {
411  TDataType *tempDict = gROOT->GetType(myLeaf->GetTypeName());
412  if (tempDict && fDict->IsA() == TDataType::Class() && tempDict->GetType() == ((TDataType*)fDict)->GetType()) {
413  //fLeafOffset = myLeaf->GetOffset() / 4;
414  branchActualType = fDict;
415  fLeaf = myLeaf;
416  fBranchName = branchName;
417  fLeafName = leafName(1, leafName.Length());
418  fHaveLeaf = fLeafName.Length() > 0;
419  fSetupStatus = kSetupMatchLeaf;
420  }
421  else {
422  errMsg = "Leaf of type ";
423  errMsg += myLeaf->GetTypeName();
424  errMsg += " cannot be read by TTreeReaderValue<";
425  errMsg += fDict->GetName();
426  errMsg += ">.";
427  fSetupStatus = kSetupMismatch;
428  return nullptr;
429  }
430  }
431  }
432 
433  return branch;
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 /// Create the proxy object for our branch.
438 
439 void ROOT::Internal::TTreeReaderValueBase::CreateProxy() {
440 
441  constexpr const char* errPrefix = "TTreeReaderValueBase::CreateProxy()";
442 
443  if (fProxy) {
444  return;
445  }
446 
447  fSetupStatus = kSetupInternalError; // Fallback; set to something concrete below.
448  if (!fTreeReader) {
449  Error(errPrefix, "TTreeReader object not set / available for branch %s!",
450  fBranchName.Data());
451  fSetupStatus = kSetupTreeDestructed;
452  return;
453  }
454 
455  auto branchFromFullName = fTreeReader->GetTree()->GetBranch(fBranchName);
456 
457  if (!fDict) {
458  const char* brDataType = "{UNDETERMINED}";
459  if (branchFromFullName) {
460  TDictionary* brDictUnused = 0;
461  brDataType = GetBranchDataType(branchFromFullName, brDictUnused, fDict);
462  }
463  Error(errPrefix, "The template argument type T of %s accessing branch %s (which contains data of type %s) is not known to ROOT. You will need to create a dictionary for it.",
464  GetDerivedTypeName(), fBranchName.Data(), brDataType);
465  fSetupStatus = kSetupMissingDictionary;
466  return;
467  }
468 
469  // Search for the branchname, determine what it contains, and wire the
470  // TBranchProxy representing it to us so we can access its data.
471 
472  TNamedBranchProxy* namedProxy = fTreeReader->FindProxy(fBranchName);
473  if (namedProxy && namedProxy->GetDict() == fDict) {
474  fProxy = namedProxy->GetProxy();
475  // But go on: we need to set fLeaf etc!
476  }
477 
478  const std::string originalBranchName = fBranchName.Data();
479 
480  TLeaf *myLeaf = nullptr;
481  TDictionary* branchActualType = nullptr;
482  std::string errMsg;
483 
484  TBranch *branch = nullptr;
485  // If the branch name contains at least a dot, we analyse it in detail and
486  // we give priority to the branch identified over the one which is found
487  // with the TTree::GetBranch method. This allows to correctly pick the desired
488  // branch in cases where a TTree has two branches, one called for example
489  // "w.v.a" and another one called "v.a".
490  // This behaviour is described in ROOT-9312.
491  if (fBranchName.Contains(".")) {
492  branch = SearchBranchWithCompositeName(myLeaf, branchActualType, errMsg);
493  // In rare cases where leaves contain the name of the branch as part of their
494  // name and a dot in the branch name (such as: branch "b." and leaf "b.a")
495  // the previous method may return the branch name ("b.") rather than the
496  // leaf name ("b.a") as we expect.
497  // For these cases, we do not have to give priority to the previously
498  // identified branch since we are in a different situation.
499  // This behaviour is described in ROOT-9757.
500  if (branch && branch->IsA() == TBranchElement::Class() && branchFromFullName){
501  branch = branchFromFullName;
502  fStaticClassOffsets = {};
503  fHaveStaticClassOffsets = 0;
504  }
505  }
506 
507  if (!branch) {
508  // We had an error, the branch name had no "." or we simply did not find anything.
509  // We check if we had a branch found with the full name with a dot in it.
510  branch = branchFromFullName;
511  if (!branch) {
512  // Also that one was empty. We need to error out. We do it properly: at
513  // first we check if the routine to find branches with names with a "."
514  // provided a specific error message. If not, we go with a generic message.
515  if (errMsg.empty()) {
516  errMsg = "The tree does not have a branch called ";
517  errMsg += fBranchName.Data();
518  errMsg += ". You could check with TTree::Print() for available branches.";
519  }
520 #if !defined(_MSC_VER)
521 #pragma GCC diagnostic push
522 #pragma GCC diagnostic ignored "-Wformat-security"
523 #endif
524  Error(errPrefix, errMsg.c_str());
525 #if !defined(_MSC_VER)
526 #pragma GCC diagnostic pop
527 #endif
528  return;
529  } else {
530  // The branch found with the simplest search approach was successful.
531  // We reset the state, we continue
532  fSetupStatus = kSetupInternalError;
533  fStaticClassOffsets = {};
534  fHaveStaticClassOffsets = 0;
535  }
536  }
537 
538  if (!myLeaf && !fHaveStaticClassOffsets) {
539  // The following two lines cannot be swapped. The GetBranchDataType can
540  // change the value of branchActualType
541  const char* branchActualTypeName = GetBranchDataType(branch, branchActualType, fDict);
542  if (!branchActualType) {
543  Error(errPrefix, "The branch %s contains data of type %s, which does not have a dictionary.",
544  fBranchName.Data(), branchActualTypeName ? branchActualTypeName : "{UNDETERMINED TYPE}");
545  fProxy = 0;
546  return;
547  }
548 
549  // Check if the dictionaries are TClass instances and if there is inheritance
550  // because in this case, we can read the values.
551  auto dictAsClass = dynamic_cast<TClass*>(fDict);
552  auto branchActualTypeAsClass = dynamic_cast<TClass*>(branchActualType);
553  auto inheritance = dictAsClass && branchActualTypeAsClass && branchActualTypeAsClass->InheritsFrom(dictAsClass);
554 
555  if (fDict != branchActualType && !inheritance) {
556  TDataType *dictdt = dynamic_cast<TDataType*>(fDict);
557  TDataType *actualdt = dynamic_cast<TDataType*>(branchActualType);
558  bool complainAboutMismatch = true;
559  if (dictdt && actualdt) {
560  if (dictdt->GetType() > 0 && dictdt->GetType() == actualdt->GetType()) {
561  // Same numerical type but different TDataType, likely Long64_t
562  complainAboutMismatch = false;
563  } else if ((actualdt->GetType() == kDouble32_t && dictdt->GetType() == kDouble_t)
564  || (actualdt->GetType() == kFloat16_t && dictdt->GetType() == kFloat_t)) {
565  // Double32_t and Float16_t never "decay" to their underlying type;
566  // we need to identify them manually here (ROOT-8731).
567  complainAboutMismatch = false;
568  }
569  }
570  if (complainAboutMismatch) {
571  Error(errPrefix,
572  "The branch %s contains data of type %s. It cannot be accessed by a TTreeReaderValue<%s>",
573  fBranchName.Data(), branchActualType->GetName(),
574  fDict->GetName());
575  return;
576  }
577  }
578  }
579 
580  if (!namedProxy) {
581  // Search for the branchname, determine what it contains, and wire the
582  // TBranchProxy representing it to us so we can access its data.
583  // A proxy for branch must not have been created before (i.e. check
584  // fProxies before calling this function!)
585 
586  TString membername;
587 
588  bool isTopLevel = branch->GetMother() == branch;
589  if (!isTopLevel) {
590  membername = strrchr(branch->GetName(), '.');
591  if (membername.IsNull()) {
592  membername = branch->GetName();
593  }
594  }
595  auto director = fTreeReader->fDirector;
596  // Determine if the branch is actually in a Friend TTree and if so which.
597  if (branch->GetTree() != fTreeReader->GetTree()->GetTree()) {
598  // It is in a friend, let's find the 'index' in the list of friend ...
599  int index = -1;
600  int current = 0;
601  TFriendElement *fe_found = nullptr;
602  for(auto fe : TRangeDynCast<TFriendElement>( fTreeReader->GetTree()->GetTree()->GetListOfFriends())) {
603  if (branch->GetTree() == fe->GetTree()) {
604  index = current;
605  fe_found = fe;
606  break;
607  }
608  ++current;
609  }
610  if (index == -1) {
611  Error(errPrefix, "The branch %s is contained in a Friend TTree that is not directly attached to the main.\n"
612  "This is not yet supported by TTreeReader.",
613  fBranchName.Data());
614  return;
615  }
616  const char *localBranchName = originalBranchName.c_str();
617  if (branch != branch->GetTree()->GetBranch(localBranchName)) {
618  // Try removing the name of the TTree.
619  auto len = strlen( branch->GetTree()->GetName());
620  if (strncmp(localBranchName, branch->GetTree()->GetName(), len) == 0
621  && localBranchName[len] == '.'
622  && branch != branch->GetTree()->GetBranch(localBranchName+len+1)) {
623  localBranchName = localBranchName + len + 1;
624  } else {
625  len = strlen(fe_found->GetName());
626  if (strncmp(localBranchName, fe_found->GetName(), len) == 0
627  && localBranchName[len] == '.'
628  && branch != branch->GetTree()->GetBranch(localBranchName+len+1)) {
629  localBranchName = localBranchName + len + 1;
630  }
631  }
632  }
633  TFriendProxy *feproxy = nullptr;
634  if ((size_t)index < fTreeReader->fFriendProxies.size()) {
635  feproxy = fTreeReader->fFriendProxies.at(index);
636  }
637  if (!feproxy) {
638  feproxy = new ROOT::Internal::TFriendProxy(director, fTreeReader->GetTree(), index);
639  fTreeReader->fFriendProxies.resize(index+1);
640  fTreeReader->fFriendProxies.at(index) = feproxy;
641  }
642  namedProxy = new TNamedBranchProxy(feproxy->GetDirector(), branch, originalBranchName.c_str(), branch->GetName(), membername);
643  } else {
644  namedProxy = new TNamedBranchProxy(director, branch, originalBranchName.c_str(), membername);
645  }
646  fTreeReader->AddProxy(namedProxy);
647  }
648 
649  // Update named proxy's dictionary
650  if (!namedProxy->GetDict())
651  namedProxy->SetDict(fDict);
652 
653  fProxy = namedProxy->GetProxy();
654  if (fProxy) {
655  fSetupStatus = kSetupMatch;
656  } else {
657  fSetupStatus = kSetupMismatch;
658  }
659 }
660 
661 ////////////////////////////////////////////////////////////////////////////////
662 /// Retrieve the type of data stored by branch; put its dictionary into
663 /// dict, return its type name. If no dictionary is available, at least
664 /// its type name should be returned.
665 
666 const char* ROOT::Internal::TTreeReaderValueBase::GetBranchDataType(TBranch* branch,
667  TDictionary* &dict,
668  TDictionary const *curDict)
669 {
670  dict = 0;
671  if (branch->IsA() == TBranchElement::Class()) {
672  TBranchElement* brElement = (TBranchElement*)branch;
673 
674  auto ResolveTypedef = [&]() -> void {
675  if (dict->IsA() != TDataType::Class())
676  return;
677  // Resolve the typedef.
678  dict = TDictionary::GetDictionary(((TDataType*)dict)->GetTypeName());
679  if (dict->IsA() != TDataType::Class()) {
680  // Might be a class.
681  if (dict != curDict) {
682  dict = TClass::GetClass(brElement->GetTypeName());
683  }
684  if (dict != curDict) {
685  dict = brElement->GetCurrentClass();
686  }
687  }
688  };
689 
690  if (brElement->GetType() == TBranchElement::kSTLNode ||
691  brElement->GetType() == TBranchElement::kLeafNode ||
692  brElement->GetType() == TBranchElement::kObjectNode) {
693 
694  TStreamerInfo *streamerInfo = brElement->GetInfo();
695  Int_t id = brElement->GetID();
696 
697  if (id >= 0){
698  TStreamerElement *element = (TStreamerElement*)streamerInfo->GetElements()->At(id);
699  if (element->IsA() == TStreamerSTL::Class()){
700  TStreamerSTL *myStl = (TStreamerSTL*)element;
701  dict = myStl->GetClass();
702  return 0;
703  }
704  }
705 
706  if (brElement->GetType() == 3 || brElement->GetType() == 4) {
707  dict = brElement->GetCurrentClass();
708  return brElement->GetTypeName();
709  }
710 
711  if (brElement->GetTypeName())
712  dict = TDictionary::GetDictionary(brElement->GetTypeName());
713 
714  if (dict)
715  ResolveTypedef();
716  else
717  dict = brElement->GetCurrentClass();
718 
719  return brElement->GetTypeName();
720  } else if (brElement->GetType() == TBranchElement::kClonesNode) {
721  dict = TClonesArray::Class();
722  return "TClonesArray";
723  } else if (brElement->GetType() == 31
724  || brElement->GetType() == 41) {
725  // it's a member, extract from GetClass()'s streamer info
726  Error("TTreeReaderValueBase::GetBranchDataType()", "Must use TTreeReaderArray to access a member of an object that is stored in a collection.");
727  } else if (brElement->GetType() == -1 && brElement->GetTypeName()) {
728  dict = TDictionary::GetDictionary(brElement->GetTypeName());
729  ResolveTypedef();
730  return brElement->GetTypeName();
731  } else {
732  Error("TTreeReaderValueBase::GetBranchDataType()", "Unknown type and class combination: %i, %s", brElement->GetType(), brElement->GetClassName());
733  }
734  return 0;
735  } else if (branch->IsA() == TBranch::Class()
736  || branch->IsA() == TBranchObject::Class()
737  || branch->IsA() == TBranchSTL::Class()) {
738  if (branch->GetTree()->IsA() == TNtuple::Class()){
739  dict = TDataType::GetDataType(kFloat_t);
740  return dict->GetName();
741  }
742  const char* dataTypeName = branch->GetClassName();
743  if ((!dataTypeName || !dataTypeName[0])
744  && branch->IsA() == TBranch::Class()) {
745  TLeaf *myLeaf = branch->GetLeaf(branch->GetName());
746  if (myLeaf){
747  TDictionary *myDataType = TDictionary::GetDictionary(myLeaf->GetTypeName());
748  if (myDataType && myDataType->IsA() == TDataType::Class()){
749  if (myLeaf->GetLeafCount() != nullptr || myLeaf->GetLenStatic() > 1) {
750  Error("TTreeReaderValueBase::GetBranchDataType()", "Must use TTreeReaderArray to read branch %s: it contains an array or a collection.", branch->GetName());
751  return 0;
752  }
753  dict = TDataType::GetDataType((EDataType)((TDataType*)myDataType)->GetType());
754  return myLeaf->GetTypeName();
755  }
756  }
757 
758  // leaflist. Can't represent.
759  Error("TTreeReaderValueBase::GetBranchDataType()", "The branch %s was created using a leaf list and cannot be represented as a C++ type. Please access one of its siblings using a TTreeReaderArray:", branch->GetName());
760  TIter iLeaves(branch->GetListOfLeaves());
761  TLeaf* leaf = 0;
762  while ((leaf = (TLeaf*) iLeaves())) {
763  Error("TTreeReaderValueBase::GetBranchDataType()", " %s.%s", branch->GetName(), leaf->GetName());
764  }
765  return 0;
766  }
767  if (dataTypeName) dict = TDictionary::GetDictionary(dataTypeName);
768  return dataTypeName;
769  } else if (branch->IsA() == TBranchClones::Class()) {
770  dict = TClonesArray::Class();
771  return "TClonesArray";
772  } else if (branch->IsA() == TBranchRef::Class()) {
773  // Can't represent.
774  Error("TTreeReaderValueBase::GetBranchDataType()", "The branch %s is a TBranchRef and cannot be represented as a C++ type.", branch->GetName());
775  return 0;
776  } else {
777  Error("TTreeReaderValueBase::GetBranchDataType()", "The branch %s is of type %s - something that is not handled yet.", branch->GetName(), branch->IsA()->GetName());
778  return 0;
779  }
780 
781  return 0;
782 }