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 "TEntryList.h"
46
47#include <cctype>
48#include <cstdio>
49#include <cmath>
50#include <cstdlib>
51#include <limits>
52#include <typeinfo>
53#include <algorithm>
54#include <sstream>
55
56const Int_t kMaxLen = 2048;
57
58/** \class TTreeFormula
59Used to pass a selection expression to the Tree drawing routine. See TTree::Draw
60
61A TreeFormula can contain any arithmetic expression including
62standard operators and mathematical functions separated by operators.
63Examples of valid expression:
64~~~{.cpp}
65 "x<y && sqrt(z)>3.2"
66~~~
67TTreeFormula now relies on a variety of TFormLeafInfo classes to handle the
68reading of the information. Here is the list of theses classes:
69 - TFormLeafInfo
70 - TFormLeafInfoDirect
71 - TFormLeafInfoNumerical
72 - TFormLeafInfoClones
73 - TFormLeafInfoCollection
74 - TFormLeafInfoPointer
75 - TFormLeafInfoMethod
76 - TFormLeafInfoMultiVarDim
77 - TFormLeafInfoMultiVarDimDirect
78 - TFormLeafInfoCast
79
80The following method are available from the TFormLeafInfo interface:
81
82 - AddOffset(Int_t offset, TStreamerElement* element)
83 - GetCounterValue(TLeaf* leaf) : return the size of the array pointed to.
84 - GetObjectAddress(TLeafElement* leaf) : Returns the location of the object pointed to.
85 - GetMultiplicity() : Returns info on the variability of the number of elements
86 - GetNdata(TLeaf* leaf) : Returns the number of elements
87 - GetNdata() : Used by GetNdata(TLeaf* leaf)
88 - GetValue(TLeaf *leaf, Int_t instance = 0) : Return the value
89 - GetValuePointer(TLeaf *leaf, Int_t instance = 0) : Returns the address of the value
90 - GetLocalValuePointer(TLeaf *leaf, Int_t instance = 0) : Returns the address of the value of 'this' LeafInfo
91 - IsString()
92 - ReadValue(char *where, Int_t instance = 0) : Internal function to interpret the location 'where'
93 - Update() : react to the possible loading of a shared library.
94*/
95
96
97////////////////////////////////////////////////////////////////////////////////
98/// The function returns the number of bytes read from the input buffer.
99/// If entry does not exist or (entry!=readEntry && quickload), the function returns 0.
100/// If an I/O error occurs, the function returns -1.
101///
103{
104 if (!quickLoad || (br->GetReadEntry() != entry)) {
105 auto res = br->GetEntry(entry);
106 return res;
107 }
108 return 0;
109}
110
111////////////////////////////////////////////////////////////////////////////////
112/// \brief Helper function checking if a string contains a literal number.
113/// Whitespaces are not allowed as part of a valid number-string
114/// \return true if it can be converted to a valid number (floating or integer),
115/// false otherwise.
116
117bool IsNumberConstant(const std::string &str)
118{
119 std::istringstream iss(str);
120 double number;
121 return iss >> std::noskipws >> number && iss.eof();
122}
123
124////////////////////////////////////////////////////////////////////////////////
125/// \class TDimensionInfo
126/// A small helper class to help in keeping track of the array
127/// dimensions encountered in the analysis of the expression.
128
129class TDimensionInfo : public TObject {
130public:
131 Int_t fCode; // Location of the leaf in TTreeFormula::fCode
132 Int_t fOper; // Location of the Helper using the leaf in TTreeFormula::fOper
137 ~TDimensionInfo() override {};
138};
139
140////////////////////////////////////////////////////////////////////////////////
141/// TreeFormula constructor only valid for ROOT I/O purposes.
142
143TTreeFormula::TTreeFormula(TRootIOCtor*): ROOT::v5::TFormula(), fQuickLoad(false), fNeedLoading(true),
144 fDidBooleanOptimization(false), fDimensionSetup(nullptr)
145
146{
147 fTree = nullptr;
148 fLookupType = nullptr;
149 fNindex = 0;
150 fNcodes = 0;
151 fAxis = nullptr;
152 fHasCast = false;
153 fManager = nullptr;
154 fMultiplicity = 0;
155 fConstLD = nullptr;
156
157 Int_t j,k;
158 for (j=0; j<kMAXCODES; j++) {
159 fNdimensions[j] = 0;
160 fCodes[j] = 0;
161 fNdata[j] = 1;
162 fHasMultipleVarDim[j] = false;
163 for (k = 0; k<kMAXFORMDIM; k++) {
164 fIndexes[j][k] = -1;
165 fCumulSizes[j][k] = 1;
166 fVarIndexes[j][k] = nullptr;
167 }
168 }
169}
170
171////////////////////////////////////////////////////////////////////////////////
172/// Normal TTree Formula Constructor
173
174TTreeFormula::TTreeFormula(const char *name,const char *expression, TTree *tree)
175 :ROOT::v5::TFormula(), fTree(tree), fQuickLoad(false), fNeedLoading(true),
176 fDidBooleanOptimization(false), fDimensionSetup(nullptr)
177{
178 Init(name,expression);
179}
180
181////////////////////////////////////////////////////////////////////////////////
182/// Constructor used during the expansion of an alias
183
184TTreeFormula::TTreeFormula(const char *name,const char *expression, TTree *tree,
185 const std::vector<std::string>& aliases)
186 :ROOT::v5::TFormula(), fTree(tree), fQuickLoad(false), fNeedLoading(true),
187 fDidBooleanOptimization(false), fDimensionSetup(nullptr), fAliasesUsed(aliases)
188{
189 Init(name,expression);
190}
191
192////////////////////////////////////////////////////////////////////////////////
193/// Initialization called from the constructors.
194
195void TTreeFormula::Init(const char*name, const char* expression)
196{
198
201 fNcodes = 0;
202 fMultiplicity = 0;
203 fAxis = nullptr;
204 fHasCast = false;
205 fConstLD = nullptr;
206 Int_t i,j,k;
208 fManager->Add(this);
209
210 for (j=0; j<kMAXCODES; j++) {
211 fNdimensions[j] = 0;
213 fCodes[j] = 0;
214 fNdata[j] = 1;
215 fHasMultipleVarDim[j] = false;
216 for (k = 0; k<kMAXFORMDIM; k++) {
217 fIndexes[j][k] = -1;
218 fCumulSizes[j][k] = 1;
219 fVarIndexes[j][k] = nullptr;
220 }
221 }
222
224
225 if (Compile(expression)) {
226 fTree = nullptr; fNdim = 0;
227 if(savedir) savedir->cd();
228 return;
229 }
230
231 if (fNcodes >= kMAXFOUND) {
232 Warning("TTreeFormula","Too many items in expression:%s",expression);
234 }
235 SetName(name);
236
237 for (i=0;i<fNoper;i++) {
238
239 if (GetAction(i)==kDefinedString) {
242 if (!leafc) continue;
243
244 // We have a string used as a string
245
246 // This dormant portion of code would be used if (when?) we allow the histogramming
247 // of the integral content (as opposed to the string content) of strings
248 // held in a variable size container delimited by a null (as opposed to
249 // a fixed size container or variable size container whose size is controlled
250 // by a variable). In GetNdata, we will then use strlen to grab the current length.
251 //fCumulSizes[i][fNdimensions[i]-1] = 1;
252 //fUsedSizes[fNdimensions[i]-1] = -TMath::Abs(fUsedSizes[fNdimensions[i]-1]);
253 //fUsedSizes[0] = - TMath::Abs( fUsedSizes[0]);
254
255 if (fNoper == 1) {
256 // If the string is by itself, then it can safely be histogrammed as
257 // in a string based axis. To histogram the number inside the string
258 // just make it part of a useless expression (for example: mystring+0)
260 }
261 continue;
262 }
263 if (GetAction(i)==kJump && GetActionParam(i)==(fNoper-1)) {
264 // We have cond ? string1 : string2
266 }
267 }
268 if (fNoper == 1 && GetAction(0)==kStringConst) {
270 }
271 if (fNoper==1 && GetAction(0)==kAliasString) {
274 if (subform->IsString()) SetBit(kIsCharacter);
275 } else if (fNoper==2 && GetAction(0)==kAlternateString) {
278 if (subform->IsString()) SetBit(kIsCharacter);
279 }
280
281 fManager->Sync();
282
283 // Let's verify the indexes and dies if we need to.
284 Int_t k0,k1;
285 for(k0 = 0; k0 < fNcodes; k0++) {
286 for(k1 = 0; k1 < fNdimensions[k0]; k1++ ) {
287 // fprintf(stderr,"Saw %d dim %d and index %d\n",k1, fFixedSizes[k0][k1], fIndexes[k0][k1]);
288 if ( fIndexes[k0][k1]>=0 && fFixedSizes[k0][k1]>=0
289 && fIndexes[k0][k1]>=fFixedSizes[k0][k1]) {
290 Error("TTreeFormula",
291 "Index %d for dimension #%d in %s is too high (max is %d)",
292 fIndexes[k0][k1],k1+1, expression,fFixedSizes[k0][k1]-1);
293 fTree = nullptr; fNdim = 0;
294 if(savedir) savedir->cd();
295 return;
296 }
297 }
298 }
299
300 // Create a list of unique branches to load.
301 for(k=0; k<fNcodes ; k++) {
302 TLeaf *leaf = k <= fLeaves.GetLast() ? (TLeaf*)fLeaves.UncheckedAt(k) : nullptr;
303 TBranch *branch = nullptr;
304 if (leaf) {
305 branch = leaf->GetBranch();
306 if (fBranches.FindObject(branch)) branch = nullptr;
307 }
309 }
310
311 if (IsInteger(false)) SetBit(kIsInteger);
312
314 // Call TTree::GetEntries() to insure that it is already calculated.
315 // This will need to be done anyway at the first iteration and insure
316 // that it will not mess up the branch reading (because TTree::GetEntries
317 // opens all the file in the chain and 'stays' on the last file.
318
321 fTree->GetEntries();
322 if (treenumber != fTree->GetTreeNumber()) {
323 if (readentry >= 0) {
325 }
327 } else {
328 if (readentry >= 0) {
330 }
331 }
332
333 }
334
335 if(savedir) savedir->cd();
336}
337
338////////////////////////////////////////////////////////////////////////////////
339/// Tree Formula default destructor.
340
342{
343 if (fManager) {
344 fManager->Remove(this);
345 if (fManager->fFormulas.GetLast()<0) {
346 delete fManager;
347 fManager = nullptr;
348 }
349 }
350 // Objects in fExternalCuts are not owned and should not be deleted
351 // fExternalCuts.Clear();
356 if (fLookupType) delete [] fLookupType;
357 for (int j=0; j<fNcodes; j++) {
358 for (int k = 0; k<fNdimensions[j]; k++) {
359 if (fVarIndexes[j][k]) delete fVarIndexes[j][k];
360 fVarIndexes[j][k] = nullptr;
361 }
362 }
363 if (fDimensionSetup) {
365 delete fDimensionSetup;
366 }
367 delete[] fConstLD;
368}
369
370////////////////////////////////////////////////////////////////////////////////
371/// This method is used internally to decode the dimensions of the variables.
372
375 Int_t& virt_dim) {
376 if (info) {
378 //if (fIndexes[code][info->fDim]<0) { // removed because the index might be out of bounds!
379 info->fVirtDim = virt_dim;
380 fManager->AddVarDims(virt_dim); // if (!fVarDims[virt_dim]) fVarDims[virt_dim] = new TArrayI;
381 //}
382 }
383
384 Int_t vsize = 0;
385 bool scalarindex = false;
386
387 if (fIndexes[code][fNdimensions[code]]==-2) {
389 // ASSERT(indexvar!=0);
390 Int_t index_multiplicity = indexvar->GetMultiplicity();
391 switch (index_multiplicity) {
392 case 0:
393 scalarindex = true;
394 vsize = 1;
395 break;
396 case -1:
397 case 2:
398 vsize = indexvar->GetNdata();
399 break;
400 case 1:
401 vsize = -1;
402 break;
403 };
404 } else vsize = size;
405
406 fCumulSizes[code][fNdimensions[code]] = size;
407
408 if ( !scalarindex && fIndexes[code][fNdimensions[code]] < 0 ) {
410 }
411
412 fNdimensions[code] ++;
413
414}
415
416////////////////////////////////////////////////////////////////////////////////
417/// This method is used internally to decode the dimensions of the variables.
418
420{
421 // We assume that there are NO white spaces in the info string
422 const char * current;
424
425 current = info;
426 vardim = 0;
427 // the next value could be before the string but
428 // that's okay because the next operation is ++
429 // (this is to avoid (?) a if statement at the end of the
430 // loop)
431 if (current[0] != '[') current--;
432 while (current) {
433 current++;
434 scanindex = sscanf(current,"%d",&size);
435 // if scanindex is 0 then we have a name index thus a variable
436 // array (or TClonesArray!).
437
438 if (scanindex==0) size = -1;
439
441
442 if (fNdimensions[code] >= kMAXFORMDIM) {
443 // NOTE: test that fNdimensions[code] is NOT too big!!
444
445 break;
446 }
447 current = (char*)strstr( current, "[" );
448 }
449 return vardim;
450}
451
452
453////////////////////////////////////////////////////////////////////////////////
454/// This method stores the dimension information for later usage.
455
463
464////////////////////////////////////////////////////////////////////////////////
465/// This method is used internally to decode the dimensions of the variables.
466
468 TFormLeafInfo * /* maininfo */,
469 bool useCollectionObject) {
470 Int_t ndim, size, current, vardim;
471 vardim = 0;
472
473 const TStreamerElement * elem = leafinfo->fElement;
474 TClass* c = elem ? elem->GetClassPointer() : nullptr;
475
477 if (multi) {
478 // We have a second variable dimensions
480 multi->fDim = fNdimensions[code];
481 return RegisterDimensions(code, -1, multi);
482 }
483 if (elem->IsA() == TStreamerBasicPointer::Class()) {
484
485 if (elem->GetArrayDim()>0) {
486
487 ndim = elem->GetArrayDim();
488 size = elem->GetMaxIndex(0);
489 vardim += RegisterDimensions(code, -1);
490 } else {
491 ndim = 1;
492 size = -1;
493 }
494
496 TClass *cl = leafinfo->fClass;
498 TStreamerElement* counter = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(array->GetCountName(),offset);
499#if 1
500 leafinfo->fCounter = new TFormLeafInfo(cl,offset,counter);
501#else /* Code is not ready yet see revision 14078 */
502 if (maininfo==0 || maininfo==leafinfo || 1) {
503 leafinfo->fCounter = new TFormLeafInfo(cl,offset,counter);
504 } else {
505 leafinfo->fCounter = maininfo->DeepCopy();
507 while(currentinfo->fNext && currentinfo->fNext->fNext) currentinfo=currentinfo->fNext;
508 delete currentinfo->fNext;
509 currentinfo->fNext = new TFormLeafInfo(cl,offset,counter);
510 }
511#endif
512 } else if (!useCollectionObject && elem->GetClassPointer() == TClonesArray::Class() ) {
513
514 ndim = 1;
515 size = -1;
516
519 TStreamerElement *counter = ((TStreamerInfo*)clonesClass->GetStreamerInfo())->GetStreamerElement("fLast",c_offset);
520 leafinfo->fCounter = new TFormLeafInfo(clonesClass,c_offset,counter);
521
522 } else if (!useCollectionObject && elem->GetClassPointer() && elem->GetClassPointer()->GetCollectionProxy() ) {
523
524 if ( typeid(*leafinfo) == typeid(TFormLeafInfoCollection) ) {
525 ndim = 1;
526 size = -1;
527 } else {
529 ndim = 1;
530 size = 1;
531 }
532
533 } else if ( c && c->GetReferenceProxy() && c->GetReferenceProxy()->HasCounter() ) {
534 ndim = 1;
535 size = -1;
536 } else if (elem->GetArrayDim()>0) {
537
538 ndim = elem->GetArrayDim();
539 size = elem->GetMaxIndex(0);
540
541 } else if ( elem->GetNewType()== TStreamerInfo::kCharStar) {
542
543 // When we implement being able to read the length from
544 // strlen, we will have:
545 // ndim = 1;
546 // size = -1;
547 // until then we more or so die:
548 ndim = 1;
549 size = 1; //NOTE: changed from 0
550
551 } else return 0;
552
553 current = 0;
554 do {
556
557 if (fNdimensions[code] >= kMAXFORMDIM) {
558 // NOTE: test that fNdimensions[code] is NOT too big!!
559
560 break;
561 }
562 current++;
563 size = elem->GetMaxIndex(current);
564 } while (current<ndim);
565
566 return vardim;
567}
568
569////////////////////////////////////////////////////////////////////////////////
570/// This method is used internally to decode the dimensions of the variables.
571
573 TBranchElement * leafcount2 = branch->GetBranchCount2();
574 if (leafcount2) {
575 // With have a second variable dimensions
576 TBranchElement *leafcount = dynamic_cast<TBranchElement*>(branch->GetBranchCount());
577
578 R__ASSERT(leafcount); // The function should only be called on a functional TBranchElement object
579
583 fHasMultipleVarDim[code] = true;
584
585 info->fCounter = new TFormLeafInfoDirect(leafcount);
586 info->fCounter2 = new TFormLeafInfoDirect(leafcount2);
587 info->fDim = fNdimensions[code];
588 //if (fIndexes[code][info->fDim]<0) {
589 // info->fVirtDim = virt_dim;
590 // if (!fVarDims[virt_dim]) fVarDims[virt_dim] = new TArrayI;
591 //}
592 return RegisterDimensions(code, -1, info);
593 }
594 return 0;
595}
596
597////////////////////////////////////////////////////////////////////////////////
598/// This method is used internally to decode the dimensions of the variables.
599
602
603 // Let see if we can understand the structure of this branch.
604 // Usually we have: leafname[fixed_array], leaftitle[var_array]/type, leaftitle[n]/d[0,10,32]
605 // (with fixed_array that can be a multi-dimensional array).
606 TString sname = leaf->GetTitle();
607 auto slash = sname.First("/");
608 sname = (slash == TString::kNPOS) ? sname : sname(0, slash);
609 const char *tname = sname.Data();
610 char *leaf_dim = (char*)strstr( tname, "[" );
611
612 const char *bname = leaf->GetBranch()->GetName();
613 char *branch_dim = (char*)strstr(bname,"[");
614 if (branch_dim) branch_dim++; // skip the '['
615
616 bool isString = false;
617 if (leaf->IsA() == TLeafElement::Class()) {
618 Int_t type =((TBranchElement*)leaf->GetBranch())->GetStreamerType();
621 } else {
622 isString = (leaf->IsA() == TLeafC::Class());
623 }
624 if (leaf_dim) {
625 leaf_dim++; // skip the '['
627 // then both are NOT the same so do the leaf title first:
631 && (leaf_dim+strlen(branch_dim))[0]=='[') {
632 // we have extra info in the leaf title
634 }
635 }
636 if (branch_dim) {
637 // then both are NOT same so do the branch name next:
638 if (isString) {
640 } else {
642 }
643 }
644 if (leaf->IsA() == TLeafElement::Class()) {
645 TBranchElement* branch = (TBranchElement*) leaf->GetBranch();
646 if (branch->GetBranchCount2()) {
647
648 if (!branch->GetBranchCount()) {
649 Warning("RegisterDimensions",
650 "Noticed an incorrect in-memory TBranchElement object (%s).\nIt has a BranchCount2 but no BranchCount!\nThe result might be incorrect!",
651 branch->GetName());
652 return numberOfVarDim;
653 }
654
655 // Switch from old direct style to using a TLeafInfo
656 if (fLookupType[code] == kDataMember)
657 Warning("RegisterDimensions",
658 "Already in kDataMember mode when handling multiple variable dimensions");
659 fLookupType[code] = kDataMember;
660
661 // Feed the information into the Dimensions system
663
664 }
665 }
666 return numberOfVarDim;
667}
668
669////////////////////////////////////////////////////////////////////////////////
670/// This method check for treat the case where expression contains `Alt$(`
671/// and load up both fAliases and fExpr. It also checks for `MinIf$(` and `MaxIf$(`
672/// We return:
673/// - -1 in case of failure
674/// - 0 in case we did not find any of `Alt$(`, `MinIf$(`, or `MaxIf$(`
675/// - the action number in case of success. (kAlternate, kMinIf or kMaxIf)
676
678{
679 static const char *altfunc = "Alt$(";
680 static const char *minfunc = "MinIf$(";
681 static const char *maxfunc = "MaxIf$(";
682 Int_t action = 0;
683 Int_t start = 0;
684
685 if ( strncmp(expression,altfunc,strlen(altfunc))==0
686 && expression[strlen(expression)-1]==')' ) {
688 start = strlen(altfunc);
689 }
690 if ( strncmp(expression,maxfunc,strlen(maxfunc))==0
691 && expression[strlen(expression)-1]==')' ) {
692 action = kMaxIf;
693 start = strlen(maxfunc);
694 }
695 if ( strncmp(expression,minfunc,strlen(minfunc))==0
696 && expression[strlen(expression)-1]==')' ) {
697 action = kMinIf;
698 start = strlen(minfunc);
699 }
700
701 if (action) {
702 TString full = expression;
705 int paran = 0;
706 int instr = 0;
707 int brack = 0;
708 for(unsigned int i=start;i<strlen(expression);++i) {
709 switch (expression[i]) {
710 case '(': paran++; break;
711 case ')': paran--; break;
712 case '"': instr = instr ? 0 : 1; break;
713 case '[': brack++; break;
714 case ']': brack--; break;
715 };
716 if (expression[i]==',' && paran==0 && instr==0 && brack==0) {
717 part1 = full( start, i-start );
718 part2 = full( i+1, full.Length() -1 - (i+1) );
719 break; // out of the for loop
720 }
721 }
722 if (part1.Length() && part2.Length()) {
723 TTreeFormula *primary = new TTreeFormula("primary",part1,fTree);
724 TTreeFormula *alternate = new TTreeFormula("alternate",part2,fTree);
725
726 short isstring = 0;
727
728 if (action == kAlternate) {
729 if (alternate->GetManager()->GetMultiplicity() != 0 ) {
730 Error("DefineAlternate","The 2nd arguments in %s can not be an array (%s,%d)!",
731 expression,alternate->GetTitle(),
732 alternate->GetManager()->GetMultiplicity());
733 return -1;
734 }
735
736 // Should check whether we have strings.
737 if (primary->IsString()) {
738 if (!alternate->IsString()) {
739 Error("DefineAlternate",
740 "The 2nd arguments in %s has to return the same type as the 1st argument (string)!",
741 expression);
742 return -1;
743 }
744 isstring = 1;
745 } else if (alternate->IsString()) {
746 Error("DefineAlternate",
747 "The 2nd arguments in %s has to return the same type as the 1st argument (numerical type)!",
748 expression);
749 return -1;
750 }
751 } else {
752 primary->GetManager()->Add( alternate );
753 primary->GetManager()->Sync();
754 if (primary->IsString() || alternate->IsString()) {
755 if (!alternate->IsString()) {
756 Error("DefineAlternate",
757 "The arguments of %s can not be strings!",
758 expression);
759 return -1;
760 }
761 }
762 }
763
765 fExpr[fNoper] = "";
767 ++fNoper;
768
770 return (Int_t)kAlias + isstring;
771 }
772 }
773 return 0;
774}
775
776////////////////////////////////////////////////////////////////////////////////
777/// Decompose 'expression' as pointing to something inside the leaf
778/// Returns:
779/// - -2 Error: some information is missing (message already printed)
780/// - -1 Error: Syntax is incorrect (message already printed)
781/// - 0
782/// - >0 the value returns is the action code.
783
785{
786 Int_t action = 0;
787
789 char *current;
790
791 char scratch[kMaxLen]; scratch[0] = '\0';
792 char work[kMaxLen]; work[0] = '\0';
793
794 const char *right = subExpression;
796
797 TBranch *branch = leaf ? leaf->GetBranch() : nullptr;
798 Long64_t readentry = fTree->GetTree()->GetReadEntry();
799 if (readentry < 0) readentry=0;
800
801 bool useLeafReferenceObject = false;
802 Int_t code = fNcodes-1;
803
804 // Make a check to prevent problem with some corrupted files (missing TStreamerInfo).
805 if (leaf && leaf->IsA()==TLeafElement::Class()) {
806 TBranchElement *br = nullptr;
807 if( branch->IsA() == TBranchElement::Class() )
808 {
810
811 if ( br->GetInfo() == nullptr ) {
812 Error("ParseWithLeaf","Missing StreamerInfo for %s. We will be unable to read!",
813 name.Data());
814 return -2;
815 }
816 }
817
818 TBranch *bmom = branch->GetMother();
819 if( bmom->IsA() == TBranchElement::Class() )
820 {
821 TBranchElement *mom = (TBranchElement*)br->GetMother();
822 if (mom!=br) {
823 if (mom->GetInfo()==nullptr) {
824 Error("ParseWithLeaf","Missing StreamerInfo for %s."
825 " We will be unable to read!",
826 mom->GetName());
827 return -2;
828 }
829 if ((mom->GetType()) < -1 && !mom->GetAddress()) {
830 Error("ParseWithLeaf", "Address not set when the type of the branch is negative for for %s. We will be unable to read!", mom->GetName());
831 return -2;
832 }
833 }
834 }
835 }
836
837 // We need to record the location in the list of leaves because
838 // the tree might actually be a chain and in that case the leaf will
839 // change from tree to tree!.
840
841 // Let's reconstruct the name of the leaf, including the possible friend alias
843 const char* alias = nullptr;
844 if (leaf) {
845 if (realtree) alias = realtree->GetFriendAlias(leaf->GetBranch()->GetTree());
846 if (!alias && realtree!=fTree) {
847 // Let's try on the chain
848 alias = fTree->GetFriendAlias(leaf->GetBranch()->GetTree());
849 }
850 }
852 if (alias) {
853 leafname_len = strlen(alias) + strlen(leaf->GetName()) + 1;
854 snprintf(scratch,kMaxLen-1,"%s.%s",alias,leaf->GetName()); // does not null-terminate if truncation happens
855 }
856 else if (leaf) {
857 leafname_len = strlen(leaf->GetName());
858 strlcpy(scratch,leaf->GetName(),kMaxLen); // null-terminates if truncation happens
859 }
860 if (leafname_len > kMaxLen - 1) {
861 Error("TTreeFormula",
862 "Length of leafname (%d) exceeds maximum allowed by the buffer (%d), formula will be truncated.",
863 leafname_len, kMaxLen - 1);
864 return -1;
865 }
866
867
869 if (leaf) {
870 tleaf = leaf->GetBranch()->GetTree();
871 fCodes[code] = tleaf->GetListOfLeaves()->IndexOf(leaf);
872 const char *mother_name = leaf->GetBranch()->GetMother()->GetName();
873 TString br_extended_name; // Could do ( strlen(mother_name)+strlen( leaf->GetBranch()->GetName() ) + 2 )
874 if (leaf->GetBranch()!=leaf->GetBranch()->GetMother()) {
875 if (mother_name[strlen(mother_name)-1]!='.') {
877 br_extended_name.Append('.');
878 }
879 }
880 br_extended_name.Append( leaf->GetBranch()->GetName() );
881 Ssiz_t dim = br_extended_name.First('[');
882 if (dim >= 0) br_extended_name.Remove(dim);
883
884 TNamed *named = new TNamed(scratch,br_extended_name.Data());
887 }
888
889 // If the leaf belongs to a friend tree which has an index, we might
890 // be in the case where some entry do not exist.
891 if (tleaf != realtree && tleaf->GetTreeIndex()) {
892 // reset the multiplicity
893 if (fMultiplicity >= 0) fMultiplicity = 1;
894 }
895
896 // Analyze the content of 'right'
897
898 // Try to find out the class (if any) of the object in the leaf.
899 TClass * cl = nullptr;
900 TFormLeafInfo *maininfo = nullptr;
901 TFormLeafInfo *previnfo = nullptr;
902 bool unwindCollection = false;
903 const static TClassRef stdStringClass = TClass::GetClass("string");
904
905 if (leaf==nullptr) {
906 TNamed *names = (TNamed*)fLeafNames.UncheckedAt(code);
907 fLeafNames.AddAt(nullptr,code);
909 fLeaves.AddAt(nullptr,code);
910
911 cl = what ? what->IsA() : TTree::Class();
914
915 delete names;
916 } else if (leaf->InheritsFrom(TLeafObject::Class()) ) {
917 TBranchObject *bobj = (TBranchObject*)leaf->GetBranch();
918 cl = TClass::GetClass(bobj->GetClassName());
919 } else if (leaf->InheritsFrom(TLeafElement::Class())) {
920 TBranchElement *branchEl = (TBranchElement *)leaf->GetBranch();
921 branchEl->SetupAddresses();
922 TStreamerInfo *info = branchEl->GetInfo();
923 TStreamerElement *element = nullptr;
924 Int_t type = branchEl->GetStreamerType();
925 switch(type) {
938 element = info->GetElement(branchEl->GetID());
939 if (element) cl = element->GetClassPointer();
940 }
941 break;
950 element = info->GetElement(branchEl->GetID());
951 if (element){
952 cl = element->GetClassPointer();
953 }
954 }
955 break;
956 case -1: {
957 cl = info->GetClass();
958 }
959 break;
960 }
961
962 // If we got a class object, we need to verify whether it is on a
963 // split TClonesArray sub branch.
964 if (cl && branchEl->GetBranchCount()) {
965 if (branchEl->GetType()==31) {
966 // This is inside a TClonesArray.
967
968 if (!element) {
969 Warning("ParseWithLeaf",
970 "Missing TStreamerElement in object in TClonesArray section");
971 return -2;
972 }
974
975 // The following code was commented out because in THIS case
976 // the dimension are actually handled by parsing the title and name of the leaf
977 // and branch (see a little further)
978 // The dimension needs to be handled!
979 // numberOfVarDim += RegisterDimensions(code,clonesinfo);
980
982
983 // We skip some cases because we can assume we have an object.
984 Int_t offset=0;
985 info->GetStreamerElement(element->GetName(),offset);
996 previnfo = new TFormLeafInfoPointer(cl,offset+branchEl->GetOffset(),element);
997 } else {
998 previnfo = new TFormLeafInfo(cl,offset+branchEl->GetOffset(),element);
999 }
1000 maininfo->fNext = previnfo;
1001 unwindCollection = true;
1002
1003 } else if (branchEl->GetType()==41) {
1004
1005 // This is inside a Collection
1006
1007 if (!element) {
1008 Warning("ParseWithLeaf","Missing TStreamerElement in object in Collection section");
1009 return -2;
1010 }
1011 // First we need to recover the collection.
1012 TBranchElement *count = branchEl->GetBranchCount();
1014 if ( count->GetID() >= 0 ) {
1016 count->GetInfo()->GetElement(count->GetID());
1017 TClass *collectionCl = collectionElement->GetClassPointer();
1018
1021 } else {
1025 }
1026
1027 // The following code was commented out because in THIS case
1028 // the dimension are actually handled by parsing the title and name of the leaf
1029 // and branch (see a little further)
1030 // The dimension needs to be handled!
1031 // numberOfVarDim += RegisterDimensions(code,clonesinfo);
1032
1034
1035 // We skip some cases because we can assume we have an object.
1036 Int_t offset=0;
1037 info->GetStreamerElement(element->GetName(),offset);
1048 previnfo = new TFormLeafInfoPointer(cl,offset+branchEl->GetOffset(),element);
1049 } else {
1050 previnfo = new TFormLeafInfo(cl,offset+branchEl->GetOffset(),element);
1051 }
1052 maininfo->fNext = previnfo;
1053 unwindCollection = true;
1054 }
1055 } else if ( branchEl->GetType()==3) {
1059 } else {
1060 clonesinfo = new TFormLeafInfoClones(cl, 0, true);
1061 // The dimension needs to be handled!
1063
1064 }
1067
1068 } else if (!useLeafCollectionObject && branchEl->GetType()==4) {
1069
1073 } else {
1074 collectioninfo = new TFormLeafInfoCollection(cl, 0, cl, true);
1075 // The dimension needs to be handled!
1077 }
1078
1081
1082 } else if (branchEl->GetStreamerType()==-1 && cl && cl->GetCollectionProxy()) {
1083
1085
1089
1090 } else {
1092 // The dimension needs to be handled!
1094
1097
1098 if (cl->GetCollectionProxy()->GetValueClass()!=nullptr &&
1099 cl->GetCollectionProxy()->GetValueClass()->GetCollectionProxy()!=nullptr) {
1100
1103
1104 fHasMultipleVarDim[code] = true;
1106 previnfo->fNext = multi;
1107 cl = cl->GetCollectionProxy()->GetValueClass();
1108 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1109 previnfo = multi->fNext;
1110
1111 }
1112 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1113 cl->GetCollectionProxy()->GetType()>0) {
1114
1115 previnfo->fNext =
1117 previnfo = previnfo->fNext;
1118 } else {
1119 // nothing to do
1120 }
1121 }
1122
1123 } else if (strlen(right)==0 && cl && element && final) {
1124
1125 TClass *elemCl = element->GetClassPointer();
1127 && elemCl && elemCl->GetCollectionProxy()
1128 && elemCl->GetCollectionProxy()->GetValueClass()
1129 && elemCl->GetCollectionProxy()->GetValueClass()->GetCollectionProxy()) {
1130
1132 new TFormLeafInfoCollection(cl, 0, elemCl);
1133
1134 // The dimension needs to be handled!
1136
1139
1142 elemCl->GetCollectionProxy()->GetValueClass(),
1144
1145 fHasMultipleVarDim[code] = true;
1147 previnfo->fNext = multi;
1148 cl = elemCl->GetCollectionProxy()->GetValueClass();
1149 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1150 previnfo = multi->fNext;
1151
1152 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1153 cl->GetCollectionProxy()->GetType()>0) {
1154
1155 previnfo->fNext =
1157 previnfo = previnfo->fNext;
1158 }
1159
1160 } else if (!useLeafCollectionObject
1161 && elemCl && elemCl->GetCollectionProxy()
1162 && elemCl->GetCollectionProxy()->GetValueClass()==nullptr
1163 && elemCl->GetCollectionProxy()->GetType()>0) {
1164
1165 // At this point we have an element which is inside a class (which is not
1166 // a collection) and this element of a collection of numerical type.
1167 // (Note: it is not possible to have more than one variable dimension
1168 // unless we were supporting variable size C-style array of collection).
1169
1171 new TFormLeafInfoCollection(cl, 0, elemCl);
1172
1173 // The dimension needs to be handled!
1175
1176 collectioninfo->fNext =
1177 new TFormLeafInfoNumerical(elemCl->GetCollectionProxy());
1178
1180 previnfo = maininfo->fNext;
1181
1182 } else if (!useLeafCollectionObject
1183 && elemCl && elemCl->GetCollectionProxy()) {
1184 if (elemCl->GetCollectionProxy()->GetValueClass()==TString::Class()) {
1185 right = "Data()";
1186 } else if (elemCl->GetCollectionProxy()->GetValueClass()==stdStringClass) {
1187 right = "c_str()";
1188 }
1189
1190 } else if (!element->IsaPointer()) {
1191
1194
1195 }
1196 }
1197 else if ( cl && cl->GetReferenceProxy() ) {
1198 if ( useLeafCollectionObject || fullExpression[0] == '@' || fullExpression[strlen(scratch)] == '@' ) {
1200 }
1201 else {
1202 if ( !maininfo ) {
1205 }
1207 for(Long64_t i=0; i<leaf->GetBranch()->GetEntries()-readentry; ++i) {
1208 auto res = R__LoadBranch(leaf->GetBranch(), readentry+i, fQuickLoad);
1209 if (res < 0) {
1210 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1211 continue;
1212 }
1213 void *refobj = maininfo->GetValuePointer(leaf,0);
1214 if (refobj) {
1215 cl = refproxy->GetValueClass(refobj);
1216 }
1217 if ( cl ) break;
1218 }
1219 if ( !cl ) {
1220 Error("ParseWithLeaf","Failed to access class type of reference target (%s)",element->GetName());
1221 return -1;
1222 }
1223 }
1224 }
1225 } else {
1226 // Regular/old TLeaf, there should not be anything afterward ...
1227 if (subExpression && subExpression[0]) {
1228 Error("ParseWithLeaf", "Found a numerical leaf but the name has trailing characters: \"%s\"", subExpression);
1229 return -1;
1230 }
1231 }
1232
1233 // Treat the dimension information in the leaf name, title and 2nd branch count
1235
1236 if (cl) {
1237 if (unwindCollection) {
1238 // So far we should get here only if we encounter a split collection of a class that contains
1239 // directly a collection.
1241
1242 if (!useLeafCollectionObject && cl && cl->GetCollectionProxy()) {
1245 fHasMultipleVarDim[code] = true;
1247 previnfo->fNext = multi;
1248
1249 multi->fNext = new TFormLeafInfoCollection(cl, 0, cl, false);
1250 previnfo = multi->fNext;
1251
1252 if (cl->GetCollectionProxy()->GetValueClass()==nullptr &&
1253 cl->GetCollectionProxy()->GetType()>0) {
1254
1255 previnfo->fNext =
1257 previnfo = previnfo->fNext;
1258 }
1259 } else if (!useLeafCollectionObject && cl == TClonesArray::Class()) {
1260
1263 fHasMultipleVarDim[code] = true;
1265 previnfo->fNext = multi;
1266
1267 multi->fNext = new TFormLeafInfoClones(cl, 0, false);
1268 previnfo = multi->fNext;
1269 }
1270 }
1271 Int_t offset=0;
1272 if (cl == TString::Class() && strcmp(right,"fData")==0) {
1273 // For backward compatibility replace TString::fData which no longer exist
1274 // by a call to TString::Data()
1275 right = "Data()";
1276 }
1277 Int_t nchname = strlen(right);
1278 TFormLeafInfo *leafinfo = nullptr;
1279 TStreamerElement* element = nullptr;
1280
1281 // Let see if the leaf was attempted to be casted.
1282 // Since there would have been something like
1283 // ((cast_class*)leafname)->.... we need to use
1284 // paran_level+1
1285 // Also we disable this functionality in case of TClonesArray
1286 // because it is not yet allowed to have 'inheritance' (or virtuality)
1287 // in play in a TClonesArray.
1288 {
1290 if (casted && cl != TClonesArray::Class()) {
1291 if ( ! casted->InheritsFrom(cl) ) {
1292 Error("ParseWithLeaf","%s does not inherit from %s. Casting not possible!",
1293 casted->GetName(),cl->GetName());
1294 return -2;
1295 }
1297 fHasCast = true;
1298 if (maininfo==nullptr) {
1300 }
1301 if (previnfo==nullptr) {
1303 } else {
1304 previnfo->fNext = leafinfo;
1306 }
1307 leafinfo = nullptr;
1308
1309 cl = casted;
1310 castqueue.AddAt(nullptr,paran_level);
1311 }
1312 }
1313 Int_t i;
1318 for (i=0, current = &(work[0]); i<=nchname;i++ ) {
1319 // We will treated the terminator as a token.
1320 if (right[i] == '(') {
1321 // Right now we do not allow nested parenthesis
1322 do {
1323 *current++ = right[i++];
1324 } while(right[i]!=')' && right[i]);
1325 *current++ = right[i];
1326 *current='\0';
1327 char *params = strchr(work,'(');
1328 if (params) {
1329 *params = 0; params++;
1330 } else params = (char *) ")";
1331 if (cl==nullptr) {
1332 Error("ParseWithLeaf","Can not call '%s' with a class",work);
1333 return -1;
1334 }
1335 if (!cl->HasDataMemberInfo() && !cl->GetCollectionProxy()) {
1336 Error("ParseWithLeaf","Class probably unavailable:%s",cl->GetName());
1337 return -2;
1338 }
1339 if (!useCollectionObject && cl == TClonesArray::Class()) {
1340 // We are not interested in the ClonesArray object but only
1341 // in its contents.
1342 // We need to retrieve the class of its content.
1343
1344 TBranch *clbranch = leaf->GetBranch();
1346 if (lres < 0) {
1347 Error("ParseWithLeaf", "Branch could not be loaded:%d", lres);
1348 return -2;
1349 }
1351 if (previnfo) clones = (TClonesArray*)previnfo->GetLocalValuePointer(leaf,0);
1352 else {
1353 bool top = (clbranch==((TBranchElement*)clbranch)->GetMother()
1354 || !leaf->IsOnTerminalBranch());
1356 if (leaf->IsA()==TLeafObject::Class()) {
1357 // in this case mother_cl is not really used
1358 mother_cl = cl;
1359 } else {
1360 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1361 }
1363
1364 // The dimension needs to be handled!
1366
1369
1370 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1371 }
1372 TClass * inside_cl = clones ? clones->GetClass() : nullptr;
1373 cl = inside_cl;
1374
1375 }
1376 else if (!useCollectionObject && cl && cl->GetCollectionProxy() ) {
1377
1378 // We are NEVER (for now!) interested in the ClonesArray object but only
1379 // in its contents.
1380 // We need to retrieve the class of its content.
1381
1382 if (previnfo==nullptr) {
1383
1384 bool top = (branch==((TBranchElement*)branch)->GetMother()
1385 || !leaf->IsOnTerminalBranch());
1386
1388 if (leaf->IsA()==TLeafObject::Class()) {
1389 // in this case mother_cl is not really used
1390 mother_cl = cl;
1391 } else {
1392 mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
1393 }
1394
1396 new TFormLeafInfoCollection(mother_cl, 0,cl,top);
1397 // The dimension needs to be handled!
1399
1402
1403 }
1404
1406 if (inside_cl) cl = inside_cl;
1407 else if (cl->GetCollectionProxy()->GetType()>0) {
1408 Warning("ParseWithLeaf","Can not call method on content of %s in %s\n",
1409 cl->GetName(),name.Data());
1410 return -2;
1411 }
1412 }
1413 TMethodCall *method = nullptr;
1414 if (cl==nullptr) {
1415 Error("ParseWithLeaf",
1416 "Could not discover the TClass corresponding to (%s)!",
1417 right);
1418 return -2;
1419 } else if (cl==TClonesArray::Class() && strcmp(work,"size")==0) {
1420 method = new TMethodCall(cl, "GetEntriesFast", "");
1421 } else if (cl->GetCollectionProxy() && strcmp(work,"size")==0) {
1422 if (maininfo==nullptr) {
1425
1426 bool top = (branch==((TBranchElement*)branch)->GetMother()
1427 || !leaf->IsOnTerminalBranch());
1429 }
1431 }
1433 cl = nullptr;
1434 } else {
1435 if (!cl->HasDataMemberInfo()) {
1436 Error("ParseWithLeaf",
1437 "Can not call method %s on class without dictionary (%s)!",
1438 right,cl->GetName());
1439 return -2;
1440 }
1441 method = new TMethodCall(cl, work, params);
1442 }
1443 if (method) {
1444 if (!method->GetMethod()) {
1445 Error("ParseWithLeaf","Unknown method:%s in %s",right,cl->GetName());
1446 return -1;
1447 }
1448 switch(method->ReturnType()) {
1449 case TMethodCall::kLong:
1451 cl = nullptr;
1452 break;
1455 cl = nullptr;
1456 break;
1459 // 1 will be replaced by -1 when we know how to use strlen
1460 numberOfVarDim += RegisterDimensions(code,1); //NOTE: changed from 0
1461 cl = nullptr;
1462 break;
1464 {
1467 }
1468 break;
1469 default:
1470 Error("DefineVariable","Method %s from %s has an impossible return type %d",
1471 work,cl->GetName(), (Int_t)method->ReturnType());
1472 return -2;
1473 }
1474 }
1475 if (maininfo==nullptr) {
1477 }
1478 if (previnfo==nullptr) {
1480 } else {
1481 previnfo->fNext = leafinfo;
1483 }
1484 leafinfo = nullptr;
1485 current = &(work[0]);
1486 *current = 0;
1488 prevUseReferenceObject = false;
1489 useCollectionObject = false;
1490
1491 if (cl && cl->GetCollectionProxy()) {
1492 if (numberOfVarDim>1) {
1493 Warning("ParseWithLeaf","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1494 cl->GetName());
1495 leafinfo = new TFormLeafInfo(cl,0,nullptr);
1496 useCollectionObject = true;
1497 } else if (numberOfVarDim==0) {
1499 leafinfo = new TFormLeafInfoCollection(cl,0,cl);
1501 } else if (numberOfVarDim==1) {
1503 leafinfo =
1505 (TStreamerElement*)nullptr,maininfo);
1506 previnfo->fNext = leafinfo;
1508 leafinfo = new TFormLeafInfoCollection(cl,0,cl);
1509
1510 fHasMultipleVarDim[code] = true;
1512 }
1513 previnfo->fNext = leafinfo;
1515 leafinfo = nullptr;
1516 }
1517 continue;
1518 } else if (right[i] == ')') {
1519 // We should have the end of a cast operator. Let's introduce a TFormLeafCast
1520 // in the chain.
1521 TClass * casted = (TClass*) ((int(--paran_level)>=0) ? castqueue.At(paran_level) : nullptr);
1522 if (casted) {
1524 fHasCast = true;
1525
1526 if (maininfo==nullptr) {
1528 }
1529 if (previnfo==nullptr) {
1531 } else {
1532 previnfo->fNext = leafinfo;
1534 }
1535 leafinfo = nullptr;
1536 current = &(work[0]);
1537 *current = 0;
1538
1539 cl = casted;
1540 continue;
1541
1542 }
1543 } else if (i > 0 && (right[i] == '.' || right[i] == '[' || right[i] == '\0') ) {
1544 // A delimiter happened let's see if what we have seen
1545 // so far does point to a data member.
1546 bool needClass = true;
1547 *current = '\0';
1548
1549 // skip it all if there is nothing to look at
1550 if (strlen(work)==0) continue;
1551
1554 if (work[0]=='@') {
1555 useReferenceObject = true;
1556 useCollectionObject = true;
1557 Int_t l = 0;
1558 for(l=0;work[l+1]!=0;++l) work[l] = work[l+1];
1559 work[l] = '\0';
1560 } else if (work[strlen(work)-1]=='@') {
1561 useReferenceObject = true;
1562 useCollectionObject = true;
1563 work[strlen(work)-1] = '\0';
1564 } else {
1565 useReferenceObject = false;
1566 useCollectionObject = false;
1567 }
1568
1569 bool mustderef = false;
1570 if ( !prevUseReferenceObject && cl && cl->GetReferenceProxy() ) {
1571 auto res = R__LoadBranch(leaf->GetBranch(), readentry, fQuickLoad);
1572 if (res < 0) {
1573 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1574 return -2;
1575 }
1576 if ( !maininfo ) {
1578 if ( cl->GetReferenceProxy()->HasCounter() ) {
1580 }
1581 prevUseReferenceObject = false;
1582 } else {
1583 previnfo->fNext = new TFormLeafInfoReference(cl, element, offset);
1584 previnfo = previnfo->fNext;
1585 }
1587 cl = nullptr;
1588 for(Long64_t entry=0; entry<leaf->GetBranch()->GetEntries()-readentry; ++entry) {
1589 auto eres = R__LoadBranch(leaf->GetBranch(), readentry+i, fQuickLoad);
1590 if (eres < 0) {
1591 Error("ParseWithLeaf", "Branch could not be loaded:%d", eres);
1592 return -2;
1593 }
1594 void *refobj = maininfo->GetValuePointer(leaf,0);
1595 if (refobj) {
1596 cl = refproxy->GetValueClass(refobj);
1597 }
1598 if ( cl ) break;
1599 }
1600 needClass = false;
1601 mustderef = true;
1602 }
1603 else if (!prevUseCollectionObject && cl == TClonesArray::Class()) {
1604 // We are not interested in the ClonesArray object but only
1605 // in its contents.
1606 // We need to retrieve the class of its content.
1607
1608 TBranch *clbranch = leaf->GetBranch();
1610 if (res < 0) {
1611 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1612 return -2;
1613 }
1615 if (maininfo) {
1616 clones = (TClonesArray*)maininfo->GetValuePointer(leaf,0);
1617 } else {
1618 // we have a unsplit TClonesArray leaves
1619 // or we did not yet match any of the sub-branches!
1620
1622 if (leaf->IsA()==TLeafObject::Class()) {
1623 // in this case mother_cl is not really used
1624 mother_cl = cl;
1625 } else {
1626 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1627 }
1628
1630 // The dimension needs to be handled!
1632
1633 mustderef = true;
1636
1637 if (clbranch->GetListOfBranches()->GetLast()>=0) {
1638 if (clbranch->IsA() != TBranchElement::Class()) {
1639 Error("ParseWithLeaf","Unimplemented usage of ClonesArray");
1640 return -2;
1641 }
1642 //clbranch = ((TBranchElement*)clbranch)->GetMother();
1643 clones = (TClonesArray*)((TBranchElement*)clbranch)->GetObject();
1644 } else
1645 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1646 }
1647 // NOTE clones can be zero!
1648 if (clones==nullptr) {
1649 Warning("ParseWithLeaf",
1650 "TClonesArray object was not retrievable for %s!",
1651 name.Data());
1652 return -1;
1653 }
1654 TClass * inside_cl = clones->GetClass();
1655#if 1
1656 cl = inside_cl;
1657#else
1658/* Maybe we should make those test lead to warning messages */
1659 if (1 || inside_cl) cl = inside_cl;
1660 // if inside_cl is nul ... we have a problem of inconsistency :(
1661 if (0 && strlen(work)==0) {
1662 // However in this case we have NO content :(
1663 // so let get the number of objects
1664 //strcpy(work,"fLast");
1665 }
1666#endif
1667 } else if (!prevUseCollectionObject && cl && cl->GetCollectionProxy() ) {
1668
1669 // We are NEVER interested in the Collection object but only
1670 // in its contents.
1671 // We need to retrieve the class of its content.
1672
1673 TBranch *clbranch = leaf->GetBranch();
1675 if (res < 0) {
1676 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1677 return -2;
1678 }
1679
1680 if (maininfo==nullptr) {
1681
1682 // we have a unsplit Collection leaf
1683 // or we did not yet match any of the sub-branches!
1684
1686 if (leaf->IsA()==TLeafObject::Class()) {
1687 // in this case mother_cl is not really used
1688 mother_cl = cl;
1689 } else {
1690 mother_cl = ((TBranchElement*)clbranch)->GetInfo()->GetClass();
1691 }
1692
1695 // The dimension needs to be handled!
1697
1698 mustderef = true;
1701
1702 } //else if (clbranch->GetStreamerType()==0) {
1703
1704 //}
1705
1707
1708 if (!inside_cl) {
1709 Error("ParseWithLeaf","Could you not find the inner class for %s with coll type = %d",
1710 cl->GetName(),cl->GetCollectionProxy()->GetType());
1711 }
1712 if (!inside_cl && cl->GetCollectionProxy()->GetType() > 0) {
1713 Warning("ParseWithLeaf","No data member in content of %s in %s\n",
1714 cl->GetName(),name.Data());
1715 }
1716 cl = inside_cl;
1717 // if inside_cl is nul ... we have a problem of inconsistency.
1718 }
1719
1720 if (!cl) {
1721 if (leaf) leaf->GetBranch()->Print();
1722 Warning("ParseWithLeaf","Missing class for %s!",name.Data());
1723 } else {
1724 element = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1725 }
1726
1728 // We allow for looking for a data member inside a class inside
1729 // a TClonesArray without mentioning the TClonesArrays variable name
1730 TIter next( cl->GetStreamerInfo()->GetElements() );
1732 while ((curelem = (TStreamerElement*)next())) {
1733 if (curelem->GetClassPointer() == TClonesArray::Class()) {
1734 Int_t clones_offset = 0;
1735 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),clones_offset);
1739 auto res = R__LoadBranch(leaf->GetBranch(),readentry,fQuickLoad);
1740 if (res < 0) {
1741 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1742 return -2;
1743 }
1744
1745 if (previnfo) {
1746 previnfo->fNext = clonesinfo;
1747 clones = (TClonesArray*)maininfo->GetValuePointer(leaf,0);
1748 previnfo->fNext = nullptr;
1749 } else {
1750 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leaf,0);
1751 }
1752
1753 TClass *sub_cl = clones->GetClass();
1754 if (sub_cl) element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1755 delete clonesinfo;
1756
1757 if (element) {
1760 if (maininfo==nullptr) maininfo = leafinfo;
1761 if (previnfo==nullptr) previnfo = leafinfo;
1762 else {
1763 previnfo->fNext = leafinfo;
1765 }
1766 leafinfo = nullptr;
1767 cl = sub_cl;
1768 break;
1769 }
1770 } else if (curelem->GetClassPointer() && curelem->GetClassPointer()->GetCollectionProxy()) {
1771
1772 Int_t coll_offset = 0;
1773 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),coll_offset);
1774
1775 TClass *sub_cl =
1776 curelem->GetClassPointer()->GetCollectionProxy()->GetValueClass();
1777 if (sub_cl) {
1778 element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(work,offset);
1779 }
1780 if (element) {
1781 if (numberOfVarDim>1) {
1782 Warning("ParseWithLeaf","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1783 curelem->GetName());
1785 useCollectionObject = true;
1786 } else if (numberOfVarDim==1) {
1788 leafinfo =
1791 fHasMultipleVarDim[code] = true;
1794 } else {
1797 }
1798 if (maininfo==nullptr) maininfo = leafinfo;
1799 if (previnfo==nullptr) previnfo = leafinfo;
1800 else {
1801 previnfo->fNext = leafinfo;
1803 }
1804 if (leafinfo->fNext) {
1805 previnfo = leafinfo->fNext;
1806 }
1807 leafinfo = nullptr;
1808 cl = sub_cl;
1809 break;
1810 }
1811 }
1812 }
1813
1814 }
1815
1816 if (element) {
1817 Int_t type = element->GetNewType();
1818 if (type<60 && type!=0) {
1819 // This is a basic type ...
1820 if (numberOfVarDim>=1 && type>40) {
1821 // We have a variable array within a variable array!
1823 fHasMultipleVarDim[code] = true;
1824 } else {
1825 if (leafinfo && type<=40 ) {
1826 leafinfo->AddOffset(offset,element);
1827 } else {
1829 }
1830 }
1831 } else {
1832 bool object = false;
1833 bool pointer = false;
1834 bool objarr = false;
1835 switch(type) {
1846 pointer = true;
1847 break;
1849 case TStreamerInfo::kAny :
1855 object = true;
1856 break;
1860 objarr = true;
1861 break;
1864 // Unsupported case.
1865 Error("ParseWithLeaf",
1866 "%s is a datamember of %s BUT is not yet of a supported type (%d)",
1867 right,cl ? cl->GetName() : "unknown class",type);
1868 return -2;
1869 default:
1870 // Unknown and Unsupported case.
1871 Error("ParseWithLeaf",
1872 "%s is a datamember of %s BUT is not of a unknown type (%d)",
1873 right,cl ? cl->GetName() : "unknown class",type);
1874 return -2;
1875 }
1876
1877 if (object && !useCollectionObject &&
1878 ( element->GetClassPointer() == TClonesArray::Class()
1879 || element->GetClassPointer()->GetCollectionProxy() ) )
1880 {
1881 object = false;
1882 }
1883 if (object && leafinfo) {
1884 leafinfo->AddOffset(offset,element);
1885 } else if (objarr) {
1886 // This is an embedded array of objects. We can not increase the offset.
1888 mustderef = true;
1889 } else {
1890
1891 if (!useCollectionObject && element->GetClassPointer() == TClonesArray::Class()) {
1892
1894 mustderef = true;
1895
1896 } else if (!useCollectionObject && element->GetClassPointer()
1897 && element->GetClassPointer()->GetCollectionProxy()) {
1898
1899 mustderef = true;
1900 if (numberOfVarDim>1) {
1901 Warning("ParseWithLeaf","TTreeFormula support only 2 level of variables size collections. Assuming '@' notation for the collection %s.",
1902 element->GetName());
1904 useCollectionObject = true;
1905 } else if (numberOfVarDim==1) {
1907 leafinfo =
1909
1910 fHasMultipleVarDim[code] = true;
1911 //numberOfVarDim += RegisterDimensions(code,leafinfo);
1912 //cl = cl->GetCollectionProxy()->GetValueClass();
1913
1914 //if (maininfo==0) maininfo = leafinfo;
1915 //if (previnfo==0) previnfo = leafinfo;
1916 //else {
1917 // previnfo->fNext = leafinfo;
1918 // previnfo = leafinfo;
1919 //}
1921 if (element->GetClassPointer()->GetCollectionProxy()->GetValueClass()==nullptr) {
1923 element->GetClassPointer()->GetCollectionProxy());
1924 if (leafinfo->fNext) leafinfo->fNext->fNext = info;
1925 else leafinfo->fNext = info;
1926 }
1927 } else {
1929
1930 TClass *elemCl = element->GetClassPointer();
1931 TClass *valueCl = elemCl->GetCollectionProxy()->GetValueClass();
1932 if (!maininfo) maininfo = leafinfo;
1933
1934 if (valueCl!=nullptr && valueCl->GetCollectionProxy()!=nullptr) {
1935
1937 if (previnfo==nullptr) previnfo = leafinfo;
1938 else {
1939 previnfo->fNext = leafinfo;
1941 }
1943 elemCl->GetCollectionProxy()->GetValueClass(),maininfo);
1944 //numberOfVarDim += RegisterDimensions(code,previnfo->fNext);
1945 fHasMultipleVarDim[code] = true;
1946 //previnfo = previnfo->fNext;
1948 valueCl);
1949 elemCl = valueCl;
1950 }
1951 if (elemCl->GetCollectionProxy() &&
1952 elemCl->GetCollectionProxy()->GetValueClass()==nullptr) {
1953 TFormLeafInfo *info = new TFormLeafInfoNumerical(elemCl->GetCollectionProxy());
1954 if (leafinfo->fNext) leafinfo->fNext->fNext = info;
1955 else leafinfo->fNext = info;
1956 }
1957 }
1958 } else if ( (object || pointer) && !useReferenceObject && element->GetClassPointer()->GetReferenceProxy() ) {
1959 TClass* c = element->GetClassPointer();
1960 auto res = R__LoadBranch(leaf->GetBranch(),readentry,fQuickLoad);
1961 if (res < 0) {
1962 Error("ParseWithLeaf", "Branch could not be loaded:%d", res);
1963 return -2;
1964 }
1965 if ( object ) {
1967 }
1968 else {
1970 leafinfo->fNext = new TFormLeafInfoReference(c, element, 0);
1971 }
1972 //if ( c->GetReferenceProxy()->HasCounter() ) {
1973 // numberOfVarDim += RegisterDimensions(code,-1);
1974 //}
1975 prevUseReferenceObject = false;
1976 needClass = false;
1977 mustderef = true;
1978 } else if (pointer) {
1979 // this is a pointer to be followed.
1981 mustderef = true;
1982 } else {
1983 // this is an embedded object.
1984 R__ASSERT(object);
1986 }
1987 }
1988 }
1989 } else {
1990 if (cl) Error("ParseWithLeaf","%s is not a datamember of %s",work,cl->GetName());
1991 // no else, we warned earlier that the class was missing.
1992 return -1;
1993 }
1994
1995 numberOfVarDim += RegisterDimensions(code,leafinfo,maininfo,useCollectionObject); // Note or useCollectionObject||prevUseColectionObject
1996 if (maininfo==nullptr) {
1998 }
1999 if (previnfo==nullptr) {
2001 } else if (previnfo!=leafinfo) {
2002 previnfo->fNext = leafinfo;
2004 }
2005 while (previnfo->fNext) previnfo = previnfo->fNext;
2006
2007 if ( right[i] != '\0' ) {
2008 if ( !needClass && mustderef ) {
2009 maininfo->SetBranch(leaf->GetBranch());
2010 char *ptr = (char*)maininfo->GetValuePointer(leaf,0);
2012 if ( !maininfo->IsReference() ) {
2013 for( TFormLeafInfo* inf = maininfo->fNext; inf; inf = inf->fNext ) {
2014 if ( inf->IsReference() ) {
2016 }
2017 }
2018 }
2019 else {
2021 }
2022 if ( refInfo ) {
2023 cl = refInfo->GetValueClass(ptr);
2024 if ( !cl ) {
2025 Error("ParseWithLeaf","Failed to access class type of reference target (%s)",element->GetName());
2026 return -1;
2027 }
2028 element = ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(work,offset);
2029 }
2030 else {
2031 Error("ParseWithLeaf","Failed to access class type of reference target (%s)",element->GetName());
2032 return -1;
2033 }
2034 }
2035 else if ( needClass ) {
2036 cl = element->GetClassPointer();
2037 }
2038 }
2039 if (mustderef) leafinfo = nullptr;
2040 current = &(work[0]);
2041 *current = 0;
2042 R__ASSERT(right[i] != '['); // We are supposed to have removed all dimensions already!
2043
2044 if (cl == TString::Class() && strcmp(right+i+1,"fData") == 0) {
2045 // For backward compatibility replace TString::fData which no longer exist
2046 // by a call to TString::Data()
2047 right = ".Data()";
2048 i = 0;
2049 nchname = strlen(right);
2050 }
2051
2052 } else
2053 *current++ = right[i];
2054 }
2055 if (maininfo) {
2057 if (leaf) fLookupType[code] = kDataMember;
2058 else fLookupType[code] = kTreeMember;
2059 }
2060 }
2061
2062 if (strlen(work)!=0) {
2063 // We have something left to analyze. Let's make this an error case!
2064 return -1;
2065 }
2066
2067 TClass *objClass = EvalClass(code);
2068 if (objClass && !useLeafCollectionObject && objClass->GetCollectionProxy() && objClass->GetCollectionProxy()->GetValueClass()) {
2069 TFormLeafInfo *last = nullptr;
2070 if ( SwitchToFormLeafInfo(code) ) {
2071
2072 last = (TFormLeafInfo*)fDataMembers.At(code);
2073
2074 if (!last) return action;
2075 while (last->fNext) { last = last->fNext; }
2076
2077 }
2078 if (last && last->GetClass() != objClass) {
2080 if (leaf->IsA()==TLeafObject::Class()) {
2081 // in this case mother_cl is not really used
2082 mother_cl = cl;
2083 } else {
2084 mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
2085 }
2086
2088 // The dimension needs to be handled!
2090 last->fNext = collectioninfo;
2091 }
2092 numberOfVarDim += RegisterDimensions(code,1); //NOTE: changed from 0
2093 objClass = objClass->GetCollectionProxy()->GetValueClass();
2094 }
2096
2097 TFormLeafInfo *last = nullptr;
2098 if ( SwitchToFormLeafInfo(code) ) {
2099
2100 last = (TFormLeafInfo*)fDataMembers.At(code);
2101
2102 if (!last) return action;
2103 while (last->fNext) { last = last->fNext; }
2104
2105 }
2106 const char *funcname = nullptr;
2107 if (objClass == TString::Class()) {
2108 funcname = "Data";
2109 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2110 } else if (objClass == stdStringClass) {
2111 funcname = "c_str";
2112 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2113 }
2114 if (funcname) {
2116 if (last) {
2118 } else {
2120 if (leaf) fLookupType[code] = kDataMember;
2121 else fLookupType[code] = kTreeMember;
2122 }
2123 }
2124 return kDefinedString;
2125 }
2126
2127 if (objClass) {
2128 TMethodCall *method = new TMethodCall(objClass, "AsDouble", "");
2129 if (method->IsValid()
2130 && (method->ReturnType() == TMethodCall::kLong || method->ReturnType() == TMethodCall::kDouble)) {
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) {
2144 } else {
2146 if (leaf) fLookupType[code] = kDataMember;
2147 else fLookupType[code] = kTreeMember;
2148 }
2149
2150 return kDefinedVariable;
2151 }
2152 delete method;
2153 method = new TMethodCall(objClass, "AsString", "");
2154 if (method->IsValid()
2155 && method->ReturnType() == TMethodCall::kString) {
2156
2157 TFormLeafInfo *last = nullptr;
2158 if (SwitchToFormLeafInfo(code)) {
2159 last = (TFormLeafInfo*)fDataMembers.At(code);
2160 // Improbable case
2161 if (!last) {
2162 delete method;
2163 return action;
2164 }
2165 while (last->fNext) { last = last->fNext; }
2166 }
2167 if (last) {
2169 } else {
2171 if (leaf) fLookupType[code] = kDataMember;
2172 else fLookupType[code] = kTreeMember;
2173 }
2174
2175 //tobetested: numberOfVarDim += RegisterDimensions(code,1,0); // Register the dim of the implied char*
2176 return kDefinedString;
2177 }
2178 if (method->IsValid()
2179 && method->ReturnType() == TMethodCall::kOther) {
2180
2182 if ((rcl == TString::Class() || rcl == stdStringClass) ) {
2183
2184 TFormLeafInfo *last = nullptr;
2185 if (SwitchToFormLeafInfo(code)) {
2186 last = (TFormLeafInfo*)fDataMembers.At(code);
2187 // Improbable case
2188 if (!last) {
2189 delete method;
2190 return action;
2191 }
2192 while (last->fNext) { last = last->fNext; }
2193 }
2194 if (last) {
2196 last = last->fNext;
2197 } else {
2199 fDataMembers.AddAtAndExpand(last,code);
2200 if (leaf) fLookupType[code] = kDataMember;
2201 else fLookupType[code] = kTreeMember;
2202 }
2203
2204 objClass = rcl;
2205
2206 const char *funcname = nullptr;
2207 if (objClass == TString::Class()) {
2208 funcname = "Data";
2209 } else if (objClass == stdStringClass) {
2210 funcname = "c_str";
2211 }
2212 if (funcname) {
2213 method = new TMethodCall(objClass, funcname, "");
2215 }
2216 return kDefinedString;
2217 }
2218 }
2219 delete method;
2220 }
2221
2222 return action;
2223}
2224
2225////////////////////////////////////////////////////////////////////////////////
2226/// Look for the leaf corresponding to the start of expression.
2227/// It returns the corresponding leaf if any.
2228/// It also modify the following arguments:
2229///
2230/// - leftover: contain from expression that was not used to determine the leaf
2231/// - final:
2232/// * paran_level: number of un-matched open parenthesis
2233/// * cast_queue: list of cast to be done
2234/// * aliases: list of aliases used
2235/// - Return <0 in case of failure
2236///
2237/// - Return 0 if a leaf has been found
2238/// - Return 2 if info about the TTree itself has been requested.
2239
2240Int_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)
2241{
2242 // Later on we will need to read one entry, let's make sure
2243 // it is a real entry.
2244 if (fTree->GetTree()==nullptr) {
2245 fTree->LoadTree(0);
2246 if (fTree->GetTree()==nullptr) return -1;
2247 }
2248 Long64_t readentry = fTree->GetTree()->GetReadEntry();
2249 if (readentry < 0) readentry=0;
2250 const char *cname = expression;
2251 char first[kMaxLen]; first[0] = '\0';
2252 char second[kMaxLen*2]; second[0] = '\0';
2253 char right[kMaxLen*2]; right[0] = '\0';
2254 char work[kMaxLen]; work[0] = '\0';
2255 char left[kMaxLen]; left[0] = '\0';
2256 char scratch[kMaxLen*5];
2257 char scratch2[kMaxLen*5];
2258 std::string currentname;
2259 Int_t previousdot = 0;
2260 char *current;
2261 TLeaf *tmp_leaf=nullptr;
2262 TBranch *branch=nullptr, *tmp_branch=nullptr;
2264 Int_t i;
2265 bool foundAtSign = false;
2266 bool startWithParan = false;
2267
2268 for (i=0, current = &(work[0]); i<=nchname && !final;i++ ) {
2269 // We will treated the terminator as a token.
2270 *current++ = cname[i];
2271
2272 if (cname[i] == '(') {
2273 ++paran_level;
2274
2275 if (current==work+1) {
2276 // If the expression starts with a parenthesis, we are likely
2277 // to have a cast operator inside.
2278 startWithParan = true;
2279 current--;
2280 }
2281 continue;
2282 //i++;
2283 //while( cname[i]!=')' && cname[i] ) {
2284 // *current++ = cname[i++];
2285 //}
2286 //*current++ = cname[i];
2287 ////*current = 0;
2288 //continue;
2289 }
2290 if (cname[i] == ')') {
2291 if (paran_level==0) {
2292 Error("FindLeafForExpression","Unmatched parenthesis in %s",fullExpression);
2293 return -1;
2294 }
2295 paran_level--;
2296
2297 if (startWithParan) {
2298 startWithParan = false; // the next match wont be against the starting parenthesis.
2299
2300 // Let's see if work is a classname and thus we have a cast.
2301 *(--current) = 0;
2302 TString cast_name = gInterpreter->TypeName(work);
2304 if (cast_cl) {
2305 // We must have a cast
2306 castqueue.AddAtAndExpand(cast_cl,paran_level);
2307 current = &(work[0]);
2308 *current = 0;
2309 // Warning("FindLeafForExpression","Found cast to %s",cast_fullExpression);
2310 continue;
2311 } else if (gROOT->GetType(cast_name)) {
2312 // We reset work
2313 current = &(work[0]);
2314 *current = 0;
2315 Warning("FindLeafForExpression",
2316 "Casting to primary types like \"%s\" is not supported yet",cast_name.Data());
2317 continue;
2318 }
2319 *(current++)=')';
2320 }
2321
2322 *current='\0';
2323 char *params = strchr(work,'(');
2324 if (params) {
2325 *params = 0; params++;
2326
2327 if (branch && !leaf) {
2328 // We have a branch but not a leaf. We are likely to have found
2329 // the top of split branch.
2330 if (BranchHasMethod(nullptr, branch, work, params, readentry)) {
2331 //fprintf(stderr, "Does have a method %s for %s.\n", work, branch->GetName());
2332 }
2333 }
2334
2335 // What we have so far might be a member function of one of the
2336 // leaves that are not split (for example "GetNtrack" for the Event class).
2338 TLeaf* leafcur = nullptr;
2339 while (!leaf && (leafcur = (TLeaf*) next())) {
2340 TBranch* br = leafcur->GetBranch();
2341 bool yes = BranchHasMethod(leafcur, br, work, params, readentry);
2342 if (yes) {
2343 leaf = leafcur;
2344 //fprintf(stderr, "Does have a method %s for %s found in leafcur %s.\n", work, leafcur->GetBranch()->GetName(), leafcur->GetName());
2345 }
2346 }
2347 if (!leaf) {
2348 // Check for an alias.
2349 if (strlen(left) && left[strlen(left)-1]=='.') left[strlen(left)-1]=0;
2350 const char *aliasValue = fTree->GetAlias(left);
2351 if (aliasValue && strcspn(aliasValue, "()[]+*/-%&!=<>|") == strlen(aliasValue) &&
2353 // First check whether we are using this alias recursively (this would
2354 // lead to an infinite recursion).
2355 if (find(aliasUsed.begin(),
2356 aliasUsed.end(),
2357 left) != aliasUsed.end()) {
2358 Error("FindLeafForExpression",
2359 "The substitution of the branch alias \"%s\" by \"%s\" in \"%s\" failed\n"\
2360 "\tbecause \"%s\" is used [recursively] in its own definition!",
2361 left,aliasValue,fullExpression,left);
2362 return -3;
2363 }
2364 aliasUsed.push_back(left);
2366 newExpression += (cname+strlen(left));
2369 if (res<0) {
2370 Error("FindLeafForExpression",
2371 "The substitution of the alias \"%s\" by \"%s\" failed.",left,aliasValue);
2372 return -3;
2373 }
2374 return res;
2375 }
2376
2377 // This is actually not really any error, we probably received something
2378 // like "abs(some_val)", let ROOT::v5::TFormula decompose it first.
2379 return -1;
2380 }
2381 // if (!leaf->InheritsFrom(TLeafObject::Class()) ) {
2382 // If the leaf that we found so far is not a TLeafObject then there is
2383 // nothing we would be able to do.
2384 // Error("FindLeafForExpression","Need a TLeafObject to call a function!");
2385 // return -1;
2386 //}
2387 // We need to recover the info not used.
2388 strlcpy(right,work,2*kMaxLen);
2389 strncat(right,"(",2*kMaxLen-1-strlen(right));
2390 strncat(right,params,2*kMaxLen-1-strlen(right));
2391 final = true;
2392
2393 // Record in 'i' what we consumed
2394 i += 2; // open and close parentheses
2395
2396 // we reset work
2397 current = &(work[0]);
2398 *current = 0;
2399 break;
2400 }
2401 }
2402 if (cname[i] == '.' || cname[i] == '\0' || cname[i] == ')') {
2403 // A delimiter happened let's see if what we have seen
2404 // so far does point to a leaf.
2405 *current = '\0';
2406
2407 Int_t len = strlen(work);
2408 if (work[0]=='@') {
2409 foundAtSign = true;
2410 Int_t l = 0;
2411 for(l=0;work[l+1]!=0;++l) work[l] = work[l+1];
2412 work[l] = '\0';
2413 --current;
2414 } else if (len>=2 && work[len-2]=='@') {
2415 foundAtSign = true;
2416 work[len-2] = cname[i];
2417 work[len-1] = '\0';
2418 --current;
2419 } else {
2420 foundAtSign = false;
2421 }
2422
2423 if (left[0]==0) strlcpy(left,work,kMaxLen);
2424 if (!leaf && !branch) {
2425 // So far, we have not found a matching leaf or branch.
2426 strlcpy(first,work,kMaxLen);
2427
2428 std::string treename(first);
2429 if (!treename.empty() && treename[treename.size()-1]=='.') {
2430 treename.erase(treename.size()-1);
2431 }
2432 if (treename== "This" /* || treename == fTree->GetName() */ ) {
2433 // Request info about the TTree object itself,
2437 if (cname[i]) leftover = &(cname[i+1]);
2438 return 2;
2439 }
2440 // The following would allow to access the friend by name
2441 // however, it would also prevent the access of the leaves
2442 // within the friend. We could use the '@' notation here
2443 // however this would not be aesthetically pleasing :(
2444 // What we need to do, is add the ability to look ahead to
2445 // the next 'token' to decide whether we to access the tree
2446 // or its leaf.
2447 //} else {
2448 // TTree *tfriend = fTree->GetFriend(treename.c_str());
2449 // TTree *realtree = fTree->GetTree();
2450 // if (!tfriend && realtree != fTree){
2451 // // If it is a chain and we did not find a friend,
2452 // // let's try with the internal tree.
2453 // tfriend = realtree->GetFriend(treename.c_str());
2454 // }
2455 // if (tfriend) {
2456 // TNamed *named = new TNamed(treename.c_str(),tfriend->GetName());
2457 // fLeafNames.AddAtAndExpand(named,fNcodes);
2458 // fLeaves.AddAtAndExpand(tfriend,fNcodes);
2459 // if (cname[i]) leftover = &(cname[i+1]);
2460 // return 2;
2461 // }
2462 //}
2463
2464 branch = fTree->FindBranch(first);
2465 leaf = fTree->FindLeaf(first);
2466
2467 // Now look with the delimiter removed (we looked with it first
2468 // because a dot is allowed at the end of some branches).
2469 if (cname[i]) first[strlen(first)-1]='\0';
2470 if (!branch) branch = fTree->FindBranch(first);
2471 if (!leaf) leaf = fTree->FindLeaf(first);
2472 TClass* cl = nullptr;
2473 if ( branch && branch->InheritsFrom(TBranchElement::Class()) ) {
2474 int offset=0;
2476 TStreamerInfo* info = bElt->GetInfo();
2477 TStreamerElement* element = info ? info->GetStreamerElement(first,offset) : nullptr;
2478 if (element) cl = element->GetClassPointer();
2479 if ( cl && !cl->GetReferenceProxy() ) cl = nullptr;
2480 }
2481 if ( cl ) { // We have a reference class here....
2482 final = true;
2484 // we reset work
2485 current = &(work[0]);
2486 *current = 0;
2487 }
2488 else if (branch && (foundAtSign || cname[i] != 0) ) {
2489 // Since we found a branch and there is more information in the name,
2490 // we do NOT look at the 'IsOnTerminalBranch' status of the leaf
2491 // we found ... yet!
2492
2493 if (leaf==nullptr) {
2494 // Note we do not know (yet?) what (if anything) to do
2495 // for a TBranchObject branch.
2496 if (branch->InheritsFrom(TBranchElement::Class()) ) {
2497 Int_t type = ((TBranchElement*)branch)->GetType();
2498 if ( type == 3 || type ==4) {
2499 // We have a Collection branch.
2500 leaf = (TLeaf*)branch->GetListOfLeaves()->At(0);
2501 if (foundAtSign) {
2503 foundAtSign = false;
2504 current = &(work[0]);
2505 *current = 0;
2506 ++i;
2507 break;
2508 }
2509 }
2510 }
2511 }
2512
2513 // we reset work
2515 foundAtSign = false;
2516 current = &(work[0]);
2517 *current = 0;
2518 } else if (leaf || branch) {
2519 if (leaf && branch) {
2520 // We found both a leaf and branch matching the request name
2521 // let's see which one is the proper one to use! (On annoying case
2522 // is that where the same name is repeated ( varname.varname )
2523
2524 // We always give priority to the branch
2525 // leaf = 0;
2526 }
2527 if (leaf && leaf->IsOnTerminalBranch()) {
2528 // This is a non-object leaf, it should NOT be specified more except for
2529 // dimensions.
2530 final = true;
2531 }
2532 // we reset work
2533 current = &(work[0]);
2534 *current = 0;
2535 } else {
2536 // What we have so far might be a data member of one of the
2537 // leaves that are not split (for example "fNtrack" for the Event class.
2539 if (leafcur) {
2540 leaf = leafcur;
2541 branch = leaf->GetBranch();
2542 if (leaf->IsOnTerminalBranch()) {
2543 final = true;
2544 strlcpy(right,first,kMaxLen);
2545 //We need to put the delimiter back!
2546 if (foundAtSign) strncat(right,"@",2*kMaxLen-1-strlen(right));
2547 if (cname[i]=='.') strncat(right,".",2*kMaxLen-1-strlen(right));
2548
2549 // We reset work
2550 current = &(work[0]);
2551 *current = 0;
2552 };
2553 } else if (cname[i] == '.') {
2554 // If we have a branch that match a name preceded by a dot
2555 // then we assume we are trying to drill down the branch
2556 // Let look if one of the top level branch has a branch with the name
2557 // we are looking for.
2559 TIter next( fTree->GetListOfBranches() );
2560 while(!branch && (branchcur=(TBranch*)next()) ) {
2561 branch = branchcur->FindBranch(first);
2562 }
2563 if (branch) {
2564 // We reset work
2565 current = &(work[0]);
2566 *current = 0;
2567 }
2568 }
2569 }
2570 } else { // correspond to if (leaf || branch)
2571 if (final) {
2572 Error("FindLeafForExpression", "Unexpected control flow!");
2573 return -1;
2574 }
2575
2576 // No dot is allowed in subbranches and leaves, so
2577 // we always remove it in the present case.
2578 if (cname[i]) work[strlen(work)-1] = '\0';
2579 snprintf(scratch,sizeof(scratch),"%s.%s",first,work);
2580 snprintf(scratch2,sizeof(scratch2),"%s.%s.%s",first,second,work);
2581
2582 if (previousdot) {
2584 }
2585
2586 // First look for the current 'word' in the list of
2587 // leaf of the
2588 if (branch) {
2589 tmp_leaf = branch->FindLeaf(work);
2590 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch);
2591 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch2);
2592 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(currentname.c_str());
2593 }
2594 if (tmp_leaf && tmp_leaf->IsOnTerminalBranch() ) {
2595 // This is a non-object leaf, it should NOT be specified more except for
2596 // dimensions.
2597 final = true;
2598 }
2599
2600 if (branch) {
2601 tmp_branch = branch->FindBranch(work);
2602 if (!tmp_branch) tmp_branch = branch->FindBranch(scratch);
2603 if (!tmp_branch) tmp_branch = branch->FindBranch(scratch2);
2604 if (!tmp_branch) tmp_branch = branch->FindBranch(currentname.c_str());
2605 }
2606 if (tmp_branch) {
2608
2609 // NOTE: Should we look for a leaf within here?
2610 if (!final) {
2611 tmp_leaf = branch->FindLeaf(work);
2612 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch);
2613 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(scratch2);
2614 if (!tmp_leaf) tmp_leaf = branch->FindLeaf(currentname.c_str());
2615 if (tmp_leaf && tmp_leaf->IsOnTerminalBranch() ) {
2616 // This is a non-object leaf, it should NOT be specified
2617 // more except for dimensions.
2618 final = true;
2619 leaf = tmp_leaf;
2620 }
2621 }
2622 }
2623 if (tmp_leaf) {
2624 // Something was found.
2625 if (second[0]) strncat(second,".",2*kMaxLen-1-strlen(second));
2626 strncat(second,work,2*kMaxLen-1-strlen(second));
2627 leaf = tmp_leaf;
2629 foundAtSign = false;
2630
2631 // we reset work
2632 current = &(work[0]);
2633 *current = 0;
2634 } else {
2635 //We need to put the delimiter back!
2636 if (strlen(work)) {
2637 if (foundAtSign) {
2639 work[where] = '@';
2640 work[where+1] = cname[i];
2641 ++current;
2642 previousdot = where+1;
2643 } else {
2645 work[strlen(work)] = cname[i];
2646 }
2647 } else --current;
2648 }
2649 }
2650 }
2651 }
2652
2653 // Copy the left over for later use.
2654 if (strlen(work)) {
2655 strncat(right,work,2*kMaxLen-1-strlen(right));
2656 }
2657
2658 if (i<nchname) {
2659 if (strlen(right) && right[strlen(right)-1]!='.' && cname[i]!='.') {
2660 // In some cases we remove a little too fast the period, we add
2661 // it back if we need. It is assumed that 'right' and the rest of
2662 // the name was cut by a delimiter, so this should be safe.
2663 strncat(right,".",2*kMaxLen-1-strlen(right));
2664 }
2665 strncat(right,&cname[i],2*kMaxLen-1-strlen(right));
2666 }
2667
2668 if (!final && branch) {
2669 if (!leaf) {
2670 leaf = (TLeaf*)branch->GetListOfLeaves()->UncheckedAt(0);
2671 if (!leaf) return -1;
2672 }
2673 final = leaf->IsOnTerminalBranch();
2674 }
2675
2676 if (leaf && leaf->InheritsFrom(TLeafObject::Class()) ) {
2677 if (strlen(right)==0) strlcpy(right,work,2*kMaxLen);
2678 }
2679
2680 if (leaf==nullptr && left[0]!=0) {
2681 if (left[strlen(left)-1]=='.') left[strlen(left)-1]=0;
2682
2683 // Check for an alias.
2684 const char *aliasValue = fTree->GetAlias(left);
2685 if (aliasValue && strcspn(aliasValue, "()[]+*/-%&!=<>|") == strlen(aliasValue) && !IsNumberConstant(aliasValue)) {
2686 // First check whether we are using this alias recursively (this would
2687 // lead to an infinite recursion).
2688 if (find(aliasUsed.begin(),
2689 aliasUsed.end(),
2690 left) != aliasUsed.end()) {
2691 Error("FindLeafForExpression",
2692 "The substitution of the branch alias \"%s\" by \"%s\" in \"%s\" failed\n"\
2693 "\tbecause \"%s\" is used [recursively] in its own definition!",
2694 left,aliasValue,fullExpression,left);
2695 return -3;
2696 }
2697 aliasUsed.push_back(left);
2699 newExpression += (cname+strlen(left));
2702 if (res<0) {
2703 Error("FindLeafForExpression",
2704 "The substitution of the alias \"%s\" by \"%s\" failed.",left,aliasValue);
2705 return -3;
2706 }
2707 return res;
2708 }
2709 }
2710 leftover = right;
2711
2712 return 0;
2713}
2714
2715////////////////////////////////////////////////////////////////////////////////
2716/// Check if name is in the list of Tree/Branch leaves.
2717///
2718/// This member function redefines the function in ROOT::v5::TFormula
2719/// If a leaf has a name corresponding to the argument name, then
2720/// returns a new code.
2721///
2722/// A TTreeFormula may contain more than one variable.
2723/// For each variable referenced, the pointers to the corresponding
2724/// branch and leaf is stored in the object arrays fBranches and fLeaves.
2725///
2726/// name can be :
2727/// - Leaf_Name (simple variable or data member of a ClonesArray)
2728/// - Branch_Name.Leaf_Name
2729/// - Branch_Name.Method_Name
2730/// - Leaf_Name[index]
2731/// - Branch_Name.Leaf_Name[index]
2732/// - Branch_Name.Leaf_Name[index1]
2733/// - Branch_Name.Leaf_Name[][index2]
2734/// - Branch_Name.Leaf_Name[index1][index2]
2735///
2736/// New additions:
2737/// - Branch_Name.Leaf_Name[OtherLeaf_Name]
2738/// - Branch_Name.Datamember_Name
2739/// - '.' can be replaced by '->'
2740///
2741/// and
2742/// - Branch_Name[index1].Leaf_Name[index2]
2743/// - Leaf_name[index].Action().OtherAction(param)
2744/// - Leaf_name[index].Action()[val].OtherAction(param)
2745///
2746/// The expected returned values are
2747/// - -2 : the name has been recognized but won't be usable
2748/// - -1 : the name has not been recognized, or is too long, or tree does not exist.
2749/// - >=0 : the name has been recognized, return the internal code for this name.
2750
2752{
2753
2755 if (!fTree) return -1;
2756
2757 fNpar = 0;
2758 if (name.Length() > kMaxLen) {
2759 Error("TTreeFormula", "The length of the variable name (%d) exceeds the maximum allowed (%d)", name.Length(), kMaxLen);
2760 return -1;
2761 }
2762 Int_t i,k;
2763
2764 if (name == "Entry$") {
2765 Int_t code = fNcodes++;
2766 fCodes[code] = 0;
2767 fLookupType[code] = kIndexOfEntry;
2768 return code;
2769 }
2770 if (name == "LocalEntry$") {
2771 Int_t code = fNcodes++;
2772 fCodes[code] = 0;
2774 return code;
2775 }
2776 if (name == "Entries$") {
2777 Int_t code = fNcodes++;
2778 fCodes[code] = 0;
2779 fLookupType[code] = kEntries;
2782 return code;
2783 }
2784 if (name == "LocalEntries$") {
2785 Int_t code = fNcodes++;
2786 fCodes[code] = 0;
2787 fLookupType[code] = kLocalEntries;
2788 SetBit(kNeedEntries); // FIXME: necessary?
2789 fManager->SetBit(kNeedEntries); // FIXME: necessary?
2790 return code;
2791 }
2792 if (name == "Iteration$") {
2793 Int_t code = fNcodes++;
2794 fCodes[code] = 0;
2795 fLookupType[code] = kIteration;
2796 return code;
2797 }
2798 if (name == "Length$") {
2799 Int_t code = fNcodes++;
2800 fCodes[code] = 0;
2801 fLookupType[code] = kLength;
2802 return code;
2803 }
2804 static const char *lenfunc = "Length$(";
2805 if (strncmp(name.Data(),"Length$(",strlen(lenfunc))==0
2806 && name[name.Length()-1]==')') {
2807
2808 TString subform = name.Data()+strlen(lenfunc);
2809 subform.Remove( subform.Length() - 1 );
2810 TTreeFormula *lengthForm = new TTreeFormula("lengthForm",subform,fTree);
2812 Int_t code = fNcodes++;
2813 fCodes[code] = 0;
2814 fLookupType[code] = kLengthFunc;
2815 return code;
2816 }
2817 static const char *minfunc = "Min$(";
2818 if (strncmp(name.Data(),"Min$(",strlen(minfunc))==0
2819 && name[name.Length()-1]==')') {
2820
2821 TString subform = name.Data()+strlen(minfunc);
2822 subform.Remove( subform.Length() - 1 );
2823 TTreeFormula *minForm = new TTreeFormula("minForm",subform,fTree);
2825 Int_t code = fNcodes++;
2826 fCodes[code] = 0;
2827 fLookupType[code] = kMin;
2828 return code;
2829 }
2830 static const char *maxfunc = "Max$(";
2831 if (strncmp(name.Data(),"Max$(",strlen(maxfunc))==0
2832 && name[name.Length()-1]==')') {
2833
2834 TString subform = name.Data()+strlen(maxfunc);
2835 subform.Remove( subform.Length() - 1 );
2836 TTreeFormula *maxForm = new TTreeFormula("maxForm",subform,fTree);
2838 Int_t code = fNcodes++;
2839 fCodes[code] = 0;
2840 fLookupType[code] = kMax;
2841 return code;
2842 }
2843 static const char *sumfunc = "Sum$(";
2844 if (strncmp(name.Data(),"Sum$(",strlen(sumfunc))==0
2845 && name[name.Length()-1]==')') {
2846
2847 TString subform = name.Data()+strlen(sumfunc);
2848 subform.Remove( subform.Length() - 1 );
2849 TTreeFormula *sumForm = new TTreeFormula("sumForm",subform,fTree);
2851 Int_t code = fNcodes++;
2852 fCodes[code] = 0;
2853 fLookupType[code] = kSum;
2854 return code;
2855 }
2856
2857
2858
2859 // Check for $Alt(expression1,expression2)
2860 Int_t res = DefineAlternate(name.Data());
2861 if (res!=0) {
2862 // There was either a syntax error or we found $Alt
2863 if (res<0) return res;
2864 action = res;
2865 return 0;
2866 }
2867
2868 // Find the top level leaf and deal with dimensions
2869
2870 char cname[kMaxLen]; strlcpy(cname,name.Data(),kMaxLen);
2871 char dims[kMaxLen]; dims[0] = '\0';
2872
2873 bool final = false;
2874
2875 UInt_t paran_level = 0;
2877
2878 // First, it is easier to remove all dimensions information from 'cname'
2880 for(i=0,k=0; i<cnamelen; ++i, ++k) {
2881 if (cname[i] == '[') {
2882 int bracket = i;
2883 int bracket_level = 1;
2884 int j;
2885 for (j=++i; j<cnamelen && (bracket_level>0 || cname[j]=='['); j++, i++) {
2886 if (cname[j]=='[') bracket_level++;
2887 else if (cname[j]==']') bracket_level--;
2888 }
2889 if (bracket_level != 0) {
2890 //Error("DefinedVariable","Bracket unbalanced");
2891 return -1;
2892 }
2893 strncat(dims,&cname[bracket],j-bracket);
2894 //k += j-bracket;
2895 }
2896 if (i!=k) cname[k] = cname[i];
2897 }
2898 cname[k]='\0';
2899
2900 bool useLeafCollectionObject = false;
2902 TLeaf *leaf = nullptr;
2903 {
2904 std::vector<std::string> aliasSofar = fAliasesUsed;
2906 }
2907 if (res<0) return res;
2908
2909 if (!leaf && res!=2) {
2910 // Check for an alias.
2911 const char *aliasValue = fTree->GetAlias(cname);
2912 if (aliasValue) {
2913 // First check whether we are using this alias recursively (this would
2914 // lead to an infinite recursion).
2915 if (find(fAliasesUsed.begin(),
2916 fAliasesUsed.end(),
2917 cname) != fAliasesUsed.end()) {
2918 Error("DefinedVariable",
2919 "The substitution of the alias \"%s\" by \"%s\" failed\n"\
2920 "\tbecause \"%s\" is recursively used in its own definition!",
2922 return -3;
2923 }
2924
2925 if (strcspn(aliasValue, "()[]+*/-%&!=<>|") != strlen(aliasValue) || IsNumberConstant(aliasValue)) {
2926 // If the alias contains an operator, we need to use a nested formula
2927 // (since DefinedVariable must only add one entry to the operation's list).
2928
2929 // Need to check the aliases used so far
2930 std::vector<std::string> aliasSofar = fAliasesUsed;
2931 aliasSofar.push_back( cname );
2932
2934 if (dims[0]) {
2935 subValue += dims;
2936 }
2937
2938 TTreeFormula *subform = new TTreeFormula(cname,subValue,fTree,aliasSofar); // Need to pass the aliases used so far.
2939
2940 if (subform->GetNdim()==0) {
2941 delete subform;
2942 Error("DefinedVariable",
2943 "The substitution of the alias \"%s\" by \"%s\" failed.",cname,aliasValue);
2944 return -3;
2945 }
2946
2949
2950 if (subform->IsString()) {
2952 return 0;
2953 } else {
2954 action = kAlias;
2955 return 0;
2956 }
2957 } else { /* assumes strcspn(aliasValue,"[]")!=strlen(aliasValue) */
2959 thisAlias += dims;
2961 if (aliasRes<0) {
2962 // We failed but DefinedVariable has not printed why yet.
2963 // and because we want those to be printed _before_ the notice
2964 // of the failure of the substitution, we need to print them here.
2965 if (aliasRes==-1) {
2966 Error("Compile", " Bad numerical expression : \"%s\"",thisAlias.Data());
2967 } else if (aliasRes==-2) {
2968 Error("Compile", " Part of the Variable \"%s\" exists but some of it is not accessible or useable",thisAlias.Data());
2969
2970 }
2971 Error("DefinedVariable",
2972 "The substitution of the alias \"%s\" by \"%s\" failed.",cname,aliasValue);
2973 return -3;
2974 }
2975 return aliasRes;
2976 }
2977 }
2978 }
2979
2980
2981 if (leaf || res==2) {
2982
2983 if (leaf && leaf->GetBranch() && leaf->GetBranch()->TestBit(kDoNotProcess)) {
2984 Error("DefinedVariable","the branch \"%s\" has to be enabled to be used",leaf->GetBranch()->GetName());
2985 return -2;
2986 }
2987
2988 Int_t code = fNcodes++;
2989
2990 // If needed will now parse the indexes specified for
2991 // arrays.
2992 if (dims[0]) {
2993 char *current = &( dims[0] );
2994 Int_t dim = 0;
2996 Int_t index;
2998 while (current) {
2999 current++;
3000 if (current[0] == ']') {
3001 fIndexes[code][dim] = -1; // Loop over all elements;
3002 } else {
3003 TString tempIndex(current);
3004 auto closePos = tempIndex.First(']');
3005 if (closePos != -1)
3007 if (tempIndex.IsDigit() && (scanindex = sscanf(current, "%d", &index)) && scanindex == 1) {
3008 fIndexes[code][dim] = index;
3009 } else {
3010 fIndexes[code][dim] = -2; // Index is calculated via a variable.
3011 varindex = current;
3012 char *end = (char*)(varindex.Data());
3013 for(char bracket_level = 0;*end!=0;end++) {
3014 if (*end=='[') bracket_level++;
3015 if (bracket_level==0 && *end==']') break;
3016 if (*end==']') bracket_level--;
3017 }
3018 *end = '\0';
3019 fVarIndexes[code][dim] = new TTreeFormula("index_var",
3020 varindex,
3021 fTree);
3022 if (fVarIndexes[code][dim]->GetNdim() == 0) {
3023 // Parsing failed for the index, let's stop here ....
3024 return -1;
3025 }
3026 current += strlen(varindex)+1; // move to the end of the index array
3027 }
3028 }
3029 dim ++;
3030 if (dim >= kMAXFORMDIM) {
3031 // NOTE: test that dim this is NOT too big!!
3032 break;
3033 }
3034 current = (char*)strstr( current, "[" );
3035 }
3036 }
3037
3038 // Now that we have cleaned-up the expression, let's compare it to the content
3039 // of the leaf!
3040
3042 if (res<0) return res;
3043 if (res>0) action = res;
3044 return code;
3045 }
3046
3047//*-*- May be a graphical cut ?
3048 TCutG *gcut = (TCutG*)gROOT->GetListOfSpecials()->FindObject(name.Data());
3049 if (gcut) {
3050 if (gcut->GetObjectX()) {
3051 if(!gcut->GetObjectX()->InheritsFrom(TTreeFormula::Class()))
3052 gcut->SetObjectX(nullptr);
3053 }
3054 if (gcut->GetObjectY()) {
3055 if(!gcut->GetObjectY()->InheritsFrom(TTreeFormula::Class()))
3056 gcut->SetObjectY(nullptr);
3057 }
3058
3059 Int_t code = fNcodes;
3060
3061 if (strlen(gcut->GetVarX()) && strlen(gcut->GetVarY()) ) {
3062
3063 TTreeFormula *fx = new TTreeFormula("f_x",gcut->GetVarX(),fTree);
3064 gcut->SetObjectX(fx);
3065
3066 TTreeFormula *fy = new TTreeFormula("f_y",gcut->GetVarY(),fTree);
3067 gcut->SetObjectY(fy);
3068
3069 fCodes[code] = -2;
3070
3071 } else if (strlen(gcut->GetVarX())) {
3072
3073 // Let's build the equivalent formula:
3074 // min(gcut->X) <= VarX <= max(gcut->Y)
3075 Double_t min = 0;
3076 Double_t max = 0;
3077 Int_t n = gcut->GetN();
3078 Double_t *x = gcut->GetX();
3079 min = max = x[0];
3080 for(Int_t i2 = 1; i2<n; i2++) {
3081 if (x[i2] < min) min = x[i2];
3082 if (x[i2] > max) max = x[i2];
3083 }
3084 TString formula = "(";
3085 formula += min;
3086 formula += "<=";
3087 formula += gcut->GetVarX();
3088 formula += " && ";
3089 formula += gcut->GetVarX();
3090 formula += "<=";
3091 formula += max;
3092 formula += ")";
3093
3094 TTreeFormula *fx = new TTreeFormula("f_x",formula.Data(),fTree);
3095 gcut->SetObjectX(fx);
3096
3097 fCodes[code] = -1;
3098
3099 } else {
3100
3101 Error("DefinedVariable","Found a TCutG without leaf information (%s)",
3102 gcut->GetName());
3103 return -1;
3104
3105 }
3106
3108 fNcodes++;
3109 fLookupType[code] = -1;
3110 return code;
3111 }
3112
3113 //may be an entrylist
3114 TEntryList *elist = dynamic_cast<TEntryList*> (gDirectory->Get(name.Data()));
3115 if (elist) {
3116 Int_t code = fNcodes;
3117 fCodes[code] = 0;
3118 fExternalCuts.AddAtAndExpand(elist, code);
3119 fNcodes++;
3120 fLookupType[code] = kEntryList;
3121 return code;
3122
3123 }
3124
3125 return -1;
3126}
3127
3128////////////////////////////////////////////////////////////////////////////////
3129/// Return the leaf (if any) which contains an object containing
3130/// a data member which has the name provided in the arguments.
3131
3133{
3134 TClass * cl = nullptr;
3136 TFormLeafInfo* clonesinfo = nullptr;
3137 TLeaf *leafcur;
3138 while ((leafcur = (TLeaf*)nextleaf())) {
3139 // The following code is used somewhere else, we need to factor it out.
3140
3141 // Here since we are interested in data member, we want to consider only
3142 // 'terminal' branch and leaf.
3143 cl = nullptr;
3144 if (leafcur->InheritsFrom(TLeafObject::Class()) &&
3145 leafcur->GetBranch()->GetListOfBranches()->Last()==nullptr) {
3147 cl = lobj->GetClass();
3148 } else if (leafcur->InheritsFrom(TLeafElement::Class()) && leafcur->IsOnTerminalBranch()) {
3150 if (lElem->IsOnTerminalBranch()) {
3151 TBranchElement *branchEl = (TBranchElement *)leafcur->GetBranch();
3152 Int_t type = branchEl->GetStreamerType();
3153 if (type==-1) {
3154 cl = branchEl->GetInfo() ? branchEl->GetInfo()->GetClass() : nullptr;
3155 } else if (type>60 || type==0) {
3156 // Case of an object data member. Here we allow for the
3157 // variable name to be omitted. Eg, for Event.root with split
3158 // level 1 or above Draw("GetXaxis") is the same as Draw("fH.GetXaxis()")
3159 TStreamerElement* element = branchEl->GetInfo()->GetElement(branchEl->GetID());
3160 if (element) cl = element->GetClassPointer();
3161 else cl = nullptr;
3162 }
3163 }
3164
3165 }
3166 if (clonesinfo) { delete clonesinfo; clonesinfo = nullptr; }
3167 if (cl == TClonesArray::Class()) {
3168 // We have a unsplit TClonesArray leaves
3169 // In this case we assume that cl is the class in which the TClonesArray
3170 // belongs.
3171 auto res = R__LoadBranch(leafcur->GetBranch(),readentry,fQuickLoad);
3172 if (res < 0) {
3173 Error("GetLeafWithDatamember", "Branch could not be loaded:%d", res);
3174 continue;
3175 }
3177
3178 TBranch *branch = leafcur->GetBranch();
3179 if ( branch->IsA()==TBranchElement::Class()
3180 && ((TBranchElement*)branch)->GetType()==31) {
3181
3182 // We have an unsplit TClonesArray as part of a split TClonesArray!
3183
3184 // Let's not dig any further. If the user really wants a data member
3185 // inside the nested TClonesArray, it has to specify it explicitly.
3186
3187 continue;
3188
3189 } else {
3190 bool toplevel = (branch == branch->GetMother());
3192 clones = (TClonesArray*)clonesinfo->GetLocalValuePointer(leafcur,0);
3193 }
3194 if (clones) cl = clones->GetClass();
3195 } else if (cl && cl->GetCollectionProxy()) {
3196
3197 // We have a unsplit Collection leaves
3198 // In this case we assume that cl is the class in which the TClonesArray
3199 // belongs.
3200
3201 TBranch *branch = leafcur->GetBranch();
3202 if ( branch->IsA()==TBranchElement::Class()
3203 && ((TBranchElement*)branch)->GetType()==41) {
3204
3205 // We have an unsplit Collection as part of a split Collection!
3206
3207 // Let's not dig any further. If the user really wants a data member
3208 // inside the nested Collection, it has to specify it explicitly.
3209
3210 continue;
3211
3212 } else {
3214 }
3215 cl = cl->GetCollectionProxy()->GetValueClass();
3216 }
3217 if (cl) {
3218 // Now that we have the class, let's check if the topchoice is of its datamember
3219 // or if the nextchoice is a datamember of one of its datamember.
3220 Int_t offset;
3222 TStreamerElement* element = info?info->GetStreamerElement(topchoice,offset):nullptr;
3223 if (!element) {
3226 while ((curelem = (TStreamerElement*)nextel())) {
3227
3228 if (curelem->GetClassPointer() == TClonesArray::Class()) {
3229 // In case of a TClonesArray we need to load the data and read the
3230 // clonesArray object before being able to look into the class inside.
3231 // We need to do that because we are never interested in the TClonesArray
3232 // itself but only in the object inside.
3233 TBranch *branch = leafcur->GetBranch();
3234 TFormLeafInfo *leafinfo = nullptr;
3235 if (clonesinfo) {
3237 } else if (branch->IsA()==TBranchElement::Class()
3238 && ((TBranchElement*)branch)->GetType()==31) {
3239 // Case of a sub branch of a TClonesArray
3241 TStreamerInfo *bel_info = branchEl->GetInfo();
3242 TClass * mother_cl = ((TBranchElement*)branch)->GetInfo()->GetClass();
3244 bel_info->GetElement(branchEl->GetID());
3246 }
3247
3248 Int_t clones_offset = 0;
3249 ((TStreamerInfo*)cl->GetStreamerInfo())->GetStreamerElement(curelem->GetName(),clones_offset);
3251 if (leafinfo)
3252 if (leafinfo->fNext) leafinfo->fNext->fNext = sub_clonesinfo;
3253 else leafinfo->fNext = sub_clonesinfo;
3254 else leafinfo = sub_clonesinfo;
3255
3257 if (res < 0) {
3258 Error("GetLeafWithDatamember", "Branch could not be loaded:%d", res);
3259 continue;
3260 }
3261
3262 TClonesArray * clones = (TClonesArray*)leafinfo->GetValuePointer(leafcur,0);
3263
3264 delete leafinfo; clonesinfo = nullptr;
3265 // If TClonesArray object does not exist we have no information, so let go
3266 // on. This is a weakish test since the TClonesArray object might exist in
3267 // the next entry ... In other word, we ONLY rely on the information available
3268 // in entry #0.
3269 if (!clones) continue;
3270 TClass *sub_cl = clones->GetClass();
3271
3272 // Now that we finally have the inside class, let's query it.
3273 element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(nextchoice,offset);
3274 if (element) break;
3275 } // if clones array
3276 else if (curelem->GetClassPointer() && curelem->GetClassPointer()->GetCollectionProxy()) {
3277
3278 TClass *sub_cl = curelem->GetClassPointer()->GetCollectionProxy()->GetValueClass();
3279
3280 while(sub_cl && sub_cl->GetCollectionProxy())
3281 sub_cl = sub_cl->GetCollectionProxy()->GetValueClass();
3282
3283 // Now that we finally have the inside class, let's query it.
3284 if (sub_cl) element = ((TStreamerInfo*)sub_cl->GetStreamerInfo())->GetStreamerElement(nextchoice,offset);
3285 if (element) break;
3286
3287 }
3288 } // loop on elements
3289 }
3290 if (element) break;
3291 else cl = nullptr;
3292 }
3293 }
3294 delete clonesinfo;
3295 if (cl) {
3296 return leafcur;
3297 } else {
3298 return nullptr;
3299 }
3300}
3301
3302////////////////////////////////////////////////////////////////////////////////
3303/// Return the leaf (if any) of the tree with contains an object of a class
3304/// having a method which has the name provided in the argument.
3305
3306bool TTreeFormula::BranchHasMethod(TLeaf* leafcur, TBranch* branch, const char* method, const char* params, Long64_t readentry) const
3307{
3308 TClass *cl = nullptr;
3309 TLeafObject* lobj = nullptr;
3310
3311 // Since the user does not want this branch to be loaded anyway, we just
3312 // skip it. This prevents us from warning the user that the method might
3313 // be on a disabled branch. However, and more usefully, this allows the
3314 // user to avoid error messages from branches that cannot be currently
3315 // read without warnings/errors.
3316
3317 if (branch->TestBit(kDoNotProcess)) {
3318 return false;
3319 }
3320
3321 // FIXME: The following code is used somewhere else, we need to factor it out.
3322 if (branch->InheritsFrom(TBranchObject::Class())) {
3323 lobj = (TLeafObject*) branch->GetListOfLeaves()->At(0);
3324 cl = lobj->GetClass();
3325 } else if (branch->InheritsFrom(TBranchElement::Class())) {
3327 Int_t type = branchEl->GetStreamerType();
3328 if (type == -1) {
3329 cl = branchEl->GetInfo() ? branchEl->GetInfo()->GetClass() : nullptr;
3330 } else if (type > 60) {
3331 // Case of an object data member. Here we allow for the
3332 // variable name to be omitted. Eg, for Event.root with split
3333 // level 1 or above Draw("GetXaxis") is the same as Draw("fH.GetXaxis()")
3334 TStreamerElement* element = branchEl->GetInfo()->GetElement(branchEl->GetID());
3335 if (element) {
3336 cl = element->GetClassPointer();
3337 } else {
3338 cl = nullptr;
3339 }
3340 if ((cl == TClonesArray::Class()) && (branchEl->GetType() == 31)) {
3341 // we have a TClonesArray inside a split TClonesArray,
3342 // Let's not dig any further. If the user really wants a data member
3343 // inside the nested TClonesArray, it has to specify it explicitly.
3344 cl = nullptr;
3345 }
3346 // NOTE do we need code for Collection here?
3347 }
3348 }
3349
3350 if (cl == TClonesArray::Class()) {
3351 // We might be try to call a method of the top class inside a
3352 // TClonesArray.
3353 // Since the leaf was not terminal, we might have a split or
3354 // unsplit and/or top leaf/branch.
3355 TClonesArray* clones = nullptr;
3357 if (res < 0) {
3358 Error("BranchHasMethod", "Branch could not be loaded:%d", res);
3359 return false;
3360 }
3361 if (branch->InheritsFrom(TBranchObject::Class())) {
3362 clones = (TClonesArray*) lobj->GetObject();
3363 } else if (branch->InheritsFrom(TBranchElement::Class())) {
3364 // We do not know exactly where the leaf of the TClonesArray is
3365 // in the hierarchy but we still need to get the correct class
3366 // holder.
3368 if (bc == bc->GetMother()) {
3369 // Top level branch
3370 //clones = *((TClonesArray**) bc->GetAddress());
3371 clones = (TClonesArray*) bc->GetObject();
3372 } else if (!leafcur || !leafcur->IsOnTerminalBranch()) {
3373 TStreamerElement* element = bc->GetInfo()->GetElement(bc->GetID());
3374 if (element->IsaPointer()) {
3375 clones = *((TClonesArray**) bc->GetAddress());
3376 //clones = *((TClonesArray**) bc->GetObject());
3377 } else {
3378 //clones = (TClonesArray*) bc->GetAddress();
3379 clones = (TClonesArray*) bc->GetObject();
3380 }
3381 }
3382 if (!clones) {
3384 if (cres < 0) {
3385 Error("BranchHasMethod", "Branch could not be loaded:%d", cres);
3386 return false;
3387 }
3389 mother_cl = bc->GetInfo()->GetClass();
3391 // if (!leafcur) { leafcur = (TLeaf*) branch->GetListOfLeaves()->At(0); }
3392 clones = (TClonesArray*) clonesinfo->GetLocalValuePointer(leafcur, 0);
3393 // cl = clones->GetClass();
3394 delete clonesinfo;
3395 }
3396 } else {
3397 Error("BranchHasMethod","A TClonesArray was stored in a branch type no yet support (i.e. neither TBranchObject nor TBranchElement): %s",branch->IsA()->GetName());
3398 return false;
3399 }
3400 cl = clones ? clones->GetClass() : nullptr;
3401 } else if (cl && cl->GetCollectionProxy()) {
3402 cl = cl->GetCollectionProxy()->GetValueClass();
3403 }
3404
3405 if (cl) {
3406 if (cl->GetClassInfo()) {
3407 if (cl->GetMethodAllAny(method)) {
3408 // Let's try to see if the function we found belongs to the current
3409 // class. Note that this implementation currently can not work if
3410 // one the argument is another leaf or data member of the object.
3411 // (Anyway we do NOT support this case).
3412 TMethodCall methodcall(cl, method, params);
3413 if (methodcall.GetMethod()) {
3414 // We have a method that works.
3415 // We will use it.
3416 return true;
3417 }
3418 }
3419 }
3420 }
3421
3422 return false;
3423}
3424
3425////////////////////////////////////////////////////////////////////////////////
3426/// Now let calculate what physical instance we really need.
3427/// Some redundant code is used to speed up the cases where
3428/// they are no dimensions.
3429///
3430/// We know that instance is less that fCumulUsedSize[0] so
3431/// we can skip the modulo when virt_dim is 0.
3432
3434 Int_t real_instance = 0;
3436
3437 bool check = false;
3438 if (codeindex<0) {
3439 codeindex = 0;
3440 check = true;
3441 }
3442
3443 TFormLeafInfo * info = nullptr;
3444 Int_t max_dim = fNdimensions[codeindex];
3445 if ( max_dim ) {
3446 virt_dim = 0;
3447 max_dim--;
3448
3449 if (!fManager->fMultiVarDim) {
3450 if (fIndexes[codeindex][0]>=0) {
3452 } else {
3455 if (fIndexes[codeindex][0]==-2) {
3456 // NOTE: Should we check that this is a valid index?
3457 if (check) {
3460 // out of bounds
3461 return fNdata[0]+1;
3462 }
3463 }
3465 // Force the loading of the index.
3467 }
3469 if (local_index<0) {
3470 Error("EvalInstance","Index %s is out of bound (%d) in formula %s",
3473 GetTitle());
3474 return fNdata[0]+1;
3475 }
3476 }
3478 virt_dim ++;
3479 }
3480 } else {
3481 // NOTE: We assume that ONLY the first dimension of a leaf can have a variable
3482 // size AND contain the index for the size of yet another sub-dimension.
3483 // I.e. a variable size array inside a variable size array can only have its
3484 // size vary with the VERY FIRST physical dimension of the leaf.
3485 // Thus once the index of the first dimension is found, all other dimensions
3486 // are fixed!
3487
3488 // NOTE: We could unroll some of this loops to avoid a few tests.
3491 // if (info && info->GetVarDim()==-1) info = 0;
3492 }
3494
3495 switch (fIndexes[codeindex][0]) {
3496 case -2:
3498 // Force the loading of the index.
3500 }
3502 if (local_index<0) {
3503 Error("EvalInstance","Index %s is out of bound (%d) in formula %s",
3506 GetTitle());
3507 local_index = 0;
3508 }
3509 break;
3510 case -1: {
3514 }
3518
3520 if (maxloop == 0) {
3521 local_index--;
3522 instance = fNdata[0]+1; // out of bounds.
3523 if (check) return fNdata[0]+1;
3524 } else {
3525 do {
3527 local_index++;
3528 } while( instance >= virt_accum && local_index<maxloop);
3529 local_index--;
3530 // update the cache
3533
3534 if (local_index==(maxloop-1) && (instance >= virt_accum)) {
3535 instance = fNdata[0]+1; // out of bounds.
3536 if (check) return fNdata[0]+1;
3537 } else {
3540 } else {
3541 instance = fNdata[0]+1; // out of bounds.
3542 if (check) return fNdata[0]+1;
3543 }
3544 }
3545 }
3546 virt_dim ++;
3547 }
3548 break;
3549 default:
3551 }
3552
3553 // Inform the (appropriate) MultiVarLeafInfo that the clones array index is
3554 // local_index.
3555
3558 } else {
3560 }
3561 for(Int_t d = kMAXFORMDIM-1; d>0; d--) {
3562 if (fManager->fVarDims[d]) {
3564 } else {
3566 }
3567 }
3568 if (info) {
3569 // When we have multiple variable dimensions, the LeafInfo only expect
3570 // the instance after the primary index has been set.
3571 info->SetPrimaryIndex(local_index);
3572 real_instance = 0;
3573
3574 // Let's update fCumulSizes for the rest of the code.
3575 Int_t vdim = info->GetVarDim();
3576 Int_t isize = info->GetSize(local_index);
3577 if (fIndexes[codeindex][vdim]>=0) {
3578 info->SetSecondaryIndex(fIndexes[codeindex][vdim]);
3579 }
3580 if (isize!=1 && fIndexes[codeindex][vdim]>isize) {
3581 // We are out of bounds!
3582 return fNdata[0]+1;
3583 }
3585 for(Int_t k=vdim -1; k>0; --k) {
3587 }
3588 } else {
3590 }
3591 }
3592 if (max_dim>0) {
3593 for (Int_t dim = 1; dim < max_dim; dim++) {
3594 if (fIndexes[codeindex][dim]>=0) {
3596 } else {
3601 } else {
3603 }
3604 if (fIndexes[codeindex][dim]==-2) {
3605 // NOTE: Should we check that this is a valid index?
3607 // Force the loading of the index.
3609 }
3611 if (local_index<0 ||
3613 Error("EvalInstance","Index %s is out of bound (%d/%d) in formula %s",
3617 GetTitle());
3619 }
3620 }
3622 virt_dim ++;
3623 }
3624 }
3625 if (fIndexes[codeindex][max_dim]>=0) {
3626 if (!info) real_instance += fIndexes[codeindex][max_dim];
3627 } else {
3631 } else {
3633 }
3634 if (info && local_index>=fCumulSizes[codeindex][max_dim]) {
3635 // We are out of bounds! [Multiple var dims, See same message a few line above]
3636 return fNdata[0]+1;
3637 }
3638 if (fIndexes[codeindex][max_dim]==-2) {
3640 // Force the loading of the index.
3641 fVarIndexes[codeindex][max_dim]->LoadBranches();
3642 }
3644 if (local_index<0 ||
3645 local_index>=fCumulSizes[codeindex][max_dim]) {
3646 Error("EvalInstance","Index %s is of out bound (%d/%d) in formula %s",
3647 fVarIndexes[codeindex][max_dim]->GetTitle(),
3649 fCumulSizes[codeindex][max_dim],
3650 GetTitle());
3651 local_index = fCumulSizes[codeindex][max_dim]-1;
3652 }
3653 }
3655 }
3656 } // if (max_dim-1>0)
3657 } // if (max_dim)
3658
3659 return real_instance;
3660}
3661
3662////////////////////////////////////////////////////////////////////////////////
3663/// Evaluate the class of this treeformula.
3664///
3665/// If the 'value' of this formula is a simple pointer to an object,
3666/// this function returns the TClass corresponding to its type.
3667
3669{
3670 if (fNoper != 1 || fNcodes <=0 ) return nullptr;
3671
3672 return EvalClass(0);
3673}
3674
3675////////////////////////////////////////////////////////////////////////////////
3676/// Evaluate the class of the operation oper.
3677///
3678/// If the 'value' in the requested operation is a simple pointer to an object,
3679/// this function returns the TClass corresponding to its type.
3680
3682{
3684 switch(fLookupType[oper]) {
3685 case kDirect: {
3686 if (leaf->IsA()==TLeafObject::Class()) {
3687 return ((TLeafObject*)leaf)->GetClass();
3688 } else if ( leaf->IsA()==TLeafElement::Class()) {
3689 TBranchElement * branch = (TBranchElement*)((TLeafElement*)leaf)->GetBranch();
3690 TStreamerInfo * info = branch->GetInfo();
3691 Int_t id = branch->GetID();
3692 if (id>=0) {
3693 if (info==nullptr || !info->IsCompiled()) {
3694 // we probably do not have a way to know the class of the object.
3695 return nullptr;
3696 }
3697 TStreamerElement* elem = (TStreamerElement*)info->GetElement(id);
3698 if (elem==nullptr) {
3699 // we probably do not have a way to know the class of the object.
3700 return nullptr;
3701 } else {
3702 return elem->GetClass();
3703 }
3704 } else return TClass::GetClass( branch->GetClassName() );
3705 } else {
3706 return nullptr;
3707 }
3708 }
3709 case kMethod: return nullptr; // kMethod is deprecated so let's no waste time implementing this.
3710 case kTreeMember:
3711 case kDataMember: {
3713 if (!obj) return nullptr;
3714 return ((TFormLeafInfo*)obj)->GetClass();
3715 }
3716
3717 default: return nullptr;
3718 }
3719
3720
3721}
3722
3723////////////////////////////////////////////////////////////////////////////////
3724/// Evaluate this treeformula.
3725///
3726/// Return the address of the object pointed to by the formula.
3727/// Return 0 if the formula is not a single object
3728/// The object type can be retrieved using by call EvalClass();
3729
3731{
3732 if (fNoper != 1 || fNcodes <=0 ) return nullptr;
3733
3734
3735 switch (fLookupType[0]) {
3736 case kIndexOfEntry:
3737 case kIndexOfLocalEntry:
3738 case kEntries:
3739 case kLocalEntries:
3740 case kLength:
3741 case kLengthFunc:
3742 case kIteration:
3743 case kEntryList:
3744 return nullptr;
3745 }
3746
3748
3750
3751 if (instance==0 || fNeedLoading) {
3752 fNeedLoading = false;
3753 auto res = R__LoadBranch(leaf->GetBranch(),
3754 leaf->GetBranch()->GetTree()->GetReadEntry(),
3755 fQuickLoad);
3756 if (res < 0) {
3757 Error("EvalObject", "Branch could not be loaded:%d", res);
3758 return nullptr;
3759 }
3760 }
3761 else if (real_instance>=fNdata[0]) return nullptr;
3762 if (fAxis) {
3763 return nullptr;
3764 }
3765 switch(fLookupType[0]) {
3766 case kDirect: {
3767 if (real_instance) {
3768 Warning("EvalObject","Not yet implement for kDirect and arrays (for %s).\nPlease contact the developers",GetName());
3769 }
3770 return leaf->GetValuePointer();
3771 }
3772 case kMethod: return GetValuePointerFromMethod(0,leaf);
3773 case kTreeMember:
3774 case kDataMember: return ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->GetValuePointer(leaf,real_instance);
3775 default: return nullptr;
3776 }
3777
3778
3779}
3780
3781
3782////////////////////////////////////////////////////////////////////////////////
3783/// Eval the instance as a string.
3784
3786{
3787 const Int_t kMAXSTRINGFOUND = 10;
3788 const char *stringStack[kMAXSTRINGFOUND];
3789
3790 if (fNoper==1 && fNcodes>0 && IsString()) {
3792
3794
3795 if (instance==0 || fNeedLoading) {
3796 fNeedLoading = false;
3797 TBranch *branch = leaf->GetBranch();
3798 auto res = R__LoadBranch(branch,branch->GetTree()->GetReadEntry(),fQuickLoad);
3799 if (res < 0) {
3800 Error("EvalStringInstance", "Branch could not be loaded:%d", res);
3801 return nullptr;
3802 }
3803 } else if (real_instance>=fNdata[0]) {
3804 return nullptr;
3805 }
3806
3807 if (fLookupType[0]==kDirect) {
3808 return (char*)leaf->GetValuePointer();
3809 } else {
3810 return (char*)GetLeafInfo(0)->GetValuePointer(leaf,real_instance);
3811 }
3812 }
3813
3815
3816 return stringStack[0];
3817}
3818
3819#define TT_EVAL_INIT \
3820 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(0); \
3821 \
3822 const Int_t real_instance = GetRealInstance(instance,0); \
3823 \
3824 if (instance==0) fNeedLoading = true; \
3825 if (real_instance>=fNdata[0]) return TMath::SignalingNaN(); \
3826 \
3827 /* Since the only operation in this formula is reading this branch, \
3828 we are guaranteed that this function is first called with instance==0 and \
3829 hence we are guaranteed that the branch is always properly read */ \
3830 \
3831 if (fNeedLoading) { \
3832 fNeedLoading = false; \
3833 TBranch *br = leaf->GetBranch(); \
3834 if (br && br->GetTree()) { \
3835 Long64_t tEntry = br->GetTree()->GetReadEntry(); \
3836 auto lres = R__LoadBranch(br, tEntry, fQuickLoad); \
3837 if (lres < 0) \
3838 Error("TTreeFormula::TT_EVAL_INIT", \
3839 "Could not read entry (%lld) of leaf (%s), r=(%d).", tEntry, leaf->GetName(), lres);\
3840 } else { \
3841 Error("TTreeFormula::TT_EVAL_INIT", \
3842 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3843 } \
3844 } \
3845 \
3846 if (fAxis) { \
3847 char * label; \
3848 /* This portion is a duplicate (for speed reason) of the code \
3849 located in the main for loop at "a tree string" (and in EvalStringInstance) */ \
3850 if (fLookupType[0]==kDirect) { \
3851 label = (char*)leaf->GetValuePointer(); \
3852 } else { \
3853 label = (char*)GetLeafInfo(0)->GetValuePointer(leaf,instance); \
3854 } \
3855 Int_t bin = fAxis->FindBin(label); \
3856 return bin-0.5; \
3857 }
3858
3859#define TREE_EVAL_INIT \
3860 const Int_t real_instance = GetRealInstance(instance,0); \
3861 \
3862 if (real_instance>=fNdata[0]) return TMath::SignalingNaN(); \
3863 \
3864 if (fAxis) { \
3865 char * label; \
3866 /* This portion is a duplicate (for speed reason) of the code \
3867 located in the main for loop at "a tree string" (and in EvalStringInstance) */ \
3868 label = (char*)GetLeafInfo(0)->GetValuePointer((TLeaf*)0x0,instance); \
3869 Int_t bin = fAxis->FindBin(label); \
3870 return bin-0.5; \
3871 }
3872
3873#define TT_EVAL_INIT_LOOP \
3874 TLeaf *leaf = (TLeaf*)fLeaves.UncheckedAt(code); \
3875 \
3876 /* Now let calculate what physical instance we really need. */ \
3877 const Int_t real_instance = GetRealInstance(instance,code); \
3878 \
3879 if (willLoad) { \
3880 TBranch *branch = (TBranch*)fBranches.UncheckedAt(code); \
3881 if (branch) { \
3882 if (branch->GetTree()) { \
3883 Long64_t tEntry = branch->GetTree()->GetReadEntry(); \
3884 auto lres = R__LoadBranch(branch, tEntry, fQuickLoad); \
3885 if (lres < 0) { \
3886 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3887 "Could not read entry (%lld) of leaf (%s), r=(%d).", tEntry, leaf->GetName(), lres);\
3888 } \
3889 } else { \
3890 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3891 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3892 } \
3893 } else if (fDidBooleanOptimization) { \
3894 branch = leaf->GetBranch(); \
3895 if (branch && branch->GetTree()) { \
3896 Long64_t tEntry = branch->GetTree()->GetReadEntry(); \
3897 if (branch->GetReadEntry() != tEntry) branch->GetEntry(tEntry); \
3898 } else { \
3899 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3900 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3901 } \
3902 } \
3903 } else { \
3904 /* In the cases where we are behind (i.e. right of) a potential boolean optimization \
3905 this tree variable reading may have not been executed with instance==0 which would \
3906 result in the branch being potentially not read in. */ \
3907 if (fDidBooleanOptimization) { \
3908 TBranch *br = leaf->GetBranch(); \
3909 if (br->GetTree()) { \
3910 Long64_t tEntry = br->GetTree()->GetReadEntry(); \
3911 if (br->GetReadEntry() != tEntry) br->GetEntry(tEntry); \
3912 } else { \
3913 Error("TTreeFormula::TT_EVAL_INIT_LOOP", \
3914 "Could not init branch associated to this leaf (%s).", leaf->GetName()); \
3915 } \
3916 } \
3917 } \
3918 if (real_instance>=fNdata[code]) return TMath::SignalingNaN();
3919
3920#define TREE_EVAL_INIT_LOOP \
3921 /* Now let calculate what physical instance we really need. */ \
3922 const Int_t real_instance = GetRealInstance(instance,code); \
3923 \
3924 if (real_instance>=fNdata[code]) return TMath::SignalingNaN();
3925
3926
3927template<typename T> T Summing(TTreeFormula *sum) {
3928 Int_t len = sum->GetNdata();
3929 T res = 0;
3930 for (int i=0; i<len; ++i) res += sum->EvalInstance<T>(i);
3931 return res;
3932}
3933
3934template<typename T> T FindMin(TTreeFormula *arr) {
3935 Int_t len = arr->GetNdata();
3936 T res = 0;
3937 if (len) {
3938 res = arr->EvalInstance<T>(0);
3939 for (int i=1; i<len; ++i) {
3940 T val = arr->EvalInstance<T>(i);
3941 if (val < res) {
3942 res = val;
3944 }
3945 }
3946 return res;
3947}
3948
3949template<typename T> T FindMax(TTreeFormula *arr) {
3950 Int_t len = arr->GetNdata();
3951 T res = 0;
3952 if (len) {
3953 res = arr->EvalInstance<T>(0);
3954 for (int i=1; i<len; ++i) {
3955 T val = arr->EvalInstance(i);
3956 if (val > res) {
3957 res = val;
3959 }
3960 }
3961 return res;
3962}
3963
3964template<typename T> T FindMin(TTreeFormula *arr, TTreeFormula *condition) {
3965 Int_t len = arr->GetNdata();
3966 T res = 0;
3967 if (len) {
3968 int i = 0;
3969 T condval;
3970 do {
3971 condval = condition->EvalInstance<T>(i);
3972 ++i;
3973 } while (!condval && i<len);
3974 if (!condval && i==len) {
3975 return 0;
3976 }
3977 if (i!=1) {
3978 // Insure the loading of the branch.
3979 arr->EvalInstance<T>(0);
3980 }
3981 // Now we know that i>0 && i<len and cond==true
3982 res = arr->EvalInstance<T>(i-1);
3983 for (; i<len; ++i) {
3984 condval = condition->EvalInstance<T>(i);
3985 if (condval) {
3986 T val = arr->EvalInstance<T>(i);
3987 if (val < res) {
3988 res = val;
3989 }
3991 }
3992 }
3993 return res;
3994}
3995
3996template<typename T> T FindMax(TTreeFormula *arr, TTreeFormula *condition) {
3997 Int_t len = arr->GetNdata();
3998 T res = 0;
3999 if (len) {
4000 int i = 0;
4001 T condval;
4002 do {
4003 condval = condition->EvalInstance<T>(i);
4004 ++i;
4005 } while (!condval && i<len);
4006 if (!condval && i==len) {
4007 return 0;
4008 }
4009 if (i!=1) {
4010 // Insure the loading of the branch.
4011 arr->EvalInstance<T>(0);
4012 }
4013 // Now we know that i>0 && i<len and cond==true
4014 res = arr->EvalInstance<T>(i-1);
4015 for (; i<len; ++i) {
4016 condval = condition->EvalInstance<T>(i);
4017 if (condval) {
4018 T val = arr->EvalInstance<T>(i);
4019 if (val > res) {
4020 res = val;
4021 }
4022 }
4023 }
4024 }
4025 return res;
4026}
4027
4028namespace {
4029
4030template <typename T> T fmod_local(T x, T y) { return fmod(x,y); }
4031template <> Long64_t fmod_local(Long64_t x, Long64_t y) { return fmod((LongDouble_t)x,(LongDouble_t)y); }
4032
4033template<typename T> inline void SetMethodParam(TMethodCall *method, T p) { method->SetParam(p); }
4034template<> void SetMethodParam(TMethodCall *method, LongDouble_t p) { method->SetParam((Double_t)p); }
4035
4036// TTree::Scan prints each integer column through TTreeFormula, which evaluates it
4037// in a `long double` accumulator (for both the "l" and "ll" formats). That holds
4038// integers exactly only up to 2^digits (its mantissa size: 64 bits on x86, but
4039// just 53 where `long double` is merely a 64-bit `double`, e.g. macOS ARM), so a
4040// value beyond that range is printed rounded. This cannot be fixed without
4041// changing the (frozen) floating-point arithmetic of TTreeFormula, so it is a
4042// known limitation; flag it loudly instead. See
4043// https://github.com/root-project/root/issues/7844.
4044inline void CheckIntegerPrintPrecision(LongDouble_t evaluated, const char *expression)
4045{
4046 // Integers are represented exactly only while their magnitude stays below
4047 // 2^digits. At or above that threshold not every integer is representable, so
4048 // the printed value may be rounded -- and a rounded result can even land back
4049 // on the threshold (e.g. 2^53 + 1 -> 2^53 in a 53-bit type), so the comparison
4050 // is inclusive to avoid missing such cases.
4051 const LongDouble_t threshold = std::ldexp(1.0L, std::numeric_limits<LongDouble_t>::digits); // 2^digits
4052 // Deliberately a kError (not kWarning) emitted for every offending value with
4053 // no deduplication: silently printing a wrong integer is the trap we want to
4054 // make impossible to miss, so the diagnostic is intentionally as loud as
4055 // possible even on a many-row Scan.
4056 if (evaluated >= threshold || evaluated <= -threshold)
4057 ::Error("TTreeFormula::PrintValue",
4058 "the integer value of \"%s\" may be inexact: its magnitude reaches 2^%d, "
4059 "the point beyond which the long double used to evaluate it can no longer "
4060 "represent every integer exactly, so the printed value may be rounded. "
4061 "This is a known limitation "
4062 "(https://github.com/root-project/root/issues/7844).",
4063 expression, std::numeric_limits<LongDouble_t>::digits);
4064}
4065}
4066
4067template<typename T> inline T TTreeFormula::GetConstant(Int_t k) { return fConst[k]; }
4068template<> inline LongDouble_t TTreeFormula::GetConstant(Int_t k) {
4069 if( !fConstLD ) {
4070 // create LD version of the constants list by rescanning all literals used in the expression
4072 for (Int_t op=0; op<fNoper ; ++op) {
4073 const Int_t oper = GetOper()[op];
4074 if( (oper >> kTFOperShift) == kConstant ) {
4075 int i = (oper & kTFOperMask);
4076 if( !strncmp(fExpr[op], "0x", 2) || !strncmp(fExpr[op], "0X", 2) ) {
4077 ULong64_t val;
4078 sscanf( fExpr[op], "%llx", &val );
4079 fConstLD[i] = (LongDouble_t)val;
4080 } else {
4081 sscanf( fExpr[op], "%Lg", &fConstLD[i] );
4083 }
4084 }
4085 }
4086 return fConstLD[k];
4087}
4089
4090////////////////////////////////////////////////////////////////////////////
4091/// \brief Evaluate this treeformula
4092/// \tparam T The type used to interpret the numbers then used for the operations
4093/// \param instance iteration instance
4094/// \param stringStackArg formula as string
4095/// \return the result of the evaluation, or a signaling NaN if out of bounds
4096///
4097/// \warning Care has to be taken before calling this function with std::vector
4098/// or dynamically sized objects, rather than plain fixed-size arrays.
4099/// For example, this works without problems:
4100/// ~~~{.cpp}
4101/// TTree t("t", "t");
4102/// Float_t x[2]{};
4103/// t.Branch("xa", &x, "x[2]/F");
4104/// x[1] = 1;
4105/// t.Fill();
4106/// x[1] = 2;
4107/// t.Fill();
4108/// t.Scan();
4109/// TTreeFormula tfx("tfx", "xa[1]", &t);
4110/// t.GetEntry(0);
4111/// tfx.EvalInstance()
4112/// t.GetEntry(1);
4113/// tfx.EvalInstance()
4114/// ~~~
4115/// But the following fails (independently on whether the size changed or not between entries):
4116/// ~~~{.cpp}
4117/// TTree t("t", "t");
4118/// vector<Short_t> v;
4119/// t.Branch("vec", &v);
4120/// v.push_back(2);
4121/// v.push_back(3);
4122/// t.Fill();
4123/// v.clear();
4124/// v.push_back(4);
4125/// v.push_back(5);
4126/// t.Fill();
4127/// t.Scan();
4128/// TTreeFormula tfv1("tfv1", "vec[1]", &t);
4129/// TTreeFormula tfv("tfv", "vec", &t);
4130/// t.GetEntry(0);
4131/// tfv1.EvalInstance()
4132/// tfv.EvalInstance(1)
4133/// t.GetEntry(1);
4134/// tfv1.EvalInstance()
4135/// tfv.EvalInstance(1)
4136/// ~~~
4137/// To prevent this, when working with objects with dynamic size for each entry, one needs
4138/// to mimick what TTree::Scan does, i.e. to check the value of
4139/// `GetNdata()` before calling `EvalInstance()`:
4140/// ~~~{.cpp}
4141/// t.GetEntry(0);
4142/// if (tfv1.GetNdata() > 0)
4143/// tfv1.EvalInstance()
4144/// if (tfv.GetNdata() > 1)
4145/// tfv.EvalInstance(1)
4146/// t.GetEntry(1);
4147/// if (tfv1.GetNdata() > 0)
4148/// tfv1.EvalInstance()
4149/// if (tfv.GetNdata() > 1)
4150/// tfv.EvalInstance(1)
4151/// ~~~
4152/// Note that for `tfv1`, even if the index is fixed in the formula and even if each entry
4153/// had the same std::vector size, since the formula contains a branch with theoretically variable size,
4154/// one must check GetNData() as there might 0 or 1 answers. Since even with fixed index,
4155/// the collection might be too small to fulfill it.
4156/// TTreeFormula::GetMultiplicity tells you (indirectly) whether you need to call GetNData or not for a given formula.
4157
4158template<typename T>
4160{
4161// Note that the redundancy and structure in this code is tailored to improve
4162// efficiencies.
4164 if (fNoper == 1 && fNcodes > 0) {
4165
4166 switch (fLookupType[0]) {
4167 case kDirect: {
4169 return leaf->GetTypedValue<T>(real_instance);
4170 }
4171 case kMethod: {
4173 ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->SetBranch(leaf->GetBranch());
4174 return GetValueFromMethod(0,leaf);
4175 }
4176 case kDataMember: {
4178 ((TFormLeafInfo*)fDataMembers.UncheckedAt(0))->SetBranch(leaf->GetBranch());
4180 }
4181 case kTreeMember: {
4184 }
4185 case kIndexOfEntry: return (T)fTree->GetReadEntry();
4186 case kIndexOfLocalEntry: return (T)fTree->GetTree()->GetReadEntry();
4187 case kEntries: return (T)fTree->GetEntries();
4188 case kLocalEntries: return (T)fTree->GetTree()->GetEntries();
4189 case kLength: return fManager->fNdata;
4190 case kLengthFunc: return ((TTreeFormula*)fAliases.UncheckedAt(0))->GetNdata();
4191 case kIteration: return instance;
4192 case kSum: return Summing<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4193 case kMin: return FindMin<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4194 case kMax: return FindMax<T>((TTreeFormula*)fAliases.UncheckedAt(0));
4195 case kEntryList: {
4196 TEntryList *elist = (TEntryList*)fExternalCuts.At(0);
4197 return elist->Contains(fTree->GetTree()->GetReadEntry());
4198 }
4199 case -1: break;
4200 }
4201 switch (fCodes[0]) {
4202 case -2: {
4204 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4205 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
4207 fx->ResetLoading();
4208 fy->ResetLoading();
4209 }
4210 T xcut = fx->EvalInstance<T>(instance);
4211 T ycut = fy->EvalInstance<T>(instance);
4212 return gcut->IsInside(xcut,ycut);
4213 }
4214 case -1: {
4216 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4218 fx->ResetLoading();
4219 }
4220 return fx->EvalInstance<T>(instance);
4221 }
4222 default: return TMath::SignalingNaN();
4223 }
4224 }
4225
4226 T tab[kMAXFOUND];
4227 const Int_t kMAXSTRINGFOUND = 10;
4228 const char *stringStackLocal[kMAXSTRINGFOUND];
4230
4231 const bool willLoad = (instance==0 || fNeedLoading); fNeedLoading = false;
4232 if (willLoad) fDidBooleanOptimization = false;
4233
4234 Int_t pos = 0;
4235 Int_t pos2 = 0;
4236 for (Int_t i=0; i<fNoper ; ++i) {
4237
4238 const Int_t oper = GetOper()[i];
4239 const Int_t newaction = oper >> kTFOperShift;
4240
4242 // ROOT::v5::TFormula operands.
4243
4244 // one of the most used cases
4245 if (newaction==kConstant) { pos++; tab[pos-1] = GetConstant<T>(oper & kTFOperMask); continue; }
4246
4247 switch(newaction) {
4248
4249 case kEnd : return tab[0];
4250 case kAdd : pos--; tab[pos-1] += tab[pos]; continue;
4251 case kSubstract : pos--; tab[pos-1] -= tab[pos]; continue;
4252 case kMultiply : pos--; tab[pos-1] *= tab[pos]; continue;
4253 case kDivide : pos--; if (tab[pos] == 0) tab[pos-1] = 0; // division by 0
4254 else tab[pos-1] /= tab[pos];
4255 continue;
4256 case kModulo : {pos--;
4257 Long64_t int1((Long64_t)tab[pos-1]);
4258 Long64_t int2((Long64_t)tab[pos]);
4259 tab[pos-1] = T(int1 % int2);
4260 continue;}
4261
4262 case kcos : tab[pos-1] = TMath::Cos(tab[pos-1]); continue;
4263 case ksin : tab[pos-1] = TMath::Sin(tab[pos-1]); continue;
4264 case ktan : if (TMath::Cos(tab[pos-1]) == 0) {tab[pos-1] = 0;} // { tangente indeterminee }
4265 else tab[pos-1] = TMath::Tan(tab[pos-1]);
4266 continue;
4267 case kacos : if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4268 else tab[pos-1] = TMath::ACos(tab[pos-1]);
4269 continue;
4270 case kasin : if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4271 else tab[pos-1] = TMath::ASin(tab[pos-1]);
4272 continue;
4273 case katan : tab[pos-1] = TMath::ATan(tab[pos-1]); continue;
4274 case kcosh : tab[pos-1] = TMath::CosH(tab[pos-1]); continue;
4275 case ksinh : tab[pos-1] = TMath::SinH(tab[pos-1]); continue;
4276 case ktanh : if (TMath::CosH(tab[pos-1]) == 0) {tab[pos-1] = 0;} // { tangente indeterminee }
4277 else tab[pos-1] = TMath::TanH(tab[pos-1]);
4278 continue;
4279 case kacosh: if (tab[pos-1] < 1) {tab[pos-1] = 0;} // indetermination
4280 else tab[pos-1] = TMath::ACosH(tab[pos-1]);
4281 continue;
4282 case kasinh: tab[pos-1] = TMath::ASinH(tab[pos-1]); continue;
4283 case katanh: if (TMath::Abs(tab[pos-1]) > 1) {tab[pos-1] = 0;} // indetermination
4284 else tab[pos-1] = TMath::ATanH(tab[pos-1]);
4285 continue;
4286 case katan2: pos--; tab[pos-1] = TMath::ATan2(tab[pos-1],tab[pos]); continue;
4287
4288 case kfmod : pos--; tab[pos-1] = fmod_local(tab[pos-1],tab[pos]); continue;
4289 case kpow : pos--; tab[pos-1] = TMath::Power(tab[pos-1],tab[pos]); continue;
4290 case ksq : tab[pos-1] = tab[pos-1]*tab[pos-1]; continue;
4291 case ksqrt : tab[pos-1] = TMath::Sqrt(tab[pos-1]); continue;
4292
4293 case kstrstr : pos2 -= 2; pos++;if (strstr(stringStack[pos2],stringStack[pos2+1])) tab[pos-1]=1;
4294 else tab[pos-1]=0;
4295 continue;
4296
4297 case kmin : pos--; tab[pos-1] = std::min(tab[pos-1],tab[pos]); continue;
4298 case kmax : pos--; tab[pos-1] = std::max(tab[pos-1],tab[pos]); continue;
4299
4300 case klog : if (tab[pos-1] > 0) tab[pos-1] = TMath::Log(tab[pos-1]);
4301 else {tab[pos-1] = 0;} //{indetermination }
4302 continue;
4303 case kexp : { Double_t dexp = tab[pos-1];
4304 if (dexp < -700) {tab[pos-1] = 0; continue;}
4305 if (dexp > 700) {tab[pos-1] = TMath::Exp(700); continue;}
4306 tab[pos-1] = TMath::Exp(dexp); continue;
4307 }
4308 case klog10: if (tab[pos-1] > 0) tab[pos-1] = TMath::Log10(tab[pos-1]);
4309 else {tab[pos-1] = 0;} //{indetermination }
4310 continue;
4311
4312 case kpi : pos++; tab[pos-1] = TMath::Pi(); continue;
4313
4314 case kabs : tab[pos-1] = TMath::Abs(tab[pos-1]); continue;
4315 case ksign : if (tab[pos-1] < 0) tab[pos-1] = -1; else tab[pos-1] = 1;
4316 continue;
4317 case kint : tab[pos-1] = T(Long64_t(tab[pos-1])); continue;
4318 case kSignInv: tab[pos-1] = -1 * tab[pos-1]; continue;
4319 case krndm : pos++; tab[pos-1] = gRandom->Rndm(); continue;
4320
4321 case kAnd : pos--; if (tab[pos-1]!=0 && tab[pos]!=0) tab[pos-1]=1;
4322 else tab[pos-1]=0;
4323 continue;
4324 case kOr : pos--; if (tab[pos-1]!=0 || tab[pos]!=0) tab[pos-1]=1;
4325 else tab[pos-1]=0;
4326 continue;
4327
4328 case kEqual : pos--; tab[pos-1] = (tab[pos-1] == tab[pos]) ? 1 : 0; continue;
4329 case kNotEqual : pos--; tab[pos-1] = (tab[pos-1] != tab[pos]) ? 1 : 0; continue;
4330 case kLess : pos--; tab[pos-1] = (tab[pos-1] < tab[pos]) ? 1 : 0; continue;
4331 case kGreater : pos--; tab[pos-1] = (tab[pos-1] > tab[pos]) ? 1 : 0; continue;
4332 case kLessThan : pos--; tab[pos-1] = (tab[pos-1] <= tab[pos]) ? 1 : 0; continue;
4333 case kGreaterThan: pos--; tab[pos-1] = (tab[pos-1] >= tab[pos]) ? 1 : 0; continue;
4334 case kNot : tab[pos-1] = (tab[pos-1] != 0) ? 0 : 1; continue;
4335
4336 case kStringEqual : pos2 -= 2; pos++; if (!strcmp(stringStack[pos2+1],stringStack[pos2])) tab[pos-1]=1;
4337 else tab[pos-1]=0;
4338 continue;
4339 case kStringNotEqual: pos2 -= 2; pos++;if (strcmp(stringStack[pos2+1],stringStack[pos2])) tab[pos-1]=1;
4340 else tab[pos-1]=0;
4341 continue;
4342
4343 case kBitAnd : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) & ((ULong64_t) tab[pos]); continue;
4344 case kBitOr : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) | ((ULong64_t) tab[pos]); continue;
4345 case kLeftShift : pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) <<((ULong64_t) tab[pos]); continue;
4346 case kRightShift: pos--; tab[pos-1]= ((ULong64_t) tab[pos-1]) >>((ULong64_t) tab[pos]); continue;
4347
4348 case kJump : i = (oper & kTFOperMask); continue;
4349 case kJumpIf : {
4350 pos--;
4351 if (!tab[pos]) {
4352 i = (oper & kTFOperMask);
4353 // If we skip the left (true) side of the if statement we may,
4354 // skip some of the branch loading (since we remove duplicate branch
4355 // request (in TTreeFormula constructor) and so we need to force the
4356 // loading here.
4358 }
4359 continue;
4360 }
4361
4362 case kStringConst: {
4363 // String
4364 pos2++; stringStack[pos2-1] = (char*)fExpr[i].Data();
4365 if (fAxis) {
4366 // See TT_EVAL_INIT
4368 return bin;
4369 }
4370 continue;
4371 }
4372
4373 case kBoolOptimize: {
4374 // boolean operation optimizer
4375
4376 int param = (oper & kTFOperMask);
4377 bool skip = false;
4378 int op = param % 10; // 1 is && , 2 is ||
4379
4380 if (op == 1 && (!tab[pos-1]) ) {
4381 // &&: skip the right part if the left part is already false
4382
4383 skip = true;
4384
4385 // Preserve the existing behavior (i.e. the result of a&&b is
4386 // either 0 or 1)
4387 tab[pos-1] = 0;
4388
4389 } else if (op == 2 && tab[pos-1] ) {
4390 // ||: skip the right part if the left part is already true
4391
4392 skip = true;
4393
4394 // Preserve the existing behavior (i.e. the result of a||b is
4395 // either 0 or 1)
4396 tab[pos-1] = 1;
4397 }
4398
4399 if (skip) {
4400 int toskip = param / 10;
4401 i += toskip;
4403 }
4404 continue;
4405 }
4406
4407 case kFunctionCall: {
4408 // an external function call
4409
4410 int param = (oper & kTFOperMask);
4411 int fno = param / 1000;
4412 int nargs = param % 1000;
4413
4414 // Retrieve the function
4416
4417 // Set the arguments
4418 method->ResetParam();
4419 if (nargs) {
4420 UInt_t argloc = pos-nargs;
4421 for(Int_t j=0;j<nargs;j++,argloc++,pos--) {
4423 }
4424 }
4425 pos++;
4426 Double_t ret = 0;
4427 method->Execute(ret);
4428 tab[pos-1] = ret; // check for the correct conversion!
4429
4430 continue;
4431 }
4432
4433// case kParameter: { pos++; tab[pos-1] = fParams[(oper & kTFOperMask)]; continue; }
4434 }
4435
4436 } else {
4437 // TTreeFormula operands.
4438
4439 // a tree variable (the most used case).
4440
4441 if (newaction == kDefinedVariable) {
4442
4443 const Int_t code = (oper & kTFOperMask);
4444 const Int_t lookupType = fLookupType[code];
4445 switch (lookupType) {
4446 case kIndexOfEntry: tab[pos++] = (T)fTree->GetReadEntry(); continue;
4447 case kIndexOfLocalEntry: tab[pos++] = (T)fTree->GetTree()->GetReadEntry(); continue;
4448 case kEntries: tab[pos++] = (T)fTree->GetEntries(); continue;
4449 case kLocalEntries: tab[pos++] = (T)fTree->GetTree()->GetEntries(); continue;
4450 case kLength: tab[pos++] = fManager->fNdata; continue;
4451 case kLengthFunc: tab[pos++] = ((TTreeFormula*)fAliases.UncheckedAt(i))->GetNdata(); continue;
4452 case kIteration: tab[pos++] = instance; continue;
4453 case kSum: tab[pos++] = Summing<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4454 case kMin: tab[pos++] = FindMin<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4455 case kMax: tab[pos++] = FindMax<T>((TTreeFormula*)fAliases.UncheckedAt(i)); continue;
4456
4457 case kDirect: { TT_EVAL_INIT_LOOP; tab[pos++] = leaf->GetTypedValue<T>(real_instance); continue; }
4458 case kMethod: { TT_EVAL_INIT_LOOP; tab[pos++] = GetValueFromMethod(code,leaf); continue; }
4460 GetTypedValue<T>(leaf,real_instance); continue; }
4462 GetTypedValue<T>((TLeaf*)nullptr,real_instance); continue; }
4463 case kEntryList: { TEntryList *elist = (TEntryList*)fExternalCuts.At(code);
4464 tab[pos++] = elist->Contains(fTree->GetReadEntry());
4465 continue;}
4466 case -1: break;
4467 default: tab[pos++] = 0; continue;
4468 }
4469 switch (fCodes[code]) {
4470 case -2: {
4471 TCutG *gcut = (TCutG*)fExternalCuts.At(code);
4472 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4473 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
4475 fx->ResetLoading();
4476 fy->ResetLoading();
4477 }
4478 T xcut = fx->EvalInstance<T>(instance);
4479 T ycut = fy->EvalInstance<T>(instance);
4480 tab[pos++] = gcut->IsInside(xcut,ycut);
4481 continue;
4482 }
4483 case -1: {
4484 TCutG *gcut = (TCutG*)fExternalCuts.At(code);
4485 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
4487 fx->ResetLoading();
4488 }
4489 tab[pos++] = fx->EvalInstance<T>(instance);
4490 continue;
4491 }
4492 default: {
4493 tab[pos++] = 0;
4494 continue;
4495 }
4496 }
4497 }
4498 switch(newaction) {
4499
4500 // a TTree Variable Alias (i.e. a sub-TTreeFormula)
4501 case kAlias: {
4502 int aliasN = i;
4505
4506 subform->fDidBooleanOptimization = fDidBooleanOptimization;
4507 T param = subform->EvalInstance<T>(instance);
4508
4509 tab[pos] = param; pos++;
4510 continue;
4511 }
4512 // a TTree Variable Alias String (i.e. a sub-TTreeFormula)
4513 case kAliasString: {
4514 int aliasN = i;
4517
4518 pos2++;
4519 subform->fDidBooleanOptimization = fDidBooleanOptimization;
4520 stringStack[pos2-1] = subform->EvalStringInstance(instance);
4521 continue;
4522 }
4523 case kMinIf: {
4524 int alternateN = i;
4526 TTreeFormula *condition = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN+1));
4527 T param = FindMin<T>(primary,condition);
4528 ++i; // skip the place holder for the condition
4529 tab[pos] = param; pos++;
4530 continue;
4531 }
4532 case kMaxIf: {
4533 int alternateN = i;
4535 TTreeFormula *condition = static_cast<TTreeFormula*>(fAliases.UncheckedAt(alternateN+1));
4536 T param = FindMax<T>(primary,condition);
4537 ++i; // skip the place holder for the condition
4538 tab[pos] = param; pos++;
4539 continue;
4540 }
4541
4542 // a TTree Variable Alternate (i.e. a sub-TTreeFormula)
4543 case kAlternate: {
4544 int alternateN = i;
4546
4547 // First check whether we are in range for the primary formula
4549
4550 T param = primary->EvalInstance<T>(instance);
4551
4552 ++i; // skip the alternate value.
4553
4554 tab[pos] = param; pos++;
4555 } else {
4556 // The primary is not in range, we will calculate the alternate value
4557 // via the next operation (which will be a intentional).
4558
4559 // kAlias no operations
4560 }
4561 continue;
4562 }
4563 case kAlternateString: {
4564 int alternateN = i;
4566
4567 // First check whether we are in range for the primary formula
4569
4570 pos2++;
4571 stringStack[pos2-1] = primary->EvalStringInstance(instance);
4572
4573 ++i; // skip the alternate value.
4574
4575 } else {
4576 // The primary is not in range, we will calculate the alternate value
4577 // via the next operation (which will be a kAlias).
4578
4579 // intentional no operations
4580 }
4581 continue;
4582 }
4583
4584 // a tree string
4585 case kDefinedString: {
4588
4589 // Now let calculate what physical instance we really need.
4591
4592 if (instance==0 || fNeedLoading) {
4593 fNeedLoading = false;
4594 TBranch *branch = leafc->GetBranch();
4595 Long64_t readentry = branch->GetTree()->GetReadEntry();
4597 if (res < 0) {
4598 Error("EvalInstance", "Branch could not be loaded:%d", res);
4599 continue;
4600 }
4601 } else {
4602 // In the cases where we are behind (i.e. right of) a potential boolean optimization
4603 // this tree variable reading may have not been executed with instance==0 which would
4604 // result in the branch being potentially not read in.
4606 TBranch *br = leafc->GetBranch();
4607 Long64_t treeEntry = br->GetTree()->GetReadEntry();
4608 auto res = R__LoadBranch(br, treeEntry, true);
4609 if (res < 0) {
4610 Error("EvalInstance", "Branch could not be loaded:%d", res);
4611 continue;
4612 }
4613 }
4615 }
4616 pos2++;
4618 stringStack[pos2-1] = (char*)leafc->GetValuePointer();
4619 } else {
4621 }
4622 continue;
4623 }
4624
4625 }
4626 }
4627
4628 R__ASSERT(i<fNoper);
4629 }
4630
4631 //std::cout << __PRETTY_FUNCTION__ << " returning " << tab[0] << std::endl;
4632 return tab[0];
4633}
4634
4635// Template instantiations
4636template double TTreeFormula::EvalInstance<double> (int, char const**);
4637template long double TTreeFormula::EvalInstance<long double> (int, char const**);
4638template long long TTreeFormula::EvalInstance<long long> (int, char const**);
4639
4640////////////////////////////////////////////////////////////////////////////////
4641/// Return DataMember corresponding to code.
4642///
4643/// function called by TLeafObject::GetValue
4644/// with the value of fLookupType computed in TTreeFormula::DefinedVariable
4645
4647{
4648 return (TFormLeafInfo *)fDataMembers.UncheckedAt(code);
4650}
4651
4652////////////////////////////////////////////////////////////////////////////////
4653/// Return leaf corresponding to serial number n.
4654
4656{
4657 return (TLeaf*)fLeaves.UncheckedAt(n);
4658}
4659
4660////////////////////////////////////////////////////////////////////////////////
4661/// Return methodcall corresponding to code.
4662///
4663/// function called by TLeafObject::GetValue
4664/// with the value of fLookupType computed in TTreeFormula::DefinedVariable
4665
4667{
4668 return (TMethodCall *)fMethods.UncheckedAt(code);
4670}
4671
4672////////////////////////////////////////////////////////////////////////////////
4673/// Return number of available instances in the formula.
4674
4676{
4678}
4679
4680////////////////////////////////////////////////////////////////////////////////
4681/// Return result of a leafobject method.
4682
4684{
4686
4687 if (!m) {
4688 return 0.0;
4689 }
4690
4691 void* thisobj = nullptr;
4692 if (leaf->InheritsFrom(TLeafObject::Class())) {
4693 thisobj = ((TLeafObject*) leaf)->GetObject();
4694 } else {
4695 TBranchElement* branch = (TBranchElement*) ((TLeafElement*) leaf)->GetBranch();
4696 Int_t id = branch->GetID();
4697 // FIXME: This is wrong for a top-level branch.
4698 Int_t offset = 0;
4699 if (id > -1) {
4700 TStreamerInfo* info = branch->GetInfo();
4701 if (info) {
4702 offset = info->GetElementOffset(id);
4703 } else {
4704 Warning("GetValueFromMethod", "No streamer info for branch %s.", branch->GetName());
4705 }
4706 }
4707 if (id < 0) {
4708 char* address = branch->GetObject();
4709 thisobj = address;
4710 } else {
4711 //char* address = branch->GetAddress();
4712 char* address = branch->GetObject();
4713 if (address) {
4714 thisobj = *((char**) (address + offset));
4715 } else {
4716 // FIXME: If the address is not set, the object won't be either!
4717 thisobj = branch->GetObject();
4718 }
4719 }
4720 }
4721
4722 TMethodCall::EReturnType r = m->ReturnType();
4723
4724 if (r == TMethodCall::kLong) {
4725 Longptr_t l = 0;
4726 m->Execute(thisobj, l);
4727 return (Double_t) l;
4728 }
4729
4730 if (r == TMethodCall::kDouble) {
4731 Double_t d = 0.0;
4732 m->Execute(thisobj, d);
4733 return d;
4734 }
4735
4736 m->Execute(thisobj);
4737
4738 return 0;
4739}
4740
4741////////////////////////////////////////////////////////////////////////////////
4742/// Return result of a leafobject method.
4743
4745{
4747
4748 if (!m) {
4749 return nullptr;
4750 }
4751
4752 void* thisobj;
4753 if (leaf->InheritsFrom(TLeafObject::Class())) {
4754 thisobj = ((TLeafObject*) leaf)->GetObject();
4755 } else {
4756 TBranchElement* branch = (TBranchElement*) ((TLeafElement*) leaf)->GetBranch();
4757 Int_t id = branch->GetID();
4758 Int_t offset = 0;
4759 if (id > -1) {
4760 TStreamerInfo* info = branch->GetInfo();
4761 if (info) {
4762 offset = info->GetElementOffset(id);
4763 } else {
4764 Warning("GetValuePointerFromMethod", "No streamer info for branch %s.", branch->GetName());
4765 }
4766 }
4767 if (id < 0) {
4768 char* address = branch->GetObject();
4769 thisobj = address;
4770 } else {
4771 //char* address = branch->GetAddress();
4772 char* address = branch->GetObject();
4773 if (address) {
4774 thisobj = *((char**) (address + offset));
4775 } else {
4776 // FIXME: If the address is not set, the object won't be either!
4777 thisobj = branch->GetObject();
4778 }
4779 }
4780 }
4781
4782 TMethodCall::EReturnType r = m->ReturnType();
4783
4784 if (r == TMethodCall::kLong) {
4785 Longptr_t l = 0;
4786 m->Execute(thisobj, l);
4787 return nullptr;
4788 }
4789
4790 if (r == TMethodCall::kDouble) {
4791 Double_t d = 0.0;
4792 m->Execute(thisobj, d);
4793 return nullptr;
4794 }
4795
4796 if (r == TMethodCall::kOther) {
4797 char* c = nullptr;
4798 m->Execute(thisobj, &c);
4799 return c;
4800 }
4801
4802 m->Execute(thisobj);
4803
4804 return nullptr;
4805}
4806
4807////////////////////////////////////////////////////////////////////////////////
4808/// Return TRUE if the formula corresponds to one single Tree leaf
4809/// and this leaf is short, int or unsigned short, int
4810/// When a leaf is of type integer or string, the generated histogram is forced
4811/// to have an integer bin width
4812
4813bool TTreeFormula::IsInteger(bool fast) const
4814{
4815 if (fast) {
4816 if (TestBit(kIsInteger)) return true;
4817 else return false;
4818 }
4819
4820 if (fNoper==2 && GetAction(0)==kAlternate) {
4823 return subform->IsInteger(false);
4824 }
4825
4826 if (GetAction(0)==kMinIf || GetAction(0)==kMaxIf) {
4827 return false;
4828 }
4829
4830 if (fNoper > 1) return false;
4831
4832 if (GetAction(0)==kAlias) {
4835 return subform->IsInteger(false);
4836 }
4837
4838 if (fLeaves.GetEntries() != 1) {
4839 switch (fLookupType[0]) {
4840 case kIndexOfEntry:
4841 case kIndexOfLocalEntry:
4842 case kEntries:
4843 case kLocalEntries:
4844 case kLength:
4845 case kLengthFunc:
4846 case kIteration:
4847 return true;
4848 case kSum:
4849 case kMin:
4850 case kMax:
4851 case kEntryList:
4852 default:
4853 return false;
4854 }
4855 }
4856
4857 if (EvalClass()==TBits::Class()) return true;
4858
4859 if (IsLeafInteger(0) || IsLeafString(0)) return true;
4860 return false;
4861}
4863////////////////////////////////////////////////////////////////////////////////
4864/// Return TRUE if the leaf corresponding to code is short, int or unsigned
4865/// short, int When a leaf is of type integer, the generated histogram is
4866/// forced to have an integer bin width
4867
4868bool TTreeFormula::IsLeafInteger(Int_t code) const
4869{
4870 TLeaf *leaf = (TLeaf*)fLeaves.At(code);
4871 if (!leaf) {
4872 switch (fLookupType[code]) {
4873 case kIndexOfEntry:
4874 case kIndexOfLocalEntry:
4875 case kEntries:
4876 case kLocalEntries:
4877 case kLength:
4878 case kLengthFunc:
4879 case kIteration:
4880 return true;
4881 case kSum:
4882 case kMin:
4883 case kMax:
4884 case kEntryList:
4885 default:
4886 return false;
4887 }
4888 }
4889 if (fAxis) return true;
4891 switch (fLookupType[code]) {
4892 case kMethod:
4893 case kTreeMember:
4894 case kDataMember:
4895 info = GetLeafInfo(code);
4896 return info->IsInteger();
4897 case kDirect:
4898 break;
4899 }
4900 if (!strcmp(leaf->GetTypeName(),"Int_t")) return true;
4901 if (!strcmp(leaf->GetTypeName(),"Short_t")) return true;
4902 if (!strcmp(leaf->GetTypeName(),"UInt_t")) return true;
4903 if (!strcmp(leaf->GetTypeName(),"UShort_t")) return true;
4904 if (!strcmp(leaf->GetTypeName(),"Bool_t")) return true;
4905 if (!strcmp(leaf->GetTypeName(),"Char_t")) return true;
4906 if (!strcmp(leaf->GetTypeName(),"UChar_t")) return true;
4907 if (!strcmp(leaf->GetTypeName(),"Long64_t")) return true;
4908 if (!strcmp(leaf->GetTypeName(),"ULong64_t")) return true;
4909 if (!strcmp(leaf->GetTypeName(),"string")) return true;
4910 return false;
4911}
4912
4913////////////////////////////////////////////////////////////////////////////////
4914/// Return TRUE if the formula is a string
4915
4916bool TTreeFormula::IsString() const
4917{
4918 // See TTreeFormula::Init for the setting of kIsCharacter.
4919 return TestBit(kIsCharacter);
4921
4922////////////////////////////////////////////////////////////////////////////////
4923/// Return true if the expression at the index 'oper' is to be treated as
4924/// as string.
4925
4927{
4928 if (ROOT::v5::TFormula::IsString(oper)) return true;
4929 if (GetAction(oper)==kDefinedString) return true;
4930 if (GetAction(oper)==kAliasString) return true;
4931 if (GetAction(oper)==kAlternateString) return true;
4932 return false;
4933}
4934
4935////////////////////////////////////////////////////////////////////////////////
4936/// Return TRUE if the leaf or data member corresponding to code is a string
4937
4938bool TTreeFormula::IsLeafString(Int_t code) const
4939{
4940 TLeaf *leaf = (TLeaf*)fLeaves.At(code);
4942 if (fLookupType[code]==kTreeMember) {
4943 info = GetLeafInfo(code);
4944 return info->IsString();
4945 }
4946
4947 switch(fLookupType[code]) {
4948 case kDirect:
4949 if ( !leaf->IsUnsigned() && (leaf->InheritsFrom(TLeafC::Class()) || leaf->InheritsFrom(TLeafB::Class()) ) ) {
4950 // Need to find out if it is an 'array' or a pointer.
4951 if (leaf->GetLenStatic() > 1) return true;
4952
4953 // Now we need to differentiate between a variable length array and
4954 // a TClonesArray.
4955 if (leaf->GetLeafCount()) {
4956 const char* indexname = leaf->GetLeafCount()->GetName();
4957 if (indexname[strlen(indexname)-1] == '_' ) {
4958 // This in a clones array
4959 return false;
4960 } else {
4961 // this is a variable length char array
4962 return true;
4963 }
4964 }
4965 return false;
4966 } else if (leaf->InheritsFrom(TLeafElement::Class())) {
4967 TBranchElement * br = (TBranchElement*)leaf->GetBranch();
4968 Int_t bid = br->GetID();
4969 if (bid < 0) return false;
4970 if (br->GetInfo()==nullptr || !br->GetInfo()->IsCompiled()) {
4971 // Case where the file is corrupted is some ways.
4972 // We can not get to the actual type of the data
4973 // let's assume it is NOT a string.
4974 return false;
4975 }
4976 TStreamerElement * elem = (TStreamerElement*) br->GetInfo()->GetElement(bid);
4977 if (!elem) {
4978 // Case where the file is corrupted is some ways.
4979 // We can not get to the actual type of the data
4980 // let's assume it is NOT a string.
4981 return false;
4982 }
4983 if (elem->GetNewType() == TStreamerInfo::kOffsetL + TStreamerInfo::kChar) {
4984 // Check whether a specific element of the string is specified!
4985 if (fIndexes[code][fNdimensions[code]-1] != -1) return false;
4986 return true;
4987 }
4988 if ( elem->GetNewType() == TStreamerInfo::kCharStar) {
4989 // Check whether a specific element of the string is specified!
4990 if (fNdimensions[code] && fIndexes[code][fNdimensions[code]-1] != -1) return false;
4991 return true;
4992 }
4993 return false;
4994 } else {
4995 return false;
4996 }
4997 case kMethod:
4998 //TMethodCall *m = GetMethodCall(code);
4999 //TMethodCall::EReturnType r = m->ReturnType();
5000 return false;
5001 case kDataMember:
5002 info = GetLeafInfo(code);
5003 return info->IsString();
5004 default:
5005 return false;
5006 }
5007}
5008
5009////////////////////////////////////////////////////////////////////////////////
5010/// Return value of variable as a string
5011///
5012/// - mode = -2 : Print line with ***
5013/// - mode = -1 : Print column names
5014/// - mode = 0 : Print column values
5015
5017{
5018 return PrintValue(mode,0);
5019}
5020
5021////////////////////////////////////////////////////////////////////////////////
5022/// Return value of variable as a string
5023///
5024/// - mode = -2 : Print line with ***
5025/// - mode = -1 : Print column names
5026/// - mode = 0 : Print column values
5027///
5028/// decform contains the requested format (with the same convention as printf).
5029
5030char *TTreeFormula::PrintValue(Int_t mode, Int_t instance, const char *decform) const
5031{
5032 const int kMAXLENGTH = kMaxLen;
5033 static char value[kMAXLENGTH];
5034
5035 if (mode == -2) {
5036 for (int i = 0; i < kMAXLENGTH-1; i++)
5037 value[i] = '*';
5038 value[kMAXLENGTH-1] = 0;
5039 } else if (mode == -1) {
5040 snprintf(value, kMAXLENGTH-1, "%s", GetTitle());
5041 } else if (mode == 0) {
5042 if ( (fNstring && fNval==0 && fNoper==1) || IsString() )
5043 {
5044 const char * val = nullptr;
5045 if (GetAction(0)==kStringConst) {
5046 val = fExpr[0].Data();
5047 } else if (instance<fNdata[0]) {
5048 if (fNoper == 1) {
5049 if (fLookupType[0]==kTreeMember) {
5050 val = (char*)GetLeafInfo(0)->GetValuePointer((TLeaf*)nullptr,instance);
5051 } else {
5053 TBranch *branch = leaf->GetBranch();
5054 Long64_t readentry = branch->GetTree()->GetReadEntry();
5056 if (res < 0) {
5057 Error("PrintValue", "Branch could not be loaded:%d", res);
5058 } else {
5059 if (fLookupType[0]==kDirect && fNoper==1) {
5060 val = (const char*)leaf->GetValuePointer();
5061 } else {
5062 val = ((TTreeFormula*)this)->EvalStringInstance(instance);
5063 }
5064 }
5065 }
5066 } else {
5067 val = ((TTreeFormula*)this)->EvalStringInstance(instance);
5068 }
5069 }
5070 if (val) {
5071 strlcpy(value, val, kMAXLENGTH);
5072 } else {
5073 value[0] = '\0';
5074 }
5075 value[kMAXLENGTH-1] = 0;
5076 } else {
5077 //NOTE: This is terrible form ... but is forced upon us by the fact that we can not
5078 //use the mutable keyword AND we should keep PrintValue const.
5079 Int_t real_instance = ((TTreeFormula*)this)->GetRealInstance(instance,-1);
5080 if (real_instance<fNdata[0]) {
5083 char *expo = nullptr;
5084 if (len > 1) {
5085 switch (decform[len-2]) {
5086 case 'l':
5087 case 'L': {
5088 outputSizeLevel = 2;
5089 if (len > 2 && tolower(decform[len - 3]) == 'l') {
5090 outputSizeLevel = 3;
5091 }
5092 break;
5093 }
5094 case 'h': outputSizeLevel = 0; break;
5095 }
5096 }
5097 switch(decform[len-1]) {
5098 case 'c':
5099 case 'd':
5100 case 'i':
5101 {
5102 switch (outputSizeLevel) {
5103 case 0: snprintf(value,kMAXLENGTH,Form("%%%s",decform),(Short_t)((TTreeFormula*)this)->EvalInstance(instance)); break;
5104 // Evaluate both the "long" ("l") and "long long" ("ll") formats
5105 // through `long double` so that, where it is wide enough (x86),
5106 // the full 64-bit value prints exactly; this keeps floating-point
5107 // arithmetic semantics unchanged.
5108 case 2: {
5109 LongDouble_t v = ((TTreeFormula *)this)->EvalInstance<LongDouble_t>(instance);
5112 break;
5113 }
5114 case 3: {
5115 LongDouble_t v = ((TTreeFormula *)this)->EvalInstance<LongDouble_t>(instance);
5118 break;
5119 }
5120 case 1:
5121 default: snprintf(value,kMAXLENGTH,Form("%%%s",decform),(Int_t)((TTreeFormula*)this)->EvalInstance(instance)); break;
5122 }
5123 break;
5124 }
5125 case 'o':
5126 case 'x':
5127 case 'X':
5128 case 'u':
5129 {
5130 switch (outputSizeLevel) {
5131 case 0: snprintf(value,kMAXLENGTH,Form("%%%s",decform),(UShort_t)((TTreeFormula*)this)->EvalInstance(instance)); break;
5132 // See the signed 'd'/'i' case above: both "l" and "ll" evaluate
5133 // through `long double` to print the full value exactly where it
5134 // is wide enough, without changing arithmetic semantics.
5135 case 2: {
5136 LongDouble_t v = ((TTreeFormula *)this)->EvalInstance<LongDouble_t>(instance);
5139 break;
5140 }
5141 case 3: {
5142 LongDouble_t v = ((TTreeFormula *)this)->EvalInstance<LongDouble_t>(instance);
5145 break;
5146 }
5147 case 1:
5148 default: snprintf(value,kMAXLENGTH,Form("%%%s",decform),(UInt_t)((TTreeFormula*)this)->EvalInstance(instance)); break;
5149 }
5150 break;
5151 }
5152 case 'f':
5153 case 'e':
5154 case 'E':
5155 case 'g':
5156 case 'G':
5157 {
5158 switch (outputSizeLevel) {
5160 case 1:
5161 default: snprintf(value,kMAXLENGTH,Form("%%%s",decform),((TTreeFormula*)this)->EvalInstance(instance)); break;
5162 }
5163 expo = strchr(value,'e');
5164 break;
5165 }
5166 default:
5168 expo = strchr(value,'e');
5169 }
5170 if (expo) {
5171 // If there is an exponent we may be longer than planned.
5172 // so let's trim off the excess precision!
5173 UInt_t declen = atoi(decform);
5174 if (strlen(value)>declen) {
5175 UInt_t off = strlen(value)-declen;
5176 char *start = expo - off;
5178 for(UInt_t z=0;z<=vlen;++z) {
5179 start[z] = expo[z];
5180 }
5181 //strcpy(expo-off,expo);
5182 }
5183 }
5184 } else {
5185 if (isalpha(decform[strlen(decform)-1])) {
5187 short_decform.Remove(short_decform.Length()-1);
5188 snprintf(value,kMAXLENGTH,Form(" %%%sc",short_decform.Data()),' ');
5189 } else {
5190 snprintf(value,kMAXLENGTH,Form(" %%%sc",decform),' ');
5191 }
5192
5193 }
5194 }
5195 }
5196 return &value[0];
5197}
5198
5199////////////////////////////////////////////////////////////////////////////////
5200/// Tell the formula that we are going to request a new entry.
5201
5203{
5204 fNeedLoading = true;
5206
5207 for(Int_t i=0; i<fNcodes; ++i) {
5208 UInt_t max_dim = fNdimensions[i];
5209 for(UInt_t dim=0; dim<max_dim ;++dim) {
5210 if (fVarIndexes[i][dim]) {
5211 fVarIndexes[i][dim]->ResetLoading();
5212 }
5213 }
5214 }
5215 Int_t n = fAliases.GetLast();
5216 if ( fNoper < n ) {
5217 n = fNoper;
5218 }
5219 for(Int_t k=0; k <= n; ++k) {
5220 TTreeFormula *f = static_cast<TTreeFormula*>(fAliases.UncheckedAt(k));
5221 if (f) {
5222 f->ResetLoading();
5223 }
5224 }
5225 for (int i=0; i<fExternalCuts.GetSize(); i++) {
5226 auto c = dynamic_cast<TCutG*>(fExternalCuts.At(i));
5227 if (c) {
5228 ((TTreeFormula *)(c->GetObjectX()))->ResetLoading();
5229 ((TTreeFormula *)(c->GetObjectY()))->ResetLoading();
5230 }
5231 }
5235}
5236
5237////////////////////////////////////////////////////////////////////////////////
5238/// Set the axis (in particular get the type).
5239
5240void TTreeFormula::SetAxis(TAxis *axis)
5241{
5242 if (!axis) {fAxis = nullptr; return;}
5243 if (IsString()) {
5244 fAxis = axis;
5245 if (fNoper==1 && GetAction(0)==kAliasString){
5248 subform->SetAxis(axis);
5249 } else if (fNoper==2 && GetAction(0)==kAlternateString){
5252 subform->SetAxis(axis);
5253 }
5254 // Since the bin are corresponding to 'string', we currently must also set
5255 // the axis to align the bins exactly on integer boundaries.
5257 } else if (IsInteger()) {
5260}
5261
5262////////////////////////////////////////////////////////////////////////////////
5263/// Stream an object of class TTreeFormula.
5264
5266{
5267 if (R__b.IsReading()) {
5268 UInt_t R__s, R__c;
5269 Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
5270 if (R__v > 2) {
5271 R__b.ReadClassBuffer(TTreeFormula::Class(), this, R__v, R__s, R__c);
5272 return;
5273 }
5274 //====process old versions before automatic schema evolution
5276 R__b >> fTree;
5277 R__b >> fNcodes;
5278 R__b.ReadFastArray(fCodes, fNcodes);
5281 R__b >> instance; //data member removed
5282 R__b >> fNindex;
5283 if (fNindex) {
5284 fLookupType = new Int_t[fNindex];
5285 R__b.ReadFastArray(fLookupType, fNindex);
5286 }
5288 //====end of old versions
5289
5290 } else {
5291 R__b.WriteClassBuffer(TTreeFormula::Class(),this);
5292 }
5294
5295////////////////////////////////////////////////////////////////////////////////
5296/// Try to 'demote' a string into an array bytes. If this is not possible,
5297/// return false.
5298
5300{
5301 Int_t code = GetActionParam(oper);
5303 if (oper>0 && GetAction(oper-1)==kJump) {
5304 // We are the second hand of a ternary operator, let's not do the fixing.
5305 return false;
5306 }
5307 TLeaf *leaf = (TLeaf*)fLeaves.At(code);
5308 if (leaf && (leaf->InheritsFrom(TLeafC::Class()) || leaf->InheritsFrom(TLeafB::Class()) ) ) {
5310 fNval++;
5311 fNstring--;
5312 return true;
5313 }
5314 }
5315 return false;
5316}
5317
5318
5319////////////////////////////////////////////////////////////////////////////////
5320/// This function is called TTreePlayer::UpdateFormulaLeaves, itself
5321/// called by TChain::LoadTree when a new Tree is loaded.
5322/// Because Trees in a TChain may have a different list of leaves, one
5323/// must update the leaves numbers in the TTreeFormula used by the TreePlayer.
5324///
5325/// A safer alternative would be to recompile the whole thing .... However
5326/// currently compile HAS TO be called from the constructor!
5327
5329{
5332 for (Int_t i=0;i<nleaves;i++) {
5333 if (!fTree) break;
5334 if (!fLeafNames[i]) continue;
5335
5337 fLeaves[i] = leaf;
5338 if (fBranches[i] && leaf) {
5339 fBranches[i] = leaf->GetBranch();
5340 // Since sometimes we might no read all the branches for all the entries, we
5341 // might sometimes only read the branch count and thus reset the collection
5342 // but might not read the data branches, to insure that a subsequent read
5343 // from TTreeFormula will properly load the data branches even if fQuickLoad is true,
5344 // we reset the entry of all branches in the TTree.
5345 ((TBranch*)fBranches[i])->ResetReadEntry();
5346 }
5347 if (leaf==nullptr) SetBit( kMissingLeaf );
5348 }
5349 for (Int_t j=0; j<kMAXCODES; j++) {
5350 for (Int_t k = 0; k<kMAXFORMDIM; k++) {
5351 if (fVarIndexes[j][k]) {
5353 }
5354 }
5356 if (j<fNval && fCodes[j]<0) {
5358 if (gcut) {
5359 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
5360 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
5361 if (fx) fx->UpdateFormulaLeaves();
5362 if (fy) fy->UpdateFormulaLeaves();
5363 }
5364 }
5365 }
5366 for(Int_t k=0;k<fNoper;k++) {
5367 const Int_t oper = GetOper()[k];
5368 switch(oper >> kTFOperShift) {
5369 case kAlias:
5370 case kAliasString:
5371 case kAlternate:
5372 case kAlternateString:
5373 case kMinIf:
5374 case kMaxIf:
5375 {
5378 subform->UpdateFormulaLeaves();
5379 break;
5380 }
5381 case kDefinedVariable:
5382 {
5383 Int_t code = GetActionParam(k);
5384 if (fCodes[code]==0) switch(fLookupType[code]) {
5385 case kLengthFunc:
5386 case kSum:
5387 case kMin:
5388 case kMax:
5389 {
5392 subform->UpdateFormulaLeaves();
5393 break;
5394 }
5395 default:
5396 break;
5397 }
5398 }
5399 default:
5400 break;
5401 }
5403}
5404
5405////////////////////////////////////////////////////////////////////////////////
5406/// Populate the TTreeFormulaManager with the dimension information.
5407
5409 Int_t i,k;
5410
5411 // Now that we saw all the expressions and variables AND that
5412 // we know whether arrays of chars are treated as string or
5413 // not, we can properly setup the dimensions.
5414 TIter next(fDimensionSetup);
5415 Int_t last_code = -1;
5416 Int_t virt_dim = 0;
5417 for(TDimensionInfo * info; (info = (TDimensionInfo*)next()); ) {
5418 if (last_code!=info->fCode) {
5419 // We know that the list is ordered by code number then by
5420 // dimension. Thus a different code means that we need to
5421 // restart at the lowest dimensions.
5422 virt_dim = 0;
5423 last_code = info->fCode;
5425 }
5426
5427 if (GetAction(info->fOper)==kDefinedString) {
5428
5429 // We have a string used as a string (and not an array of number)
5430 // We need to determine which is the last dimension and skip it.
5432 while(nextinfo && nextinfo->fCode==info->fCode) {
5433 DefineDimensions(info->fCode,info->fSize, info->fMultiDim, virt_dim);
5434 nextinfo = (TDimensionInfo*)next();
5435 }
5436 if (!nextinfo) break;
5437
5438 info = nextinfo;
5439 virt_dim = 0;
5440 last_code = info->fCode;
5442
5443 info->fSize = 1; // Maybe this should actually do nothing!
5444 }
5445
5446
5447 DefineDimensions(info->fCode,info->fSize, info->fMultiDim, virt_dim);
5448 }
5449
5450 fMultiplicity = 0;
5451 for(i=0;i<fNoper;i++) {
5452 Int_t action = GetAction(i);
5453
5454 if (action==kMinIf || action==kMaxIf) {
5455 // Skip/Ignore the 2nd args
5456 ++i;
5457 continue;
5458 }
5459 if (action==kAlias || action==kAliasString) {
5462 switch(subform->GetMultiplicity()) {
5463 case 0: break;
5464 case 1: fMultiplicity = 1; break;
5465 case 2: if (fMultiplicity!=1) fMultiplicity = 2; break;
5466 }
5468 // since we are addint to this manager 'subform->ResetDimensions();'
5469 // will be called a little latter
5470 continue;
5471 }
5472 if (action==kDefinedString) {
5473 //if (fOper[i] >= 105000 && fOper[i]<110000) {
5474 // We have a string used as a string
5475
5476 // This dormant portion of code would be used if (when?) we allow the histogramming
5477 // of the integral content (as opposed to the string content) of strings
5478 // held in a variable size container delimited by a null (as opposed to
5479 // a fixed size container or variable size container whose size is controlled
5480 // by a variable). In GetNdata, we will then use strlen to grab the current length.
5481 //fCumulSizes[i][fNdimensions[i]-1] = 1;
5482 //fUsedSizes[fNdimensions[i]-1] = -TMath::Abs(fUsedSizes[fNdimensions[i]-1]);
5483 //fUsedSizes[0] = - TMath::Abs( fUsedSizes[0]);
5484
5485 //continue;
5486 }
5487 }
5488
5489 for (i=0;i<fNcodes;i++) {
5490 if (fCodes[i] < 0) {
5492 if (!gcut) continue;
5493 TTreeFormula *fx = (TTreeFormula *)gcut->GetObjectX();
5494 TTreeFormula *fy = (TTreeFormula *)gcut->GetObjectY();
5495
5496 if (fx) {
5497 switch(fx->GetMultiplicity()) {
5498 case 0: break;
5499 case 1: fMultiplicity = 1; break;
5500 case 2: if (fMultiplicity!=1) fMultiplicity = 2; break;
5501 }
5502 fManager->Add(fx);
5503 }
5504 if (fy) {
5505 switch(fy->GetMultiplicity()) {
5506 case 0: break;
5507 case 1: fMultiplicity = 1; break;
5508 case 2: if (fMultiplicity!=1) fMultiplicity = 2; break;
5509 }
5510 fManager->Add(fy);
5511 }
5512
5513 continue;
5514 }
5515
5516 if (fLookupType[i]==kIteration) {
5517 fMultiplicity = 1;
5518 continue;
5519 }
5520
5521 TLeaf *leaf = i <= fLeaves.GetLast() ? (TLeaf*)fLeaves.UncheckedAt(i) : nullptr;
5522 if (!leaf) continue;
5523
5524 // Reminder of the meaning of fMultiplicity:
5525 // -1: Only one or 0 element per entry but contains variable length
5526 // -array! (Only used for TTreeFormulaManager)
5527 // 0: Only one element per entry, no variable length array
5528 // 1: loop over the elements of a variable length array
5529 // 2: loop over elements of fixed length array (nData is the same for all entry)
5530
5531 if (leaf->GetLeafCount()) {
5532 // We assume only one possible variable length dimension (the left most)
5533 fMultiplicity = 1;
5534 } else if (fLookupType[i]==kDataMember) {
5536 TStreamerElement * elem = leafinfo->fElement;
5537 if (fMultiplicity!=1) {
5538 if (leafinfo->HasCounter() ) fMultiplicity = 1;
5539 else if (elem && elem->GetArrayDim()>0) fMultiplicity = 2;
5540 else if (leaf->GetLenStatic()>1) fMultiplicity = 2;
5541 }
5542 } else {
5543 if (leaf->GetLenStatic()>1 && fMultiplicity!=1) fMultiplicity = 2;
5544 }
5545 if (fMultiplicity!=1) {
5546 // If the leaf belongs to a friend tree which has an index, we might
5547 // be in the case where some entry do not exist.
5548
5549 TTree *realtree = fTree ? fTree->GetTree() : nullptr;
5550 TTree *tleaf = leaf->GetBranch()->GetTree();
5551 if (tleaf && tleaf != realtree && tleaf->GetTreeIndex()) {
5552 // Reset the multiplicity if we have a friend tree with an index.
5553 fMultiplicity = 1;
5554 }
5555 }
5556
5557 Int_t virt_dim2 = 0;
5558 for (k = 0; k < fNdimensions[i]; k++) {
5559 // At this point fCumulSizes[i][k] actually contain the physical
5560 // dimension of the k-th dimensions.
5561 if ( (fCumulSizes[i][k]>=0) && (fIndexes[i][k] >= fCumulSizes[i][k]) ) {
5562 // unreachable element requested:
5563 fManager->CancelDimension(virt_dim2); // fCumulUsedSizes[virt_dim2] = 0;
5564 }
5565 if ( fIndexes[i][k] < 0 ) virt_dim2++;
5566 fFixedSizes[i][k] = fCumulSizes[i][k];
5567 }
5568
5569 // Add up the cumulative size
5570 for (k = fNdimensions[i] - 1; (k > 0); k--) {
5571 // NOTE: When support for inside variable dimension is added this
5572 // will become inaccurate (since one of the value in the middle of the chain
5573 // is unknown until GetNdata is called.
5574 fCumulSizes[i][k-1] *= TMath::Abs(fCumulSizes[i][k]);
5575 }
5576 // NOTE: We assume that the inside variable dimensions are dictated by the
5577 // first index.
5578 if (fCumulSizes[i][0]>0) fNdata[i] = fCumulSizes[i][0];
5579
5580 //for (k = 0; k<kMAXFORMDIM; k++) {
5581 // if (fVarIndexes[i][k]) fManager->Add(fVarIndexes[i][k]);
5582 //}
5583
5584 }
5585
5586 // Handle the case of an Alt$(primary,alternate) expression that provides
5587 // the only source of multiplicity, i.e. it's used not alongside another
5588 // array that drives the iteration. Example: Scan("Alt$(x,-1)"), instead of
5589 // Scan("y-Alt$(x,-1)"). In that case the number of instances must be
5590 // dictated by the `primary` array, so we register it with the manager. We
5591 // only get here when fMultiplicity == 0, i.e. nothing else in this formula
5592 // drives an iteration.
5593 if (fMultiplicity == 0) {
5594 for (i = 0; i < fNoper; i++) {
5595 Int_t action = GetAction(i);
5599 switch (primary->GetManager()->GetMultiplicity()) {
5600 case 1: // primary is a variable length array: drive a variable length loop
5601 fMultiplicity = 1;
5603 break;
5604 case 2: // primary is a fixed length array: drive a fixed length loop
5605 if (fMultiplicity != 1)
5606 fMultiplicity = 2;
5608 break;
5609 // multiplicity 0 (scalar) or -1 (0-or-1 element): nothing to loop
5610 // over, leave fMultiplicity at 0
5611 }
5612 // An Alt$(primary,alternate) expression occupies two consecutive
5613 // operands: the `primary` action we just handled, immediately
5614 // followed by the `alternate`, which we can skip.
5615 ++i;
5616 }
5617 }
5619}
5620
5621////////////////////////////////////////////////////////////////////////////////
5622/// Make sure that all the branches have been loaded properly.
5623
5625{
5626 Int_t i;
5627 for (i=0; i<fNoper ; ++i) {
5629 if (leaf==nullptr) continue;
5630
5631 TBranch *br = leaf->GetBranch();
5632 Long64_t treeEntry = br->GetTree()->GetReadEntry();
5633 auto res = R__LoadBranch(br, treeEntry, true);
5634 if (res < 0) {
5635 Error("LoadBranches", "Branch could not be loaded:%d", res);
5636 continue;
5637 }
5638
5640 if (alias) alias->LoadBranches();
5641
5642 Int_t max_dim = fNdimensions[i];
5643 for (Int_t dim = 0; dim < max_dim; ++dim) {
5644 if (fVarIndexes[i][dim]) fVarIndexes[i][dim]->LoadBranches();
5645 }
5647}
5648
5649////////////////////////////////////////////////////////////////////////////////
5650/// Calculate the actual dimension for the current entry.
5651
5653 Int_t size;
5654 bool outofbounds = false;
5655
5656 for (Int_t i=0;i<fNcodes;i++) {
5657 if (fCodes[i] < 0) continue;
5658
5659 // NOTE: Currently only the leafcount can indicate a dimension that
5660 // is physically variable. So only the left-most dimension is variable.
5661 // When an API is introduced to be able to determine a variable inside dimensions
5662 // one would need to add a way to recalculate the values of fCumulSizes for this
5663 // leaf. This would probably require the addition of a new data member
5664 // fSizes[kMAXCODES][kMAXFORMDIM];
5665 // Also note that EvalInstance expect all the values (but the very first one)
5666 // of fCumulSizes to be positive. So indicating that a physical dimension is
5667 // variable (expected for the first one) can NOT be done via negative values of
5668 // fCumulSizes.
5669
5670 TLeaf *leaf = i <= fLeaves.GetLast() ? (TLeaf *)fLeaves.UncheckedAt(i) : nullptr;
5671 if (!leaf) {
5672 switch(fLookupType[i]) {
5673 case kDirect:
5674 case kMethod:
5675 case kTreeMember:
5676 case kDataMember:
5677 fNdata[i] = 0;
5678 outofbounds = true;
5679 }
5680 continue;
5681 }
5682
5684 TTree *tleaf = leaf->GetBranch()->GetTree();
5685 if (tleaf && tleaf != realtree && tleaf->GetTreeIndex()) {
5686 if (tleaf->GetReadEntry() < 0) {
5687 fNdata[i] = 0;
5688 outofbounds = true;
5689 continue;
5690 } else {
5691 fNdata[i] = fCumulSizes[i][0];
5692 }
5693 }
5694 bool hasBranchCount2 = false;
5695 if (leaf->GetLeafCount()) {
5696 TLeaf* leafcount = leaf->GetLeafCount();
5697 TBranch *branchcount = leafcount->GetBranch();
5698 TFormLeafInfo * info = nullptr;
5699 if (leaf->IsA() == TLeafElement::Class()) {
5700 //if branchcount address not yet set, GetEntry will set the address
5701 // read branchcount value
5702 Long64_t readentry = leaf->GetBranch()->GetTree()->GetReadEntry();
5703 if (readentry < 0) readentry=0;
5704 if (!branchcount->GetAddress()) {
5706 if (res < 0) {
5707 Error("LoadCurrentDim", "Branch could not be loaded:%d", res);
5708 return false;
5709 }
5710 } else {
5711 // Since we do not read the full branch let's reset the read entry number
5712 // so that a subsequent read from TTreeFormula will properly load the full
5713 // object even if fQuickLoad is true.
5714 branchcount->TBranch::GetEntry(readentry);
5715 branchcount->ResetReadEntry();
5716 }
5717
5718 size = ((TBranchElement*)branchcount)->GetNdata();
5719 // Reading the size as above is correct only when the branchcount
5720 // is of streamer type kCounter which require the underlying data
5721 // member to be signed integral type.
5722
5723 TBranchElement* branch = (TBranchElement*) leaf->GetBranch();
5724 if (branch->GetAddress() == nullptr) {
5725 // Humm there is no space reserve to write the data,
5726 // the data member is likely 'removed' from the class
5727 // layout, so rather than crashing by accessing
5728 // random memory, make it clear we can't read it.
5729 size = 0;
5730 }
5731
5732 // NOTE: could be sped up
5733 if (fHasMultipleVarDim[i]) {// info && info->GetVarDim()>=0) {
5735 auto res = R__LoadBranch(branch->GetBranchCount2() ? branch->GetBranchCount2() : branch, readentry, fQuickLoad);
5736 if (res < 0) {
5737 Error("LoadCurrentDim", "Branch could not be loaded:%d", res);
5738 return false;
5739 }
5740
5741 // Here we need to add the code to take in consideration the
5742 // double variable length
5743 // We fill up the array of sizes in the TLeafInfo:
5744 info->LoadSizes(branch);
5745 hasBranchCount2 = true;
5746 if (info->GetVirtVarDim()>=0) info->UpdateSizes(fManager->fVarDims[info->GetVirtVarDim()]);
5747
5748 // Refresh the fCumulSizes[i] to have '1' for the
5749 // double variable dimensions
5750 Int_t vdim = info->GetVarDim();
5751 fCumulSizes[i][vdim] = fCumulSizes[i][vdim+1];
5752 for(Int_t k=vdim -1; k>=0; k--) {
5753 fCumulSizes[i][k] = fCumulSizes[i][k+1]*fFixedSizes[i][k];
5754 }
5755 // Update fCumulUsedSizes
5756 // UpdateMultiVarSizes(vdim,info,i)
5757 //Int_t fixed = fCumulSizes[i][vdim+1];
5758 //for(Int_t k=vdim - 1; k>=0; k++) {
5759 // Int_t fixed *= fFixedSizes[i][k];
5760 // for(Int_t l=0;l<size; l++) {
5761 // fCumulSizes[i][k] += info->GetSize(l) * fixed;
5762 //}
5763 }
5764 } else {
5765 Long64_t readentry = leaf->GetBranch()->GetTree()->GetReadEntry();
5766 if (readentry < 0) readentry=0;
5768 if (res < 0) {
5769 Error("LoadCurrentDim", "Branch could not be loaded:%d", res);
5770 return false;
5771 }
5772 size = leaf->GetLen() / leaf->GetLenStatic();
5773 }
5774 if (hasBranchCount2) {
5775 // We assume that fCumulSizes[i][1] contains the product of the fixed sizes
5776 fNdata[i] = fCumulSizes[i][1] * ((TFormLeafInfo *)fDataMembers.At(i))->GetSumOfSizes();
5777 } else {
5778 fNdata[i] = size * fCumulSizes[i][1];
5779 }
5780 if (fIndexes[i][0]==-1) {
5781 // Case where the index is not specified AND the 1st dimension has a variable
5782 // size.
5783 if (fManager->fUsedSizes[0]==1 || (size<fManager->fUsedSizes[0]) ) fManager->fUsedSizes[0] = size;
5784 if (info && fIndexes[i][info->GetVarDim()]>=0) {
5785 for(Int_t j=0; j<size; j++) {
5786 if (fIndexes[i][info->GetVarDim()] >= info->GetSize(j)) {
5787 info->SetSize(j,0);
5790 } else if (fIndexes[i][info->GetVarDim()]>=0) {
5791 // There is an index and it is not too large
5792 info->SetSize(j,1);
5795 }
5796 }
5797 }
5798 } else if (fIndexes[i][0] >= size) {
5799 // unreachable element requested:
5800 fManager->fUsedSizes[0] = 0;
5801 fNdata[i] = 0;
5802 outofbounds = true;
5803 } else if (hasBranchCount2) {
5806 if (fIndexes[i][0]<0
5807 || fIndexes[i][info2->GetVarDim()] >= info2->GetSize(fIndexes[i][0])) {
5808 // unreachable element requested:
5809 fManager->fUsedSizes[0] = 0;
5810 fNdata[i] = 0;
5811 outofbounds = true;
5812 }
5813 }
5814 } else if (fLookupType[i]==kDataMember) {
5816 if (leafinfo->HasCounter()) {
5817 TBranch *branch = leaf->GetBranch();
5818 Long64_t readentry = branch->GetTree()->GetReadEntry();
5819 if (readentry < 0) readentry=0;
5821 if (res < 0) {
5822 Error("LoadCurrentDim", "Branch could not be loaded:%d", res);
5823 return false;
5824 }
5825 size = (Int_t) leafinfo->GetCounterValue(leaf);
5826 if (fIndexes[i][0]==-1) {
5827 // Case where the index is not specified AND the 1st dimension has a variable
5828 // size.
5829 if (fManager->fUsedSizes[0]==1 || (size<fManager->fUsedSizes[0]) ) {
5830 fManager->fUsedSizes[0] = size;
5831 }
5832 } else if (fIndexes[i][0] >= size) {
5833 // unreachable element requested:
5834 fManager->fUsedSizes[0] = 0;
5835 fNdata[i] = 0;
5836 outofbounds = true;
5837 } else {
5838 fNdata[i] = size*fCumulSizes[i][1];
5839 }
5840 Int_t vdim = leafinfo->GetVarDim();
5841 if (vdim>=0) {
5842 // Here we need to add the code to take in consideration the
5843 // double variable length
5844 // We fill up the array of sizes in the TLeafInfo:
5845 // here we can assume that branch is a TBranch element because the other style does NOT support this type
5846 // of complexity.
5847 leafinfo->LoadSizes(branch);
5848 hasBranchCount2 = true;
5849 if (fIndexes[i][0]==-1&&fIndexes[i][vdim] >= 0) {
5850 for(int z=0; z<size; ++z) {
5851 if (fIndexes[i][vdim] >= leafinfo->GetSize(z)) {
5852 leafinfo->SetSize(z,0);
5853 // --fManager->fUsedSizes[0];
5854 } else if (fIndexes[i][vdim] >= 0 ) {
5855 leafinfo->SetSize(z,1);
5856 }
5857 }
5858 }
5859 leafinfo->UpdateSizes(fManager->fVarDims[vdim]);
5860
5861 // Refresh the fCumulSizes[i] to have '1' for the
5862 // double variable dimensions
5863 fCumulSizes[i][vdim] = fCumulSizes[i][vdim+1];
5864 for(Int_t k=vdim -1; k>=0; k--) {
5865 fCumulSizes[i][k] = fCumulSizes[i][k+1]*fFixedSizes[i][k];
5866 }
5867 fNdata[i] = fCumulSizes[i][1] * leafinfo->GetSumOfSizes();
5868 } else {
5869 fNdata[i] = size * fCumulSizes[i][1];
5870 }
5871 } else if (leafinfo->GetMultiplicity()==-1) {
5872 TBranch *branch = leaf->GetBranch();
5873 Long64_t readentry = branch->GetTree()->GetReadEntry();
5874 if (readentry < 0) readentry=0;
5876 if (res < 0) {
5877 Error("LoadCurrentDim", "Branch could not be loaded:%d", res);
5878 return false;
5879 }
5880 if (leafinfo->GetNdata(leaf)==0) {
5881 outofbounds = true;
5882 }
5883 }
5884 }
5885 // However we allow several dimensions that virtually vary via the size of their
5886 // index variables. So we have code to recalculate fCumulUsedSizes.
5887 TFormLeafInfo * info = nullptr;
5888 if (fLookupType[i]!=kDirect) {
5890 }
5891 for(Int_t k=0, virt_dim=0; k < fNdimensions[i]; k++) {
5892 if (fIndexes[i][k]<0) {
5893 if (info && fIndexes[i][k]==-2 && fVarIndexes[i][k]->GetManager()->GetMultiplicity()==0) {
5894 // Index and thus local size provided by a "index variable of size 1"
5895 Int_t index = fVarIndexes[i][k]->EvalInstance(0);
5896 Int_t index_size = info->GetSize(index);
5897 if (fManager->fUsedSizes[virt_dim]==1 || (index_size!=1 && index_size<fManager->fUsedSizes[virt_dim]) )
5899 } else if (fIndexes[i][k]==-2 && fManager->fVirtUsedSizes[virt_dim]<0) {
5900
5901 // if fVirtUsedSize[virt_dim] is positive then VarIndexes[i][k]->GetNdata()
5902 // is always the same and has already been factored in fUsedSize[virt_dim]
5904 if (index_size==1) {
5905 // We could either have a variable size array which is currently of size one
5906 // or a single element that might or not might not be present (and is currently present!)
5907 if (fVarIndexes[i][k]->GetManager()->GetMultiplicity()==1) {
5909 }
5910
5912 index_size<fManager->fUsedSizes[virt_dim]) {
5914 }
5915
5916 } else if (hasBranchCount2 && info && k==info->GetVarDim()) {
5917 // NOTE: We assume the indexing of variable sizes on the first index!
5918 if (fIndexes[i][0]>=0) {
5919 Int_t index_size = info->GetSize(fIndexes[i][0]);
5920 if (fManager->fUsedSizes[virt_dim]==1 || (index_size!=1 && index_size<fManager->fUsedSizes[virt_dim]) )
5922 }
5923 }
5924 virt_dim++;
5925 } else if (hasBranchCount2 && info && k==info->GetVarDim()) {
5926
5927 // nothing to do, at some point I thought this might be useful:
5928 // if (fIndexes[i][k]>=0) {
5929 // index = info->GetSize(fIndexes[i][k]);
5930 // if (fManager->fUsedSizes[virt_dim]==1 || (index!=1 && index<fManager->fUsedSizes[virt_dim]) )
5931 // fManager->fUsedSizes[virt_dim] = index;
5932 // virt_dim++;
5933 // }
5934
5935 }
5936 }
5937 }
5938 return ! outofbounds;
5939
5940
5941
5942}
5943
5945{
5946 // Convert the fOper of a TTTreeFormula version fromVersion to the current in memory version
5947
5948 enum { kOldAlias = /*ROOT::v5::TFormula::kVariable*/ 100000+10000+1,
5952 };
5953
5954 for (int k=0; k<fNoper; k++) {
5955 // First hide from ROOT::v5::TFormula convertion
5956
5957 Int_t action = GetOper()[k];
5958
5959 switch (action) {
5960
5961 case kOldAlias: GetOper()[k] = -kOldAlias; break;
5962 case kOldAliasString: GetOper()[k] = -kOldAliasString; break;
5963 case kOldAlternate: GetOper()[k] = -kOldAlternate; break;
5964 case kOldAlternateString: GetOper()[k] = -kOldAlternateString; break;
5965 }
5966 }
5967
5969
5970 for (int i=0,offset=0; i<fNoper; i++) {
5971 Int_t action = GetOper()[i+offset];
5972
5973 switch (action) {
5974 case -kOldAlias: SetAction(i, kAlias, 0); break;
5975 case -kOldAliasString: SetAction(i, kAliasString, 0); break;
5976 case -kOldAlternate: SetAction(i, kAlternate, 0); break;
5977 case -kOldAlternateString: SetAction(i, kAlternateString, 0); break;
5978 }
5979 }
5980
5981}
5982
5983////////////////////////////////////////////////////////////////////////////////
5984/// Convert the underlying lookup method from the direct technique
5985/// (dereferencing the address held by the branch) to the method using
5986/// TFormLeafInfo. This is in particular useful in the case where we
5987/// need to append an additional TFormLeafInfo (for example to call a
5988/// method).
5989/// Return false if the switch was unsuccessful (basically in the
5990/// case of an old style split tree).
5991
5993{
5994 TFormLeafInfo *last = nullptr;
5995 TLeaf *leaf = (TLeaf*)fLeaves.At(code);
5996 if (!leaf) return false;
5997
5998 if (fLookupType[code]==kDirect) {
5999 if (leaf->InheritsFrom(TLeafElement::Class())) {
6000 TBranchElement * br = (TBranchElement*)leaf->GetBranch();
6001 if (br->GetType()==31) {
6002 // sub branch of a TClonesArray
6003 TStreamerInfo *info = br->GetInfo();
6004 TClass* cl = info->GetClass();
6005 TStreamerElement *element = (TStreamerElement *)info->GetElement(br->GetID());
6007 Int_t offset;
6008 info->GetStreamerElement(element->GetName(),offset);
6009 clonesinfo->fNext = new TFormLeafInfo(cl,offset+br->GetOffset(),element);
6010 last = clonesinfo->fNext;
6013
6014 } else if (br->GetType()==41) {
6015 // sub branch of a Collection
6016
6017 TBranchElement *count = br->GetBranchCount();
6019 if ( count->GetID() >= 0 ) {
6021 (TStreamerElement *)count->GetInfo()->GetElement(count->GetID());
6022 TClass *collectionCl = collectionElement->GetClassPointer();
6023
6026 } else {
6030 }
6031
6032 TStreamerInfo *info = br->GetInfo();
6033 TClass* cl = info->GetClass();
6034 TStreamerElement *element = (TStreamerElement *)info->GetElement(br->GetID());
6035 Int_t offset;
6036 info->GetStreamerElement(element->GetName(),offset);
6037 collectioninfo->fNext = new TFormLeafInfo(cl,offset+br->GetOffset(),element);
6038 last = collectioninfo->fNext;
6041
6042 } else if (br->GetID()<0) {
6043 return false;
6044 } else {
6045 last = new TFormLeafInfoDirect(br);
6046 fDataMembers.AddAtAndExpand(last,code);
6048 }
6049 } else {
6050 //last = new TFormLeafInfoDirect(br);
6051 //fDataMembers.AddAtAndExpand(last,code);
6052 //fLookupType[code]=kDataMember;
6053 return false;
6054 }
6055 }
6056 return true;
6057}
6058
6060{
6061 // TTreeFormula version of AnalyzePrimitive(). Does nothing. Predefined
6062 // primitive functions are not supported by TTreeFormula since they
6063 // operate on x[] and parameters, which are unavailable here.
6064
6065 return kFALSE;
6066}
6067
6069{
6070 // TTreeFormula version of Optimize(). Does nothing. TTreeFormula does not
6071 // support the TFormula-style optimization since it requires variables and
6072 // parameters in fixed locations, which are unavailable here.
6073
6074 return;
6075}
#define lenfunc
Definition CPyCppyy.h:224
#define d(i)
Definition RSha256.hxx:102
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
static Roo_reg_AGKInteg1D instance
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
bool Bool_t
Boolean (0=false, 1=true) (bool)
Definition RtypesCore.h:78
int Int_t
Signed integer 4 bytes (int)
Definition RtypesCore.h:60
long Longptr_t
Integer large enough to hold a pointer (platform-dependent)
Definition RtypesCore.h:90
short Version_t
Class version identifier (short)
Definition RtypesCore.h:80
char Char_t
Character 1 byte (char)
Definition RtypesCore.h:52
unsigned long ULong_t
Unsigned long integer 4 bytes (unsigned long). Size depends on architecture.
Definition RtypesCore.h:70
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:69
unsigned int UInt_t
Unsigned integer 4 bytes (unsigned int)
Definition RtypesCore.h:61
short Short_t
Signed Short integer 2 bytes (short)
Definition RtypesCore.h:54
constexpr Bool_t kFALSE
Definition RtypesCore.h:109
double Double_t
Double 8 bytes.
Definition RtypesCore.h:74
long double LongDouble_t
Long Double (not portable)
Definition RtypesCore.h:76
long long Long64_t
Portable signed long integer 8 bytes.
Definition RtypesCore.h:84
unsigned long long ULong64_t
Portable unsigned long integer 8 bytes.
Definition RtypesCore.h:85
const Int_t kDoNotProcess
Definition TBranch.h:56
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
const Int_t kMaxLen
#define gDirectory
Definition TDirectory.h:385
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:130
void Error(const char *location, const char *msgfmt,...)
Use this function in case an error occurred.
Definition TError.cxx:208
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char cname
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t UChar_t len
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:142
#define gInterpreter
#define gROOT
Definition TROOT.h:417
R__EXTERN TRandom * gRandom
Definition TRandom.h:73
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2571
#define TT_EVAL_INIT_LOOP
#define TREE_EVAL_INIT
#define TT_EVAL_INIT
static Int_t R__LoadBranch(TBranch *br, Long64_t entry, bool quickLoad)
The function returns the number of bytes read from the input buffer.
T FindMin(TTreeFormula *arr)
T FindMax(TTreeFormula *arr)
const Int_t kMaxLen
#define TREE_EVAL_INIT_LOOP
T Summing(TTreeFormula *sum)
bool IsNumberConstant(const std::string &str)
Helper function checking if a string contains a literal number.
const Int_t kMAXCODES
const Int_t kMAXFORMDIM
const_iterator begin() const
const_iterator end() const
Double_t * fConst
Definition TFormula.h:82
TObjArray fFunctions
Definition TFormula.h:85
virtual void Convert(UInt_t fromVersion)
Int_t * GetOper() const
Definition TFormula.h:104
TString * fExpr
Definition TFormula.h:78
virtual Int_t GetNdim() const
Definition TFormula.h:238
Short_t GetAction(Int_t code) const
Definition TFormula.h:105
Int_t GetActionParam(Int_t code) const
Definition TFormula.h:106
virtual Int_t Compile(const char *expression="")
Compile expression already stored in fTitle.
void SetAction(Int_t code, Int_t value, Int_t param=0)
Definition TFormula.h:108
void Streamer(TBuffer &b, const TClass *onfile_class)
Stream a class object.
virtual Bool_t IsString(Int_t oper) const
Return true if the expression at the index 'oper' has to be treated as a string.
void Set(Int_t n) override
Set size of this array to n ints.
Definition TArrayI.cxx:104
const Int_t * GetArray() const
Definition TArrayI.h:43
Int_t At(Int_t i) const
Definition TArrayI.h:79
void AddAt(Int_t c, Int_t i)
Add Int_t c at position i. Check for out of bounds.
Definition TArrayI.cxx:92
Int_t GetSize() const
Definition TArray.h:47
Class to manage histogram axis.
Definition TAxis.h:32
@ kIsInteger
Definition TAxis.h:76
virtual Int_t FindBin(Double_t x)
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:293
static TClass * Class()
A Branch for the case of an object.
static TClass * Class()
Int_t GetID() const
const char * GetClassName() const override
Return the name of the user class whose content is stored in this branch, if any.
TStreamerInfo * GetInfo() const
Get streamer info for the branch class.
A Branch for the case of an object.
static TClass * Class()
A TTree is a list of TBranches.
Definition TBranch.h:93
Buffer base class used for serializing objects.
Definition TBuffer.h:43
TClassRef is used to implement a permanent reference to a TClass object.
Definition TClassRef.h:29
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t HasDataMemberInfo() const
Definition TClass.h:420
ClassInfo_t * GetClassInfo() const
Definition TClass.h:448
TVirtualStreamerInfo * GetStreamerInfo(Int_t version=0, Bool_t isTransient=kFALSE) const
returns a pointer to the TVirtualStreamerInfo object for version If the object does not exist,...
Definition TClass.cxx:4720
TVirtualCollectionProxy * GetCollectionProxy() const
Return the proxy describing the collection (if any).
Definition TClass.cxx:2923
TMethod * GetMethodAllAny(const char *method)
Return pointer to method without looking at parameters.
Definition TClass.cxx:4505
TVirtualRefProxy * GetReferenceProxy() const
Definition TClass.h:499
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2999
An array of clone (identical) objects.
static TClass * Class()
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
Graphical cut class.
Definition TCutG.h:20
A small helper class to help in keeping track of the array dimensions encountered in the analysis of ...
TDimensionInfo(Int_t code, Int_t oper, Int_t size, TFormLeafInfoMultiVarDim *multiDim)
~TDimensionInfo() override
TFormLeafInfoMultiVarDim * fMultiDim
Describe directory structure in memory.
Definition TDirectory.h:45
A List of entry numbers in a TTree or TChain.
Definition TEntryList.h:26
virtual Int_t Contains(Long64_t entry, TTree *tree=nullptr)
A small helper class to implement casting an object to a different type (equivalent to dynamic_cast)
A small helper class to implement reading a data member on a TClonesArray object stored in a TTree.
A small helper class to implement reading a data member on a TClonesArray object stored in a TTree.
Used to return the size of a collection.
A small helper class to implement reading a data member on a generic collection object stored in a TT...
A small helper class to implement reading a data member on an object stored in a TTree.
Asmall helper class to implement executing a method of an object stored in a TTree.
static TClass * ReturnTClass(TMethodCall *mc)
Return the TClass corresponding to the return type of the function if it is an object type or if the ...
A small helper class to implement reading a data member on a variable size array inside a TClonesArra...
A small helper class to implement reading a data member on a variable size array inside a TClonesArra...
A small helper class to implement reading a data member on a variable size array inside a TClonesArra...
A helper class to implement reading a data member on a variable size array inside a TClonesArray obje...
A small helper class to implement reading a numerical value inside a collection.
A small helper class to implement reading a data member by following a pointer inside a branch of TTr...
A small helper class to implement the following of reference objects stored in a TTree.
A small helper class to implement reading from the containing TTree object itself.
This class is a small helper class to implement reading a data member on an object stored in a TTree.
virtual void * GetValuePointer(TLeaf *leaf, Int_t instance=0)
returns the address of the value pointed to by the serie of TFormLeafInfo.
virtual bool Update()
We reloading all cached information in case the underlying class information has changed (for example...
virtual TClass * GetClass() const
Get the class of the underlying data.
TFormLeafInfo * fNext
follow this to grab the inside information
The Formula class.
Definition TFormula.h:89
static TClass * Class()
static TClass * Class()
A TLeaf for the general case when using the branches created via a TStreamerInfo (i....
static TClass * Class()
A TLeaf for a general object derived from TObject.
Definition TLeafObject.h:31
static TClass * Class()
A TLeaf describes individual elements of a TBranch See TBranch structure in TTree.
Definition TLeaf.h:57
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:81
void Delete(Option_t *option="") override
Remove all objects from the list AND delete all heap based objects.
Definition TList.cxx:600
Method or function calling interface.
Definition TMethodCall.h:37
static const EReturnType kLong
Definition TMethodCall.h:43
static const EReturnType kString
Definition TMethodCall.h:45
static const EReturnType kOther
Definition TMethodCall.h:46
static const EReturnType kDouble
Definition TMethodCall.h:44
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
TNamed()
Definition TNamed.h:38
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:149
An array of TObjects.
Definition TObjArray.h:31
Int_t GetEntriesFast() const
Definition TObjArray.h:58
void AddAt(TObject *obj, Int_t idx) override
Add object at position ids.
virtual void AddAtAndExpand(TObject *obj, Int_t idx)
Add object at position idx.
void Streamer(TBuffer &) override
Stream all objects in the array to or from the I/O buffer.
Int_t GetEntries() const override
Return the number of objects in array (i.e.
void Delete(Option_t *option="") override
Remove all objects from the array AND delete all heap based objects.
TObject * At(Int_t idx) const override
Definition TObjArray.h:170
TObject * UncheckedAt(Int_t i) const
Definition TObjArray.h:90
TObject * FindObject(const char *name) const override
Find an object in this collection using its name.
Int_t GetLast() const override
Return index of last object in array.
Mother of all ROOT objects.
Definition TObject.h:42
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1082
virtual void Execute(const char *method, const char *params, Int_t *error=nullptr)
Execute method on this object with the given parameter string, e.g.
Definition TObject.cxx:377
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:886
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1096
void ResetBit(UInt_t f)
Definition TObject.h:203
Double_t Rndm() override
Machine independent random number generator.
Definition TRandom.cxx:558
static TClass * Class()
const char * GetCountName() const
Describe one element (data member) to be Streamed.
Describes a persistent version of a class.
TStreamerElement * GetElement(Int_t id) const override
Basic string class.
Definition TString.h:137
static constexpr Ssiz_t kNPOS
Definition TString.h:285
const char * Data() const
Definition TString.h:385
static TClass * Class()
bool fMultiVarDim
True if one of the variable has 2 variable size dimensions.
virtual void UpdateUsedSize(Int_t &virt_dim, Int_t vsize)
Reload the array sizes.
Int_t fVirtUsedSizes[kMAXFORMDIM+1]
Virtual size of lower dimensions as seen for this formula.
Int_t fNdata
! Last value calculated by GetNdata
TArrayI * fCumulUsedVarDims
fCumulUsedSizes(1) for multi variable dimensions case
TArrayI * fVarDims[kMAXFORMDIM+1]
List of variable sizes dimensions.
virtual void EnableMultiVarDims()
Set the manager as handling a formula with multiple variable dimensions.
virtual void CancelDimension(Int_t virt_dim)
Cancel a dimension.
Int_t fCumulUsedSizes[kMAXFORMDIM+1]
Accumulated size of lower dimensions as seen for this entry.
Int_t fUsedSizes[kMAXFORMDIM+1]
Actual size of the dimensions as seen for this entry.
virtual Int_t GetNdata(bool forceLoadDim=false)
Return number of available instances in the formulas.
virtual void Add(TTreeFormula *)
Add a new formula to the list of formulas managed The manager of the formula will be changed and the ...
virtual bool Sync()
Synchronize all the formulae.
virtual void AddVarDims(Int_t virt_dim)
Add a variable dimension.
virtual void Remove(TTreeFormula *)
Remove a formula from this manager.
virtual Int_t GetMultiplicity() const
Used to pass a selection expression to the Tree drawing routine.
bool LoadCurrentDim()
Calculate the actual dimension for the current entry.
virtual void ResetLoading()
Tell the formula that we are going to request a new entry.
virtual bool IsInteger(bool fast=true) const
Return TRUE if the formula corresponds to one single Tree leaf and this leaf is short,...
bool fHasCast
Record whether the formula contain a cast operation or not.
UChar_t fHasMultipleVarDim[kMAXCODES]
True if the corresponding variable is an array with more than one variable dimension.
Int_t fMultiplicity
Indicator of the variability of the formula.
Int_t 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)
Look for the leaf corresponding to the start of expression.
TTreeFormula()=delete
Int_t DefineAlternate(const char *expression)
This method check for treat the case where expression contains Alt$( and load up both fAliases and fE...
TTreeFormulaManager * GetManager() const
virtual TLeaf * GetLeaf(Int_t n) const
Return leaf corresponding to serial number n.
TAxis * fAxis
! pointer to histogram axis if this is a string
Bool_t AnalyzePrimitive(TString &chain, TObjArray &args, Int_t &err, Int_t offset) override
Check if the given string matches a defined function primitive.
Int_t fNindex
Size of fIndex.
Int_t fNcodes
Number of leaves referenced in formula.
Int_t RegisterDimensions(Int_t code, Int_t size, TFormLeafInfoMultiVarDim *multidim=nullptr)
This method stores the dimension information for later usage.
TObjArray fExternalCuts
! List of TCutG and TEntryList used in the formula
Int_t DefinedVariable(TString &variable, Int_t &action) override
Check if name is in the list of Tree/Branch leaves.
virtual Double_t GetValueFromMethod(Int_t i, TLeaf *leaf) const
Return result of a leafobject method.
virtual const char * EvalStringInstance(Int_t i=0)
Eval the instance as a string.
void Init(const char *name, const char *formula)
Initialization called from the constructors.
Int_t * fLookupType
[fNindex] Array indicating how each leaf should be looked-up
bool fQuickLoad
! If true, branch GetEntry is only called when the entry number changes.
Int_t ParseWithLeaf(TLeaf *leaf, const char *expression, bool final, UInt_t paran_level, TObjArray &castqueue, bool useLeafCollectionObject, const char *fullExpression)
Decompose 'expression' as pointing to something inside the leaf Returns:
virtual bool SwitchToFormLeafInfo(Int_t code)
Convert the underlying lookup method from the direct technique (dereferencing the address held by the...
std::vector< std::string > fAliasesUsed
! List of aliases used during the parsing of the expression.
Int_t fCodes[kMAXCODES]
List of leaf numbers referenced in formula.
virtual char * PrintValue(Int_t mode=0) const
Return value of variable as a string.
bool fNeedLoading
! If true, the current entry has not been loaded yet.
Int_t fIndexes[kMAXCODES][kMAXFORMDIM]
Index of array selected by user for each leaf.
virtual Int_t GetMultiplicity() const
void Streamer(TBuffer &) override
Stream an object of class TTreeFormula.
Int_t fCumulSizes[kMAXCODES][kMAXFORMDIM]
Accumulated sizes of lower dimensions for each leaf after variable dimensions has been calculated.
virtual bool IsString() const
Return TRUE if the formula is a string.
TList * fDimensionSetup
! list of dimension setups, for delayed creation of the dimension information.
T EvalInstance(Int_t i=0, const char *stringStack[]=nullptr)
Evaluate this treeformula.
void ResetDimensions()
Populate the TTreeFormulaManager with the dimension information.
Int_t fNdimensions[kMAXCODES]
Number of array dimensions in each leaf.
TTree * fTree
! Pointer to Tree
TLeaf * GetLeafWithDatamember(const char *topchoice, const char *nextchice, Long64_t readentry) const
Return the leaf (if any) which contains an object containing a data member which has the name provide...
TMethodCall * GetMethodCall(Int_t code) const
Return methodcall corresponding to code.
virtual void UpdateFormulaLeaves()
This function is called TTreePlayer::UpdateFormulaLeaves, itself called by TChain::LoadTree when a ne...
~TTreeFormula() override
Tree Formula default destructor.
TObjArray fAliases
! List of TTreeFormula for each alias used.
T GetConstant(Int_t k)
virtual void * EvalObject(Int_t i=0)
Evaluate this treeformula.
TObjArray fLeafNames
List of TNamed describing leaves.
bool StringToNumber(Int_t code) override
Try to 'demote' a string into an array bytes.
static TClass * Class()
virtual bool IsLeafInteger(Int_t code) const
Return TRUE if the leaf corresponding to code is short, int or unsigned short, int When a leaf is of ...
void DefineDimensions(Int_t code, Int_t size, TFormLeafInfoMultiVarDim *info, Int_t &virt_dim)
This method is used internally to decode the dimensions of the variables.
virtual bool IsLeafString(Int_t code) const
Return TRUE if the leaf or data member corresponding to code is a string.
void LoadBranches()
Make sure that all the branches have been loaded properly.
virtual TClass * EvalClass() const
Evaluate the class of this treeformula.
Int_t GetRealInstance(Int_t instance, Int_t codeindex)
Now let calculate what physical instance we really need.
virtual void SetAxis(TAxis *axis=nullptr)
Set the axis (in particular get the type).
TFormLeafInfo * GetLeafInfo(Int_t code) const
Return DataMember corresponding to code.
TObjArray fDataMembers
! List of leaf data members
bool BranchHasMethod(TLeaf *leaf, TBranch *branch, const char *method, const char *params, Long64_t readentry) const
Return the leaf (if any) of the tree with contains an object of a class having a method which has the...
TTreeFormula * fVarIndexes[kMAXCODES][kMAXFORMDIM]
Pointer to a variable index.
LongDouble_t * fConstLD
! local version of fConsts able to store bigger numbers
Int_t fFixedSizes[kMAXCODES][kMAXFORMDIM]
Physical sizes of lower dimensions for each leaf.
virtual Int_t GetNdata()
Return number of available instances in the formula.
RealInstanceCache fRealInstanceCache
! Cache accelerating the GetRealInstance function
TObjArray fMethods
! List of leaf method calls
Int_t fNdata[kMAXCODES]
! This caches the physical number of element in the leaf or data member.
void Optimize() override
MI include.
TObjArray fLeaves
! List of leaf used in this formula.
bool fDidBooleanOptimization
! True if we executed one boolean optimization since the last time instance number 0 was evaluated
friend class TTreeFormulaManager
virtual void * GetValuePointerFromMethod(Int_t i, TLeaf *leaf) const
Return result of a leafobject method.
TObjArray fBranches
! List of branches to read. Similar to fLeaves but duplicates are zeroed out.
TTreeFormulaManager * fManager
! The dimension coordinator.
void Convert(UInt_t fromVersion) override
A TTree represents a columnar dataset.
Definition TTree.h:89
virtual TBranch * FindBranch(const char *name)
Return the branch that correspond to the path 'branchname', which can include the name of the tree or...
Definition TTree.cxx:4969
virtual TIterator * GetIteratorOnAllLeaves(bool dir=kIterForward)
Creates a new iterator that will go through all the leaves on the tree itself and its friend.
Definition TTree.cxx:6220
virtual Long64_t GetEntries() const
Definition TTree.h:518
virtual TLeaf * GetLeaf(const char *branchname, const char *leafname)
Searches in this tree and any of its friends for a leaf named leafname in branch branchname ,...
Definition TTree.cxx:6325
virtual Long64_t GetReadEntry() const
Definition TTree.h:604
virtual TObjArray * GetListOfBranches()
Definition TTree.h:583
virtual TTree * GetTree() const
Definition TTree.h:612
virtual Long64_t LoadTree(Long64_t entry)
Set current entry.
Definition TTree.cxx:6727
virtual const char * GetAlias(const char *aliasName) const
Returns the expanded value of the alias. Search in the friends if any.
Definition TTree.cxx:5302
virtual Int_t GetTreeNumber() const
Definition TTree.h:614
static TClass * Class()
virtual TLeaf * FindLeaf(const char *name)
Find first leaf containing searchname.
Definition TTree.cxx:4992
virtual const char * GetFriendAlias(TTree *) const
If the 'tree' is a friend, this method returns its alias name.
Definition TTree.cxx:6140
virtual EDataType GetType() const =0
If the value type is a fundamental data type, return its type (see enumeration EDataType).
virtual TClass * GetValueClass() const =0
If the value type is a user-defined class, return a pointer to the TClass representing the value type...
virtual Bool_t HasCounter() const =0
virtual TObjArray * GetElements() const =0
RVec< PromoteTypes< T0, T1 > > fmod(const T0 &x, const RVec< T1 > &v)
Definition RVec.hxx:1814
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
Double_t CosH(Double_t)
Returns the hyperbolic cosine of x.
Definition TMath.h:625
Double_t ACos(Double_t)
Returns the principal value of the arc cosine of x, expressed in radians.
Definition TMath.h:645
Double_t ASin(Double_t)
Returns the principal value of the arc sine of x, expressed in radians.
Definition TMath.h:637
Double_t Exp(Double_t x)
Returns the base-e exponential function of x, which is e raised to the power x.
Definition TMath.h:722
Double_t ATan(Double_t)
Returns the principal value of the arc tangent of x, expressed in radians.
Definition TMath.h:653
Double_t ASinH(Double_t)
Returns the area hyperbolic sine of x.
Definition TMath.cxx:67
Double_t TanH(Double_t)
Returns the hyperbolic tangent of x.
Definition TMath.h:631
Double_t ACosH(Double_t)
Returns the nonnegative area hyperbolic cosine of x.
Definition TMath.cxx:81
Double_t ATan2(Double_t y, Double_t x)
Returns the principal value of the arc tangent of y/x, expressed in radians.
Definition TMath.h:659
Double_t Log(Double_t x)
Returns the natural logarithm of x.
Definition TMath.h:769
Double_t Sqrt(Double_t x)
Returns the square root of x.
Definition TMath.h:675
LongDouble_t Power(LongDouble_t x, LongDouble_t y)
Returns x raised to the power y.
Definition TMath.h:734
Double_t Cos(Double_t)
Returns the cosine of an angle of x radians.
Definition TMath.h:607
constexpr Double_t Pi()
Definition TMath.h:40
Double_t Sin(Double_t)
Returns the sine of an angle of x radians.
Definition TMath.h:601
Double_t SignalingNaN()
Returns a signaling NaN as defined by IEEE 754.
Definition TMath.h:923
Double_t Tan(Double_t)
Returns the tangent of an angle of x radians.
Definition TMath.h:613
Double_t ATanH(Double_t)
Returns the area hyperbolic tangent of x.
Definition TMath.cxx:95
Double_t Log10(Double_t x)
Returns the common (base-10) logarithm of x.
Definition TMath.h:775
Short_t Abs(Short_t d)
Returns the absolute value of parameter Short_t d.
Definition TMathBase.h:122
Double_t SinH(Double_t)
Returns the hyperbolic sine of x.
Definition TMath.h:619
TCanvas * slash()
Definition slash.C:1
static const char * what
Definition stlLoader.cc:5
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2335
const UChar_t kTFOperShift
Definition TFormula.h:33
const Int_t kTFOperMask
Definition TFormula.h:32
const Int_t kMAXFOUND
Definition TFormula.h:31