Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TTreeFormula.cxx
Go to the documentation of this file.
1// @(#)root/treeplayer:$Id$
2// Author: Rene Brun 19/01/96
3
4/*************************************************************************
5 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
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 "TROOT.h"
13#include "TTreeFormula.h"
14#include "TList.h"
15#include "TTree.h"
16#include "TBuffer.h"
17#include "TBranch.h"
18#include "TBranchObject.h"
19#include "TBranchElement.h"
20#include "TClonesArray.h"
21#include "TLeafB.h"
22#include "TLeafC.h"
23#include "TLeafElement.h"
24#include "TLeafObject.h"
25#include "TMethodCall.h"
26#include "TCutG.h"
27#include "TRandom.h"
28#include "TInterpreter.h"
29#include "TDataType.h"
30#include "TStreamerInfo.h"
31#include "TStreamerElement.h"
32#include "TArrayI.h"
33#include "TAxis.h"
34#include "TError.h"
36#include "TString.h"
37#include "TMath.h"
38
39#include "TVirtualRefProxy.h"
40#include "TTreeFormulaManager.h"
41#include "TFormLeafInfo.h"
42#include "TMethod.h"
44#include "strlcpy.h"
45#include "snprintf.h"
46#include "TEntryList.h"
47
48#include <cctype>
49#include <cstdio>
50#include <cmath>
51#include <cstdlib>
52#include <typeinfo>
53#include <algorithm>
54
55const Int_t kMaxLen = 2048;
56
57/** \class TTreeFormula
58Used to pass a selection expression to the Tree drawing routine. See TTree::Draw
59
60A TreeFormula can contain any arithmetic expression including
61standard operators and mathematical functions separated by operators.
62Examples of valid expression:
63~~~{.cpp}
64 "x<y && sqrt(z)>3.2"
65~~~
66TTreeFormula now relies on a variety of TFormLeafInfo classes to handle the
67reading of the information. Here is the list of theses classes:
68 - TFormLeafInfo
69 - TFormLeafInfoDirect
70 - TFormLeafInfoNumerical
71 - TFormLeafInfoClones
72 - TFormLeafInfoCollection
73 - TFormLeafInfoPointer
74 - TFormLeafInfoMethod
75 - TFormLeafInfoMultiVarDim
76 - TFormLeafInfoMultiVarDimDirect
77 - TFormLeafInfoCast
78
79The following method are available from the TFormLeafInfo interface:
80
81 - AddOffset(Int_t offset, TStreamerElement* element)
82 - GetCounterValue(TLeaf* leaf) : return the size of the array pointed to.
83 - GetObjectAddress(TLeafElement* leaf) : Returns the location of the object pointed to.
84 - GetMultiplicity() : Returns info on the variability of the number of elements
85 - GetNdata(TLeaf* leaf) : Returns the number of elements
86 - GetNdata() : Used by GetNdata(TLeaf* leaf)
87 - GetValue(TLeaf *leaf, Int_t instance = 0) : Return the value
88 - GetValuePointer(TLeaf *leaf, Int_t instance = 0) : Returns the address of the value
89 - GetLocalValuePointer(TLeaf *leaf, Int_t instance = 0) : Returns the address of the value of 'this' LeafInfo
90 - IsString()
91 - ReadValue(char *where, Int_t instance = 0) : Internal function to interpret the location 'where'
92 - Update() : react to the possible loading of a shared library.
93*/
94
96
97////////////////////////////////////////////////////////////////////////////////
98
99inline static void R__LoadBranch(TBranch* br, Long64_t entry, bool quickLoad)
100{
101 if (!quickLoad || (br->GetReadEntry() != entry)) {
102 br->GetEntry(entry);
103 }
104}
105
106////////////////////////////////////////////////////////////////////////////////
107/// \class TDimensionInfo
108/// A small helper class to help in keeping track of the array
109/// dimensions encountered in the analysis of the expression.
110
111class TDimensionInfo : public TObject {
112public:
113 Int_t fCode; // Location of the leaf in TTreeFormula::fCode
114 Int_t fOper; // Location of the Helper using the leaf in TTreeFormula::fOper
118 : fCode(code), fOper(oper), fSize(size), fMultiDim(multiDim) {};
119 ~TDimensionInfo() override {};
120};
121
122////////////////////////////////////////////////////////////////////////////////
123
124TTreeFormula::TTreeFormula(): ROOT::v5::TFormula(), fQuickLoad(false), fNeedLoading(true),
125 fDidBooleanOptimization(false), fDimensionSetup(nullptr)
126
127{
128 // Tree Formula default constructor
129
130 fTree = nullptr;
131 fLookupType = nullptr;
132 fNindex = 0;
133 fNcodes = 0;
134 fAxis = nullptr;
135 fHasCast = false;
136 fManager = nullptr;
137 fMultiplicity = 0;
138 fConstLD = nullptr;
139
140 Int_t j,k;
141 for (j=0; j<kMAXCODES; j++) {
142 fNdimensions[j] = 0;
143 fCodes[j] = 0;
144 fNdata[j] = 1;
145 fHasMultipleVarDim[j] = false;
146 for (k = 0; k<kMAXFORMDIM; k++) {
147 fIndexes[j][k] = -1;
148 fCumulSizes[j][k] = 1;
149 fVarIndexes[j][k] = nullptr;
150 }
151 }
152}
153
154////////////////////////////////////////////////////////////////////////////////
155/// Normal TTree Formula Constructor
156
157TTreeFormula::TTreeFormula(const char *name,const char *expression, TTree *tree)
158 :ROOT::v5::TFormula(), fTree(tree), fQuickLoad(false), fNeedLoading(true),
159 fDidBooleanOptimization(false), fDimensionSetup(nullptr)
160{
161 Init(name,expression);
162}
163
164////////////////////////////////////////////////////////////////////////////////
165/// Constructor used during the expansion of an alias
166
167TTreeFormula::TTreeFormula(const char *name,const char *expression, TTree *tree,
168 const std::vector<std::string>& aliases)
169 :ROOT::v5::TFormula(), fTree(tree), fQuickLoad(false), fNeedLoading(true),
170 fDidBooleanOptimization(false), fDimensionSetup(nullptr), fAliasesUsed(aliases)
171{
172 Init(name,expression);
173}
174
175////////////////////////////////////////////////////////////////////////////////
176/// Initialization called from the constructors.
177
178void TTreeFormula::Init(const char*name, const char* expression)
179{
180 TDirectory *const savedir=gDirectory;
181
184 fNcodes = 0;
185 fMultiplicity = 0;
186 fAxis = nullptr;
187 fHasCast = false;
188 fConstLD = nullptr;
189 Int_t i,j,k;
191 fManager->Add(this);
192
193 for (j=0; j<kMAXCODES; j++) {
194 fNdimensions[j] = 0;
195 fLookupType[j] = kDirect;
196 fCodes[j] = 0;
197 fNdata[j] = 1;
198 fHasMultipleVarDim[j] = false;
199 for (k = 0; k<kMAXFORMDIM; k++) {
200 fIndexes[j][k] = -1;
201 fCumulSizes[j][k] = 1;
202 fVarIndexes[j][k] = nullptr;
203 }
204 }
205
207
208 if (Compile(expression)) {
209 fTree = nullptr; fNdim = 0;
210 if(savedir) savedir->cd();
211 return;
212 }
213
214 if (fNcodes >= kMAXFOUND) {
215 Warning("TTreeFormula","Too many items in expression:%s",expression);
217 }
218 SetName(name);
219
220 for (i=0;i<fNoper;i++) {
221
222 if (GetAction(i)==kDefinedString) {
223 Int_t string_code = GetActionParam(i);
224 TLeaf *leafc = (TLeaf*)fLeaves.UncheckedAt(string_code);
225 if (!leafc) continue;
226
227 // We have a string used as a string
228
229 // This dormant portion of code would be used if (when?) we allow the histogramming
230 // of the integral content (as opposed to the string content) of strings
231 // held in a variable size container delimited by a null (as opposed to
232 // a fixed size container or variable size container whose size is controlled
233 // by a variable). In GetNdata, we will then use strlen to grab the current length.
234 //fCumulSizes[i][fNdimensions[i]-1] = 1;
235 //fUsedSizes[fNdimensions[i]-1] = -TMath::Abs(fUsedSizes[fNdimensions[i]-1]);
236 //fUsedSizes[0] = - TMath::Abs( fUsedSizes[0]);
237
238 if (fNoper == 1) {
239 // If the string is by itself, then it can safely be histogrammed as
240 // in a string based axis. To histogram the number inside the string
241 // just make it part of a useless expression (for example: mystring+0)
243 }
244 continue;
245 }
246 if (GetAction(i)==kJump && GetActionParam(i)==(fNoper-1)) {
247 // We have cond ? string1 : string2
249 }
250 }
251 if (fNoper == 1 && GetAction(0)==kStringConst) {
253 }
254 if (fNoper==1 && GetAction(0)==kAliasString) {
255 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(0));
256 R__ASSERT(subform);
257 if (subform->IsString()) SetBit(kIsCharacter);
258 } else if (fNoper==2 && GetAction(0)==kAlternateString) {
259 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(0));
260 R__ASSERT(subform);
261 if (subform->IsString()) SetBit(kIsCharacter);
262 }
263
264 fManager->Sync();
265
266 // Let's verify the indexes and dies if we need to.
267 Int_t k0,k1;
268 for(k0 = 0; k0 < fNcodes; k0++) {
269 for(k1 = 0; k1 < fNdimensions[k0]; k1++ ) {
270 // fprintf(stderr,"Saw %d dim %d and index %d\n",k1, fFixedSizes[k0][k1], fIndexes[k0][k1]);
271 if ( fIndexes[k0][k1]>=0 && fFixedSizes[k0][k1]>=0
272 && fIndexes[k0][k1]>=fFixedSizes[k0][k1]) {
273 Error("TTreeFormula",
274 "Index %d for dimension #%d in %s is too high (max is %d)",
275 fIndexes[k0][k1],k1+1, expression,fFixedSizes[k0][k1]-1);
276 fTree = nullptr; fNdim = 0;
277 if(savedir) savedir->cd();
278 return;
279 }
280 }
281 }
282
283 // Create a list of unique branches to load.
284 for(k=0; k<fNcodes ; k++) {
285 TLeaf *leaf = k <= fLeaves.GetLast() ? (TLeaf*)fLeaves.UncheckedAt(k) : nullptr;
286 TBranch *branch = nullptr;
287 if (leaf) {
288 branch = leaf->GetBranch();
289 if (fBranches.FindObject(branch)) branch = nullptr;
290 }
291 fBranches.AddAtAndExpand(branch,k);
292 }
293
294 if (IsInteger(false)) SetBit(kIsInteger);
295
297 // Call TTree::GetEntries() to insure that it is already calculated.
298 // This will need to be done anyway at the first iteration and insure
299 // that it will not mess up the branch reading (because TTree::GetEntries
300 // opens all the file in the chain and 'stays' on the last file.
301
302 Long64_t readentry = fTree->GetReadEntry();
303 Int_t treenumber = fTree->GetTreeNumber();
304 fTree->GetEntries();
305 if (treenumber != fTree->GetTreeNumber()) {
306 if (readentry >= 0) {
307 fTree->LoadTree(readentry);
308 }
310 } else {
311 if (readentry >= 0) {
312 fTree->LoadTree(readentry);
313 }
314 }
315
316 }
317
318 if(savedir) savedir->cd();
319}
320
321////////////////////////////////////////////////////////////////////////////////
322/// Tree Formula default destructor.
323
325{
326 if (fManager) {
327 fManager->Remove(this);
328 if (fManager->fFormulas.GetLast()<0) {
329 delete fManager;
330 fManager = nullptr;
331 }
332 }
333 // Objects in fExternalCuts are not owned and should not be deleted
334 // fExternalCuts.Clear();
339 if (fLookupType) delete [] fLookupType;
340 for (int j=0; j<fNcodes; j++) {
341 for (int k = 0; k<fNdimensions[j]; k++) {
342 if (fVarIndexes[j][k]) delete fVarIndexes[j][k];
343 fVarIndexes[j][k] = nullptr;
344 }
345 }
346 if (fDimensionSetup) {
348 delete fDimensionSetup;
349 }
350 delete[] fConstLD;
351}
352
353////////////////////////////////////////////////////////////////////////////////
354/// This method is used internally to decode the dimensions of the variables.
355
358 Int_t& virt_dim) {
359 if (info) {
361 //if (fIndexes[code][info->fDim]<0) { // removed because the index might be out of bounds!
362 info->fVirtDim = virt_dim;
363 fManager->AddVarDims(virt_dim); // if (!fVarDims[virt_dim]) fVarDims[virt_dim] = new TArrayI;
364 //}
365 }
366
367 Int_t vsize = 0;
368 bool scalarindex = false;
369
370 if (fIndexes[code][fNdimensions[code]]==-2) {
371 TTreeFormula *indexvar = fVarIndexes[code][fNdimensions[code]];
372 // ASSERT(indexvar!=0);
373 Int_t index_multiplicity = indexvar->GetMultiplicity();
374 switch (index_multiplicity) {
375 case 0:
376 scalarindex = true;
377 vsize = 1;
378 break;
379 case -1:
380 case 2:
381 vsize = indexvar->GetNdata();
382 break;
383 case 1:
384 vsize = -1;
385 break;
386 };
387 } else vsize = size;
388
389 fCumulSizes[code][fNdimensions[code]] = size;
390
391 if ( !scalarindex && fIndexes[code][fNdimensions[code]] < 0 ) {
392 fManager->UpdateUsedSize(virt_dim, vsize);
393 }
394
395 fNdimensions[code] ++;
396
397}
398
399////////////////////////////////////////////////////////////////////////////////
400/// This method is used internally to decode the dimensions of the variables.
401
403{
404 // We assume that there are NO white spaces in the info string
405 const char * current;
406 Int_t size, scanindex, vardim;
407
408 current = info;
409 vardim = 0;
410 // the next value could be before the string but
411 // that's okay because the next operation is ++
412 // (this is to avoid (?) a if statement at the end of the
413 // loop)
414 if (current[0] != '[') current--;
415 while (current) {
416 current++;
417 scanindex = sscanf(current,"%d",&size);
418 // if scanindex is 0 then we have a name index thus a variable
419 // array (or TClonesArray!).
420
421 if (scanindex==0) size = -1;
422
423 vardim += RegisterDimensions(code, size);
424
425 if (fNdimensions[code] >= kMAXFORMDIM) {
426 // NOTE: test that fNdimensions[code] is NOT too big!!
427
428 break;
429 }
430 current = (char*)strstr( current, "[" );
431 }
432 return vardim;
433}
434
435
436////////////////////////////////////////////////////////////////////////////////
437/// This method stores the dimension information for later usage.
438
440 TDimensionInfo * info = new TDimensionInfo(code,fNoper,size,multidim);
441 fDimensionSetup->Add(info);
442 fCumulSizes[code][fNdimensions[code]] = size;
443 fNdimensions[code] ++;
444 return (size==-1) ? 1 : 0;
445}
446
447////////////////////////////////////////////////////////////////////////////////
448/// This method is used internally to decode the dimensions of the variables.
449
451 TFormLeafInfo * /* maininfo */,
452 bool useCollectionObject) {
453 Int_t ndim, size, current, vardim;
454 vardim = 0;
455
456 const TStreamerElement * elem = leafinfo->fElement;
457 TClass* c = elem ? elem->GetClassPointer() : nullptr;
458
459 TFormLeafInfoMultiVarDim * multi = dynamic_cast<TFormLeafInfoMultiVarDim * >(leafinfo);
460 if (multi) {
461 // We have a second variable dimensions
463 multi->fDim = fNdimensions[code];
464 return RegisterDimensions(code, -1, multi);
465 }
466 if (elem->IsA() == TStreamerBasicPointer::Class()) {
467
468 if (elem->GetArrayDim()>0) {
469
470 ndim = elem->GetArrayDim();
471 size = elem->GetMaxIndex(0);
472 vardim += RegisterDimensions(code, -1);
473 } else {
474 ndim = 1;
475 size = -1;
476 }
477
479 TClass *cl = leafinfo->fClass;
481 TStreamerElement* counter = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(array->GetCountName(),offset);
482#if 1
483 leafinfo->fCounter = new TFormLeafInfo(cl,offset,counter);
484#else /* Code is not ready yet see revision 14078 */
485 if (maininfo==0 || maininfo==leafinfo || 1) {
486 leafinfo->fCounter = new TFormLeafInfo(cl,offset,counter);
487 } else {
488 leafinfo->fCounter = maininfo->DeepCopy();
489 TFormLeafInfo *currentinfo = leafinfo->fCounter;
490 while(currentinfo->fNext && currentinfo->fNext->fNext) currentinfo=currentinfo->fNext;
491 delete currentinfo->fNext;
492 currentinfo->fNext = new TFormLeafInfo(cl,offset,counter);
493 }
494#endif
495 } else if (!useCollectionObject && elem->GetClassPointer() == TClonesArray::Class() ) {
496
497 ndim = 1;
498 size = -1;
499
500 TClass * clonesClass = TClonesArray::Class();
501 Int_t c_offset;
502 TStreamerElement *counter = ((TStreamerInfo*)clonesClass->GetStreamerInfo())->GetStreamerElement("fLast",c_offset);
503 leafinfo->fCounter = new TFormLeafInfo(clonesClass,c_offset,counter);
504
505 } else if (!useCollectionObject && elem->GetClassPointer() && elem->GetClassPointer()->GetCollectionProxy() ) {
506
507 if ( typeid(*leafinfo) == typeid(TFormLeafInfoCollection) ) {
508 ndim = 1;
509 size = -1;
510 } else {
512 ndim = 1;
513 size = 1;
514 }
515
516 } else if ( c && c->GetReferenceProxy() && c->GetReferenceProxy()->HasCounter() ) {
517 ndim = 1;
518 size = -1;
519 } else if (elem->GetArrayDim()>0) {
520
521 ndim = elem->GetArrayDim();
522 size = elem->GetMaxIndex(0);
523
524 } else if ( elem->GetNewType()== TStreamerInfo::kCharStar) {
525
526 // When we implement being able to read the length from
527 // strlen, we will have:
528 // ndim = 1;
529 // size = -1;
530 // until then we more or so die:
531 ndim = 1;
532 size = 1; //NOTE: changed from 0
533
534 } else return 0;
535
536 current = 0;
537 do {
538 vardim += RegisterDimensions(code, size);
539
540 if (fNdimensions[code] >= kMAXFORMDIM) {
541 // NOTE: test that fNdimensions[code] is NOT too big!!
542
543 break;
544 }
545 current++;
546 size = elem->GetMaxIndex(current);
547 } while (current<ndim);
548
549 return vardim;
550}
551
552////////////////////////////////////////////////////////////////////////////////
553/// This method is used internally to decode the dimensions of the variables.
554
556 TBranchElement * leafcount2 = branch->GetBranchCount2();
557 if (leafcount2) {
558 // With have a second variable dimensions
559 TBranchElement *leafcount = dynamic_cast<TBranchElement*>(branch->GetBranchCount());
560
561 R__ASSERT(leafcount); // The function should only be called on a functional TBranchElement object
562
565 fDataMembers.AddAtAndExpand(info, code);
566 fHasMultipleVarDim[code] = true;
567
568 info->fCounter = new TFormLeafInfoDirect(leafcount);
569 info->fCounter2 = new TFormLeafInfoDirect(leafcount2);
570 info->fDim = fNdimensions[code];
571 //if (fIndexes[code][info->fDim]<0) {
572 // info->fVirtDim = virt_dim;
573 // if (!fVarDims[virt_dim]) fVarDims[virt_dim] = new TArrayI;
574 //}
575 return RegisterDimensions(code, -1, info);
576 }
577 return 0;
578}
579
580////////////////////////////////////////////////////////////////////////////////
581/// This method is used internally to decode the dimensions of the variables.
582
584 Int_t numberOfVarDim = 0;
585
586 // Let see if we can understand the structure of this branch.
587 // Usually we have: leafname[fixed_array] leaftitle[var_array]\type
588 // (with fixed_array that can be a multi-dimension array.
589 const char *tname = leaf->GetTitle();
590 char *leaf_dim = (char*)strstr( tname, "[" );
591
592 const char *bname = leaf->GetBranch()->GetName();
593 char *branch_dim = (char*)strstr(bname,"[");
594 if (branch_dim) branch_dim++; // skip the '['
595
596 bool isString = false;
597 if (leaf->IsA() == TLeafElement::Class()) {
598 Int_t type =((TBranchElement*)leaf->GetBranch())->GetStreamerType();
601 } else {
602 isString = (leaf->IsA() == TLeafC::Class());
603 }
604 if (leaf_dim) {
605 leaf_dim++; // skip the '['
606 if (!branch_dim || strncmp(branch_dim,leaf_dim,strlen(branch_dim))) {
607 // then both are NOT the same so do the leaf title first:
608 numberOfVarDim += RegisterDimensions( leaf_dim, code);
609 } else if (branch_dim && strncmp(branch_dim,leaf_dim,strlen(branch_dim))==0
610 && strlen(leaf_dim)>strlen(branch_dim)
611 && (leaf_dim+strlen(branch_dim))[0]=='[') {
612 // we have extra info in the leaf title
613 numberOfVarDim += RegisterDimensions( leaf_dim+strlen(branch_dim)+1, code);
614 }
615 }
616 if (branch_dim) {
617 // then both are NOT same so do the branch name next:
618 if (isString) {
619 numberOfVarDim += RegisterDimensions( code, 1);
620 } else {
621 numberOfVarDim += RegisterDimensions( branch_dim, code);
622 }
623 }
624 if (leaf->IsA() == TLeafElement::Class()) {
625 TBranchElement* branch = (TBranchElement*) leaf->GetBranch();
626 if (branch->GetBranchCount2()) {
627
628 if (!branch->GetBranchCount()) {
629 Warning("DefinedVariable",
630 "Noticed an incorrect in-memory TBranchElement object (%s).\nIt has a BranchCount2 but no BranchCount!\nThe result might be incorrect!",
631 branch->GetName());
632 return numberOfVarDim;
633 }
634
635 // Switch from old direct style to using a TLeafInfo
636 if (fLookupType[code] == kDataMember)
637 Warning("DefinedVariable",
638 "Already in kDataMember mode when handling multiple variable dimensions");
639 fLookupType[code] = kDataMember;
640
641 // Feed the information into the Dimensions system
642 numberOfVarDim += RegisterDimensions( code, branch);
643
644 }
645 }
646 return numberOfVarDim;
647}
648
649////////////////////////////////////////////////////////////////////////////////
650/// This method check for treat the case where expression contains $Atl and load up
651/// both fAliases and fExpr.
652/// We return:
653/// - -1 in case of failure
654/// - 0 in case we did not find $Alt
655/// - the action number in case of success.
656
658{
659 static const char *altfunc = "Alt$(";
660 static const char *minfunc = "MinIf$(";
661 static const char *maxfunc = "MaxIf$(";
662 Int_t action = 0;
663 Int_t start = 0;
664
665 if ( strncmp(expression,altfunc,strlen(altfunc))==0
666 && expression[strlen(expression)-1]==')' ) {
667 action = kAlternate;
668 start = strlen(altfunc);
669 }
670 if ( strncmp(expression,maxfunc,strlen(maxfunc))==0
671 && expression[strlen(expression)-1]==')' ) {
672 action = kMaxIf;
673 start = strlen(maxfunc);
674 }
675 if ( strncmp(expression,minfunc,strlen(minfunc))==0
676 && expression[strlen(expression)-1]==')' ) {
677 action = kMinIf;
678 start = strlen(minfunc);
679 }
680
681 if (action) {
682 TString full = expression;
683 TString part1;
684 TString part2;
685 int paran = 0;
686 int instr = 0;
687 int brack = 0;
688 for(unsigned int i=start;i<strlen(expression);++i) {
689 switch (expression[i]) {
690 case '(': paran++; break;
691 case ')': paran--; break;
692 case '"': instr = instr ? 0 : 1; break;
693 case '[': brack++; break;
694 case ']': brack--; break;
695 };
696 if (expression[i]==',' && paran==0 && instr==0 && brack==0) {
697 part1 = full( start, i-start );
698 part2 = full( i+1, full.Length() -1 - (i+1) );
699 break; // out of the for loop
700 }
701 }
702 if (part1.Length() && part2.Length()) {
703 TTreeFormula *primary = new TTreeFormula("primary",part1,fTree);
704 TTreeFormula *alternate = new TTreeFormula("alternate",part2,fTree);
705
706 short isstring = 0;
707
708 if (action == kAlternate) {
709 if (alternate->GetManager()->GetMultiplicity() != 0 ) {
710 Error("DefinedVariable","The 2nd arguments in %s can not be an array (%s,%d)!",
711 expression,alternate->GetTitle(),
712 alternate->GetManager()->GetMultiplicity());
713 return -1;
714 }
715
716 // Should check whether we have strings.
717 if (primary->IsString()) {
718 if (!alternate->IsString()) {
719 Error("DefinedVariable",
720 "The 2nd arguments in %s has to return the same type as the 1st argument (string)!",
721 expression);
722 return -1;
723 }
724 isstring = 1;
725 } else if (alternate->IsString()) {
726 Error("DefinedVariable",
727 "The 2nd arguments in %s has to return the same type as the 1st argument (numerical type)!",
728 expression);
729 return -1;
730 }
731 } else {
732 primary->GetManager()->Add( alternate );
733 primary->GetManager()->Sync();
734 if (primary->IsString() || alternate->IsString()) {
735 if (!alternate->IsString()) {
736 Error("DefinedVariable",
737 "The arguments of %s can not be strings!",
738 expression);
739 return -1;
740 }
741 }
742 }
743
745 fExpr[fNoper] = "";
746 SetAction(fNoper, (Int_t)action + isstring, 0 );
747 ++fNoper;
748
749 fAliases.AddAtAndExpand(alternate,fNoper);
750 return (Int_t)kAlias + isstring;
751 }
752 }
753 return 0;
754}
755
756////////////////////////////////////////////////////////////////////////////////
757/// Decompose 'expression' as pointing to something inside the leaf
758/// Returns:
759/// - -2 Error: some information is missing (message already printed)
760/// - -1 Error: Syntax is incorrect (message already printed)
761/// - 0
762/// - >0 the value returns is the action code.
763
764Int_t TTreeFormula::ParseWithLeaf(TLeaf* leaf, const char* subExpression, bool final, UInt_t paran_level, TObjArray& castqueue, bool useLeafCollectionObject, const char* fullExpression)
765{
766 Int_t action = 0;
767
768 Int_t numberOfVarDim = 0;
769 char *current;
770
771 char scratch[kMaxLen]; scratch[0] = '\0';
772 char work[kMaxLen]; work[0] = '\0';
773
774 const char *right = subExpression;
775 TString name = fullExpression;
776
777 TBranch *branch = leaf ? leaf->GetBranch() : nullptr;
778 Long64_t readentry = fTree->GetTree()->GetReadEntry();
779 if (readentry < 0) readentry=0;
780
781 bool useLeafReferenceObject = false;
782 Int_t code = fNcodes-1;
783
784 // Make a check to prevent problem with some corrupted files (missing TStreamerInfo).
785 if (leaf && leaf->IsA()==TLeafElement::Class()) {
786 TBranchElement *br = nullptr;
787 if( branch->IsA() == TBranchElement::Class() )
788 {
789 br = ((TBranchElement*)branch);
790
791 if ( br->GetInfo() == nullptr ) {
792 Error("DefinedVariable","Missing StreamerInfo for %s. We will be unable to read!",
793 name.Data());
794 return -2;
795 }
796 }
797
798 TBranch *bmom = branch->GetMother();
799 if( bmom->IsA() == TBranchElement::Class() )
800 {
802 if (mom!=br) {
803 if (mom->GetInfo()==nullptr) {
804 Error("DefinedVariable","Missing StreamerInfo for %s."
805 " We will be unable to read!",
806 mom->GetName());
807 return -2;
808 }
809 if ((mom->GetType()) < -1 && !mom->GetAddress()) {
810 Error("DefinedVariable", "Address not set when the type of the branch is negative for for %s. We will be unable to read!", mom->GetName());
811 return -2;
812 }
813 }
814 }
815 }
816
817 // We need to record the location in the list of leaves because
818 // the tree might actually be a chain and in that case the leaf will
819 // change from tree to tree!.
820
821 // Let's reconstruct the name of the leaf, including the possible friend alias
822 TTree *realtree = fTree->GetTree();
823 const char* alias = nullptr;
824 if (leaf) {
825 if (realtree) alias = realtree->GetFriendAlias(leaf->GetBranch()->GetTree());
826 if (!alias && realtree!=fTree) {
827 // Let's try on the chain
828 alias = fTree->GetFriendAlias(leaf->GetBranch()->GetTree());
829 }
830 }
831 Int_t leafname_len = 0;
832 if (alias) {
833 leafname_len = strlen(alias) + strlen(leaf->GetName()) + 1;
834 snprintf(scratch,kMaxLen-1,"%s.%s",alias,leaf->GetName()); // does not null-terminate if truncation happens
835 }
836 else if (leaf) {
837 leafname_len = strlen(leaf->GetName());
838 strlcpy(scratch,leaf->GetName(),kMaxLen); // null-terminates if truncation happens
839 }
840 if (leafname_len > kMaxLen - 1) {
841 Error("TTreeFormula",
842 "Length of leafname (%d) exceeds maximum allowed by the buffer (%d), formula will be truncated.",
843 leafname_len, kMaxLen - 1);
844 return -1;
845 }
846
847
848 TTree *tleaf = realtree;
849 if (leaf) {
850 tleaf = leaf->GetBranch()->GetTree();
851 fCodes[code] = tleaf->GetListOfLeaves()->IndexOf(leaf);
852 const char *mother_name = leaf->GetBranch()->GetMother()->GetName();
853 TString br_extended_name; // Could do ( strlen(mother_name)+strlen( leaf->GetBranch()->GetName() ) + 2 )
854 if (leaf->GetBranch()!=leaf->GetBranch()->GetMother()) {
855 if (mother_name[strlen(mother_name)-1]!='.') {
856 br_extended_name = mother_name;
857 br_extended_name.Append('.');
858 }
859 }
860 br_extended_name.Append( leaf->GetBranch()->GetName() );
861 Ssiz_t dim = br_extended_name.First('[');
862 if (dim >= 0) br_extended_name.Remove(dim);
863
864 TNamed *named = new TNamed(scratch,br_extended_name.Data());
865 fLeafNames.AddAtAndExpand(named,code);
866 fLeaves.AddAtAndExpand(leaf,code);
867 }
868
869 // If the leaf belongs to a friend tree which has an index, we might
870 // be in the case where some entry do not exist.
871 if (tleaf != realtree && tleaf->GetTreeIndex()) {
872 // reset the multiplicity
873 if (fMultiplicity >= 0) fMultiplicity = 1;
874 }
875
876 // Analyze the content of 'right'
877
878 // Try to find out the class (if any) of the object in the leaf.
879 TClass * cl = nullptr;
880 TFormLeafInfo *maininfo = nullptr;
881 TFormLeafInfo *previnfo = nullptr;
882 bool unwindCollection = false;
883 const static TClassRef stdStringClass = TClass::GetClass("string");
884
885 if (leaf==nullptr) {
886 TNamed *names = (TNamed*)fLeafNames.UncheckedAt(code);
887 fLeafNames.AddAt(nullptr,code);
889 fLeaves.AddAt(nullptr,code);
890
891 cl = what ? what->IsA() : TTree::Class();
892 maininfo = new TFormLeafInfoTTree(fTree,names->GetName(),what);
893 previnfo = maininfo;
894
895 delete names;
896 } else if (leaf->InheritsFrom(TLeafObject::Class()) ) {
897 TBranchObject *bobj = (TBranchObject*)leaf->GetBranch();
898 cl = TClass::GetClass(bobj->GetClassName());
899 } else if (leaf->InheritsFrom(TLeafElement::Class())) {
900 TBranchElement *branchEl = (TBranchElement *)leaf->GetBranch();
901 branchEl->SetupAddresses();
902 TStreamerInfo *info = branchEl->GetInfo();
903 TStreamerElement *element = nullptr;
904 Int_t type = branchEl->GetStreamerType();
905 switch(type) {
918 element = info->GetElement(branchEl->GetID());
919 if (element) cl = element->GetClassPointer();
920 }
921 break;
930 element = info->GetElement(branchEl->GetID());
931 if (element){
932 cl = element->GetClassPointer();
933 }
934 }
935 break;
936 case -1: {
937 cl = info->GetClass();
938 }
939 break;
940 }
941
942 // If we got a class object, we need to verify whether it is on a
943 // split TClonesArray sub branch.
944 if (cl && branchEl->GetBranchCount()) {
945 if (branchEl->GetType()==31) {
946 // This is inside a TClonesArray.
947
948 if (!element) {
949 Warning("DefinedVariable",
950 "Missing TStreamerElement in object in TClonesArray section");
951 return -2;
952 }
953 TFormLeafInfo* clonesinfo = new TFormLeafInfoClones(cl, 0, element, true);
954
955 // The following code was commented out because in THIS case
956 // the dimension are actually handled by parsing the title and name of the leaf
957 // and branch (see a little further)
958 // The dimension needs to be handled!
959 // numberOfVarDim += RegisterDimensions(code,clonesinfo);
960
961 maininfo = clonesinfo;
962
963 // We skip some cases because we can assume we have an object.
964 Int_t offset=0;
965 info->GetStreamerElement(element->GetName(),offset);
976 previnfo = new TFormLeafInfoPointer(cl,offset+branchEl->GetOffset(),element);
977 } else {
978 previnfo = new TFormLeafInfo(cl,offset+branchEl->GetOffset(),element);
979 }
980 maininfo->fNext = previnfo;
981 unwindCollection = true;
982
983 } else if (branchEl->GetType()==41) {
984
985 // This is inside a Collection
986
987 if (!element) {
988 Warning("DefinedVariable","Missing TStreamerElement in object in Collection section");
989 return -2;
990 }
991 // First we need to recover the collection.
992 TBranchElement *count = branchEl->GetBranchCount();
993 TFormLeafInfo* collectioninfo;
994 if ( count->GetID() >= 0 ) {
995 TStreamerElement *collectionElement =
996 count->GetInfo()->GetElement(count->GetID());
997 TClass *collectionCl = collectionElement->GetClassPointer();
998
999 collectioninfo =
1000 new TFormLeafInfoCollection(collectionCl, 0, collectionElement, true);
1001 } else {
1002 TClass *collectionCl = TClass::GetClass(count->GetClassName());
1003 collectioninfo =
1004 new TFormLeafInfoCollection(collectionCl, 0, collectionCl, true);
1005 }
1006
1007 // The following code was commented out because in THIS case
1008 // the dimension are actually handled by parsing the title and name of the leaf
1009 // and branch (see a little further)
1010 // The dimension needs to be handled!
1011 // numberOfVarDim += RegisterDimensions(code,clonesinfo);
1012
1013 maininfo = collectioninfo;
1014
1015 // We skip some cases because we can assume we have an object.
1016 Int_t offset=0;
1017 info->GetStreamerElement(element->GetName(),offset);
1028 previnfo = new TFormLeafInfoPointer(cl,offset+branchEl->GetOffset(),element);
1029 } else {
1030 previnfo = new TFormLeafInfo(cl,offset+branchEl->GetOffset(),element);
1031 }
1032 maininfo->fNext = previnfo;
1033 unwindCollection = true;
1034 }
1035 } else if ( branchEl->GetType()==3) {
1036 TFormLeafInfo* clonesinfo;
1037 if (useLeafCollectionObject) {
1038 clonesinfo = new TFormLeafInfoCollectionObject(cl);
1039 } else {
1040 clonesinfo = new TFormLeafInfoClones(cl, 0, true);
1041 // The dimension needs to be handled!
1042 numberOfVarDim += RegisterDimensions(code,clonesinfo,maininfo,useLeafCollectionObject);
1043
1044 }
1045 maininfo = clonesinfo;
1046 previnfo = maininfo;
1047
1048 } else if (!useLeafCollectionObject && branchEl->GetType()==4) {
1049
1050 TFormLeafInfo* collectioninfo;
1051 if (useLeafCollectionObject) {
1052 collectioninfo = new TFormLeafInfoCollectionObject(cl);
1053 } else {
1054 collectioninfo = new TFormLeafInfoCollection(cl, 0, cl, true);
1055 // The dimension needs to be handled!
1056 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,useLeafCollectionObject);
1057 }
1058
1059 maininfo = collectioninfo;
1060 previnfo = maininfo;
1061
1062 } else if (branchEl->GetStreamerType()==-1 && cl && cl->GetCollectionProxy()) {
1063
1064 if (useLeafCollectionObject) {
1065
1066 TFormLeafInfo *collectioninfo = new TFormLeafInfoCollectionObject(cl);
1067 maininfo = collectioninfo;
1068 previnfo = collectioninfo;
1069
1070 } else {
1071 TFormLeafInfo *collectioninfo = new TFormLeafInfoCollection(cl, 0, cl, true);
1072 // The dimension needs to be handled!
1073 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
1074
1075 maininfo = collectioninfo;
1076 previnfo = collectioninfo;
1077
1078 if (cl->GetCollectionProxy()->GetValueClass()!=nullptr &&
1080
1082 cl->GetCollectionProxy()->GetValueClass(),collectioninfo);
1083
1084 fHasMultipleVarDim[code] = true;
1085 numberOfVarDim += RegisterDimensions(code,multi,maininfo,false);
1086 previnfo->fNext = multi;
1087 cl = cl->GetCollectionProxy()->GetValueClass();
1088 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1089 previnfo = multi->fNext;
1090
1091 }
1092 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1093 cl->GetCollectionProxy()->GetType()>0) {
1094
1095 previnfo->fNext =
1097 previnfo = previnfo->fNext;
1098 } else {
1099 // nothing to do
1100 }
1101 }
1102
1103 } else if (strlen(right)==0 && cl && element && final) {
1104
1105 TClass *elemCl = element->GetClassPointer();
1106 if (!useLeafCollectionObject
1107 && elemCl && elemCl->GetCollectionProxy()
1108 && elemCl->GetCollectionProxy()->GetValueClass()
1110
1111 TFormLeafInfo *collectioninfo =
1112 new TFormLeafInfoCollection(cl, 0, elemCl);
1113
1114 // The dimension needs to be handled!
1115 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
1116
1117 maininfo = collectioninfo;
1118 previnfo = collectioninfo;
1119
1120 TFormLeafInfo *multi =
1122 elemCl->GetCollectionProxy()->GetValueClass(),
1123 collectioninfo);
1124
1125 fHasMultipleVarDim[code] = true;
1126 numberOfVarDim += RegisterDimensions(code,multi,maininfo,false);
1127 previnfo->fNext = multi;
1128 cl = elemCl->GetCollectionProxy()->GetValueClass();
1129 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1130 previnfo = multi->fNext;
1131
1132 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1133 cl->GetCollectionProxy()->GetType()>0) {
1134
1135 previnfo->fNext =
1137 previnfo = previnfo->fNext;
1138 }
1139
1140 } else if (!useLeafCollectionObject
1141 && elemCl && elemCl->GetCollectionProxy()
1142 && elemCl->GetCollectionProxy()->GetValueClass()==nullptr
1143 && elemCl->GetCollectionProxy()->GetType()>0) {
1144
1145 // At this point we have an element which is inside a class (which is not
1146 // a collection) and this element of a collection of numerical type.
1147 // (Note: it is not possible to have more than one variable dimension
1148 // unless we were supporting variable size C-style array of collection).
1149
1150 TFormLeafInfo* collectioninfo =
1151 new TFormLeafInfoCollection(cl, 0, elemCl);
1152
1153 // The dimension needs to be handled!
1154 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
1155
1156 collectioninfo->fNext =
1158
1159 maininfo = collectioninfo;
1160 previnfo = maininfo->fNext;
1161
1162 } else if (!useLeafCollectionObject
1163 && elemCl && elemCl->GetCollectionProxy()) {
1164 if (elemCl->GetCollectionProxy()->GetValueClass()==TString::Class()) {
1165 right = "Data()";
1166 } else if (elemCl->GetCollectionProxy()->GetValueClass()==stdStringClass) {
1167 right = "c_str()";
1168 }
1169
1170 } else if (!element->IsaPointer()) {
1171
1172 maininfo = new TFormLeafInfoDirect(branchEl);
1173 previnfo = maininfo;
1174
1175 }
1176 }
1177 else if ( cl && cl->GetReferenceProxy() ) {
1178 if ( useLeafCollectionObject || fullExpression[0] == '@' || fullExpression[strlen(scratch)] == '@' ) {
1179 useLeafReferenceObject = true;
1180 }
1181 else {
1182 if ( !maininfo ) {
1183 maininfo = previnfo = new TFormLeafInfoReference(cl, element, 0);
1184 numberOfVarDim += RegisterDimensions(code,maininfo,maininfo,false);
1185 }
1186 TVirtualRefProxy *refproxy = cl->GetReferenceProxy();
1187 for(Long64_t i=0; i<leaf->GetBranch()->GetEntries()-readentry; ++i) {
1188 R__LoadBranch(leaf->GetBranch(), readentry+i, fQuickLoad);
1189 void *refobj = maininfo->GetValuePointer(leaf,0);
1190 if (refobj) {
1191 cl = refproxy->GetValueClass(refobj);
1192 }
1193 if ( cl ) break;
1194 }
1195 if ( !cl ) {
1196 Error("DefinedVariable","Failed to access class type of reference target (%s)",element->GetName());
1197 return -1;
1198 }
1199 }
1200 }
1201 } else {
1202 // Regular/old TLeaf, there should not be anything afterward ...
1203 if (subExpression && subExpression[0]) {
1204 Error("ParseWithLeaf", "Found a numerical leaf but the name has trailing characters: \"%s\"", subExpression);
1205 return -1;
1206 }
1207 }
1208
1209 // Treat the dimension information in the leaf name, title and 2nd branch count
1210 if (leaf) numberOfVarDim += RegisterDimensions(code,leaf);
1211
1212 if (cl) {
1213 if (unwindCollection) {
1214 // So far we should get here only if we encounter a split collection of a class that contains
1215 // directly a collection.
1216 R__ASSERT(numberOfVarDim==1 && maininfo);
1217
1218 if (!useLeafCollectionObject && cl && cl->GetCollectionProxy()) {
1219 TFormLeafInfo *multi =
1220 new TFormLeafInfoMultiVarDimCollection(cl, 0, cl, maininfo);
1221 fHasMultipleVarDim[code] = true;
1222 numberOfVarDim += RegisterDimensions(code,multi,maininfo,false);
1223 previnfo->fNext = multi;
1224
1225 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1226 previnfo = multi->fNext;
1227
1228 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1229 cl->GetCollectionProxy()->GetType()>0) {
1230
1231 previnfo->fNext =
1233 previnfo = previnfo->fNext;
1234 }
1235 } else if (!useLeafCollectionObject && cl == TClonesArray::Class()) {
1236
1237 TFormLeafInfo *multi =
1238 new TFormLeafInfoMultiVarDimClones(cl, 0, cl, maininfo);
1239 fHasMultipleVarDim[code] = true;
1240 numberOfVarDim += RegisterDimensions(code,multi,maininfo,false);
1241 previnfo->fNext = multi;
1242
1243 multi->fNext = new TFormLeafInfoClones(cl, 0, false);
1244 previnfo = multi->fNext;
1245 }
1246 }
1247 Int_t offset=0;
1248 if (cl == TString::Class() && strcmp(right,"fData")==0) {
1249 // For backward compatibility replace TString::fData which no longer exist
1250 // by a call to TString::Data()
1251 right = "Data()";
1252 }
1253 Int_t nchname = strlen(right);
1254 TFormLeafInfo *leafinfo = nullptr;
1255 TStreamerElement* element = nullptr;
1256
1257 // Let see if the leaf was attempted to be casted.
1258 // Since there would have been something like
1259 // ((cast_class*)leafname)->.... we need to use
1260 // paran_level+1
1261 // Also we disable this functionality in case of TClonesArray
1262 // because it is not yet allowed to have 'inheritance' (or virtuality)
1263 // in play in a TClonesArray.
1264 {
1265 TClass * casted = (TClass*) castqueue.At(paran_level+1);
1266 if (casted && cl != TClonesArray::Class()) {
1267 if ( ! casted->InheritsFrom(cl) ) {
1268 Error("DefinedVariable","%s does not inherit from %s. Casting not possible!",
1269 casted->GetName(),cl->GetName());
1270 return -2;
1271 }
1272 leafinfo = new TFormLeafInfoCast(cl,casted);
1273 fHasCast = true;
1274 if (maininfo==nullptr) {
1275 maininfo = leafinfo;
1276 }
1277 if (previnfo==nullptr) {
1278 previnfo = leafinfo;
1279 } else {
1280 previnfo->fNext = leafinfo;
1281 previnfo = leafinfo;
1282 }
1283 leafinfo = nullptr;
1284
1285 cl = casted;
1286 castqueue.AddAt(nullptr,paran_level);
1287 }
1288 }
1289 Int_t i;
1290 bool prevUseCollectionObject = useLeafCollectionObject;
1291 bool useCollectionObject = useLeafCollectionObject;
1292 bool useReferenceObject = useLeafReferenceObject;
1293 bool prevUseReferenceObject = useLeafReferenceObject;
1294 for (i=0, current = &(work[0]); i<=nchname;i++ ) {
1295 // We will treated the terminator as a token.
1296 if (right[i] == '(') {
1297 // Right now we do not allow nested parenthesis
1298 do {
1299 *current++ = right[i++];
1300 } while(right[i]!=')' && right[i]);
1301 *current++ = right[i];
1302 *current='\0';
1303 char *params = strchr(work,'(');
1304 if (params) {
1305 *params = 0; params++;
1306 } else params = (char *) ")";
1307 if (cl==nullptr) {
1308 Error("DefinedVariable","Can not call '%s' with a class",work);
1309 return -1;
1310 }
1311 if (!cl->HasDataMemberInfo() && !cl->GetCollectionProxy()) {
1312 Error("DefinedVariable","Class probably unavailable:%s",cl->GetName());
1313 return -2;
1314 }
1315 if (!useCollectionObject && cl == TClonesArray::Class()) {
1316 // We are not interested in the ClonesArray object but only
1317 // in its contents.
1318 // We need to retrieve the class of its content.
1319
1320 TBranch *clbranch = leaf->GetBranch();
1321 R__LoadBranch(clbranch,readentry,fQuickLoad);
1322 TClonesArray * clones;
1323 if (previnfo) clones = (TClonesArray*)previnfo->GetLocalValuePointer(leaf,0);
1324 else {
1325 bool top = (clbranch==((TBranchElement*)clbranch)->GetMother()
1326 || !leaf->IsOnTerminalBranch());
1327 TClass *mother_cl;
1328 if (leaf->IsA()==TLeafObject::Class()) {
1329 // in this case mother_cl is not really used
1330 mother_cl = cl;
1331 } else {
1332 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1333 }
1334 TFormLeafInfo* clonesinfo = new TFormLeafInfoClones(mother_cl, 0, top);
1335
1336 // The dimension needs to be handled!
1337 numberOfVarDim += RegisterDimensions(code,clonesinfo,maininfo,false);
1338
1339 previnfo = clonesinfo;
1340 maininfo = clonesinfo;
1341
1342 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1343 }
1344 TClass * inside_cl = clones ? clones->GetClass() : nullptr;
1345 cl = inside_cl;
1346
1347 }
1348 else if (!useCollectionObject && cl && cl->GetCollectionProxy() ) {
1349
1350 // We are NEVER (for now!) interested in the ClonesArray object but only
1351 // in its contents.
1352 // We need to retrieve the class of its content.
1353
1354 if (previnfo==nullptr) {
1355
1356 bool top = (branch==((TBranchElement*)branch)->GetMother()
1357 || !leaf->IsOnTerminalBranch());
1358
1359 TClass *mother_cl;
1360 if (leaf->IsA()==TLeafObject::Class()) {
1361 // in this case mother_cl is not really used
1362 mother_cl = cl;
1363 } else {
1364 mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
1365 }
1366
1367 TFormLeafInfo* collectioninfo =
1368 new TFormLeafInfoCollection(mother_cl, 0,cl,top);
1369 // The dimension needs to be handled!
1370 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
1371
1372 previnfo = collectioninfo;
1373 maininfo = collectioninfo;
1374
1375 }
1376
1377 TClass * inside_cl = cl->GetCollectionProxy()->GetValueClass();
1378 if (inside_cl) cl = inside_cl;
1379 else if (cl->GetCollectionProxy()->GetType()>0) {
1380 Warning("DefinedVariable","Can not call method on content of %s in %s\n",
1381 cl->GetName(),name.Data());
1382 return -2;
1383 }
1384 }
1385 TMethodCall *method = nullptr;
1386 if (cl==nullptr) {
1387 Error("DefinedVariable",
1388 "Could not discover the TClass corresponding to (%s)!",
1389 right);
1390 return -2;
1391 } else if (cl==TClonesArray::Class() && strcmp(work,"size")==0) {
1392 method = new TMethodCall(cl, "GetEntriesFast", "");
1393 } else if (cl->GetCollectionProxy() && strcmp(work,"size")==0) {
1394 if (maininfo==nullptr) {
1395 TFormLeafInfo* collectioninfo=nullptr;
1396 if (useLeafCollectionObject) {
1397
1398 bool top = (branch==((TBranchElement*)branch)->GetMother()
1399 || !leaf->IsOnTerminalBranch());
1400 collectioninfo = new TFormLeafInfoCollectionObject(cl,top);
1401 }
1402 maininfo=previnfo=collectioninfo;
1403 }
1404 leafinfo = new TFormLeafInfoCollectionSize(cl);
1405 cl = nullptr;
1406 } else {
1407 if (!cl->HasDataMemberInfo()) {
1408 Error("DefinedVariable",
1409 "Can not call method %s on class without dictionary (%s)!",
1410 right,cl->GetName());
1411 return -2;
1412 }
1413 method = new TMethodCall(cl, work, params);
1414 }
1415 if (method) {
1416 if (!method->GetMethod()) {
1417 Error("DefinedVariable","Unknown method:%s in %s",right,cl->GetName());
1418 return -1;
1419 }
1420 switch(method->ReturnType()) {
1421 case TMethodCall::kLong:
1422 leafinfo = new TFormLeafInfoMethod(cl,method);
1423 cl = nullptr;
1424 break;
1426 leafinfo = new TFormLeafInfoMethod(cl,method);
1427 cl = nullptr;
1428 break;
1430 leafinfo = new TFormLeafInfoMethod(cl,method);
1431 // 1 will be replaced by -1 when we know how to use strlen
1432 numberOfVarDim += RegisterDimensions(code,1); //NOTE: changed from 0
1433 cl = nullptr;
1434 break;
1436 {
1437 leafinfo = new TFormLeafInfoMethod(cl,method);
1439 }
1440 break;
1441 default:
1442 Error("DefineVariable","Method %s from %s has an impossible return type %d",
1443 work,cl->GetName(), (Int_t)method->ReturnType());
1444 return -2;
1445 }
1446 }
1447 if (maininfo==nullptr) {
1448 maininfo = leafinfo;
1449 }
1450 if (previnfo==nullptr) {
1451 previnfo = leafinfo;
1452 } else {
1453 previnfo->fNext = leafinfo;
1454 previnfo = leafinfo;
1455 }
1456 leafinfo = nullptr;
1457 current = &(work[0]);
1458 *current = 0;
1459 prevUseCollectionObject = false;
1460 prevUseReferenceObject = false;
1461 useCollectionObject = false;
1462
1463 if (cl && cl->GetCollectionProxy()) {
1464 if (numberOfVarDim>1) {
1465 Warning("DefinedVariable","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1466 cl->GetName());
1467 leafinfo = new TFormLeafInfo(cl,0,nullptr);
1468 useCollectionObject = true;
1469 } else if (numberOfVarDim==0) {
1470 R__ASSERT(maininfo);
1471 leafinfo = new TFormLeafInfoCollection(cl,0,cl);
1472 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1473 } else if (numberOfVarDim==1) {
1474 R__ASSERT(maininfo);
1475 leafinfo =
1477 (TStreamerElement*)nullptr,maininfo);
1478 previnfo->fNext = leafinfo;
1479 previnfo = leafinfo;
1480 leafinfo = new TFormLeafInfoCollection(cl,0,cl);
1481
1482 fHasMultipleVarDim[code] = true;
1483 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1484 }
1485 previnfo->fNext = leafinfo;
1486 previnfo = leafinfo;
1487 leafinfo = nullptr;
1488 }
1489 continue;
1490 } else if (right[i] == ')') {
1491 // We should have the end of a cast operator. Let's introduce a TFormLeafCast
1492 // in the chain.
1493 TClass * casted = (TClass*) ((int(--paran_level)>=0) ? castqueue.At(paran_level) : nullptr);
1494 if (casted) {
1495 leafinfo = new TFormLeafInfoCast(cl,casted);
1496 fHasCast = true;
1497
1498 if (maininfo==nullptr) {
1499 maininfo = leafinfo;
1500 }
1501 if (previnfo==nullptr) {
1502 previnfo = leafinfo;
1503 } else {
1504 previnfo->fNext = leafinfo;
1505 previnfo = leafinfo;
1506 }
1507 leafinfo = nullptr;
1508 current = &(work[0]);
1509 *current = 0;
1510
1511 cl = casted;
1512 continue;
1513
1514 }
1515 } else if (i > 0 && (right[i] == '.' || right[i] == '[' || right[i] == '\0') ) {
1516 // A delimiter happened let's see if what we have seen
1517 // so far does point to a data member.
1518 bool needClass = true;
1519 *current = '\0';
1520
1521 // skip it all if there is nothing to look at
1522 if (strlen(work)==0) continue;
1523
1524 prevUseCollectionObject = useCollectionObject;
1525 prevUseReferenceObject = useReferenceObject;
1526 if (work[0]=='@') {
1527 useReferenceObject = true;
1528 useCollectionObject = true;
1529 Int_t l = 0;
1530 for(l=0;work[l+1]!=0;++l) work[l] = work[l+1];
1531 work[l] = '\0';
1532 } else if (work[strlen(work)-1]=='@') {
1533 useReferenceObject = true;
1534 useCollectionObject = true;
1535 work[strlen(work)-1] = '\0';
1536 } else {
1537 useReferenceObject = false;
1538 useCollectionObject = false;
1539 }
1540
1541 bool mustderef = false;
1542 if ( !prevUseReferenceObject && cl && cl->GetReferenceProxy() ) {
1543 R__LoadBranch(leaf->GetBranch(), readentry, fQuickLoad);
1544 if ( !maininfo ) {
1545 maininfo = previnfo = new TFormLeafInfoReference(cl, element, offset);
1546 if ( cl->GetReferenceProxy()->HasCounter() ) {
1547 numberOfVarDim += RegisterDimensions(code,-1);
1548 }
1549 prevUseReferenceObject = false;
1550 } else {
1551 previnfo->fNext = new TFormLeafInfoReference(cl, element, offset);
1552 previnfo = previnfo->fNext;
1553 }
1554 TVirtualRefProxy *refproxy = cl->GetReferenceProxy();
1555 cl = nullptr;
1556 for(Long64_t entry=0; entry<leaf->GetBranch()->GetEntries()-readentry; ++entry) {
1557 R__LoadBranch(leaf->GetBranch(), readentry+i, fQuickLoad);
1558 void *refobj = maininfo->GetValuePointer(leaf,0);
1559 if (refobj) {
1560 cl = refproxy->GetValueClass(refobj);
1561 }
1562 if ( cl ) break;
1563 }
1564 needClass = false;
1565 mustderef = true;
1566 }
1567 else if (!prevUseCollectionObject && cl == TClonesArray::Class()) {
1568 // We are not interested in the ClonesArray object but only
1569 // in its contents.
1570 // We need to retrieve the class of its content.
1571
1572 TBranch *clbranch = leaf->GetBranch();
1573 R__LoadBranch(clbranch,readentry,fQuickLoad);
1574 TClonesArray * clones;
1575 if (maininfo) {
1576 clones = (TClonesArray*)maininfo->GetValuePointer(leaf,0);
1577 } else {
1578 // we have a unsplit TClonesArray leaves
1579 // or we did not yet match any of the sub-branches!
1580
1581 TClass *mother_cl;
1582 if (leaf->IsA()==TLeafObject::Class()) {
1583 // in this case mother_cl is not really used
1584 mother_cl = cl;
1585 } else {
1586 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1587 }
1588
1589 TFormLeafInfo* clonesinfo = new TFormLeafInfoClones(mother_cl, 0);
1590 // The dimension needs to be handled!
1591 numberOfVarDim += RegisterDimensions(code,clonesinfo,maininfo,false);
1592
1593 mustderef = true;
1594 previnfo = clonesinfo;
1595 maininfo = clonesinfo;
1596
1597 if (clbranch->GetListOfBranches()->GetLast()>=0) {
1598 if (clbranch->IsA() != TBranchElement::Class()) {
1599 Error("DefinedVariable","Unimplemented usage of ClonesArray");
1600 return -2;
1601 }
1602 //clbranch = ((TBranchElement*)clbranch)->GetMother();
1603 clones = (TClonesArray*)((TBranchElement*)clbranch)->GetObject();
1604 } else
1605 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1606 }
1607 // NOTE clones can be zero!
1608 if (clones==nullptr) {
1609 Warning("DefinedVariable",
1610 "TClonesArray object was not retrievable for %s!",
1611 name.Data());
1612 return -1;
1613 }
1614 TClass * inside_cl = clones->GetClass();
1615#if 1
1616 cl = inside_cl;
1617#else
1618/* Maybe we should make those test lead to warning messages */
1619 if (1 || inside_cl) cl = inside_cl;
1620 // if inside_cl is nul ... we have a problem of inconsistency :(
1621 if (0 && strlen(work)==0) {
1622 // However in this case we have NO content :(
1623 // so let get the number of objects
1624 //strcpy(work,"fLast");
1625 }
1626#endif
1627 } else if (!prevUseCollectionObject && cl && cl->GetCollectionProxy() ) {
1628
1629 // We are NEVER interested in the Collection object but only
1630 // in its contents.
1631 // We need to retrieve the class of its content.
1632
1633 TBranch *clbranch = leaf->GetBranch();
1634 R__LoadBranch(clbranch,readentry,fQuickLoad);
1635
1636 if (maininfo==nullptr) {
1637
1638 // we have a unsplit Collection leaf
1639 // or we did not yet match any of the sub-branches!
1640
1641 TClass *mother_cl;
1642 if (leaf->IsA()==TLeafObject::Class()) {
1643 // in this case mother_cl is not really used
1644 mother_cl = cl;
1645 } else {
1646 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1647 }
1648
1649 TFormLeafInfo* collectioninfo =
1650 new TFormLeafInfoCollection(mother_cl, 0, cl);
1651 // The dimension needs to be handled!
1652 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
1653
1654 mustderef = true;
1655 previnfo = collectioninfo;
1656 maininfo = collectioninfo;
1657
1658 } //else if (clbranch->GetStreamerType()==0) {
1659
1660 //}
1661
1662 TClass * inside_cl = cl->GetCollectionProxy()->GetValueClass();
1663
1664 if (!inside_cl) {
1665 Error("DefinedVariable","Could you not find the inner class for %s with coll type = %d",
1666 cl->GetName(),cl->GetCollectionProxy()->GetType());
1667 }
1668 if (!inside_cl && cl->GetCollectionProxy()->GetType() > 0) {
1669 Warning("DefinedVariable","No data member in content of %s in %s\n",
1670 cl->GetName(),name.Data());
1671 }
1672 cl = inside_cl;
1673 // if inside_cl is nul ... we have a problem of inconsistency.
1674 }
1675
1676 if (!cl) {
1677 if (leaf) leaf->GetBranch()->Print();
1678 Warning("DefinedVariable","Missing class for %s!",name.Data());
1679 } else {
1680 element = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1681 }
1682
1683 if (!element && !prevUseCollectionObject) {
1684 // We allow for looking for a data member inside a class inside
1685 // a TClonesArray without mentioning the TClonesArrays variable name
1686 TIter next( cl->GetStreamerInfo()->GetElements() );
1687 TStreamerElement * curelem;
1688 while ((curelem = (TStreamerElement*)next())) {
1689 if (curelem->GetClassPointer() == TClonesArray::Class()) {
1690 Int_t clones_offset = 0;
1691 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),clones_offset);
1692 TFormLeafInfo* clonesinfo =
1693 new TFormLeafInfo(cl, clones_offset, curelem);
1694 TClonesArray * clones;
1695 R__LoadBranch(leaf->GetBranch(),readentry,fQuickLoad);
1696
1697 if (previnfo) {
1698 previnfo->fNext = clonesinfo;
1699 clones = (TClonesArray*)maininfo->GetValuePointer(leaf,0);
1700 previnfo->fNext = nullptr;
1701 } else {
1702 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1703 }
1704
1705 TClass *sub_cl = clones->GetClass();
1706 if (sub_cl) element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1707 delete clonesinfo;
1708
1709 if (element) {
1710 leafinfo = new TFormLeafInfoClones(cl,clones_offset,curelem);
1711 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1712 if (maininfo==nullptr) maininfo = leafinfo;
1713 if (previnfo==nullptr) previnfo = leafinfo;
1714 else {
1715 previnfo->fNext = leafinfo;
1716 previnfo = leafinfo;
1717 }
1718 leafinfo = nullptr;
1719 cl = sub_cl;
1720 break;
1721 }
1722 } else if (curelem->GetClassPointer() && curelem->GetClassPointer()->GetCollectionProxy()) {
1723
1724 Int_t coll_offset = 0;
1725 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),coll_offset);
1726
1727 TClass *sub_cl =
1729 if (sub_cl) {
1730 element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1731 }
1732 if (element) {
1733 if (numberOfVarDim>1) {
1734 Warning("DefinedVariable","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1735 curelem->GetName());
1736 leafinfo = new TFormLeafInfo(cl,coll_offset,curelem);
1737 useCollectionObject = true;
1738 } else if (numberOfVarDim==1) {
1739 R__ASSERT(maininfo);
1740 leafinfo =
1741 new TFormLeafInfoMultiVarDimCollection(cl,coll_offset,
1742 curelem,maininfo);
1743 fHasMultipleVarDim[code] = true;
1744 leafinfo->fNext = new TFormLeafInfoCollection(cl,coll_offset,curelem);
1745 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1746 } else {
1747 leafinfo = new TFormLeafInfoCollection(cl,coll_offset,curelem);
1748 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1749 }
1750 if (maininfo==nullptr) maininfo = leafinfo;
1751 if (previnfo==nullptr) previnfo = leafinfo;
1752 else {
1753 previnfo->fNext = leafinfo;
1754 previnfo = leafinfo;
1755 }
1756 if (leafinfo->fNext) {
1757 previnfo = leafinfo->fNext;
1758 }
1759 leafinfo = nullptr;
1760 cl = sub_cl;
1761 break;
1762 }
1763 }
1764 }
1765
1766 }
1767
1768 if (element) {
1769 Int_t type = element->GetNewType();
1770 if (type<60 && type!=0) {
1771 // This is a basic type ...
1772 if (numberOfVarDim>=1 && type>40) {
1773 // We have a variable array within a variable array!
1774 leafinfo = new TFormLeafInfoMultiVarDim(cl,offset,element,maininfo);
1775 fHasMultipleVarDim[code] = true;
1776 } else {
1777 if (leafinfo && type<=40 ) {
1778 leafinfo->AddOffset(offset,element);
1779 } else {
1780 leafinfo = new TFormLeafInfo(cl,offset,element);
1781 }
1782 }
1783 } else {
1784 bool object = false;
1785 bool pointer = false;
1786 bool objarr = false;
1787 switch(type) {
1798 pointer = true;
1799 break;
1801 case TStreamerInfo::kAny :
1807 object = true;
1808 break;
1812 objarr = true;
1813 break;
1816 // Unsupported case.
1817 Error("DefinedVariable",
1818 "%s is a datamember of %s BUT is not yet of a supported type (%d)",
1819 right,cl ? cl->GetName() : "unknown class",type);
1820 return -2;
1821 default:
1822 // Unknown and Unsupported case.
1823 Error("DefinedVariable",
1824 "%s is a datamember of %s BUT is not of a unknown type (%d)",
1825 right,cl ? cl->GetName() : "unknown class",type);
1826 return -2;
1827 }
1828
1829 if (object && !useCollectionObject &&
1830 ( element->GetClassPointer() == TClonesArray::Class()
1831 || element->GetClassPointer()->GetCollectionProxy() ) )
1832 {
1833 object = false;
1834 }
1835 if (object && leafinfo) {
1836 leafinfo->AddOffset(offset,element);
1837 } else if (objarr) {
1838 // This is an embedded array of objects. We can not increase the offset.
1839 leafinfo = new TFormLeafInfo(cl,offset,element);
1840 mustderef = true;
1841 } else {
1842
1843 if (!useCollectionObject && element->GetClassPointer() == TClonesArray::Class()) {
1844
1845 leafinfo = new TFormLeafInfoClones(cl,offset,element);
1846 mustderef = true;
1847
1848 } else if (!useCollectionObject && element->GetClassPointer()
1849 && element->GetClassPointer()->GetCollectionProxy()) {
1850
1851 mustderef = true;
1852 if (numberOfVarDim>1) {
1853 Warning("DefinedVariable","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1854 element->GetName());
1855 leafinfo = new TFormLeafInfo(cl,offset,element);
1856 useCollectionObject = true;
1857 } else if (numberOfVarDim==1) {
1858 R__ASSERT(maininfo);
1859 leafinfo =
1860 new TFormLeafInfoMultiVarDimCollection(cl,offset,element,maininfo);
1861
1862 fHasMultipleVarDim[code] = true;
1863 //numberOfVarDim += RegisterDimensions(code,leafinfo);
1864 //cl = cl->GetCollectionProxy()->GetValueClass();
1865
1866 //if (maininfo==0) maininfo = leafinfo;
1867 //if (previnfo==0) previnfo = leafinfo;
1868 //else {
1869 // previnfo->fNext = leafinfo;
1870 // previnfo = leafinfo;
1871 //}
1872 leafinfo->fNext = new TFormLeafInfoCollection(cl, offset, element);
1873 if (element->GetClassPointer()->GetCollectionProxy()->GetValueClass()==nullptr) {
1875 element->GetClassPointer()->GetCollectionProxy());
1876 if (leafinfo->fNext) leafinfo->fNext->fNext = info;
1877 else leafinfo->fNext = info;
1878 }
1879 } else {
1880 leafinfo = new TFormLeafInfoCollection(cl, offset, element);
1881
1882 TClass *elemCl = element->GetClassPointer();
1883 TClass *valueCl = elemCl->GetCollectionProxy()->GetValueClass();
1884 if (!maininfo) maininfo = leafinfo;
1885
1886 if (valueCl!=nullptr && valueCl->GetCollectionProxy()!=nullptr) {
1887
1888 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,false);
1889 if (previnfo==nullptr) previnfo = leafinfo;
1890 else {
1891 previnfo->fNext = leafinfo;
1892 previnfo = leafinfo;
1893 }
1894 leafinfo = new TFormLeafInfoMultiVarDimCollection(elemCl,0,
1895 elemCl->GetCollectionProxy()->GetValueClass(),maininfo);
1896 //numberOfVarDim += RegisterDimensions(code,previnfo->fNext);
1897 fHasMultipleVarDim[code] = true;
1898 //previnfo = previnfo->fNext;
1899 leafinfo->fNext = new TFormLeafInfoCollection(elemCl,0,
1900 valueCl);
1901 elemCl = valueCl;
1902 }
1903 if (elemCl->GetCollectionProxy() &&
1904 elemCl->GetCollectionProxy()->GetValueClass()==nullptr) {
1906 if (leafinfo->fNext) leafinfo->fNext->fNext = info;
1907 else leafinfo->fNext = info;
1908 }
1909 }
1910 } else if ( (object || pointer) && !useReferenceObject && element->GetClassPointer()->GetReferenceProxy() ) {
1911 TClass* c = element->GetClassPointer();
1912 R__LoadBranch(leaf->GetBranch(),readentry,fQuickLoad);
1913 if ( object ) {
1914 leafinfo = new TFormLeafInfoReference(c, element, offset);
1915 }
1916 else {
1917 leafinfo = new TFormLeafInfoPointer(cl,offset,element);
1918 leafinfo->fNext = new TFormLeafInfoReference(c, element, 0);
1919 }
1920 //if ( c->GetReferenceProxy()->HasCounter() ) {
1921 // numberOfVarDim += RegisterDimensions(code,-1);
1922 //}
1923 prevUseReferenceObject = false;
1924 needClass = false;
1925 mustderef = true;
1926 } else if (pointer) {
1927 // this is a pointer to be followed.
1928 leafinfo = new TFormLeafInfoPointer(cl,offset,element);
1929 mustderef = true;
1930 } else {
1931 // this is an embedded object.
1932 R__ASSERT(object);
1933 leafinfo = new TFormLeafInfo(cl,offset,element);
1934 }
1935 }
1936 }
1937 } else {
1938 if (cl) Error("DefinedVariable","%s is not a datamember of %s",work,cl->GetName());
1939 // no else, we warned earlier that the class was missing.
1940 return -1;
1941 }
1942
1943 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,useCollectionObject); // Note or useCollectionObject||prevUseColectionObject
1944 if (maininfo==nullptr) {
1945 maininfo = leafinfo;
1946 }
1947 if (previnfo==nullptr) {
1948 previnfo = leafinfo;
1949 } else if (previnfo!=leafinfo) {
1950 previnfo->fNext = leafinfo;
1951 previnfo = leafinfo;
1952 }
1953 while (previnfo->fNext) previnfo = previnfo->fNext;
1954
1955 if ( right[i] != '\0' ) {
1956 if ( !needClass && mustderef ) {
1957 maininfo->SetBranch(leaf->GetBranch());
1958 char *ptr = (char*)maininfo->GetValuePointer(leaf,0);
1959 TFormLeafInfoReference* refInfo = nullptr;
1960 if ( !maininfo->IsReference() ) {
1961 for( TFormLeafInfo* inf = maininfo->fNext; inf; inf = inf->fNext ) {
1962 if ( inf->IsReference() ) {
1963 refInfo = (TFormLeafInfoReference*)inf;
1964 }
1965 }
1966 }
1967 else {
1968 refInfo = (TFormLeafInfoReference*)maininfo;
1969 }
1970 if ( refInfo ) {
1971 cl = refInfo->GetValueClass(ptr);
1972 if ( !cl ) {
1973 Error("DefinedVariable","Failed to access class type of reference target (%s)",element->GetName());
1974 return -1;
1975 }
1976 element = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1977 }
1978 else {
1979 Error("DefinedVariable","Failed to access class type of reference target (%s)",element->GetName());
1980 return -1;
1981 }
1982 }
1983 else if ( needClass ) {
1984 cl = element->GetClassPointer();
1985 }
1986 }
1987 if (mustderef) leafinfo = nullptr;
1988 current = &(work[0]);
1989 *current = 0;
1990 R__ASSERT(right[i] != '['); // We are supposed to have removed all dimensions already!
1991
1992 if (cl == TString::Class() && strcmp(right+i+1,"fData") == 0) {
1993 // For backward compatibility replace TString::fData which no longer exist
1994 // by a call to TString::Data()
1995 right = ".Data()";
1996 i = 0;
1997 nchname = strlen(right);
1998 }
1999
2000 } else
2001 *current++ = right[i];
2002 }
2003 if (maininfo) {
2004 fDataMembers.AddAtAndExpand(maininfo,code);
2005 if (leaf) fLookupType[code] = kDataMember;
2006 else fLookupType[code] = kTreeMember;
2007 }
2008 }
2009
2010 if (strlen(work)!=0) {
2011 // We have something left to analyze. Let's make this an error case!
2012 return -1;
2013 }
2014
2015 TClass *objClass = EvalClass(code);
2016 if (objClass && !useLeafCollectionObject && objClass->GetCollectionProxy() && objClass->GetCollectionProxy()->GetValueClass()) {
2017 TFormLeafInfo *last = nullptr;
2018 if ( SwitchToFormLeafInfo(code) ) {
2019
2020 last = (TFormLeafInfo*)fDataMembers.At(code);
2021
2022 if (!last) return action;
2023 while (last->fNext) { last = last->fNext; }
2024
2025 }
2026 if (last && last->GetClass() != objClass) {
2027 TClass *mother_cl;
2028 if (leaf->IsA()==TLeafObject::Class()) {
2029 // in this case mother_cl is not really used
2030 mother_cl = cl;
2031 } else {
2032 mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
2033 }
2034
2035 TFormLeafInfo* collectioninfo = new TFormLeafInfoCollection(mother_cl, 0, objClass, false);
2036 // The dimension needs to be handled!
2037 numberOfVarDim += RegisterDimensions(code,collectioninfo,maininfo,false);
2038 last->fNext = collectioninfo;
2039 }
2040 numberOfVarDim += RegisterDimensions(code,1); //NOTE: changed from 0
2041 objClass = objClass->GetCollectionProxy()->GetValueClass();
2042 }
2043 if (IsLeafString(code) || objClass == TString::Class() || objClass == stdStringClass) {
2044
2045 TFormLeafInfo *last = nullptr;
2046 if ( SwitchToFormLeafInfo(code) ) {
2047
2048 last = (TFormLeafInfo*)fDataMembers.At(code);
2049
2050 if (!last) return action;
2051 while (last->fNext) { last = last->fNext; }
2052
2053 }
2054 const char *funcname = nullptr;
2055 if (objClass == TString::Class()) {
2056 funcname = "Data";
2057 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2058 } else if (objClass == stdStringClass) {
2059 funcname = "c_str";
2060 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2061 }
2062 if (funcname) {
2063 TMethodCall *method = new TMethodCall(objClass, funcname, "");
2064 if (last) {
2065 last->fNext = new TFormLeafInfoMethod(objClass,method);
2066 } else {
2067 fDataMembers.AddAtAndExpand(new TFormLeafInfoMethod(objClass,method),code);
2068 if (leaf) fLookupType[code] = kDataMember;
2069 else fLookupType[code] = kTreeMember;
2070 }
2071 }
2072 return kDefinedString;
2073 }
2074
2075 if (objClass) {
2076 TMethodCall *method = new TMethodCall(objClass, "AsDouble", "");
2077 if (method->IsValid()
2078 && (method->ReturnType() == TMethodCall::kLong || method->ReturnType() == TMethodCall::kDouble)) {
2079
2080 TFormLeafInfo *last = nullptr;
2081 if (SwitchToFormLeafInfo(code)) {
2082 last = (TFormLeafInfo*)fDataMembers.At(code);
2083 // Improbable case
2084 if (!last) {
2085 delete method;
2086 return action;
2087 }
2088 while (last->fNext) { last = last->fNext; }
2089 }
2090 if (last) {
2091 last->fNext = new TFormLeafInfoMethod(objClass,method);
2092 } else {
2093 fDataMembers.AddAtAndExpand(new TFormLeafInfoMethod(objClass,method),code);
2094 if (leaf) fLookupType[code] = kDataMember;
2095 else fLookupType[code] = kTreeMember;
2096 }
2097
2098 return kDefinedVariable;
2099 }
2100 delete method;
2101 method = new TMethodCall(objClass, "AsString", "");
2102 if (method->IsValid()
2103 && method->ReturnType() == TMethodCall::kString) {
2104
2105 TFormLeafInfo *last = nullptr;
2106 if (SwitchToFormLeafInfo(code)) {
2107 last = (TFormLeafInfo*)fDataMembers.At(code);
2108 // Improbable case
2109 if (!last) {
2110 delete method;
2111 return action;
2112 }
2113 while (last->fNext) { last = last->fNext; }
2114 }
2115 if (last) {
2116 last->fNext = new TFormLeafInfoMethod(objClass,method);
2117 } else {
2118 fDataMembers.AddAtAndExpand(new TFormLeafInfoMethod(objClass,method),code);
2119 if (leaf) fLookupType[code] = kDataMember;
2120 else fLookupType[code] = kTreeMember;
2121 }
2122
2123 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2124 return kDefinedString;
2125 }
2126 if (method->IsValid()
2127 && method->ReturnType() == TMethodCall::kOther) {
2128
2130 if ((rcl == TString::Class() || rcl == stdStringClass) ) {
2131
2132 TFormLeafInfo *last = nullptr;
2133 if (SwitchToFormLeafInfo(code)) {
2134 last = (TFormLeafInfo*)fDataMembers.At(code);
2135 // Improbable case
2136 if (!last) {
2137 delete method;
2138 return action;
2139 }
2140 while (last->fNext) { last = last->fNext; }
2141 }
2142 if (last) {
2143 last->fNext = new TFormLeafInfoMethod(objClass,method);
2144 last = last->fNext;
2145 } else {
2146 last = new TFormLeafInfoMethod(objClass,method);
2147 fDataMembers.AddAtAndExpand(last,code);
2148 if (leaf) fLookupType[code] = kDataMember;
2149 else fLookupType[code] = kTreeMember;
2150 }
2151
2152 objClass = rcl;
2153
2154 const char *funcname = nullptr;
2155 if (objClass == TString::Class()) {
2156 funcname = "Data";
2157 } else if (objClass == stdStringClass) {
2158 funcname = "c_str";
2159 }
2160 if (funcname) {
2161 method = new TMethodCall(objClass, funcname, "");
2162 last->fNext = new TFormLeafInfoMethod(objClass,method);
2163 }
2164 return kDefinedString;
2165 }
2166 }
2167 delete method;
2168 }
2169
2170 return action;
2171}
2172
2173////////////////////////////////////////////////////////////////////////////////
2174/// Look for the leaf corresponding to the start of expression.
2175/// It returns the corresponding leaf if any.
2176/// It also modify the following arguments:
2177///
2178/// - leftover: contain from expression that was not used to determine the leaf
2179/// - final:
2180/// * paran_level: number of un-matched open parenthesis
2181/// * cast_queue: list of cast to be done
2182/// * aliases: list of aliases used
2183/// - Return <0 in case of failure
2184///
2185/// - Return 0 if a leaf has been found
2186/// - Return 2 if info about the TTree itself has been requested.
2187
2188Int_t TTreeFormula::FindLeafForExpression(const char* expression, TLeaf*& leaf, TString& leftover, bool& final, UInt_t& paran_level, TObjArray& castqueue, std::vector<std::string>& aliasUsed, bool& useLeafCollectionObject, const char* fullExpression)
2189{
2190 // Later on we will need to read one entry, let's make sure
2191 // it is a real entry.
2192 if (fTree->GetTree()==nullptr) {
2193 fTree->LoadTree(0);
2194 if (fTree->GetTree()==nullptr) return -1;
2195 }
2196 Long64_t readentry = fTree->GetTree()->GetReadEntry();
2197 if (readentry < 0) readentry=0;
2198 const char *cname = expression;
2199 char first[kMaxLen]; first[0] = '\0';
2200 char second[kMaxLen*2]; second[0] = '\0';
2201 char right[kMaxLen*2]; right[0] = '\0';
2202 char work[kMaxLen]; work[0] = '\0';
2203 char left[kMaxLen]; left[0] = '\0';
2204 char scratch[kMaxLen*5];
2205 char scratch2[kMaxLen*5];
2206 std::string currentname;
2207 Int_t previousdot = 0;
2208 char *current;
2209 TLeaf *tmp_leaf=nullptr;
2210 TBranch *branch=nullptr, *tmp_branch=nullptr;
2211 Int_t nchname = strlen(cname);
2212 Int_t i;
2213 bool foundAtSign = false;
2214 bool startWithParan = false;
2215
2216 for (i=0, current = &(work[0]); i<=nchname && !final;i++ ) {
2217 // We will treated the terminator as a token.
2218 *current++ = cname[i];
2219
2220 if (cname[i] == '(') {
2221 ++paran_level;
2222
2223 if (current==work+1) {
2224 // If the expression starts with a parenthesis, we are likely
2225 // to have a cast operator inside.
2226 startWithParan = true;
2227 current--;
2228 }
2229 continue;
2230 //i++;
2231 //while( cname[i]!=')' && cname[i] ) {
2232 // *current++ = cname[i++];
2233 //}
2234 //*current++ = cname[i];
2235 ////*current = 0;
2236 //continue;
2237 }
2238 if (cname[i] == ')') {
2239 if (paran_level==0) {
2240 Error("DefinedVariable","Unmatched parenthesis in %s",fullExpression);
2241 return -1;
2242 }
2243 paran_level--;
2244
2245 if (startWithParan) {
2246 startWithParan = false; // the next match wont be against the starting parenthesis.
2247
2248 // Let's see if work is a classname and thus we have a cast.
2249 *(--current) = 0;
2250 TString cast_name = gInterpreter->TypeName(work);
2251 TClass *cast_cl = TClass::GetClass(cast_name);
2252 if (cast_cl) {
2253 // We must have a cast
2254 castqueue.AddAtAndExpand(cast_cl,paran_level);
2255 current = &(work[0]);
2256 *current = 0;
2257 // Warning("DefinedVariable","Found cast to %s",cast_fullExpression);
2258 continue;
2259 } else if (gROOT->GetType(cast_name)) {
2260 // We reset work
2261 current = &(work[0]);
2262 *current = 0;
2263 Warning("DefinedVariable",
2264 "Casting to primary types like \"%s\" is not supported yet",cast_name.Data());
2265 continue;
2266 }
2267 *(current++)=')';
2268 }
2269
2270 *current='\0';
2271 char *params = strchr(work,'(');
2272 if (params) {
2273 *params = 0; params++;
2274
2275 if (branch && !leaf) {
2276 // We have a branch but not a leaf. We are likely to have found
2277 // the top of split branch.
2278 if (BranchHasMethod(nullptr, branch, work, params, readentry)) {
2279 //fprintf(stderr, "Does have a method %s for %s.\n", work, branch->GetName());
2280 }
2281 }
2282
2283 // What we have so far might be a member function of one of the
2284 // leaves that are not split (for example "GetNtrack" for the Event class).
2286 TLeaf* leafcur = nullptr;
2287 while (!leaf && (leafcur = (TLeaf*) next())) {
2288 TBranch* br = leafcur->GetBranch();
2289 bool yes = BranchHasMethod(leafcur, br, work, params, readentry);
2290 if (yes) {
2291 leaf = leafcur;
2292 //fprintf(stderr, "Does have a method %s for %s found in leafcur %s.\n", work, leafcur->GetBranch()->GetName(), leafcur->GetName());
2293 }
2294 }
2295 if (!leaf) {
2296 // Check for an alias.
2297 if (strlen(left) && left[strlen(left)-1]=='.') left[strlen(left)-1]=0;
2298 const char *aliasValue = fTree->GetAlias(left);
2299 if (aliasValue && strcspn(aliasValue,"+*/-%&!=<>|")==strlen(aliasValue)) {
2300 // First check whether we are using this alias recursively (this would
2301 // lead to an infinite recursion.
2302 if (find(aliasUsed.begin(),
2303 aliasUsed.end(),
2304 left) != aliasUsed.end()) {
2305 Error("DefinedVariable",
2306 "The substitution of the branch alias \"%s\" by \"%s\" in \"%s\" failed\n"\
2307 "\tbecause \"%s\" is used [recursively] in its own definition!",
2308 left,aliasValue,fullExpression,left);
2309 return -3;
2310 }
2311 aliasUsed.push_back(left);
2312 TString newExpression = aliasValue;
2313 newExpression += (cname+strlen(left));
2314 Int_t res = FindLeafForExpression(newExpression, leaf, leftover, final, paran_level,
2315 castqueue, aliasUsed, useLeafCollectionObject, fullExpression);
2316 if (res<0) {
2317 Error("DefinedVariable",
2318 "The substitution of the alias \"%s\" by \"%s\" failed.",left,aliasValue);
2319 return -3;
2320 }
2321 return res;
2322 }
2323
2324 // This is actually not really any error, we probably received something
2325 // like "abs(some_val)", let ROOT::v5::TFormula decompose it first.
2326 return -1;
2327 }
2328 // if (!leaf->InheritsFrom(TLeafObject::Class()) ) {
2329 // If the leaf that we found so far is not a TLeafObject then there is
2330 // nothing we would be able to do.
2331 // Error("DefinedVariable","Need a TLeafObject to call a function!");
2332 // return -1;
2333 //}
2334 // We need to recover the info not used.
2335 strlcpy(right,work,2*kMaxLen);
2336 strncat(right,"(",2*kMaxLen-1-strlen(right));
2337 strncat(right,params,2*kMaxLen-1-strlen(right));
2338 final = true;
2339
2340 // Record in 'i' what we consumed
2341 i += strlen(params);
2342
2343 // we reset work
2344 current = &(work[0]);
2345 *current = 0;
2346 break;
2347 }
2348 }
2349 if (cname[i] == '.' || cname[i] == '\0' || cname[i] == ')') {
2350 // A delimiter happened let's see if what we have seen
2351 // so far does point to a leaf.
2352 *current = '\0';
2353
2354 Int_t len = strlen(work);
2355 if (work[0]=='@') {
2356 foundAtSign = true;
2357 Int_t l = 0;
2358 for(l=0;work[l+1]!=0;++l) work[l] = work[l+1];
2359 work[l] = '\0';
2360 --current;
2361 } else if (len>=2 && work[len-2]=='@') {
2362 foundAtSign = true;
2363 work[len-2] = cname[i];
2364 work[len-1] = '\0';
2365 --current;
2366 } else {
2367 foundAtSign = false;
2368 }
2369
2370 if (left[0]==0) strlcpy(left,work,kMaxLen);
2371 if (!leaf && !branch) {
2372 // So far, we have not found a matching leaf or branch.
2373 strlcpy(first,work,kMaxLen);
2374
2375 std::string treename(first);
2376 if (!treename.empty() && treename[treename.size()-1]=='.') {
2377 treename.erase(treename.size()-1);
2378 }
2379 if (treename== "This" /* || treename == fTree->GetName() */ ) {
2380 // Request info about the TTree object itself,
2381 TNamed *named = new TNamed(fTree->GetName(),fTree->GetName());
2384 if (cname[i]) leftover = &(cname[i+1]);
2385 return 2;
2386 }
2387 // The following would allow to access the friend by name
2388 // however, it would also prevent the access of the leaves
2389 // within the friend. We could use the '@' notation here
2390 // however this would not be aesthetically pleasing :(
2391 // What we need to do, is add the ability to look ahead to
2392 // the next 'token' to decide whether we to access the tree
2393 // or its leaf.
2394 //} else {
2395 // TTree *tfriend = fTree->GetFriend(treename.c_str());
2396 // TTree *realtree = fTree->GetTree();
2397 // if (!tfriend && realtree != fTree){
2398 // // If it is a chain and we did not find a friend,
2399 // // let's try with the internal tree.
2400 // tfriend = realtree->GetFriend(treename.c_str());
2401 // }
2402 // if (tfriend) {
2403 // TNamed *named = new TNamed(treename.c_str(),tfriend->GetName());
2404 // fLeafNames.AddAtAndExpand(named,fNcodes);
2405 // fLeaves.AddAtAndExpand(tfriend,fNcodes);
2406 // if (cname[i]) leftover = &(cname[i+1]);
2407 // return 2;
2408 // }
2409 //}
2410
2411 branch = fTree->FindBranch(first);
2412 leaf = fTree->FindLeaf(first);
2413
2414 // Now look with the delimiter removed (we looked with it first
2415 // because a dot is allowed at the end of some branches).
2416 if (cname[i]) first[strlen(first)-1]='\0';
2417 if (!branch) branch = fTree->FindBranch(first);
2418 if (!leaf) leaf = fTree->FindLeaf(first);
2419 TClass* cl = nullptr;
2420 if ( branch && branch->InheritsFrom(TBranchElement::Class()) ) {
2421 int offset=0;
2422 TBranchElement* bElt = (TBranchElement*)branch;
2423 TStreamerInfo* info = bElt->GetInfo();
2424 TStreamerElement* element = info ? info->GetStreamerElement(first,offset) : nullptr;
2425 if (element) cl = element->GetClassPointer();
2426 if ( cl && !cl->GetReferenceProxy() ) cl = nullptr;
2427 }
2428 if ( cl ) { // We have a reference class here....
2429 final = true;
2430 useLeafCollectionObject = foundAtSign;
2431 // we reset work
2432 current = &(work[0]);
2433 *current = 0;
2434 }
2435 else if (branch && (foundAtSign || cname[i] != 0) ) {
2436 // Since we found a branch and there is more information in the name,
2437 // we do NOT look at the 'IsOnTerminalBranch' status of the leaf
2438 // we found ... yet!
2439
2440 if (leaf==nullptr) {
2441 // Note we do not know (yet?) what (if anything) to do
2442 // for a TBranchObject branch.
2443 if (branch->InheritsFrom(TBranchElement::Class()) ) {
2444 Int_t type = ((TBranchElement*)branch)->GetType();
2445 if ( type == 3 || type ==4) {
2446 // We have a Collection branch.
2447 leaf = (TLeaf*)branch->GetListOfLeaves()->At(0);
2448 if (foundAtSign) {
2449 useLeafCollectionObject = foundAtSign;
2450 foundAtSign = false;
2451 current = &(work[0]);
2452 *current = 0;
2453 ++i;
2454 break;
2455 }
2456 }
2457 }
2458 }
2459
2460 // we reset work
2461 useLeafCollectionObject = foundAtSign;
2462 foundAtSign = false;
2463 current = &(work[0]);
2464 *current = 0;
2465 } else if (leaf || branch) {
2466 if (leaf && branch) {
2467 // We found both a leaf and branch matching the request name
2468 // let's see which one is the proper one to use! (On annoying case
2469 // is that where the same name is repeated ( varname.varname )
2470
2471 // We always give priority to the branch
2472 // leaf = 0;
2473 }
2474 if (leaf && leaf->IsOnTerminalBranch()) {
2475 // This is a non-object leaf, it should NOT be specified more except for
2476 // dimensions.
2477 final = true;
2478 }
2479 // we reset work
2480 current = &(work[0]);
2481 *current = 0;
2482 } else {
2483 // What we have so far might be a data member of one of the
2484 // leaves that are not split (for example "fNtrack" for the Event class.
2485 TLeaf *leafcur = GetLeafWithDatamember(first,work,readentry);
2486 if (leafcur) {
2487 leaf = leafcur;
2488 branch = leaf->GetBranch();
2489 if (leaf->IsOnTerminalBranch()) {
2490 final = true;
2491 strlcpy(right,first,kMaxLen);
2492 //We need to put the delimiter back!
2493 if (foundAtSign) strncat(right,"@",2*kMaxLen-1-strlen(right));
2494 if (cname[i]=='.') strncat(right,".",2*kMaxLen-1-strlen(right));
2495
2496 // We reset work
2497 current = &(work[0]);
2498 *current = 0;
2499 };
2500 } else if (cname[i] == '.') {
2501 // If we have a branch that match a name preceded by a dot
2502 // then we assume we are trying to drill down the branch
2503 // Let look if one of the top level branch has a branch with the name
2504 // we are looking for.
2505 TBranch *branchcur;
2506 TIter next( fTree->GetListOfBranches() );
2507 while(!branch && (branchcur=(TBranch*)next()) ) {
2508 branch = branchcur->FindBranch(first);
2509 }
2510 if (branch) {
2511 // We reset work
2512 current = &(work[0]);
2513 *current = 0;
2514 }
2515 }
2516 }
2517 } else { // correspond to if (leaf || branch)
2518 if (final) {
2519 Error("DefinedVariable", "Unexpected control flow!");
2520 return -1;
2521 }
2522
2523 // No dot is allowed in subbranches and leaves, so
2524 // we always remove it in the present case.
2525 if (cname[i]) work[strlen(work)-1] = '\0';
2526 snprintf(scratch,sizeof(scratch),"%s.%s",first,work);
2527 snprintf(scratch2,sizeof(scratch2),"%s.%s.%s",first,second,work);
2528
2529 if (previousdot) {
2530 currentname = &(work[previousdot+1]);
2531 }
2532
2533 // First look for the current 'word' in the list of
2534 // leaf of the
2535 if (branch) {
2536 tmp_leaf = branch->FindLeaf(work);
2537 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch);
2538 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch2);
2539 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(currentname.c_str());
2540 }
2541 if (tmp_leaf && tmp_leaf->IsOnTerminalBranch() ) {
2542 // This is a non-object leaf, it should NOT be specified more except for
2543 // dimensions.
2544 final = true;
2545 }
2546
2547 if (branch) {
2548 tmp_branch = branch->FindBranch(work);
2549 if (!tmp_branch) tmp_branch = branch->FindBranch(scratch);
2550 if (!tmp_branch) tmp_branch = branch->FindBranch(scratch2);
2551 if (!tmp_branch) tmp_branch = branch->FindBranch(currentname.c_str());
2552 }
2553 if (tmp_branch) {
2554 branch=tmp_branch;
2555
2556 // NOTE: Should we look for a leaf within here?
2557 if (!final) {
2558 tmp_leaf = branch->FindLeaf(work);
2559 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch);
2560 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch2);
2561 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(currentname.c_str());
2562 if (tmp_leaf && tmp_leaf->IsOnTerminalBranch() ) {
2563 // This is a non-object leaf, it should NOT be specified
2564 // more except for dimensions.
2565 final = true;
2566 leaf = tmp_leaf;
2567 }
2568 }
2569 }
2570 if (tmp_leaf) {
2571 // Something was found.
2572 if (second[0]) strncat(second,".",2*kMaxLen-1-strlen(second));
2573 strncat(second,work,2*kMaxLen-1-strlen(second));
2574 leaf = tmp_leaf;
2575 useLeafCollectionObject = foundAtSign;
2576 foundAtSign = false;
2577
2578 // we reset work
2579 current = &(work[0]);
2580 *current = 0;
2581 } else {
2582 //We need to put the delimiter back!
2583 if (strlen(work)) {
2584 if (foundAtSign) {
2585 Int_t where = strlen(work);
2586 work[where] = '@';
2587 work[where+1] = cname[i];
2588 ++current;
2589 previousdot = where+1;
2590 } else {
2591 previousdot = strlen(work);
2592 work[strlen(work)] = cname[i];
2593 }
2594 } else --current;
2595 }
2596 }
2597 }
2598 }
2599
2600 // Copy the left over for later use.
2601 if (strlen(work)) {
2602 strncat(right,work,2*kMaxLen-1-strlen(right));
2603 }
2604
2605 if (i<nchname) {
2606 if (strlen(right) && right[strlen(right)-1]!='.' && cname[i]!='.') {
2607 // In some cases we remove a little to fast the period, we add
2608 // it back if we need. It is assumed that 'right' and the rest of
2609 // the name was cut by a delimiter, so this should be safe.
2610 strncat(right,".",2*kMaxLen-1-strlen(right));
2611 }
2612 strncat(right,&cname[i],2*kMaxLen-1-strlen(right));
2613 }
2614
2615 if (!final && branch) {
2616 if (!leaf) {
2617 leaf = (TLeaf*)branch->GetListOfLeaves()->UncheckedAt(0);
2618 if (!leaf) return -1;
2619 }
2620 final = leaf->IsOnTerminalBranch();
2621 }
2622
2623 if (leaf && leaf->InheritsFrom(TLeafObject::Class()) ) {
2624 if (strlen(right)==0) strlcpy(right,work,2*kMaxLen);
2625 }
2626
2627 if (leaf==nullptr && left[0]!=0) {
2628 if (left[strlen(left)-1]=='.') left[strlen(left)-1]=0;
2629
2630 // Check for an alias.
2631 const char *aliasValue = fTree->GetAlias(left);
2632 if (aliasValue && strcspn(aliasValue,"()[]+*/-%&!=<>|")==strlen(aliasValue)) {
2633 // First check whether we are using this alias recursively (this would
2634 // lead to an infinite recursion).
2635 if (find(aliasUsed.begin(),
2636 aliasUsed.end(),
2637 left) != aliasUsed.end()) {
2638 Error("DefinedVariable",
2639 "The substitution of the branch alias \"%s\" by \"%s\" in \"%s\" failed\n"\
2640 "\tbecause \"%s\" is used [recursively] in its own definition!",
2641 left,aliasValue,fullExpression,left);
2642 return -3;
2643 }
2644 aliasUsed.push_back(left);
2645 TString newExpression = aliasValue;
2646 newExpression += (cname+strlen(left));
2647 Int_t res = FindLeafForExpression(newExpression, leaf, leftover, final, paran_level,
2648 castqueue, aliasUsed, useLeafCollectionObject, fullExpression);
2649 if (res<0) {
2650 Error("DefinedVariable",
2651 "The substitution of the alias \"%s\" by \"%s\" failed.",left,aliasValue);
2652 return -3;
2653 }
2654 return res;
2655 }
2656 }
2657 leftover = right;
2658
2659 return 0;
2660}
2661
2662////////////////////////////////////////////////////////////////////////////////
2663/// Check if name is in the list of Tree/Branch leaves.
2664///
2665/// This member function redefines the function in ROOT::v5::TFormula
2666/// If a leaf has a name corresponding to the argument name, then
2667/// returns a new code.
2668///
2669/// A TTreeFormula may contain more than one variable.
2670/// For each variable referenced, the pointers to the corresponding
2671/// branch and leaf is stored in the object arrays fBranches and fLeaves.
2672///
2673/// name can be :
2674/// - Leaf_Name (simple variable or data member of a ClonesArray)
2675/// - Branch_Name.Leaf_Name
2676/// - Branch_Name.Method_Name
2677/// - Leaf_Name[index]
2678/// - Branch_Name.Leaf_Name[index]
2679/// - Branch_Name.Leaf_Name[index1]
2680/// - Branch_Name.Leaf_Name[][index2]
2681/// - Branch_Name.Leaf_Name[index1][index2]
2682///
2683/// New additions:
2684/// - Branch_Name.Leaf_Name[OtherLeaf_Name]
2685/// - Branch_Name.Datamember_Name
2686/// - '.' can be replaced by '->'
2687///
2688/// and
2689/// - Branch_Name[index1].Leaf_Name[index2]
2690/// - Leaf_name[index].Action().OtherAction(param)
2691/// - Leaf_name[index].Action()[val].OtherAction(param)
2692///
2693/// The expected returned values are
2694/// - -2 : the name has been recognized but won't be usable
2695/// - -1 : the name has not been recognized, or is too long, or tree does not exist.
2696/// - >=0 : the name has been recognized, return the internal code for this name.
2697
2699{
2700
2701 action = kDefinedVariable;
2702 if (!fTree) return -1;
2703
2704 fNpar = 0;
2705 if (name.Length() > kMaxLen) {
2706 Error("TTreeFormula", "The length of the variable name (%d) exceeds the maximum allowed (%d)", name.Length(), kMaxLen);
2707 return -1;
2708 }
2709 Int_t i,k;
2710
2711 if (name == "Entry$") {
2712 Int_t code = fNcodes++;
2713 fCodes[code] = 0;
2714 fLookupType[code] = kIndexOfEntry;
2715 return code;
2716 }
2717 if (name == "LocalEntry$") {
2718 Int_t code = fNcodes++;
2719 fCodes[code] = 0;
2721 return code;
2722 }
2723 if (name == "Entries$") {
2724 Int_t code = fNcodes++;
2725 fCodes[code] = 0;
2726 fLookupType[code] = kEntries;
2729 return code;
2730 }
2731 if (name == "LocalEntries$") {
2732 Int_t code = fNcodes++;
2733 fCodes[code] = 0;
2734 fLookupType[code] = kLocalEntries;
2735 SetBit(kNeedEntries); // FIXME: necessary?
2736 fManager->SetBit(kNeedEntries); // FIXME: necessary?
2737 return code;
2738 }
2739 if (name == "Iteration$") {
2740 Int_t code = fNcodes++;
2741 fCodes[code] = 0;
2742 fLookupType[code] = kIteration;
2743 return code;
2744 }
2745 if (name == "Length$") {
2746 Int_t code = fNcodes++;
2747 fCodes[code] = 0;
2748 fLookupType[code] = kLength;
2749 return code;
2750 }
2751 static const char *lenfunc = "Length$(";
2752 if (strncmp(name.Data(),"Length$(",strlen(lenfunc))==0
2753 && name[name.Length()-1]==')') {
2754
2755 TString subform = name.Data()+strlen(lenfunc);
2756 subform.Remove( subform.Length() - 1 );
2757 TTreeFormula *lengthForm = new TTreeFormula("lengthForm",subform,fTree);
2758 fAliases.AddAtAndExpand(lengthForm,fNoper);
2759 Int_t code = fNcodes++;
2760 fCodes[code] = 0;
2761 fLookupType[code] = kLengthFunc;
2762 return code;
2763 }
2764 static const char *minfunc = "Min$(";
2765 if (strncmp(name.Data(),"Min$(",strlen(minfunc))==0
2766 && name[name.Length()-1]==')') {
2767
2768 TString subform = name.Data()+strlen(minfunc);
2769 subform.Remove( subform.Length() - 1 );
2770 TTreeFormula *minForm = new TTreeFormula("minForm",subform,fTree);
2772 Int_t code = fNcodes++;
2773 fCodes[code] = 0;
2774 fLookupType[code] = kMin;
2775 return code;
2776 }
2777 static const char *maxfunc = "Max$(";
2778 if (strncmp(name.Data(),"Max$(",strlen(maxfunc))==0
2779 && name[name.Length()-1]==')') {
2780
2781 TString subform = name.Data()+strlen(maxfunc);
2782 subform.Remove( subform.Length() - 1 );
2783 TTreeFormula *maxForm = new TTreeFormula("maxForm",subform,fTree);
2785 Int_t code = fNcodes++;
2786 fCodes[code] = 0;
2787 fLookupType[code] = kMax;
2788 return code;
2789 }
2790 static const char *sumfunc = "Sum$(";
2791 if (strncmp(name.Data(),"Sum$(",strlen(sumfunc))==0
2792 && name[name.Length()-1]==')') {
2793
2794 TString subform = name.Data()+strlen(sumfunc);
2795 subform.Remove( subform.Length() - 1 );
2796 TTreeFormula *sumForm = new TTreeFormula("sumForm",subform,fTree);
2798 Int_t code = fNcodes++;
2799 fCodes[code] = 0;
2800 fLookupType[code] = kSum;
2801 return code;
2802 }
2803
2804
2805
2806 // Check for $Alt(expression1,expression2)
2807 Int_t res = DefineAlternate(name.Data());
2808 if (res!=0) {
2809 // There was either a syntax error or we found $Alt
2810 if (res<0) return res;
2811 action = res;
2812 return 0;
2813 }
2814
2815 // Find the top level leaf and deal with dimensions
2816
2817 char cname[kMaxLen]; strlcpy(cname,name.Data(),kMaxLen);
2818 char dims[kMaxLen]; dims[0] = '\0';
2819
2820 bool final = false;
2821
2822 UInt_t paran_level = 0;
2823 TObjArray castqueue;
2824
2825 // First, it is easier to remove all dimensions information from 'cname'
2826 Int_t cnamelen = strlen(cname);
2827 for(i=0,k=0; i<cnamelen; ++i, ++k) {
2828 if (cname[i] == '[') {
2829 int bracket = i;
2830 int bracket_level = 1;
2831 int j;
2832 for (j=++i; j<cnamelen && (bracket_level>0 || cname[j]=='['); j++, i++) {
2833 if (cname[j]=='[') bracket_level++;
2834 else if (cname[j]==']') bracket_level--;
2835 }
2836 if (bracket_level != 0) {
2837 //Error("DefinedVariable","Bracket unbalanced");
2838 return -1;
2839 }
2840 strncat(dims,&cname[bracket],j-bracket);
2841 //k += j-bracket;
2842 }
2843 if (i!=k) cname[k] = cname[i];
2844 }
2845 cname[k]='\0';
2846
2847 bool useLeafCollectionObject = false;
2848 TString leftover;
2849 TLeaf *leaf = nullptr;
2850 {
2851 std::vector<std::string> aliasSofar = fAliasesUsed;
2852 res = FindLeafForExpression(cname, leaf, leftover, final, paran_level, castqueue, aliasSofar, useLeafCollectionObject, name);
2853 }
2854 if (res<0) return res;
2855
2856 if (!leaf && res!=2) {
2857 // Check for an alias.
2858 const char *aliasValue = fTree->GetAlias(cname);
2859 if (aliasValue) {
2860 // First check whether we are using this alias recursively (this would
2861 // lead to an infinite recursion.
2862 if (find(fAliasesUsed.begin(),
2863 fAliasesUsed.end(),
2864 cname) != fAliasesUsed.end()) {
2865 Error("DefinedVariable",
2866 "The substitution of the alias \"%s\" by \"%s\" failed\n"\
2867 "\tbecause \"%s\" is recursively used in its own definition!",
2868 cname,aliasValue,cname);
2869 return -3;
2870 }
2871
2872
2873 if (strcspn(aliasValue,"()+*/-%&!=<>|")!=strlen(aliasValue)) {
2874 // If the alias contains an operator, we need to use a nested formula
2875 // (since DefinedVariable must only add one entry to the operation's list).
2876
2877 // Need to check the aliases used so far
2878 std::vector<std::string> aliasSofar = fAliasesUsed;
2879 aliasSofar.push_back( cname );
2880
2881 TString subValue( aliasValue );
2882 if (dims[0]) {
2883 subValue += dims;
2884 }
2885
2886 TTreeFormula *subform = new TTreeFormula(cname,subValue,fTree,aliasSofar); // Need to pass the aliases used so far.
2887
2888 if (subform->GetNdim()==0) {
2889 delete subform;
2890 Error("DefinedVariable",
2891 "The substitution of the alias \"%s\" by \"%s\" failed.",cname,aliasValue);
2892 return -3;
2893 }
2894
2895 fManager->Add(subform);
2897
2898 if (subform->IsString()) {
2899 action = kAliasString;
2900 return 0;
2901 } else {
2902 action = kAlias;
2903 return 0;
2904 }
2905 } else { /* assumes strcspn(aliasValue,"[]")!=strlen(aliasValue) */
2906 TString thisAlias( aliasValue );
2907 thisAlias += dims;
2908 Int_t aliasRes = DefinedVariable(thisAlias,action);
2909 if (aliasRes<0) {
2910 // We failed but DefinedVariable has not printed why yet.
2911 // and because we want those to be printed _before_ the notice
2912 // of the failure of the substitution, we need to print them here.
2913 if (aliasRes==-1) {
2914 Error("Compile", " Bad numerical expression : \"%s\"",thisAlias.Data());
2915 } else if (aliasRes==-2) {
2916 Error("Compile", " Part of the Variable \"%s\" exists but some of it is not accessible or useable",thisAlias.Data());
2917
2918 }
2919 Error("DefinedVariable",
2920 "The substitution of the alias \"%s\" by \"%s\" failed.",cname,aliasValue);
2921 return -3;
2922 }
2923 return aliasRes;
2924 }
2925 }
2926 }
2927
2928
2929 if (leaf || res==2) {
2930
2931 if (leaf && leaf->GetBranch() && leaf->GetBranch()->TestBit(kDoNotProcess)) {
2932 Error("DefinedVariable","the branch \"%s\" has to be enabled to be used",leaf->GetBranch()->GetName());
2933 return -2;
2934 }
2935
2936 Int_t code = fNcodes++;
2937
2938 // If needed will now parse the indexes specified for
2939 // arrays.
2940 if (dims[0]) {
2941 char *current = &( dims[0] );
2942 Int_t dim = 0;
2943 TString varindex;
2944 Int_t index;
2945 Int_t scanindex ;
2946 while (current) {
2947 current++;
2948 if (current[0] == ']') {
2949 fIndexes[code][dim] = -1; // Loop over all elements;
2950 } else {
2951 scanindex = sscanf(current,"%d",&index);
2952 if (scanindex) {
2953 fIndexes[code][dim] = index;
2954 } else {
2955 fIndexes[code][dim] = -2; // Index is calculated via a variable.
2956 varindex = current;
2957 char *end = (char*)(varindex.Data());
2958 for(char bracket_level = 0;*end!=0;end++) {
2959 if (*end=='[') bracket_level++;
2960 if (bracket_level==0 && *end==']') break;
2961 if (*end==']') bracket_level--;
2962 }
2963 *end = '\0';
2964 fVarIndexes[code][dim] = new TTreeFormula("index_var",
2965 varindex,
2966 fTree);
2967 if (fVarIndexes[code][dim]->GetNdim() == 0) {
2968 // Parsing failed for the index, let's stop here ....
2969 return -1;
2970 }
2971 current += strlen(varindex)+1; // move to the end of the index array
2972 }
2973 }
2974 dim ++;
2975 if (dim >= kMAXFORMDIM) {
2976 // NOTE: test that dim this is NOT too big!!
2977 break;
2978 }
2979 current = (char*)strstr( current, "[" );
2980 }
2981 }
2982
2983 // Now that we have cleaned-up the expression, let's compare it to the content
2984 // of the leaf!
2985
2986 res = ParseWithLeaf(leaf,leftover,final,paran_level,castqueue,useLeafCollectionObject,name);
2987 if (res<0) return res;
2988 if (res>0) action = res;
2989 return code;
2990 }
2991
2992//*-*- May be a graphical cut ?
2993 TCutG *gcut = (TCutG*)gROOT->GetListOfSpecials()->FindObject(name.Data());
2994 if (gcut) {
2995 if (gcut->GetObjectX()) {
2997 gcut->SetObjectX(nullptr);
2998 }
2999 if (gcut->GetObjectY()) {
3001 gcut->SetObjectY(nullptr);
3002 }
3003
3004 Int_t code = fNcodes;
3005
3006 if (strlen(gcut->GetVarX()) && strlen(gcut->GetVarY()) ) {
3007
3008 TTreeFormula *fx = new TTreeFormula("f_x",gcut->GetVarX(),fTree);
3009 gcut->SetObjectX(fx);
3010
3011 TTreeFormula *fy = new TTreeFormula("f_y",gcut->GetVarY(),fTree);
3012 gcut->SetObjectY(fy);
3013
3014 fCodes[code] = -2;
3015
3016 } else if (strlen(gcut->GetVarX())) {
3017
3018 // Let's build the equivalent formula:
3019 // min(gcut->X) <= VarX <= max(gcut->Y)
3020 Double_t min = 0;
3021 Double_t max = 0;
3022 Int_t n = gcut->GetN();
3023 Double_t *x = gcut->GetX();
3024 min = max = x[0];
3025 for(Int_t i2 = 1; i2<n; i2++) {
3026 if (x[i2] < min) min = x[i2];
3027 if (x[i2] > max) max = x[i2];
3028 }
3029 TString formula = "(";
3030 formula += min;
3031 formula += "<=";
3032 formula += gcut->GetVarX();
3033 formula += " && ";
3034 formula += gcut->GetVarX();
3035 formula += "<=";
3036 formula += max;
3037 formula += ")";
3038
3039 TTreeFormula *fx = new TTreeFormula("f_x",formula.Data(),fTree);
3040 gcut->SetObjectX(fx);
3041
3042 fCodes[code] = -1;
3043
3044 } else {
3045
3046 Error("DefinedVariable","Found a TCutG without leaf information (%s)",
3047 gcut->GetName());
3048 return -1;
3049
3050 }
3051
3052 fExternalCuts.AddAtAndExpand(gcut,code);
3053 fNcodes++;
3054 fLookupType[code] = -1;
3055 return code;
3056 }
3057
3058 //may be an entrylist
3059 TEntryList *elist = dynamic_cast<TEntryList*> (gDirectory->Get(name.Data()));
3060 if (elist) {
3061 Int_t code = fNcodes;
3062 fCodes[code] = 0;
3063 fExternalCuts.AddAtAndExpand(elist, code);
3064 fNcodes++;
3065 fLookupType[code] = kEntryList;
3066 return code;
3067
3068 }
3069
3070 return -1;
3071}
3072
3073////////////////////////////////////////////////////////////////////////////////
3074/// Return the leaf (if any) which contains an object containing
3075/// a data member which has the name provided in the arguments.
3076
3077TLeaf* TTreeFormula::GetLeafWithDatamember(const char* topchoice, const char* nextchoice, Long64_t readentry) const
3078{
3079 TClass * cl = nullptr;
3080 TIter nextleaf (fTree->GetIteratorOnAllLeaves());
3081 TFormLeafInfo* clonesinfo = nullptr;
3082 TLeaf *leafcur;
3083 while ((leafcur = (TLeaf*)nextleaf())) {
3084 // The following code is used somewhere else, we need to factor it out.
3085
3086 // Here since we are interested in data member, we want to consider only
3087 // 'terminal' branch and leaf.
3088 cl = nullptr;
3089 if (leafcur->InheritsFrom(TLeafObject::Class()) &&
3090 leafcur->GetBranch()->GetListOfBranches()->Last()==nullptr) {
3091 TLeafObject *lobj = (TLeafObject*)leafcur;
3092 cl = lobj->GetClass();
3093 } else if (leafcur->InheritsFrom(TLeafElement::Class()) && leafcur->IsOnTerminalBranch()) {
3094 TLeafElement * lElem = (TLeafElement*) leafcur;
3095 if (lElem->IsOnTerminalBranch()) {
3096 TBranchElement *branchEl = (TBranchElement *)leafcur->GetBranch();
3097 Int_t type = branchEl->GetStreamerType();
3098 if (type==-1) {
3099 cl = branchEl->GetInfo() ? branchEl->GetInfo()->GetClass() : nullptr;
3100 } else if (type>60 || type==0) {
3101 // Case of an object data member. Here we allow for the
3102 // variable name to be omitted. Eg, for Event.root with split
3103 // level 1 or above Draw("GetXaxis") is the same as Draw("fH.GetXaxis()")
3104 TStreamerElement* element = branchEl->GetInfo()->GetElement(branchEl->GetID());
3105 if (element) cl = element->GetClassPointer();
3106 else cl = nullptr;
3107 }
3108 }
3109
3110 }
3111 if (clonesinfo) { delete clonesinfo; clonesinfo = nullptr; }
3112 if (cl == TClonesArray::Class()) {
3113 // We have a unsplit TClonesArray leaves
3114 // In this case we assume that cl is the class in which the TClonesArray
3115 // belongs.
3116 R__LoadBranch(leafcur->GetBranch(),readentry,fQuickLoad);
3117 TClonesArray * clones;
3118
3119 TBranch *branch = leafcur->GetBranch();
3120 if ( branch->IsA()==TBranchElement::Class()
3121 && ((TBranchElement*)branch)->GetType()==31) {
3122
3123 // We have an unsplit TClonesArray as part of a split TClonesArray!
3124
3125 // Let's not dig any further. If the user really wants a data member
3126 // inside the nested TClonesArray, it has to specify it explicitly.
3127
3128 continue;
3129
3130 } else {
3131 bool toplevel = (branch == branch->GetMother());
3132 clonesinfo = new TFormLeafInfoClones(cl, 0, toplevel);
3133 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leafcur,0);
3134 }
3135 if (clones) cl = clones->GetClass();
3136 } else if (cl && cl->GetCollectionProxy()) {
3137
3138 // We have a unsplit Collection leaves
3139 // In this case we assume that cl is the class in which the TClonesArray
3140 // belongs.
3141
3142 TBranch *branch = leafcur->GetBranch();
3143 if ( branch->IsA()==TBranchElement::Class()
3144 && ((TBranchElement*)branch)->GetType()==41) {
3145
3146 // We have an unsplit Collection as part of a split Collection!
3147
3148 // Let's not dig any further. If the user really wants a data member
3149 // inside the nested Collection, it has to specify it explicitly.
3150
3151 continue;
3152
3153 } else {
3154 clonesinfo = new TFormLeafInfoCollection(cl, 0);
3155 }
3156 cl = cl->GetCollectionProxy()->GetValueClass();
3157 }
3158 if (cl) {
3159 // Now that we have the class, let's check if the topchoice is of its datamember
3160 // or if the nextchoice is a datamember of one of its datamember.
3161 Int_t offset;
3163 TStreamerElement* element = info?info->GetStreamerElement(topchoice,offset):nullptr;
3164 if (!element) {
3165 TIter nextel( cl->GetStreamerInfo()->GetElements() );
3166 TStreamerElement * curelem;
3167 while ((curelem = (TStreamerElement*)nextel())) {
3168
3169 if (curelem->GetClassPointer() == TClonesArray::Class()) {
3170 // In case of a TClonesArray we need to load the data and read the
3171 // clonesArray object before being able to look into the class inside.
3172 // We need to do that because we are never interested in the TClonesArray
3173 // itself but only in the object inside.
3174 TBranch *branch = leafcur->GetBranch();
3175 TFormLeafInfo *leafinfo = nullptr;
3176 if (clonesinfo) {
3177 leafinfo = clonesinfo;
3178 } else if (branch->IsA()==TBranchElement::Class()
3179 && ((TBranchElement*)branch)->GetType()==31) {
3180 // Case of a sub branch of a TClonesArray
3181 TBranchElement *branchEl = (TBranchElement*)branch;
3182 TStreamerInfo *bel_info = branchEl->GetInfo();
3183 TClass * mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
3184 TStreamerElement *bel_element =
3185 bel_info->GetElement(branchEl->GetID());
3186 leafinfo = new TFormLeafInfoClones(mother_cl, 0, bel_element, true);
3187 }
3188
3189 Int_t clones_offset = 0;
3190 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),clones_offset);
3191 TFormLeafInfo* sub_clonesinfo = new TFormLeafInfo(cl, clones_offset, curelem);
3192 if (leafinfo)
3193 if (leafinfo->fNext) leafinfo->fNext->fNext = sub_clonesinfo;
3194 else leafinfo->fNext = sub_clonesinfo;
3195 else leafinfo = sub_clonesinfo;
3196
3197 R__LoadBranch(branch,readentry,fQuickLoad);
3198
3199 TClonesArray * clones = (TClonesArray*)leafinfo->GetValuePointer(leafcur,0);
3200
3201 delete leafinfo; clonesinfo = nullptr;
3202 // If TClonesArray object does not exist we have no information, so let go
3203 // on. This is a weakish test since the TClonesArray object might exist in
3204 // the next entry ... In other word, we ONLY rely on the information available
3205 // in entry #0.
3206 if (!clones) continue;
3207 TClass *sub_cl = clones->GetClass();
3208
3209 // Now that we finally have the inside class, let's query it.
3210 element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(nextchoice,offset);
3211 if (element) break;
3212 } // if clones array
3213 else if (curelem->GetClassPointer() && curelem->GetClassPointer()->GetCollectionProxy()) {
3214
3215 TClass *sub_cl = curelem->GetClassPointer()->GetCollectionProxy()->GetValueClass();
3216
3217 while(sub_cl && sub_cl->GetCollectionProxy())
3218 sub_cl = sub_cl->GetCollectionProxy()->GetValueClass();
3219
3220 // Now that we finally have the inside class, let's query it.
3221 if (sub_cl) element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(nextchoice,offset);
3222 if (element) break;
3223
3224 }
3225 } // loop on elements
3226 }
3227 if (element) break;
3228 else cl = nullptr;
3229 }
3230 }
3231 delete clonesinfo;
3232 if (cl) {
3233 return leafcur;
3234 } else {
3235 return nullptr;
3236 }
3237}
3238
3239////////////////////////////////////////////////////////////////////////////////
3240/// Return the leaf (if any) of the tree with contains an object of a class
3241/// having a method which has the name provided in the argument.
3242
3243bool TTreeFormula::BranchHasMethod(TLeaf* leafcur, TBranch* branch, const char* method, const char* params, Long64_t readentry) const
3244{
3245 TClass *cl = nullptr;
3246 TLeafObject* lobj = nullptr;
3247
3248 // Since the user does not want this branch to be loaded anyway, we just
3249 // skip it. This prevents us from warning the user that the method might
3250 // be on a disabled branch. However, and more usefully, this allows the
3251 // user to avoid error messages from branches that cannot be currently
3252 // read without warnings/errors.
3253
3254 if (branch->TestBit(kDoNotProcess)) {
3255 return false;
3256 }
3257
3258 // FIXME: The following code is used somewhere else, we need to factor it out.
3259 if (branch->InheritsFrom(TBranchObject::Class())) {
3260 lobj = (TLeafObject*) branch->GetListOfLeaves()->At(0);
3261 cl = lobj->GetClass();
3262 } else if (branch->InheritsFrom(TBranchElement::Class())) {
3263 TBranchElement* branchEl = (TBranchElement*) branch;
3264 Int_t type = branchEl->GetStreamerType();
3265 if (type == -1) {
3266 cl = branchEl->GetInfo() ? branchEl->GetInfo()->GetClass() : nullptr;
3267 } else if (type > 60) {
3268 // Case of an object data member. Here we allow for the
3269 // variable name to be omitted. Eg, for Event.root with split
3270 // level 1 or above Draw("GetXaxis") is the same as Draw("fH.GetXaxis()")
3271 TStreamerElement* element = branchEl->GetInfo()->GetElement(branchEl->GetID());
3272 if (element) {
3273 cl = element->GetClassPointer();
3274 } else {
3275 cl = nullptr;
3276 }
3277 if ((cl == TClonesArray::Class()) && (branchEl->GetType() == 31)) {
3278 // we have a TClonesArray inside a split TClonesArray,
3279 // Let's not dig any further. If the user really wants a data member
3280 // inside the nested TClonesArray, it has to specify it explicitly.
3281 cl = nullptr;
3282 }
3283 // NOTE do we need code for Collection here?
3284 }
3285 }
3286
3287 if (cl == TClonesArray::Class()) {
3288 // We might be try to call a method of the top class inside a
3289 // TClonesArray.
3290 // Since the leaf was not terminal, we might have a split or
3291 // unsplit and/or top leaf/branch.
3292 TClonesArray* clones = nullptr;
3293 R__LoadBranch(branch, readentry, fQuickLoad);
3294 if (branch->InheritsFrom(TBranchObject::Class())) {
3295 clones = (TClonesArray*) lobj->GetObject();
3296 } else if (branch->InheritsFrom(TBranchElement::Class())) {
3297 // We do not know exactly where the leaf of the TClonesArray is
3298 // in the hierarchy but we still need to get the correct class
3299 // holder.
3300 TBranchElement* bc = (TBranchElement*) branch;
3301 if (bc == bc->GetMother()) {
3302 // Top level branch
3303 //clones = *((TClonesArray**) bc->GetAddress());
3304 clones = (TClonesArray*) bc->GetObject();
3305 } else if (!leafcur || !leafcur->IsOnTerminalBranch()) {
3306 TStreamerElement* element = bc->GetInfo()->GetElement(bc->GetID());
3307 if (element->IsaPointer()) {
3308 clones = *((TClonesArray**) bc->GetAddress());
3309 //clones = *((TClonesArray**) bc->GetObject());
3310 } else {
3311 //clones = (TClonesArray*) bc->GetAddress();
3312 clones = (TClonesArray*) bc->GetObject();
3313 }
3314 }
3315 if (!clones) {
3316 R__LoadBranch(bc, readentry, fQuickLoad);
3317 TClass* mother_cl;
3318 mother_cl = bc->GetInfo()->GetClass();
3319 TFormLeafInfo* clonesinfo = new TFormLeafInfoClones(mother_cl, 0);
3320 // if (!leafcur) { leafcur = (TLeaf*) branch->GetListOfLeaves()->At(0); }
3321 clones = (TClonesArray*) clonesinfo->GetLocalValuePointer(leafcur, 0);
3322 // cl = clones->GetClass();
3323 delete clonesinfo;
3324 }
3325 } else {
3326 Error("BranchHasMethod","A TClonesArray was stored in a branch type no yet support (i.e. neither TBranchObject nor TBranchElement): %s",branch->IsA()->GetName());
3327 return false;
3328 }
3329 cl = clones ? clones->GetClass() : nullptr;
3330 } else if (cl && cl->GetCollectionProxy()) {
3331 cl = cl->GetCollectionProxy()->GetValueClass();
3332 }
3333
3334 if (cl) {
3335 if (cl->GetClassInfo()) {
3336 if (cl->GetMethodAllAny(method)) {
3337 // Let's try to see if the function we found belongs to the current
3338 // class. Note that this implementation currently can not work if
3339 // one the argument is another leaf or data member of the object.
3340 // (Anyway we do NOT support this case).
3341 TMethodCall methodcall(cl, method, params);
3342 if (methodcall.GetMethod()) {
3343 // We have a method that works.
3344 // We will use it.
3345 return true;
3346 }
3347 }
3348 }
3349 }
3350
3351 return false;
3352}
3353
3354////////////////////////////////////////////////////////////////////////////////
3355/// Now let calculate what physical instance we really need.
3356/// Some redundant code is used to speed up the cases where
3357/// they are no dimensions.
3358///
3359/// We know that instance is less that fCumulUsedSize[0] so
3360/// we can skip the modulo when virt_dim is 0.
3361
3363 Int_t real_instance = 0;
3364 Int_t virt_dim;
3365
3366 bool check = false;
3367 if (codeindex<0) {
3368 codeindex = 0;
3369 check = true;
3370 }
3371
3372 TFormLeafInfo * info = nullptr;
3373 Int_t max_dim = fNdimensions[codeindex];
3374 if ( max_dim ) {
3375 virt_dim = 0;
3376 max_dim--;
3377
3378 if (!fManager->fMultiVarDim) {
3379 if (fIndexes[codeindex][0]>=0) {
3380 real_instance = fIndexes[codeindex][0] * fCumulSizes[codeindex][1];
3381 } else {
3382 Int_t local_index;
3383 local_index = ( instance / fManager->fCumulUsedSizes[virt_dim+1]);
3384 if (fIndexes[codeindex][0]==-2) {
3385 // NOTE: Should we check that this is a valid index?
3386 if (check) {
3387 Int_t index_real_instance = fVarIndexes[codeindex][0]->GetRealInstance(local_index,-1);
3388 if (index_real_instance >= fVarIndexes[codeindex][0]->fNdata[0]) {
3389 // out of bounds
3390 return fNdata[0]+1;
3391 }
3392 }
3393 if (fDidBooleanOptimization && local_index!=0) {
3394 // Force the loading of the index.
3395 fVarIndexes[codeindex][0]->LoadBranches();
3396 }
3397 local_index = (Int_t)fVarIndexes[codeindex][0]->EvalInstance(local_index);
3398 if (local_index<0) {
3399 Error("EvalInstance","Index %s is out of bound (%d) in formula %s",
3400 fVarIndexes[codeindex][0]->GetTitle(),
3401 local_index,
3402 GetTitle());
3403 return fNdata[0]+1;
3404 }
3405 }
3406 real_instance = local_index * fCumulSizes[codeindex][1];
3407 virt_dim ++;
3408 }
3409 } else {
3410 // NOTE: We assume that ONLY the first dimension of a leaf can have a variable
3411 // size AND contain the index for the size of yet another sub-dimension.
3412 // I.e. a variable size array inside a variable size array can only have its
3413 // size vary with the VERY FIRST physical dimension of the leaf.
3414 // Thus once the index of the first dimension is found, all other dimensions
3415 // are fixed!
3416
3417 // NOTE: We could unroll some of this loops to avoid a few tests.
3418 if (fHasMultipleVarDim[codeindex]) {
3419 info = (TFormLeafInfo *)(fDataMembers.At(codeindex));
3420 // if (info && info->GetVarDim()==-1) info = 0;
3421 }
3422 Int_t local_index;
3423
3424 switch (fIndexes[codeindex][0]) {
3425 case -2:
3427 // Force the loading of the index.
3428 fVarIndexes[codeindex][0]->LoadBranches();
3429 }
3430 local_index = (Int_t)fVarIndexes[codeindex][0]->EvalInstance(instance);
3431 if (local_index<0) {
3432 Error("EvalInstance","Index %s is out of bound (%d) in formula %s",
3433 fVarIndexes[codeindex][0]->GetTitle(),
3434 local_index,
3435 GetTitle());
3436 local_index = 0;
3437 }
3438 break;
3439 case -1: {
3443 }
3447
3449 if (maxloop == 0) {
3450 local_index--;
3451 instance = fNdata[0]+1; // out of bounds.
3452 if (check) return fNdata[0]+1;
3453 } else {
3454 do {
3455 virt_accum += fManager->fCumulUsedVarDims->GetArray()[local_index];
3456 local_index++;
3457 } while( instance >= virt_accum && local_index<maxloop);
3458 local_index--;
3459 // update the cache
3462
3463 if (local_index==(maxloop-1) && (instance >= virt_accum)) {
3464 instance = fNdata[0]+1; // out of bounds.
3465 if (check) return fNdata[0]+1;
3466 } else {
3467 if (fManager->fCumulUsedVarDims->At(local_index)) {
3468 instance -= (virt_accum - fManager->fCumulUsedVarDims->At(local_index));
3469 } else {
3470 instance = fNdata[0]+1; // out of bounds.
3471 if (check) return fNdata[0]+1;
3472 }
3473 }
3474 }
3475 virt_dim ++;
3476 }
3477 break;
3478 default:
3479 local_index = fIndexes[codeindex][0];
3480 }
3481
3482 // Inform the (appropriate) MultiVarLeafInfo that the clones array index is
3483 // local_index.
3484
3487 } else {
3489 }
3490 for(Int_t d = kMAXFORMDIM-1; d>0; d--) {
3491 if (fManager->fVarDims[d]) {
3493 } else {
3495 }
3496 }
3497 if (info) {
3498 // When we have multiple variable dimensions, the LeafInfo only expect
3499 // the instance after the primary index has been set.
3500 info->SetPrimaryIndex(local_index);
3501 real_instance = 0;
3502
3503 // Let's update fCumulSizes for the rest of the code.
3504 Int_t vdim = info->GetVarDim();
3505 Int_t isize = info->GetSize(local_index);
3506 if (fIndexes[codeindex][vdim]>=0) {
3507 info->SetSecondaryIndex(fIndexes[codeindex][vdim]);
3508 }
3509 if (isize!=1 && fIndexes[codeindex][vdim]>isize) {
3510 // We are out of bounds!
3511 return fNdata[0]+1;
3512 }
3513 fCumulSizes[codeindex][vdim] = isize*fCumulSizes[codeindex][vdim+1];
3514 for(Int_t k=vdim -1; k>0; --k) {
3515 fCumulSizes[codeindex][k] = fCumulSizes[codeindex][k+1]*fFixedSizes[codeindex][k];
3516 }
3517 } else {
3518 real_instance = local_index * fCumulSizes[codeindex][1];
3519 }
3520 }
3521 if (max_dim>0) {
3522 for (Int_t dim = 1; dim < max_dim; dim++) {
3523 if (fIndexes[codeindex][dim]>=0) {
3524 real_instance += fIndexes[codeindex][dim] * fCumulSizes[codeindex][dim+1];
3525 } else {
3526 Int_t local_index;
3527 if (virt_dim && fManager->fCumulUsedSizes[virt_dim]>1) {
3528 local_index = ( ( instance % fManager->fCumulUsedSizes[virt_dim] )
3529 / fManager->fCumulUsedSizes[virt_dim+1]);
3530 } else {
3531 local_index = ( instance / fManager->fCumulUsedSizes[virt_dim+1]);
3532 }
3533 if (fIndexes[codeindex][dim]==-2) {
3534 // NOTE: Should we check that this is a valid index?
3535 if (fDidBooleanOptimization && local_index!=0) {
3536 // Force the loading of the index.
3537 fVarIndexes[codeindex][dim]->LoadBranches();
3538 }
3539 local_index = (Int_t)fVarIndexes[codeindex][dim]->EvalInstance(local_index);
3540 if (local_index<0 ||
3541 local_index>=(fCumulSizes[codeindex][dim]/fCumulSizes[codeindex][dim+1])) {
3542 Error("EvalInstance","Index %s is out of bound (%d/%d) in formula %s",
3543 fVarIndexes[codeindex][dim]->GetTitle(),
3544 local_index,
3545 (fCumulSizes[codeindex][dim]/fCumulSizes[codeindex][dim+1]),
3546 GetTitle());
3547 local_index = (fCumulSizes[codeindex][dim]/fCumulSizes[codeindex][dim+1])-1;
3548 }
3549 }
3550 real_instance += local_index * fCumulSizes[codeindex][dim+1];
3551 virt_dim ++;
3552 }
3553 }
3554 if (fIndexes[codeindex][max_dim]>=0) {
3555 if (!info) real_instance += fIndexes[codeindex][max_dim];
3556 } else {
3557 Int_t local_index;
3558 if (virt_dim && fManager->fCumulUsedSizes[virt_dim]>1) {
3559 local_index = instance % fManager->fCumulUsedSizes[virt_dim];
3560 } else {
3561 local_index = instance;
3562 }
3563 if (info && local_index>=fCumulSizes[codeindex][max_dim]) {
3564 // We are out of bounds! [Multiple var dims, See same message a few line above]
3565 return fNdata[0]+1;
3566 }
3567 if (fIndexes[codeindex][max_dim]==-2) {
3568 if (fDidBooleanOptimization && local_index!=0) {
3569 // Force the loading of the index.
3570 fVarIndexes[codeindex][max_dim]->LoadBranches();
3571 }
3572 local_index = (Int_t)fVarIndexes[codeindex][max_dim]->EvalInstance(local_index);
3573 if (local_index<0 ||
3574 local_index>=fCumulSizes[codeindex][max_dim]) {
3575 Error("EvalInstance","Index %s is of out bound (%d/%d) in formula %s",
3576 fVarIndexes[codeindex][max_dim]->GetTitle(),
3577 local_index,
3578 fCumulSizes[codeindex][max_dim],
3579 GetTitle());
3580 local_index = fCumulSizes[codeindex][max_dim]-1;
3581 }
3582 }
3583 real_instance += local_index;
3584 }
3585 } // if (max_dim-1>0)
3586 } // if (max_dim)
3587
3588 return real_instance;
3589}
3590
3591////////////////////////////////////////////////////////////////////////////////
3592/// Evaluate the class of this treeformula.
3593///
3594/// If the 'value' of this formula is a simple pointer to an object,
3595/// this function returns the TClass corresponding to its type.
3596
3598{
3599 if (fNoper != 1 || fNcodes <=0 ) return nullptr;
3600
3601 return EvalClass(0);
3602}
3603
3604////////////////////////////////////////////////////////////////////////////////
3605/// Evaluate the class of the operation oper.
3606///
3607/// If the 'value' in the requested operation is a simple pointer to an object,
3608/// this function returns the TClass corresponding to its type.
3609
3611{
3612 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(oper);
3613 switch(fLookupType[oper]) {
3614 case kDirect: {
3615 if (leaf->IsA()==TLeafObject::Class()) {
3616 return ((TLeafObject*)leaf)->GetClass();
3617 } else if ( leaf->IsA()==TLeafElement::Class()) {
3618 TBranchElement * branch = (TBranchElement*)((TLeafElement*)leaf)->GetBranch();
3619 TStreamerInfo * info = branch->GetInfo();
3620 Int_t id = branch->GetID();
3621 if (id>=0) {
3622 if (info==nullptr || !info->IsCompiled()) {
3623 // we probably do not have a way to know the class of the object.
3624 return nullptr;
3625 }
3626 TStreamerElement* elem = (TStreamerElement*)info->GetElement(id);
3627 if (elem==nullptr) {
3628 // we probably do not have a way to know the class of the object.
3629 return nullptr;
3630 } else {
3631 return elem->GetClass();
3632 }
3633 } else return TClass::GetClass( branch->GetClassName() );
3634 } else {
3635 return nullptr;
3636 }
3637 }
3638 case kMethod: return nullptr; // kMethod is deprecated so let's no waste time implementing this.
3639 case kTreeMember:
3640 case kDataMember: {
3641 TObject *obj = fDataMembers.UncheckedAt(oper);
3642 if (!obj) return nullptr;
3643 return ((TFormLeafInfo*)obj)->GetClass();
3644 }
3645
3646 default: return nullptr;
3647 }
3648
3649
3650}
3651
3652////////////////////////////////////////////////////////////////////////////////
3653/// Evaluate this treeformula.
3654///
3655/// Return the address of the object pointed to by the formula.
3656/// Return 0 if the formula is not a single object
3657/// The object type can be retrieved using by call EvalClass();
3658
3660{
3661 if (fNoper != 1 || fNcodes <=0 ) return nullptr;
3662
3663
3664 switch (fLookupType[0]) {
3665 case kIndexOfEntry:
3666 case kIndexOfLocalEntry:
3667 case kEntries:
3668 case kLocalEntries:
3669 case kLength:
3670 case kLengthFunc:
3671 case kIteration:
3672 case kEntryList:
3673 return nullptr;
3674 }
3675
3676 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(0);
3677
3678 Int_t real_instance = GetRealInstance(instance,0);
3679
3680 if (instance==0 || fNeedLoading) {
3681 fNeedLoading = false;
3682 R__LoadBranch(leaf->GetBranch(),
3683 leaf->GetBranch()->GetTree()->GetReadEntry(),
3684 fQuickLoad);
3685 }
3686 else if (real_instance>=fNdata[0]) return nullptr;
3687 if (fAxis) {
3688 return nullptr;
3689 }
3690 switch(fLookupType[0]) {
3691 case kDirect: {
3692 if (real_instance) {
3693 Warning("EvalObject","Not yet implement for kDirect and arrays (for %s).\nPlease contact the developers",GetName());
3694 }
3695 return leaf->GetValuePointer();
3696 }
3697 case kMethod: return GetValuePointerFromMethod(0,leaf);
3698 case kTreeMember:
3699 case kDataMember: return ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->GetValuePointer(leaf,real_instance);
3700 default: return nullptr;
3701 }
3702
3703
3704}
3705
3706
3707////////////////////////////////////////////////////////////////////////////////
3708/// Eval the instance as a string.
3709
3711{
3712 const Int_t kMAXSTRINGFOUND = 10;
3713 const char *stringStack[kMAXSTRINGFOUND];
3714
3715 if (fNoper==1 && fNcodes>0 && IsString()) {
3716 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(0);
3717
3718 Int_t real_instance = GetRealInstance(instance,0);
3719
3720 if (instance==0 || fNeedLoading) {
3721 fNeedLoading = false;
3722 TBranch *branch = leaf->GetBranch();
3723 R__LoadBranch(branch,branch->GetTree()->GetReadEntry(),fQuickLoad);
3724 } else if (real_instance>=fNdata[0]) {
3725 return nullptr;
3726 }
3727
3728 if (fLookupType[0]==kDirect) {
3729 return (char*)leaf->GetValuePointer();
3730 } else {
3731 return (char*)GetLeafInfo(0)->GetValuePointer(leaf,real_instance);
3732 }
3733 }
3734
3735 EvalInstance(instance,stringStack);
3736
3737 return stringStack[0];
3738}
3739
3740#define TT_EVAL_INIT \
3741 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(0); \
3742 \
3743 const Int_t real_instance = GetRealInstance(instance,0); \
3744 \
3745 if (instance==0) fNeedLoading = true; \
3746 if (real_instance>=fNdata[0]) return 0; \
3747 \
3748 /* Since the only operation in this formula is reading this branch, \
3749 we are guaranteed that this function is first called with instance==0 and \
3750 hence we are guaranteed that the branch is always properly read */ \
3751 \
3752 if (fNeedLoading) { \
3753 fNeedLoading = false; \
3754 TBranch *br = leaf->GetBranch(); \
3755 if (br && br->GetTree()) { \
3756 Long64_t tentry = br->GetTree()->GetReadEntry(); \
3757 R__LoadBranch(br,tentry,fQuickLoad); \
3758 } else { \
3759 Error("TTreeFormula::TT_EVAL_INIT", \
3760 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3761 } \
3762 } \
3763 \
3764 if (fAxis) { \
3765 char * label; \
3766 /* This portion is a duplicate (for speed reason) of the code \
3767 located in the main for loop at "a tree string" (and in EvalStringInstance) */ \
3768 if (fLookupType[0]==kDirect) { \
3769 label = (char*)leaf->GetValuePointer(); \
3770 } else { \
3771 label = (char*)GetLeafInfo(0)->GetValuePointer(leaf,instance); \
3772 } \
3773 Int_t bin = fAxis->FindBin(label); \
3774 return bin-0.5; \
3775 }
3776
3777#define TREE_EVAL_INIT \
3778 const Int_t real_instance = GetRealInstance(instance,0); \
3779 \
3780 if (real_instance>=fNdata[0]) return 0; \
3781 \
3782 if (fAxis) { \
3783 char * label; \
3784 /* This portion is a duplicate (for speed reason) of the code \
3785 located in the main for loop at "a tree string" (and in EvalStringInstance) */ \
3786 label = (char*)GetLeafInfo(0)->GetValuePointer((TLeaf*)0x0,instance); \
3787 Int_t bin = fAxis->FindBin(label); \
3788 return bin-0.5; \
3789 }
3790
3791#define TT_EVAL_INIT_LOOP \
3792 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(code); \
3793 \
3794 /* Now let calculate what physical instance we really need. */ \
3795 const Int_t real_instance = GetRealInstance(instance,code); \
3796 \
3797 if (willLoad) { \
3798 TBranch *branch = (TBranch*)fBranches.UncheckedAt(code); \
3799 if (branch) { \
3800 if (branch->GetTree()) { \
3801 Long64_t treeEntry = branch->GetTree()->GetReadEntry(); \
3802 R__LoadBranch(branch,treeEntry,fQuickLoad); \
3803 } else { \
3804 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3805 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3806 } \
3807 } else if (fDidBooleanOptimization) { \
3808 branch = leaf->GetBranch(); \
3809 if (branch->GetTree()) { \
3810 Long64_t treeEntry = branch->GetTree()->GetReadEntry(); \
3811 if (branch->GetReadEntry() != treeEntry) branch->GetEntry( treeEntry ); \
3812 } else { \
3813 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3814 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3815 } \
3816 } \
3817 } else { \
3818 /* In the cases where we are behind (i.e. right of) a potential boolean optimization \
3819 this tree variable reading may have not been executed with instance==0 which would \
3820 result in the branch being potentially not read in. */ \
3821 if (fDidBooleanOptimization) { \
3822 TBranch *br = leaf->GetBranch(); \
3823 if (br->GetTree()) { \
3824 Long64_t treeEntry = br->GetTree()->GetReadEntry(); \
3825 if (br->GetReadEntry() != treeEntry) br->GetEntry( treeEntry ); \
3826 } else { \
3827 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3828 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3829 } \
3830 } \
3831 } \
3832 if (real_instance>=fNdata[code]) return 0;
3833
3834#define TREE_EVAL_INIT_LOOP \
3835 /* Now let calculate what physical instance we really need. */ \
3836 const Int_t real_instance = GetRealInstance(instance,code); \
3837 \
3838 if (real_instance>=fNdata[code]) return 0;
3839
3840
3841template<typename T> T Summing(TTreeFormula *sum) {
3842 Int_t len = sum->GetNdata();
3843 T res = 0;
3844 for (int i=0; i<len; ++i) res += sum->EvalInstance<T>(i);
3845 return res;
3846}
3847
3848template<typename T> T FindMin(TTreeFormula *arr) {
3849 Int_t len = arr->GetNdata();
3850 T res = 0;
3851 if (len) {
3852 res = arr->EvalInstance<T>(0);
3853 for (int i=1; i<len; ++i) {
3854 T val = arr->EvalInstance<T>(i);
3855 if (val < res) {
3856 res = val;
3858 }
3859 }
3860 return res;
3861}
3862
3863template<typename T> T FindMax(TTreeFormula *arr) {
3864 Int_t len = arr->GetNdata();
3865 T res = 0;
3866 if (len) {
3867 res = arr->EvalInstance<T>(0);
3868 for (int i=1; i<len; ++i) {
3869 T val = arr->EvalInstance(i);
3870 if (val > res) {
3871 res = val;
3873 }
3874 }
3875 return res;
3876}
3877
3878template<typename T> T FindMin(TTreeFormula *arr, TTreeFormula *condition) {
3879 Int_t len = arr->GetNdata();
3880 T res = 0;
3881 if (len) {
3882 int i = 0;
3883 T condval;
3884 do {
3885 condval = condition->EvalInstance<T>(i);
3886 ++i;
3887 } while (!condval && i<len);
3888 if (!condval && i==len) {
3889 return 0;
3890 }
3891 if (i!=1) {
3892 // Insure the loading of the branch.
3893 arr->EvalInstance<T>(0);
3894 }
3895 // Now we know that i>0 && i<len and cond==true
3896 res = arr->EvalInstance<T>(i-1);
3897 for (; i<len; ++i) {
3898 condval = condition->EvalInstance<T>(i);
3899 if (condval) {
3900 T val = arr->EvalInstance<T>(i);
3901 if (val < res) {
3902 res = val;
3903 }
3905 }
3906 }
3907 return res;
3908}
3909
3910template<typename T> T FindMax(TTreeFormula *arr, TTreeFormula *condition) {
3911 Int_t len = arr->GetNdata();
3912 T res = 0;
3913 if (len) {
3914 int i = 0;
3915 T condval;
3916 do {
3917 condval = condition->EvalInstance<T>(i);
3918 ++i;
3919 } while (!condval && i<len);
3920 if (!condval && i==len) {
3921 return 0;
3922 }
3923 if (i!=1) {
3924 // Insure the loading of the branch.
3925 arr->EvalInstance<T>(0);
3926 }
3927 // Now we know that i>0 && i<len and cond==true
3928 res = arr->EvalInstance<T>(i-1);
3929 for (; i<len; ++i) {
3930 condval = condition->EvalInstance<T>(i);
3931 if (condval) {
3932 T val = arr->EvalInstance<T>(i);
3933 if (val > res) {
3934 res = val;
3935 }
3936 }
3937 }
3938 }
3939 return res;
3940}
3941
3942namespace {
3943
3944template <typename T> T fmod_local(T x, T y) { return fmod(x,y); }
3945template <> Long64_t fmod_local(Long64_t x, Long64_t y) { return fmod((LongDouble_t)x,(LongDouble_t)y); }
3947template<typename T> inline void SetMethodParam(TMethodCall *method, T p) { method->SetParam(p); }
3948template<> void SetMethodParam(TMethodCall *method, LongDouble_t p) { method->SetParam((Double_t)p); }
3949
3950}
3951
3952template<typename T> inline T TTreeFormula::GetConstant(Int_t k) { return fConst[k]; }
3953template<> inline LongDouble_t TTreeFormula::GetConstant(Int_t k) {
3954 if( !fConstLD ) {
3955 // create LD version of the constants list by rescanning all literals used in the expression
3957 for (Int_t op=0; op<fNoper ; ++op) {
3958 const Int_t oper = GetOper()[op];
3959 if( (oper >> kTFOperShift) == kConstant ) {
3960 int i = (oper & kTFOperMask);
3961 if( !strncmp(fExpr[op], "0x", 2) || !strncmp(fExpr[op], "0X", 2) ) {
3962 ULong64_t val;
3963 sscanf( fExpr[op], "%llx", &val );
3964 fConstLD[i] = (LongDouble_t)val;
3965 } else {
3966 sscanf( fExpr[op], "%Lg", &fConstLD[i] );
3967 }
3968 }
3969 }
3970 }
3971 return fConstLD[k];
3972}
3973template<> inline Long64_t TTreeFormula::GetConstant(Int_t k) { return (Long64_t)GetConstant<LongDouble_t>(k); }
3974
3975////////////////////////////////////////////////////////////////////////////
3976/// \brief Evaluate this treeformula
3977/// \tparam T The type used to interpret the numbers then used for the operations
3978/// \param instance iteration instance
3979/// \param stringStackArg formula as string
3980/// \return the result of the evaluation
3981
3982template<typename T>
3983T TTreeFormula::EvalInstance(Int_t instance, const char *stringStackArg[])
3984{
3985// Note that the redundancy and structure in this code is tailored to improve
3986// efficiencies.
3987 if (TestBit(kMissingLeaf)) return 0;
3988 if (fNoper == 1 && fNcodes > 0) {
3989
3990 switch (fLookupType[0]) {
3991 case kDirect: {
3993 return leaf->GetTypedValue<T>(real_instance);
3994 }
3995 case kMethod: {
3997 ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->SetBranch(leaf->GetBranch());
3998 return GetValueFromMethod(0,leaf);
3999 }
4000 case kDataMember: {
4002 ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->SetBranch(leaf->GetBranch());
4003 return ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->GetTypedValue<T>(leaf,real_instance);
4004 }
4005 case kTreeMember: {
4007 return ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->GetTypedValue<T>((TLeaf*)nullptr,real_instance);
4008 }
4009 case kIndexOfEntry: return (T)fTree->GetReadEntry();
4010 case kIndexOfLocalEntry: return (T)fTree->GetTree()->GetReadEntry();
4011 case kEntries: return (T)fTree->GetEntries();
4012 case kLocalEntries: return (T)fTree->GetTree()->GetEntries();
4013 case kLength: return fManager->fNdata;
4014 case kLengthFunc: return ((TTreeFormula*)fAliases.UncheckedAt(0))->GetNdata();
4015 case kIteration: return instance;
4016 case kSum: return Summing<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4017 case kMin: return FindMin<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4018 case kMax: return FindMax<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4019 case kEntryList: {
4020 TEntryList *elist = (TEntryList*)fExternalCuts.At(0);
4021 return elist->Contains(fTree->GetTree()->GetReadEntry());
4022 }
4023 case -1: break;
4024 }
4025 switch (fCodes[0]) {
4026 case -2: {
4027 TCutG *gcut = (TCutG*)fExternalCuts.At(0);
4028 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4029 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
4031 fx->ResetLoading();
4032 fy->ResetLoading();
4033 }
4034 T xcut = fx->EvalInstance<T>(instance);
4035 T ycut = fy->EvalInstance<T>(instance);
4036 return gcut->IsInside(xcut,ycut);
4037 }
4038 case -1: {
4039 TCutG *gcut = (TCutG*)fExternalCuts.At(0);
4040 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4042 fx->ResetLoading();
4043 }
4044 return fx->EvalInstance<T>(instance);
4045 }
4046 default: return 0;
4047 }
4048 }
4049
4050 T tab[kMAXFOUND];
4051 const Int_t kMAXSTRINGFOUND = 10;
4052 const char *stringStackLocal[kMAXSTRINGFOUND];
4053 const char **stringStack = stringStackArg?stringStackArg:stringStackLocal;
4054
4055 const bool willLoad = (instance==0 || fNeedLoading); fNeedLoading = false;
4056 if (willLoad) fDidBooleanOptimization = false;
4057
4058 Int_t pos = 0;
4059 Int_t pos2 = 0;
4060 for (Int_t i=0; i<fNoper ; ++i) {
4061
4062 const Int_t oper = GetOper()[i];
4063 const Int_t newaction = oper >> kTFOperShift;
4064
4065 if (newaction<kDefinedVariable) {
4066 // ROOT::v5::TFormula operands.
4067
4068 // one of the most used cases
4069 if (newaction==kConstant) { pos++; tab[pos-1] = GetConstant<T>(oper & kTFOperMask); continue; }
4070
4071 switch(newaction) {
4072
4073 case kEnd : return tab[0];
4074 case kAdd : pos--; tab[pos-1] += tab[pos]; continue;
4075 case kSubstract : pos--; tab[pos-1] -= tab[pos]; continue;
4076 case kMultiply : pos--; tab[pos-1] *= tab[pos]; continue;
4077 case kDivide : pos--; if (tab[pos] == 0) tab[pos-1] = 0; // division by 0
4078 else tab[pos-1] /= tab[pos];
4079 continue;
4080 case kModulo : {pos--;
4081 Long64_t int1((Long64_t)tab[pos-1]);
4082 Long64_t int2((Long64_t)tab[pos]);
4083 tab[pos-1] = T(int1 % int2);
4084 continue;}
4085
4086 case kcos : tab[pos-1] = TMath::Cos(tab[pos-1]); continue;
4087 case ksin : tab[pos-1] = TMath::Sin(tab[pos-1]); continue;
4088 case ktan : if (TMath::Cos(tab[pos-1]) == 0) {tab[pos-1] = 0;} // { tangente indeterminee }
4089 else tab[pos-1] = TMath::Tan(tab[pos-1]);
4090 continue;
4091 case kacos : if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4092 else tab[pos-1] = TMath::ACos(tab[pos-1]);
4093 continue;
4094 case kasin : if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4095 else tab[pos-1] = TMath::ASin(tab[pos-1]);
4096 continue;
4097 case katan : tab[pos-1] = TMath::ATan(tab[pos-1]); continue;
4098 case kcosh : tab[pos-1] = TMath::CosH(tab[pos-1]); continue;
4099 case ksinh : tab[pos-1] = TMath::SinH(tab[pos-1]); continue;
4100 case ktanh : if (TMath::CosH(tab[pos-1]) == 0) {tab[pos-1] = 0;} // { tangente indeterminee }
4101 else tab[pos-1] = TMath::TanH(tab[pos-1]);
4102 continue;
4103 case kacosh: if (tab[pos-1] < 1) {tab[pos-1] = 0;} // indetermination
4104 else tab[pos-1] = TMath::ACosH(tab[pos-1]);
4105 continue;
4106 case kasinh: tab[pos-1] = TMath::ASinH(tab[pos-1]); continue;
4107 case katanh: if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4108 else tab[pos-1] = TMath::ATanH(tab[pos-1]);
4109 continue;
4110 case katan2: pos--; tab[pos-1] = TMath::ATan2(tab[pos-1],tab[pos]); continue;
4111
4112 case kfmod : pos--; tab[pos-1] = fmod_local(tab[pos-1],tab[pos]); continue;
4113 case kpow : pos--; tab[pos-1] = TMath::Power(tab[pos-1],tab[pos]); continue;
4114 case ksq : tab[pos-1] = tab[pos-1]*tab[pos-1]; continue;
4115 case ksqrt : tab[pos-1] = TMath::Sqrt(TMath::Abs(tab[pos-1])); continue;
4116
4117 case kstrstr : pos2 -= 2; pos++;if (strstr(stringStack[pos2],stringStack[pos2+1])) tab[pos-1]=1;
4118 else tab[pos-1]=0;
4119 continue;
4120
4121 case kmin : pos--; tab[pos-1] = std::min(tab[pos-1],tab[pos]); continue;
4122 case kmax : pos--; tab[pos-1] = std::max(tab[pos-1],tab[pos]); continue;
4123
4124 case klog : if (tab[pos-1] > 0) tab[pos-1] = TMath::Log(tab[pos-1]);
4125 else {tab[pos-1] = 0;} //{indetermination }
4126 continue;
4127 case kexp : { Double_t dexp = tab[pos-1];
4128 if (dexp < -700) {tab[pos-1] = 0; continue;}
4129 if (dexp > 700) {tab[pos-1] = TMath::Exp(700); continue;}
4130 tab[pos-1] = TMath::Exp(dexp); continue;
4131 }
4132 case klog10: if (tab[pos-1] > 0) tab[pos-1] = TMath::Log10(tab[pos-1]);
4133 else {tab[pos-1] = 0;} //{indetermination }
4134 continue;
4135
4136 case kpi : pos++; tab[pos-1] = TMath::Pi(); continue;
4137
4138 case kabs : tab[pos-1] = TMath::Abs(tab[pos-1]); continue;
4139 case ksign : if (tab[pos-1] < 0) tab[pos-1] = -1; else tab[pos-1] = 1;
4140 continue;
4141 case kint : tab[pos-1] = T(Long64_t(tab[pos-1])); continue;
4142 case kSignInv: tab[pos-1] = -1 * tab[pos-1]; continue;
4143 case krndm : pos++; tab[pos-1] = gRandom->Rndm(); continue;
4144
4145 case kAnd : pos--; if (tab[pos-1]!=0 && tab[pos]!=0) tab[pos-1]=1;
4146 else tab[pos-1]=0;
4147 continue;
4148 case kOr : pos--; if (tab[pos-1]!=0 || tab[pos]!=0) tab[pos-1]=1;
4149 else tab[pos-1]=0;
4150 continue;
4151
4152 case kEqual : pos--; tab[pos-1] = (tab[pos-1] == tab[pos]) ? 1 : 0; continue;
4153 case kNotEqual : pos--; tab[pos-1] = (tab[pos-1] != tab[pos]) ? 1 : 0; continue;
4154 case kLess : pos--; tab[pos-1] = (tab[pos-1] < tab[pos]) ? 1 : 0; continue;
4155 case kGreater : pos--; tab[pos-1] = (tab[pos-1] > tab[pos]) ? 1 : 0; continue;
4156 case kLessThan : pos--; tab[pos-1] = (tab[pos-1] <= tab[pos]) ? 1 : 0; continue;
4157 case kGreaterThan: pos--; tab[pos-1] = (tab[pos-1] >= tab[pos]) ? 1 : 0; continue;
4158 case kNot : tab[pos-1] = (tab[pos-1] != 0) ? 0 : 1; continue;
4159
4160 case kStringEqual : pos2 -= 2; pos++; if (!strcmp(stringStack[pos2+1],stringStack[pos2])) tab[pos-1]=1;
4161 else tab[pos-1]=0;
4162 continue;
4163 case kStringNotEqual: pos2 -= 2; pos++;if (strcmp(stringStack[pos2+1],stringStack[pos2])) tab[pos-1]=1;
4164 else tab[pos-1]=0;
4165 continue;
4166
4167 case kBitAnd : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) & ((ULong64_t) tab[pos]); continue;
4168 case kBitOr : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) | ((ULong64_t) tab[pos]); continue;
4169 case kLeftShift : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) <<((ULong64_t) tab[pos]); continue;
4170 case kRightShift: pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) >>((ULong64_t) tab[pos]); continue;
4171
4172 case kJump : i = (oper & kTFOperMask); continue;
4173 case kJumpIf : {
4174 pos--;
4175 if (!tab[pos]) {
4176 i = (oper & kTFOperMask);
4177 // If we skip the left (true) side of the if statement we may,
4178 // skip some of the branch loading (since we remove duplicate branch
4179 // request (in TTreeFormula constructor) and so we need to force the
4180 // loading here.
4181 if (willLoad) fDidBooleanOptimization = true;
4182 }
4183 continue;
4184 }
4185
4186 case kStringConst: {
4187 // String
4188 pos2++; stringStack[pos2-1] = (char*)fExpr[i].Data();
4189 if (fAxis) {
4190 // See TT_EVAL_INIT
4191 Int_t bin = fAxis->FindBin(stringStack[pos2-1]);
4192 return bin;
4193 }
4194 continue;
4195 }
4196
4197 case kBoolOptimize: {
4198 // boolean operation optimizer
4199
4200 int param = (oper & kTFOperMask);
4201 bool skip = false;
4202 int op = param % 10; // 1 is && , 2 is ||
4203
4204 if (op == 1 && (!tab[pos-1]) ) {
4205 // &&: skip the right part if the left part is already false
4206
4207 skip = true;
4208
4209 // Preserve the existing behavior (i.e. the result of a&&b is
4210 // either 0 or 1)
4211 tab[pos-1] = 0;
4212
4213 } else if (op == 2 && tab[pos-1] ) {
4214 // ||: skip the right part if the left part is already true
4215
4216 skip = true;
4217
4218 // Preserve the existing behavior (i.e. the result of a||b is
4219 // either 0 or 1)
4220 tab[pos-1] = 1;
4221 }
4222
4223 if (skip) {
4224 int toskip = param / 10;
4225 i += toskip;
4226 if (willLoad) fDidBooleanOptimization = true;
4227 }
4228 continue;
4229 }
4230
4231 case kFunctionCall: {
4232 // an external function call
4233
4234 int param = (oper & kTFOperMask);
4235 int fno = param / 1000;
4236 int nargs = param % 1000;
4237
4238 // Retrieve the function
4239 TMethodCall *method = (TMethodCall*)fFunctions.At(fno);
4240
4241 // Set the arguments
4242 method->ResetParam();
4243 if (nargs) {
4244 UInt_t argloc = pos-nargs;
4245 for(Int_t j=0;j<nargs;j++,argloc++,pos--) {
4246 SetMethodParam(method, tab[argloc]);
4247 }
4248 }
4249 pos++;
4250 Double_t ret = 0;
4251 method->Execute(ret);
4252 tab[pos-1] = ret; // check for the correct conversion!
4253
4254 continue;
4255 }
4256
4257// case kParameter: { pos++; tab[pos-1] = fParams[(oper & kTFOperMask)]; continue; }
4258 }
4259
4260 } else {
4261 // TTreeFormula operands.
4262
4263 // a tree variable (the most used case).
4264
4265 if (newaction == kDefinedVariable) {
4266
4267 const Int_t code = (oper & kTFOperMask);
4268 const Int_t lookupType = fLookupType[code];
4269 switch (lookupType) {
4270 case kIndexOfEntry: tab[pos++] = (T)fTree->GetReadEntry(); continue;
4271 case kIndexOfLocalEntry: tab[pos++] = (T)fTree->GetTree()->GetReadEntry(); continue;
4272 case kEntries: tab[pos++] = (T)fTree->GetEntries(); continue;
4273 case kLocalEntries: tab[pos++] = (T)fTree->GetTree()->GetEntries(); continue;
4274 case kLength: tab[pos++] = fManager->fNdata; continue;
4275 case kLengthFunc: tab[pos++] = ((TTreeFormula*)fAliases.UncheckedAt(i))->GetNdata(); continue;
4276 case kIteration: tab[pos++] = instance; continue;
4277 case kSum: tab[pos++] = Summing<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4278 case kMin: tab[pos++] = FindMin<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4279 case kMax: tab[pos++] = FindMax<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4280
4281 case kDirect: { TT_EVAL_INIT_LOOP; tab[pos++] = leaf->GetTypedValue<T>(real_instance); continue; }
4282 case kMethod: { TT_EVAL_INIT_LOOP; tab[pos++] = GetValueFromMethod(code,leaf); continue; }
4283 case kDataMember: { TT_EVAL_INIT_LOOP; tab[pos++] = ((TFormLeafInfo*)fDataMembers.UncheckedAt(code))->
4284 GetTypedValue<T>(leaf,real_instance); continue; }
4285 case kTreeMember: { TREE_EVAL_INIT_LOOP; tab[pos++] = ((TFormLeafInfo*)fDataMembers.UncheckedAt(code))->
4286 GetTypedValue<T>((TLeaf*)nullptr,real_instance); continue; }
4287 case kEntryList: { TEntryList *elist = (TEntryList*)fExternalCuts.At(code);
4288 tab[pos++] = elist->Contains(fTree->GetReadEntry());
4289 continue;}
4290 case -1: break;
4291 default: tab[pos++] = 0; continue;
4292 }
4293 switch (fCodes[code]) {
4294 case -2: {
4295 TCutG *gcut = (TCutG*)fExternalCuts.At(code);
4296 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4297 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
4299 fx->ResetLoading();
4300 fy->ResetLoading();
4301 }
4302 T xcut = fx->EvalInstance<T>(instance);
4303 T ycut = fy->EvalInstance<T>(instance);
4304 tab[pos++] = gcut->IsInside(xcut,ycut);
4305 continue;
4306 }
4307 case -1: {
4308 TCutG *gcut = (TCutG*)fExternalCuts.At(code);
4309 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4311 fx->ResetLoading();
4312 }
4313 tab[pos++] = fx->EvalInstance<T>(instance);
4314 continue;
4315 }
4316 default: {
4317 tab[pos++] = 0;
4318 continue;
4319 }
4320 }
4321 }
4322 switch(newaction) {
4323
4324 // a TTree Variable Alias (i.e. a sub-TTreeFormula)
4325 case kAlias: {
4326 int aliasN = i;
4327 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(aliasN));
4328 R__ASSERT(subform);
4329
4331 T param = subform->EvalInstance<T>(instance);
4332
4333 tab[pos] = param; pos++;
4334 continue;
4335 }
4336 // a TTree Variable Alias String (i.e. a sub-TTreeFormula)
4337 case kAliasString: {
4338 int aliasN = i;
4339 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(aliasN));
4340 R__ASSERT(subform);
4341
4342 pos2++;
4344 stringStack[pos2-1] = subform->EvalStringInstance(instance);
4345 continue;
4346 }
4347 case kMinIf: {
4348 int alternateN = i;
4349 TTreeFormula *primary = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN));
4350 TTreeFormula *condition = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN+1));
4351 T param = FindMin<T>(primary,condition);
4352 ++i; // skip the place holder for the condition
4353 tab[pos] = param; pos++;
4354 continue;
4355 }
4356 case kMaxIf: {
4357 int alternateN = i;
4358 TTreeFormula *primary = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN));
4359 TTreeFormula *condition = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN+1));
4360 T param = FindMax<T>(primary,condition);
4361 ++i; // skip the place holder for the condition
4362 tab[pos] = param; pos++;
4363 continue;
4364 }
4365
4366 // a TTree Variable Alternate (i.e. a sub-TTreeFormula)
4367 case kAlternate: {
4368 int alternateN = i;
4369 TTreeFormula *primary = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN));
4370
4371 // First check whether we are in range for the primary formula
4372 if (instance < primary->GetNdata()) {
4373
4374 T param = primary->EvalInstance<T>(instance);
4375
4376 ++i; // skip the alternate value.
4377
4378 tab[pos] = param; pos++;
4379 } else {
4380 // The primary is not in range, we will calculate the alternate value
4381 // via the next operation (which will be a intentional).
4382
4383 // kAlias no operations
4384 }
4385 continue;
4386 }
4387 case kAlternateString: {
4388 int alternateN = i;
4389 TTreeFormula *primary = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN));
4390
4391 // First check whether we are in range for the primary formula
4392 if (instance < primary->GetNdata()) {
4393
4394 pos2++;
4395 stringStack[pos2-1] = primary->EvalStringInstance(instance);
4396
4397 ++i; // skip the alternate value.
4398
4399 } else {
4400 // The primary is not in range, we will calculate the alternate value
4401 // via the next operation (which will be a kAlias).
4402
4403 // intentional no operations
4404 }
4405 continue;
4406 }
4407
4408 // a tree string
4409 case kDefinedString: {
4410 Int_t string_code = (oper & kTFOperMask);
4411 TLeaf *leafc = (TLeaf*)fLeaves.UncheckedAt(string_code);
4412
4413 // Now let calculate what physical instance we really need.
4414 const Int_t real_instance = GetRealInstance(instance,string_code);
4415
4416 if (instance==0 || fNeedLoading) {
4417 fNeedLoading = false;
4418 TBranch *branch = leafc->GetBranch();
4419 Long64_t readentry = branch->GetTree()->GetReadEntry();
4420 R__LoadBranch(branch,readentry,fQuickLoad);
4421 } else {
4422 // In the cases where we are behind (i.e. right of) a potential boolean optimization
4423 // this tree variable reading may have not been executed with instance==0 which would
4424 // result in the branch being potentially not read in.
4426 TBranch *br = leafc->GetBranch();
4427 Long64_t treeEntry = br->GetTree()->GetReadEntry();
4428 R__LoadBranch(br,treeEntry,true);
4429 }
4430 if (real_instance>=fNdata[string_code]) return 0;
4431 }
4432 pos2++;
4433 if (fLookupType[string_code]==kDirect) {
4434 stringStack[pos2-1] = (char*)leafc->GetValuePointer();
4435 } else {
4436 stringStack[pos2-1] = (char*)GetLeafInfo(string_code)->GetValuePointer(leafc,real_instance);
4437 }
4438 continue;
4439 }
4440
4441 }
4442 }
4443
4444 R__ASSERT(i<fNoper);
4445 }
4446
4447 //std::cout << __PRETTY_FUNCTION__ << " returning " << tab[0] << std::endl;
4448 return tab[0];
4449}
4450
4451// Template instantiations
4452template double TTreeFormula::EvalInstance<double> (int, char const**);
4453template long double TTreeFormula::EvalInstance<long double> (int, char const**);
4454template long long TTreeFormula::EvalInstance<long long> (int, char const**);
4455
4456////////////////////////////////////////////////////////////////////////////////
4457/// Return DataMember corresponding to code.
4458///
4459/// function called by TLeafObject::GetValue
4460/// with the value of fLookupType computed in TTreeFormula::DefinedVariable
4461
4463{
4464 return (TFormLeafInfo *)fDataMembers.UncheckedAt(code);
4466}
4467
4468////////////////////////////////////////////////////////////////////////////////
4469/// Return leaf corresponding to serial number n.
4470
4472{
4473 return (TLeaf*)fLeaves.UncheckedAt(n);
4474}
4475
4476////////////////////////////////////////////////////////////////////////////////
4477/// Return methodcall corresponding to code.
4478///
4479/// function called by TLeafObject::GetValue
4480/// with the value of fLookupType computed in TTreeFormula::DefinedVariable
4481
4483{
4484 return (TMethodCall *)fMethods.UncheckedAt(code);
4486}
4487
4488////////////////////////////////////////////////////////////////////////////////
4489/// Return number of available instances in the formula.
4490
4492{
4494}
4495
4496////////////////////////////////////////////////////////////////////////////////
4497/// Return result of a leafobject method.
4498
4500{
4502
4503 if (!m) {
4504 return 0.0;
4505 }
4506
4507 void* thisobj = nullptr;
4508 if (leaf->InheritsFrom(TLeafObject::Class())) {
4509 thisobj = ((TLeafObject*) leaf)->GetObject();
4510 } else {
4511 TBranchElement* branch = (TBranchElement*) ((TLeafElement*) leaf)->GetBranch();
4512 Int_t id = branch->GetID();
4513 // FIXME: This is wrong for a top-level branch.
4514 Int_t offset = 0;
4515 if (id > -1) {
4516 TStreamerInfo* info = branch->GetInfo();
4517 if (info) {
4518 offset = info->GetElementOffset(id);
4519 } else {
4520 Warning("GetValueFromMethod", "No streamer info for branch %s.", branch->GetName());
4521 }
4522 }
4523 if (id < 0) {
4524 char* address = branch->GetObject();
4525 thisobj = address;
4526 } else {
4527 //char* address = branch->GetAddress();
4528 char* address = branch->GetObject();
4529 if (address) {
4530 thisobj = *((char**) (address + offset));
4531 } else {
4532 // FIXME: If the address is not set, the object won't be either!
4533 thisobj = branch->GetObject();
4534 }
4535 }
4536 }
4537
4538 TMethodCall::EReturnType r = m->ReturnType();
4539
4540 if (r == TMethodCall::kLong) {
4541 Longptr_t l = 0;
4542 m->Execute(thisobj, l);
4543 return (Double_t) l;
4544 }
4545
4546 if (r == TMethodCall::kDouble) {
4547 Double_t d = 0.0;
4548 m->Execute(thisobj, d);
4549 return d;
4550 }
4551
4552 m->Execute(thisobj);
4553
4554 return 0;
4555}
4556
4557////////////////////////////////////////////////////////////////////////////////
4558/// Return result of a leafobject method.
4559
4561{
4563
4564 if (!m) {
4565 return nullptr;
4566 }
4567
4568 void* thisobj;
4569 if (leaf->InheritsFrom(TLeafObject::Class())) {
4570 thisobj = ((TLeafObject*) leaf)->GetObject();
4571 } else {
4572 TBranchElement* branch = (TBranchElement*) ((TLeafElement*) leaf)->GetBranch();
4573 Int_t id = branch->GetID();
4574 Int_t offset = 0;
4575 if (id > -1) {
4576 TStreamerInfo* info = branch->GetInfo();
4577 if (info) {
4578 offset = info->GetElementOffset(id);
4579 } else {
4580 Warning("GetValuePointerFromMethod", "No streamer info for branch %s.", branch->GetName());
4581 }
4582 }
4583 if (id < 0) {
4584 char* address = branch->GetObject();
4585 thisobj = address;
4586 } else {
4587 //char* address = branch->GetAddress();
4588 char* address = branch->GetObject();
4589 if (address) {
4590 thisobj = *((char**) (address + offset));
4591 } else {
4592 // FIXME: If the address is not set, the object won't be either!
4593 thisobj = branch->GetObject();
4594 }
4595 }
4596 }
4597
4598 TMethodCall::EReturnType r = m->ReturnType();
4599
4600 if (r == TMethodCall::kLong) {
4601 Longptr_t l = 0;
4602 m->Execute(thisobj, l);
4603 return nullptr;
4604 }
4605
4606 if (r == TMethodCall::kDouble) {
4607 Double_t d = 0.0;
4608 m->Execute(thisobj, d);
4609 return nullptr;
4610 }
4611
4612 if (r == TMethodCall::kOther) {
4613 char* c = nullptr;
4614 m->Execute(thisobj, &c);
4615 return c;
4616 }
4617
4618 m->Execute(thisobj);
4619
4620 return nullptr;
4621}
4622
4623////////////////////////////////////////////////////////////////////////////////
4624/// Return TRUE if the formula corresponds to one single Tree leaf
4625/// and this leaf is short, int or unsigned short, int
4626/// When a leaf is of type integer or string, the generated histogram is forced
4627/// to have an integer bin width
4628
4629bool TTreeFormula::IsInteger(bool fast) const
4630{
4631 if (fast) {
4632 if (TestBit(kIsInteger)) return true;
4633 else return false;
4634 }
4635
4636 if (fNoper==2 && GetAction(0)==kAlternate) {
4637 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(0));
4638 R__ASSERT(subform);
4639 return subform->IsInteger(false);
4640 }
4641
4642 if (GetAction(0)==kMinIf || GetAction(0)==kMaxIf) {
4643 return false;
4644 }
4645
4646 if (fNoper > 1) return false;
4647
4648 if (GetAction(0)==kAlias) {
4649 TTreeFormula *subform = static_cast<TTreeFormula*>(fAliases.UncheckedAt(0));
4650 R__ASSERT(subform);
4651 return subform->IsInteger(false);
4652 }
4653
4654 if (fLeaves.GetEntries() != 1) {
4655 switch (fLookupType[0]) {
4656 case kIndexOfEntry:
4657 case kIndexOfLocalEntry:
4658 case kEntries:
4659 case kLocalEntries:
4660 case kLength:
4661 case kLengthFunc:
4662 case kIteration:
4663 return true;
4664 case kSum:
4665 case kMin:
4666 case kMax:
4667 case kEntryList:
4668 default:
4669 return false;
4670 }
4671 }
4672
4673 if (EvalClass()==TBits::Class()) return true;
4674
4675 if (IsLeafInteger(0) || IsLeafString(0)) return true;
4676 return false;
4677}
4679////////////////////////////////////////////////////////////////////////////////
4680/// Return TRUE if the leaf corresponding to code is short, int or unsigned
4681/// short, int When a leaf is of type integer, the generated histogram is
4682/// forced to have an integer bin width
4683
4684bool TTreeFormula::IsLeafInteger(Int_t code) const
4685{
4686 TLeaf *leaf = (TLeaf*)fLeaves.At(code);
4687 if (!leaf) {
4688 switch (fLookupType[code]) {
4689 case kIndexOfEntry:
4690 case kIndexOfLocalEntry:
4691 case kEntries:
4692 case kLocalEntries:
4693 case kLength:
4694 case kLengthFunc:
4695 case kIteration:
4696 return true;
4697 case kSum:
4698 case kMin:
4699 case kMax:
4700 case kEntryList:
4701 default:
4702 return false;
4703 }
4704 }
4705 if (fAxis) return true;
4706 TFormLeafInfo * info;
4707 switch (fLookupType[code]) {
4708 case kMethod:
4709 case kTreeMember:
4710 case kDataMember:
4711 info = GetLeafInfo(code);
4712 return info->IsInteger();
4713 case kDirect:
4714 break;
4715 }
4716 if (!strcmp(leaf->GetTypeName(),"Int_t")) return true;
4717 if (!strcmp(leaf->GetTypeName(),"Short_t")) return true;
4718 if (!strcmp(leaf->GetTypeName(),"UInt_t")) return true;
4719 if (!strcmp(leaf->GetTypeName(),"UShort_t")) return true;
4720 if (!strcmp(leaf->GetTypeName(),"Bool_t")) return true;
4721 if (!strcmp(leaf->GetTypeName(),"Char_t")) return true;
4722 if (!strcmp(leaf->GetTypeName(),"UChar_t")) return true;
4723 if (!strcmp(leaf->GetTypeName(),"Long64_t")) return true;
4724 if (!strcmp(leaf->GetTypeName(),"ULong64_t")) return true;
4725 if (!strcmp(leaf->GetTypeName(),"string")) return true;
4726 return false;
4727}
4728
4729////////////////////////////////////////////////////////////////////////////////
4730/// Return TRUE if the formula is a string
4731
4732bool TTreeFormula::IsString() const
4733