Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TTreeReaderGenerator.cxx
Go to the documentation of this file.
1 // @(#)root/treeplayer:$Id$
2 // Author: Akos Hajdu 22/06/2015
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2015, 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 "TTreeReaderGenerator.h"
13 #include <algorithm>
14 #include <stdio.h>
15 #include <fstream>
16 
17 #include "TBranchElement.h"
18 #include "TChain.h"
19 #include "TClass.h"
20 #include "TClassEdit.h"
21 #include "TClonesArray.h"
22 #include "TError.h"
23 #include "TFile.h"
24 #include "TLeaf.h"
25 #include "TLeafC.h"
26 #include "TLeafObject.h"
27 #include "TROOT.h"
28 #include "TStreamerElement.h"
29 #include "TStreamerInfo.h"
30 #include "TTree.h"
32 #include "TVirtualStreamerInfo.h"
33 
34 namespace ROOT {
35 namespace Internal {
36 
37  ////////////////////////////////////////////////////////////////////////////////
38  /// Constructor. Analyzes the tree and writes selector.
39 
40  TTreeReaderGenerator::TTreeReaderGenerator(TTree* tree, const char *classname, Option_t *option) :
41  TTreeGeneratorBase(tree, option), fClassname(classname),
42  fIncludeAllLeaves(kFALSE), fIncludeAllTopmost(kFALSE)
43  {
44  ParseOptions();
45  AnalyzeTree(fTree);
46  WriteSelector();
47  }
48 
49  ////////////////////////////////////////////////////////////////////////////////
50  /// Add a reader to the generated code.
51 
52  void TTreeReaderGenerator::AddReader(TTreeReaderDescriptor::ReaderType type, TString dataType, TString name,
53  TString branchName, TBranchDescriptor *parent, Bool_t isLeaf)
54  {
55  if(BranchNeedsReader(branchName, parent, isLeaf)) {
56  // Ignore unknown types
57  if (dataType.EqualTo("")) {
58  Warning("TTreeReaderGenerator::AddReader", "Ignored branch %s because type is unsupported.", branchName.Data());
59  return;
60  }
61  // Loop through existing readers to check duplicate branch names
62  TIter next(&fListOfReaders);
63  TTreeReaderDescriptor *descriptor;
64  while ( ( descriptor = (TTreeReaderDescriptor*)next() ) ) {
65  if (descriptor->fBranchName.EqualTo(branchName)) {
66  Warning("AddReader", "Ignored branch %s because a branch with the same name already exists. "
67  "TTreeReader requires an unique name for the branches. You may need to "
68  "put a dot at the end of the name of top-level branches.", branchName.Data());
69  return;
70  }
71  }
72  name.ReplaceAll('.', '_'); // Replace dots with underscore
73  // Remove array dimensions from name
74  while (name.Index('[') >= 0 && name.Index(']') >= 0 && name.Index(']') > name.Index('[')) {
75  name.Remove(name.Index('['), name.Index(']') - name.Index('[') + 1);
76  }
77  fListOfReaders.Add( new TTreeReaderDescriptor(type, dataType, name, branchName) );
78  }
79  }
80 
81  ////////////////////////////////////////////////////////////////////////////////
82  /// Analyse sub-branches of 'branch' recursively and extract readers.
83 
84  UInt_t TTreeReaderGenerator::AnalyzeBranches(TBranchDescriptor *desc, TBranchElement *branch, TVirtualStreamerInfo *info)
85  {
86  if (info==0) info = branch->GetInfo();
87 
88  TIter branches(branch->GetListOfBranches());
89 
90  return AnalyzeBranches(desc, branches, info);
91  }
92 
93  ////////////////////////////////////////////////////////////////////////////////
94  /// Analyse sub-branches 'branches' recursively and extract readers.
95 
96  UInt_t TTreeReaderGenerator::AnalyzeBranches(TBranchDescriptor *desc, TIter &branches, TVirtualStreamerInfo *info)
97  {
98  UInt_t lookedAt = 0; // Number of sub-branches analyzed
99  ELocation outer_isclones = kOut; // Is the parent branch a container
100  TString containerName; // Container name
101  TString subBranchPrefix; // Prefix of sub-branch (if the elements and sub-branches do not match).
102  Bool_t skipped = false; // Should the branch be skipped
103 
104  // Check for containers (TClonesArray or STL)
105  {
106  TIter peek = branches;
107  TBranchElement *branch = (TBranchElement*)peek();
108  if (desc && desc->IsClones()) {
109  outer_isclones = kClones;
110  containerName = "TClonesArray";
111  } else if (desc && desc->IsSTL()) {
112  outer_isclones = kSTL;
113  containerName = desc->fContainerName;
114  } else if (!desc && branch && branch->GetBranchCount() == branch->GetMother()) {
115  if ( ((TBranchElement*)(branch->GetMother()))->GetType()==3) {
116  outer_isclones = kClones;
117  containerName = "TClonesArray";
118  } else {
119  outer_isclones = kSTL;
120  containerName = branch->GetMother()->GetClassName();
121  }
122  }
123  // FIXME: this is wrong because 'branch' is the first sub-branch and even though
124  // it can be a collection, it does not mean that the other sub-branches are
125  // collections as well.
126  /* else if (branch->GetType() == 3) {
127  outer_isclones = kClones;
128  containerName = "TClonesArray";
129  } else if (branch->GetType() == 4) {
130  outer_isclones = kSTL;
131  containerName = branch->GetMother()->GetSubBranch(branch)->GetClassName();
132  }*/
133  if (desc) {
134  subBranchPrefix = desc->fSubBranchPrefix;
135  } else {
136  TBranchElement *mom = (TBranchElement*)branch->GetMother();
137  subBranchPrefix = mom->GetName();
138  if (subBranchPrefix[subBranchPrefix.Length()-1]=='.') {
139  subBranchPrefix.Remove(subBranchPrefix.Length()-1);
140  } else if (mom->GetType()!=3 && mom->GetType() != 4) {
141  subBranchPrefix = "";
142  }
143  }
144  }
145 
146  // Loop through elements (i.e., sub-branches). The problem is that the elements
147  // and sub-branches do not always match. For example suppose that class A contains
148  // members x and y, and there is a class B inheriting from A and containing an extra
149  // member z. It is possible that B has two elements: A and z but three sub-branches
150  // x, y and z. Therefore, the branch iterator is treated differently.
151  TIter elements( info->GetElements() );
152  for( TStreamerElement *element = (TStreamerElement*)elements();
153  element;
154  element = (TStreamerElement*)elements() )
155  {
156  Bool_t isBase = false; // Does the element correspond to a base class
157  Bool_t usedBranch = kTRUE; // Does the branch correspond to the element (i.e., they match)
158  Bool_t isLeaf = true; // Is the branch a leaf (i.e. no sub-branches)
159  TIter peek = branches; // Iterator for sub-branches
160  // Always start with the first available sub-branch and if it does not match the element,
161  // try the next ones
162  TBranchElement *branch = (TBranchElement*)peek();
163  // There is a problem if there are more elements than branches
164  if (branch==0) {
165  if (desc) {
166  Error("AnalyzeBranches","Ran out of branches when looking in branch %s, class %s",
167  desc->fBranchName.Data(), info->GetName());
168  } else {
169  Error("AnalyzeBranches","Ran out of branches when looking in class %s, element %s",
170  info->GetName(), element->GetName());
171  }
172  return lookedAt;
173  }
174 
175  if (info->GetClass()->GetCollectionProxy() && strcmp(element->GetName(),"This")==0) {
176  continue; // Skip the artifical streamer element.
177  }
178 
179  if (element->GetType() == -1) {
180  continue; // This is an ignored TObject base class.
181  }
182 
183  // Get branch name
184  TString branchname = branch->GetName();
185  TString branchEndName;
186  {
187  TLeaf *leaf = (TLeaf*)branch->GetListOfLeaves()->At(0);
188  if (leaf && outer_isclones == kOut
189  && !(branch->GetType() == 3 || branch->GetType() == 4)) branchEndName = leaf->GetName();
190  else branchEndName = branch->GetName();
191  Int_t pos;
192  pos = branchEndName.Index(".");
193  if (pos!=-1) {
194  if (subBranchPrefix.Length() && branchEndName.BeginsWith(subBranchPrefix)) {
195  branchEndName.Remove(0, subBranchPrefix.Length() + 1);
196  }
197  }
198  }
199 
200  TString dataType; // Data type of reader
201  TTreeReaderDescriptor::ReaderType readerType = TTreeReaderDescriptor::ReaderType::kValue;
202  Bool_t ispointer = false;
203  ELocation isclones = outer_isclones; // Is the actual sub-branch a collection (inherit from parent branch)
204  // Get data type
205  switch(element->GetType()) {
206  // Built-in types
207  case TVirtualStreamerInfo::kBool: { dataType = "Bool_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
208  case TVirtualStreamerInfo::kChar: { dataType = "Char_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
209  case TVirtualStreamerInfo::kShort: { dataType = "Short_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
210  case TVirtualStreamerInfo::kInt: { dataType = "Int_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
211  case TVirtualStreamerInfo::kLong: { dataType = "Long_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
212  case TVirtualStreamerInfo::kLong64: { dataType = "Long64_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
213  case TVirtualStreamerInfo::kFloat: { dataType = "Float_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
214  case TVirtualStreamerInfo::kFloat16: { dataType = "Float16_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
215  case TVirtualStreamerInfo::kDouble: { dataType = "Double_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
216  case TVirtualStreamerInfo::kDouble32:{ dataType = "Double32_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
217  case TVirtualStreamerInfo::kUChar: { dataType = "UChar_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
218  case TVirtualStreamerInfo::kUShort: { dataType = "unsigned short"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
219  case TVirtualStreamerInfo::kUInt: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
220  case TVirtualStreamerInfo::kULong: { dataType = "ULong_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
221  case TVirtualStreamerInfo::kULong64: { dataType = "ULong64_t"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
222  case TVirtualStreamerInfo::kBits: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kValue; break; }
223  // Character arrays
224  case TVirtualStreamerInfo::kCharStar: { dataType = "Char_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
225  // Array of built-in types [8]
226  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kBool: { dataType = "Bool_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
227  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kChar: { dataType = "Char_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
228  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kShort: { dataType = "Short_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
229  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kInt: { dataType = "Int_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
230  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kLong: { dataType = "Long_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
231  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kLong64: { dataType = "Long64_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
232  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kFloat: { dataType = "Float_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
233  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kFloat16: { dataType = "Float16_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
234  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kDouble: { dataType = "Double_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
235  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kDouble32:{ dataType = "Double32_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
236  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kUChar: { dataType = "UChar_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
237  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kUShort: { dataType = "unsigned short"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
238  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kUInt: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
239  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kULong: { dataType = "ULong_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
240  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kULong64: { dataType = "ULong64_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
241  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kBits: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
242  // Array of built-in types [n]
243  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kBool: { dataType = "Bool_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
244  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kChar: { dataType = "Char_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
245  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kShort: { dataType = "Short_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
246  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kInt: { dataType = "Int_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
247  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kLong: { dataType = "Long_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
248  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kLong64: { dataType = "Long64_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
249  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kFloat: { dataType = "Float_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
250  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kFloat16: { dataType = "Float16_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
251  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kDouble: { dataType = "Double_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
252  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kDouble32:{ dataType = "Double32_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
253  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kUChar: { dataType = "UChar_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
254  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kUShort: { dataType = "unsigned short"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
255  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kUInt: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
256  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kULong: { dataType = "ULong_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
257  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kULong64: { dataType = "ULong64_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
258  case TVirtualStreamerInfo::kOffsetP + TVirtualStreamerInfo::kBits: { dataType = "unsigned int"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
259  // array counter [n]
260  case TVirtualStreamerInfo::kCounter: { dataType = "Int_t"; readerType = TTreeReaderDescriptor::ReaderType::kArray; break; }
261  // other stuff (containers, classes, ...)
262  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kObjectp:
263  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kObjectP:
264  case TVirtualStreamerInfo::kObjectp:
265  case TVirtualStreamerInfo::kObjectP:
266  case TVirtualStreamerInfo::kAnyp:
267  case TVirtualStreamerInfo::kAnyP:
268  case TVirtualStreamerInfo::kSTL + TVirtualStreamerInfo::kObjectp:
269  case TVirtualStreamerInfo::kSTL + TVirtualStreamerInfo::kObjectP:
270  // set as pointers and fall through to the next switches
271  ispointer = true;
272  case TVirtualStreamerInfo::kOffsetL + TVirtualStreamerInfo::kObject:
273  // This means an array of objects, but then fall through
274  readerType = TTreeReaderDescriptor::ReaderType::kArray;
275  case TVirtualStreamerInfo::kObject:
276  case TVirtualStreamerInfo::kTString:
277  case TVirtualStreamerInfo::kTNamed:
278  case TVirtualStreamerInfo::kTObject:
279  case TVirtualStreamerInfo::kAny:
280  case TVirtualStreamerInfo::kBase:
281  case TVirtualStreamerInfo::kSTL: {
282  TClass *cl = element->GetClassPointer();
283  R__ASSERT(cl);
284  dataType = cl->GetName();
285  // Check for containers
286  if (cl == TClonesArray::Class()) { // TClonesArray
287  isclones = kClones;
288  containerName = "TClonesArray";
289  if (outer_isclones != kOut) { // If the parent is already a collection
290  isclones = outer_isclones;
291  dataType = "TClonesArray";
292  } else {
293  readerType = TTreeReaderDescriptor::ReaderType::kArray;
294  dataType = GetContainedClassName(branch, element, ispointer);
295  }
296  } else if (cl->GetCollectionProxy()) { // STL collection
297  isclones = kSTL;
298  containerName = cl->GetName();
299  if (outer_isclones != kOut || containerName.EqualTo("vector<bool>")) {
300  // If the parent is already a collection we can only add this collection as a whole
301  // Also TTreeReaderArray does currently not support vectors of bool so
302  // that need to be added as a whole.
303  // Also getting the inner type of vector would return "unsigned" so the full
304  // name has to be compared
305  isclones = outer_isclones;
306  dataType = cl->GetName();
307  } else {
308  readerType = TTreeReaderDescriptor::ReaderType::kArray;
309  TClass *valueClass = cl->GetCollectionProxy()->GetValueClass();
310  if (valueClass) { // Get class inside container
311  dataType = valueClass->GetName();
312  }
313  else { // Get built-in type inside container
314  TDataType *valueClassBuiltIn = TDataType::GetDataType(cl->GetCollectionProxy()->GetType());
315  if (valueClassBuiltIn) dataType = valueClassBuiltIn->GetName();
316  else Error("AnalyzeBranches", "Could not get type from collection %s in branch %s", cl->GetName(), branch->GetName());
317  }
318  }
319  }
320 
321  // Analyze the actual element
322  // Possible cases:
323  // - Base class
324  // - Element and branch matches
325  // - Non-split: do nothing
326  // - Split: recurse with sub-sub-branches
327  // - Element and branch does not match: recurse with same branches
328  // - Not base class
329  // - Element and branch matches
330  // - Non-split: do nothing
331  // - Split: recurse with sub-sub-branches
332  // - Element and branch does not match: recurse with same branches
333  TBranch *parent = branch->GetMother()->GetSubBranch(branch);
334  TVirtualStreamerInfo *objInfo = 0;
335  if (branch->GetListOfBranches()->GetEntries()) {
336  objInfo = ((TBranchElement*)branch->GetListOfBranches()->At(0))->GetInfo();
337  } else {
338  objInfo = branch->GetInfo();
339  }
340  if (element->IsBase()) { // Base class
341  isBase = true;
342  if (cl == TObject::Class() && info->GetClass()->CanIgnoreTObjectStreamer())
343  {
344  continue; // Ignore TObject
345  }
346 
347  TBranchDescriptor *bdesc = 0;
348 
349  if (branchEndName == element->GetName()) { // The element and the branch matches
350  if (branch->GetListOfBranches()->GetEntries() == 0) { // The branch contains a non-split base class
351  // FIXME: nothing to do in such cases, because readers cannot access
352  // non-split members and a reader for the whole branch will be added
353  } else { // The branch contains a split base class
354  Int_t pos = branchname.Last('.');
355  if (pos != -1) {
356  branchname.Remove(pos);
357  }
358  TString local_prefix = desc ? desc->fSubBranchPrefix : TString(parent->GetName());
359  bdesc = new TBranchDescriptor(cl->GetName(), objInfo, branchname.Data(), local_prefix.Data(),
360  isclones, containerName, desc);
361  // Recurse: analyze sub-branches of the sub-branch
362  lookedAt += AnalyzeBranches(bdesc, branch, objInfo);
363  isLeaf = false;
364 
365  }
366  } else { // The element and the branch does not match, we need to loop over the next branches
367  Int_t pos = branchname.Last('.');
368  if (pos != -1) {
369  branchname.Remove(pos);
370  }
371  TString local_prefix = desc ? desc->fSubBranchPrefix : TString(parent->GetName());
372  objInfo = GetBaseClass(element);
373  if (objInfo == 0) {
374  continue; // There is no data in this base class
375  }
376  cl = objInfo->GetClass();
377  bdesc = new TBranchDescriptor(cl->GetName(), objInfo, branchname.Data(), local_prefix.Data(),
378  isclones, containerName, desc);
379  usedBranch = kFALSE;
380  // Recurse: analyze the sub-elements with the same branches
381  lookedAt += AnalyzeBranches(bdesc, branches, objInfo);
382  }
383  delete bdesc;
384  } else { // Not base class
385  TBranchDescriptor *bdesc = 0;
386  if (branchEndName == element->GetName()) { // The element and the branch matches
387  if (branch->GetListOfBranches()->GetEntries() == 0) { // The branch contains a non-split class
388  // FIXME: nothing to do in such cases, because readers cannot access
389  // non-split members and a reader for the whole branch will be added
390  } else { // The branch contains a split class
391  if (isclones != kOut) {
392  // We have to guess the version number!
393  cl = TClass::GetClass(dataType);
394  objInfo = GetStreamerInfo(branch, branch->GetListOfBranches(), cl);
395  }
396  bdesc = new TBranchDescriptor(cl->GetName(), objInfo, branch->GetName(), branch->GetName(),
397  isclones, containerName, desc);
398  // Recurse: analyze sub-branches of the sub-branch
399  lookedAt += AnalyzeBranches(bdesc, branch, objInfo);
400  isLeaf = false;
401  }
402  } else { // The element and the branch does not match, we need to loop over the next branches
403  TString local_prefix = desc ? desc->fSubBranchPrefix : TString(parent->GetName());
404  if (local_prefix.Length()) local_prefix += ".";
405  local_prefix += element->GetName();
406  objInfo = branch->GetInfo();
407  Int_t pos = branchname.Last('.');
408  if (pos != -1) {
409  branchname.Remove(pos);
410  }
411  if (isclones != kOut) {
412  // We have to guess the version number!
413  cl = TClass::GetClass(dataType);
414  objInfo = GetStreamerInfo(branch, branches, cl);
415  }
416  bdesc = new TBranchDescriptor(cl->GetName(), objInfo, branchname.Data(), local_prefix.Data(),
417  isclones, containerName, desc);
418  usedBranch = kFALSE;
419  skipped = kTRUE;
420  // Recurse: analyze the sub-elements with the same branches
421  lookedAt += AnalyzeBranches(bdesc, branches, objInfo);
422  }
423  delete bdesc;
424  }
425 
426  break;
427  }
428  default:
429  Error("AnalyzeBranch", "Unsupported type for %s (%d).", branch->GetName(), element->GetType());
430  }
431 
432  if (!isBase && !skipped) { // Add reader for the whole branch
433  if (outer_isclones != kOut && readerType == TTreeReaderDescriptor::ReaderType::kArray) {
434  Error("AnalyzeBranch", "Arrays inside collections are not supported yet (branch: %s).", branch->GetName());
435  } else {
436  if (outer_isclones != kOut || isclones != kOut) {
437  readerType = TTreeReaderDescriptor::ReaderType::kArray;
438  }
439  AddReader(readerType, dataType, branch->GetName(), branch->GetName(), desc, isLeaf);
440  }
441  }
442 
443  // If the branch was used, jump to the next
444  if (usedBranch) {
445  branches.Next();
446  ++lookedAt;
447  }
448  }
449 
450  return lookedAt;
451  }
452 
453  ////////////////////////////////////////////////////////////////////////////////
454  /// Analyze branch and add the variables found. The number of analyzed
455  /// sub-branches is returned.
456 
457  UInt_t TTreeReaderGenerator::AnalyzeOldBranch(TBranch *branch)
458  {
459  UInt_t extraLookedAt = 0;
460  TString prefix;
461 
462  TString branchName = branch->GetName();
463 
464  TObjArray *leaves = branch->GetListOfLeaves();
465  Int_t nleaves = leaves ? leaves->GetEntriesFast() : 0;
466 
467  // Loop through leaves and analyze them
468  for(int l=0;l<nleaves;l++) {
469  TLeaf *leaf = (TLeaf*)leaves->UncheckedAt(l);
470  extraLookedAt += AnalyzeOldLeaf(leaf, nleaves);
471  }
472 
473  return extraLookedAt;
474  }
475 
476  ////////////////////////////////////////////////////////////////////////////////
477  /// Analyze the leaf and add the variables found.
478 
479  UInt_t TTreeReaderGenerator::AnalyzeOldLeaf(TLeaf *leaf, Int_t nleaves)
480  {
481  if (leaf->IsA()==TLeafObject::Class()) {
482  Error("AnalyzeOldLeaf","TLeafObject not supported yet");
483  return 0;
484  }
485 
486  TString leafTypeName = leaf->GetTypeName();
487  Int_t pos = leafTypeName.Last('_');
488  //if (pos != -1) leafTypeName.Remove(pos); // FIXME: this is not required since it makes Float_t -> Float
489 
490  // Analyze dimensions
491  UInt_t dim = 0;
492  std::vector<Int_t> maxDim;
493 
494  TString dimensions;
495  TString temp = leaf->GetName();
496  pos = temp.Index("[");
497  if (pos != -1) {
498  if (pos) temp.Remove(0, pos);
499  dimensions.Append(temp);
500  }
501  temp = leaf->GetTitle();
502  pos = temp.Index("[");
503  if (pos != -1) {
504  if (pos) temp.Remove(0, pos);
505  dimensions.Append(temp);
506  }
507 
508  Int_t dimlen = dimensions.Length();
509 
510  if (dimlen) {
511  const char *current = dimensions.Data();
512 
513  Int_t index;
514  Int_t scanindex ;
515  while (current) {
516  current++;
517  if (current[0] == ']') {
518  maxDim.push_back(-1); // maxDim[dim] = -1; // Loop over all elements;
519  } else {
520  scanindex = sscanf(current,"%d",&index);
521  if (scanindex) {
522  maxDim.push_back(index); // maxDim[dim] = index;
523  } else {
524  maxDim.push_back(-2); // maxDim[dim] = -2; // Index is calculated via a variable.
525  }
526  }
527  dim ++;
528  current = (char*)strstr( current, "[" );
529  }
530  }
531 
532  if (dim == 0 && leaf->IsA() == TLeafC::Class()) {
533  dim = 1; // For C style strings
534  }
535 
536  TTreeReaderDescriptor::ReaderType type = TTreeReaderDescriptor::ReaderType::kValue;
537  TString dataType;
538  switch (dim) {
539  case 0: {
540  type = TTreeReaderDescriptor::ReaderType::kValue;
541  dataType = leafTypeName;
542  break;
543  }
544  case 1: {
545  type = TTreeReaderDescriptor::ReaderType::kArray;
546  dataType = leafTypeName;
547  break;
548  }
549  default: {
550  // TODO: transform this
551  /*type = "TArrayProxy<";
552  for(Int_t ind = dim - 2; ind > 0; --ind) {
553  type += "TMultiArrayType<";
554  }
555  type += "TArrayType<";
556  type += leaf->GetTypeName();
557  type += ",";
558  type += maxDim[dim-1];
559  type += "> ";
560  for(Int_t ind = dim - 2; ind > 0; --ind) {
561  type += ",";
562  type += maxDim[ind];
563  type += "> ";
564  }
565  type += ">";*/
566  break;
567  }
568  }
569 
570  // If there are multiple leaves (leaflist) the name of the branch is
571  // <branch_name>.<leaf_name>
572  // (Otherwise the brach name does not change)
573  TString branchName = leaf->GetBranch()->GetName();
574  if (nleaves > 1) {
575  branchName.Form("%s.%s", leaf->GetBranch()->GetName(), leaf->GetName());
576  }
577 
578  AddReader(type, dataType, leaf->GetName(), branchName, 0, kTRUE);
579 
580  return 0;
581  }
582 
583  ////////////////////////////////////////////////////////////////////////////////
584  /// Check whether a branch should have a corresponding reader added, depending
585  /// on the options provided by the user.
586 
587  Bool_t TTreeReaderGenerator::BranchNeedsReader(TString branchName, TBranchDescriptor *parent, Bool_t isLeaf)
588  {
589  if (isLeaf) { // Branch is a leaf
590  // Include if all leaves should be included or it is contained in any of the lists.
591  if (fIncludeAllLeaves) return kTRUE;
592  if (std::find(fIncludeLeaves.begin(), fIncludeLeaves.end(), branchName) != fIncludeLeaves.end()) return kTRUE;
593  if (std::find(fIncludeStruct.begin(), fIncludeStruct.end(), branchName) != fIncludeStruct.end()) return kTRUE;
594  if (!parent) { // Branch is topmost (top-level leaf)
595  if (fIncludeAllTopmost) return kTRUE;
596  } else { // Branch is not topmost
597  while (parent) { // Check if any parent is in the list of "include as leaves"
598  if (std::find(fIncludeLeaves.begin(), fIncludeLeaves.end(), parent->fBranchName) != fIncludeLeaves.end()) {
599  return kTRUE;
600  }
601  parent = parent->fParent;
602  }
603  }
604  } else { // Branch is not a leaf (has sub-branches)
605  if (std::find(fIncludeStruct.begin(), fIncludeStruct.end(), branchName) != fIncludeStruct.end()) return kTRUE;
606  if (!parent) { // Branch is topmost
607  if (fIncludeAllTopmost) return kTRUE;
608  }
609  }
610  return false;
611  }
612 
613  ////////////////////////////////////////////////////////////////////////////////
614  /// Parse the user options.
615 
616  void TTreeReaderGenerator::ParseOptions() {
617  if (fOptionStr.EqualTo("")) { // Empty -> include all leaves
618  fIncludeAllLeaves = kTRUE;
619  } else if (fOptionStr.EqualTo("@")) { // "@" -> include all topmost
620  fIncludeAllTopmost = kTRUE;
621  } else { // Otherwise split at ";" to get names
622  TObjArray *tokens = fOptionStr.Tokenize(TString(";"));
623  for (Int_t i = 0; i < tokens->GetEntries(); ++i) {
624  TString token = ((TObjString*)tokens->At(i))->GetString();
625  if ( token.Length() == 0 || (token.Length() == 1 && token[0] == '@') ) {
626  Warning("ParseOptions", "Ignored empty branch name in option string.");
627  } else if (token[0] == '@') { // "@X" means include X as a whole
628  token = TString(token.Data()+1);
629  fIncludeStruct.push_back(token);
630  } else { // "X" means include leaves of X
631  fIncludeLeaves.push_back(token);
632  }
633  if (!fTree->GetBranch(token)) { // Display a warning for non-existing branch names
634  Warning("ParseOptions", "Tree %s does not contain a branch named %s.", fTree->GetName(), token.Data());
635  }
636  }
637  delete tokens;
638  }
639  }
640 
641  ////////////////////////////////////////////////////////////////////////////////
642  /// Analyze tree and extract readers.
643 
644  void TTreeReaderGenerator::AnalyzeTree(TTree *tree)
645  {
646  TIter next(tree->GetListOfBranches());
647  TBranch *branch;
648 
649  // Loop through branches
650  while ( (branch = (TBranch*)next()) ) {
651  TVirtualStreamerInfo *info = 0;
652  // Get the name and the class of the branch
653  const char *branchName = branch->GetName();
654  const char *branchClassName = branch->GetClassName();
655  TClass *cl = TClass::GetClass(branchClassName);
656 
657  // Add headers for user classes
658  if (branchClassName && strlen(branchClassName)) {
659  AddHeader(cl);
660  }
661 
662  TString type = "unknown"; // Type of branch
663  ELocation isclones = kOut; // Type of container
664  TString containerName = ""; // Name of container
665  TBranchDescriptor *desc = 0;
666  // Check whether the branch is a container
667  if (cl) {
668  // Check if it is a TClonesArray
669  if (cl == TClonesArray::Class()) {
670  isclones = kClones;
671  containerName = "TClonesArray";
672  if (branch->IsA()==TBranchElement::Class()) {
673  // Get the class inside the TClonesArray
674  const char *cname = ((TBranchElement*)branch)->GetClonesName();
675  TClass *ncl = TClass::GetClass(cname);
676  if (ncl) {
677  cl = ncl;
678  info = GetStreamerInfo(branch, branch->GetListOfBranches(), cl);
679  } else {
680  Error("AnalyzeTree",
681  "Introspection of TClonesArray in older file not implemented yet.");
682  }
683  } else {
684  TClonesArray **ptr = (TClonesArray**)branch->GetAddress();
685  TClonesArray *clones = 0;
686  if (ptr==0) {
687  clones = new TClonesArray;
688  branch->SetAddress(&clones);
689  ptr = &clones;
690  }
691  branch->GetEntry(0);
692  TClass *ncl = *ptr ? (*ptr)->GetClass() : 0;
693  if (ncl) {
694  cl = ncl;
695  } else {
696  Error("AnalyzeTree",
697  "Introspection of TClonesArray for %s failed.",branch->GetName());
698  }
699  }
700  // Check if it is an STL container
701  } else if (cl->GetCollectionProxy()) {
702  isclones = kSTL;
703  containerName = cl->GetName();
704  // Get the type inside container
705  if (cl->GetCollectionProxy()->GetValueClass()) { // Class inside container
706  cl = cl->GetCollectionProxy()->GetValueClass();
707  } else { // RAW type (or missing class) inside container
708  // TODO: CheckForMissingClass?
709  // TTreeReaderArray does currently not support vectors of bool so that need to
710  // be added as a TTreeReaderValue<vector<bool>>. Also getting the inner type of
711  // vector would return "unsigned" so the full name has to be compared.
712  if (containerName.EqualTo("vector<bool>")) {
713  AddReader(TTreeReaderDescriptor::ReaderType::kValue,
714  containerName,
715  branch->GetName(), branch->GetName(), 0, kTRUE);
716  } else { // Otherwise we can generate a TTreeReaderArray with the inner type
717  AddReader(TTreeReaderDescriptor::ReaderType::kArray,
718  TDataType::GetDataType(cl->GetCollectionProxy()->GetType())->GetName(),
719  branch->GetName(), branch->GetName(), 0, kTRUE);
720  }
721  continue; // Nothing else to with this branch in these cases
722  }
723  }
724 
725  // Check class inside container and create a descriptor or add a reader
726  if (cl) {
727  if (cl->TestBit(TClass::kIsEmulation) || branchName[strlen(branchName)-1] == '.' || branch->GetSplitLevel()) {
728  TBranchElement *be = dynamic_cast<TBranchElement*>(branch);
729  TVirtualStreamerInfo *beinfo = (be && isclones == kOut)
730  ? be->GetInfo() : cl->GetStreamerInfo(); // the 2nd hand need to be fixed
731  // Create descriptor
732  desc = new TBranchDescriptor(cl->GetName(), beinfo, branchName, branchName, isclones, containerName);
733  info = beinfo;
734  } else {
735  // Add a reader for non-split classes
736  AddReader(isclones == kOut ?
737  TTreeReaderDescriptor::ReaderType::kValue
738  : TTreeReaderDescriptor::ReaderType::kArray,
739  cl->GetName(), branchName, branchName, 0, kTRUE);
740  // TODO: can't we just put a continue here?
741  }
742  }
743  }
744 
745  // Analyze sub-branches (if exist) and add readers
746  if (branch->GetListOfBranches()->GetEntries() == 0) { // Branch is non-splitted
747  if (cl) { // Non-split object
748  if (desc) { // If there is a descriptor add reader (otherwise
749  // it was already added).
750  AddReader(isclones == kOut ?
751  TTreeReaderDescriptor::ReaderType::kValue
752  : TTreeReaderDescriptor::ReaderType::kArray,
753  desc->GetName(), desc->fBranchName, desc->fBranchName, 0, kTRUE);
754  }
755  } else { // Top-level RAW type
756  AnalyzeOldBranch(branch); // Analyze branch and extract readers
757  }
758  } else { // Branch is splitted
759  TIter subnext( branch->GetListOfBranches() );
760  if (desc) {
761  // Analyze sub-branches and extract readers
762  TBranchElement *branchElem = dynamic_cast<TBranchElement*>(branch);
763  if (branchElem) {
764  AnalyzeBranches(desc, branchElem, info);
765  } else {
766  Error("AnalyzeTree", "Cannot analyze branch %s because it is not a TBranchElement.", branchName);
767  }
768  // Also add a reader for the whole branch
769  AddReader(isclones == kOut ?
770  TTreeReaderDescriptor::ReaderType::kValue
771  : TTreeReaderDescriptor::ReaderType::kArray,
772  desc->GetName(), desc->fBranchName, desc->fBranchName, 0, kFALSE);
773  }
774  }
775  delete desc;
776  }
777  }
778 
779  ////////////////////////////////////////////////////////////////////////////////
780  /// Generate code for selector class.
781 
782  void TTreeReaderGenerator::WriteSelector()
783  {
784  // If no name is given, set to default (name of the tree)
785  if (!fClassname) fClassname = fTree->GetName();
786 
787  TString treefile;
788  if (fTree->GetDirectory() && fTree->GetDirectory()->GetFile()) {
789  treefile = fTree->GetDirectory()->GetFile()->GetName();
790  } else {
791  treefile = "Memory Directory";
792  }
793  // In the case of a chain, the GetDirectory information usually does
794  // pertain to the Chain itself but to the currently loaded tree.
795  // So we can not rely on it.
796  Bool_t ischain = fTree->InheritsFrom(TChain::Class());
797  Bool_t isHbook = fTree->InheritsFrom("THbookTree");
798  if (isHbook)
799  treefile = fTree->GetTitle();
800 
801  //======================Generate classname.h=====================
802  TString thead;
803  thead.Form("%s.h", fClassname.Data());
804  std::ofstream ofs (thead, std::ofstream::out);
805  if (!ofs) {
806  Error("WriteSelector","cannot open output file %s", thead.Data());
807  return;
808  }
809  // Print header
810  TDatime td;
811  ofs <<
812 R"CODE(////////////////////////////////////////////////////////// // This class has been automatically generated on // )CODE" << td.AsString() << R"CODE( by ROOT version )CODE" << gROOT->GetVersion() << std::endl;
813  if (!ischain) {
814  ofs << "// from TTree " << fTree->GetName() << "/" << fTree->GetTitle() << std::endl
815  << "// found on file: " << treefile << std::endl;
816  } else {
817  ofs << "// from TChain " << fTree->GetName() << "/" << fTree->GetTitle() << std::endl;
818  }
819  ofs <<
820 R"CODE(////////////////////////////////////////////////////////// #ifndef )CODE" << fClassname << R"CODE(_h #define )CODE" << fClassname << R"CODE(_h #include <TROOT.h> #include <TChain.h> #include <TFile.h> )CODE";
821  if (isHbook) ofs << "#include <THbookFile.h>" << std::endl;
822  ofs <<
823 R"CODE(#include <TSelector.h> #include <TTreeReader.h> #include <TTreeReaderValue.h> #include <TTreeReaderArray.h> // Headers needed by this particular selector )CODE";
824 
825  TIter next(&fListOfHeaders);
826  TObject *header;
827  while ( (header = next()) ) {
828  ofs << header->GetTitle() << std::endl;
829  }
830  ofs << std::endl << std::endl;
831 
832  // Generate class declaration with TTreeReaderValues and Arrays
833  ofs <<
834 R"CODE(class )CODE" << fClassname << R"CODE( : public TSelector { public : TTreeReader fReader; //!the tree reader TTree *fChain = 0; //!pointer to the analyzed TTree or TChain // Readers to access the data (delete the ones you do not need). )CODE";
835  next = &fListOfReaders;
836  TTreeReaderDescriptor *descriptor;
837  while ( ( descriptor = (TTreeReaderDescriptor*)next() ) ) {
838  ofs << " TTreeReader" << (descriptor->fType == TTreeReaderDescriptor::ReaderType::kValue ? "Value" : "Array")
839  << "<" << descriptor->fDataType
840  << "> " << descriptor->fName
841  << " = {fReader, \"" << descriptor->fBranchName << "\"};" << std::endl;
842  }
843  // Generate class member functions prototypes
844  ofs <<
845 R"CODE( )CODE" << fClassname << R"CODE((TTree * /*tree*/ =0) { } virtual ~)CODE" << fClassname << R"CODE(() { } virtual Int_t Version() const { return 2; } virtual void Begin(TTree *tree); virtual void SlaveBegin(TTree *tree); virtual void Init(TTree *tree); virtual Bool_t Notify(); virtual Bool_t Process(Long64_t entry); virtual Int_t GetEntry(Long64_t entry, Int_t getall = 0) { return fChain ? fChain->GetTree()->GetEntry(entry, getall) : 0; } virtual void SetOption(const char *option) { fOption = option; } virtual void SetObject(TObject *obj) { fObject = obj; } virtual void SetInputList(TList *input) { fInput = input; } virtual TList *GetOutputList() const { return fOutput; } virtual void SlaveTerminate(); virtual void Terminate(); ClassDef()CODE" << fClassname << R"CODE(,0); }; #endif #ifdef )CODE" << fClassname << R"CODE(_cxx void )CODE" << fClassname << R"CODE(::Init(TTree *tree) { // The Init() function is called when the selector needs to initialize // a new tree or chain. Typically here the reader is initialized. // It is normally not necessary to make changes to the generated // code, but the routine can be extended by the user if needed. // Init() will be called many times when running on PROOF // (once per file to be processed). fReader.SetTree(tree); } Bool_t )CODE" << fClassname << R"CODE(::Notify() { // The Notify() function is called when a new file is opened. This // can be either for a new TTree in a TChain or when when a new TTree // is started when using PROOF. It is normally not necessary to make changes // to the generated code, but the routine can be extended by the // user if needed. The return value is currently not used. return kTRUE; } #endif // #ifdef )CODE" << fClassname << R"CODE(_cxx )CODE";
846  ofs.close();
847 
848  //======================Generate classname.C=====================
849  TString tcimp;
850  tcimp.Form("%s.C", fClassname.Data());
851  std::ofstream ofsc (tcimp, std::ofstream::out);
852  if (!ofsc) {
853  Error("WriteSelector","cannot open output file %s", tcimp.Data());
854  return;
855  }
856 
857  ofsc <<
858 R"CODE(#define )CODE" << fClassname << R"CODE(_cxx // The class definition in )CODE" << fClassname << R"CODE(.h has been generated automatically // by the ROOT utility TTree::MakeSelector(). This class is derived // from the ROOT class TSelector. For more information on the TSelector // framework see $ROOTSYS/README/README.SELECTOR or the ROOT User Manual. // The following methods are defined in this file: // Begin(): called every time a loop on the tree starts, // a convenient place to create your histograms. // SlaveBegin(): called after Begin(), when on PROOF called only on the // slave servers. // Process(): called for each event, in this function you decide what // to read and fill your histograms. // SlaveTerminate: called at the end of the loop on the tree, when on PROOF // called only on the slave servers. // Terminate(): called at the end of the loop on the tree, // a convenient place to draw/fit your histograms. // // To use this file, try the following session on your Tree T: // // root> T->Process(")CODE" << fClassname << R"CODE(.C") // root> T->Process(")CODE" << fClassname << R"CODE(.C","some options") // root> T->Process(")CODE" << fClassname << R"CODE(.C+") // #include ")CODE" << thead << R"CODE(" #include <TH2.h> #include <TStyle.h> void )CODE" << fClassname << R"CODE(::Begin(TTree * /*tree*/) { // The Begin() function is called at the start of the query. // When running with PROOF Begin() is only called on the client. // The tree argument is deprecated (on PROOF 0 is passed). TString option = GetOption(); } void )CODE" << fClassname << R"CODE(::SlaveBegin(TTree * /*tree*/) { // The SlaveBegin() function is called after the Begin() function. // When running with PROOF SlaveBegin() is called on each slave server. // The tree argument is deprecated (on PROOF 0 is passed). TString option = GetOption(); } Bool_t )CODE" << fClassname << R"CODE(::Process(Long64_t entry) { // The Process() function is called for each entry in the tree (or possibly // keyed object in the case of PROOF) to be processed. The entry argument // specifies which entry in the currently loaded tree is to be processed. // When processing keyed objects with PROOF, the object is already loaded // and is available via the fObject pointer. // // This function should contain the \"body\" of the analysis. It can contain // simple or elaborate selection criteria, run algorithms on the data // of the event and typically fill histograms. // // The processing can be stopped by calling Abort(). // // Use fStatus to set the return value of TTree::Process(). // // The return value is currently not used. fReader.SetLocalEntry(entry); return kTRUE; } void )CODE" << fClassname << R"CODE(::SlaveTerminate() { // The SlaveTerminate() function is called after all entries or objects // have been processed. When running with PROOF SlaveTerminate() is called // on each slave server. } void )CODE" << fClassname << R"CODE(::Terminate() { // The Terminate() function is the last function to be called during // a query. It always runs on the client, it can be used to present // the results graphically or save the results to file. })CODE";
859  ofsc.close();
860  }
861 
862 } // namespace Internal
863 } // namespace ROOT