Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TEntryList.cxx
Go to the documentation of this file.
1 // @(#)root/tree:$Id$
2 // Author: Anna Kreshuk 27/10/2006
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2006, 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 /** \class TEntryList
13 \ingroup tree
14 
15 A List of entry numbers in a TTree or TChain.
16 
17 There are two types of entry lists:
18 
19 #### 1.
20  for a TTree (fBlocks data member is non-zero)
21  Entry numbers are stored in TEntryListBlocks, which, in their turn, are stored
22  in the TObjArray fBlocks. The range of the entry numbers is cut into intervals
23  of kBlockSize entries (currently 64000), so that the first block contains
24  information which entries out of the first 64000 pass the selection, the second
25  block - which entries out of the 64000-127999 interval pass the selection, etc.
26  Some blocks, obviously, might be empty. The internal representation of entry
27  numbers in the blocks is described in the TEntryListBlock class description, and
28  this representation might be changed by calling OptimizeStorage() function
29  (when the list is filled via the Enter() function, this is done automatically,
30  except for the last block).
31  Individual entry lists can be merged (functions Merge() and Add())
32  to make an entry list for a TChain of corresponding TTrees.
33 Begin_Macro
34 entrylist_figure1.C
35 End_Macro
36 
37 #### 2.
38  for a TChain (fLists data member is non-zero)
39  It contains a TList of sub-lists (TEntryList objects, corresponding to each TTree)
40  Trees and lists are matched by the TTree name and its file name (full path).
41  All sub-lists are returned by the GetLists() function and individual lists are
42  returned by GetEntryList() function. Such lists are no different from the lists for
43  TTrees, described above.
44 Begin_Macro
45 entrylist_figure2.C
46 End_Macro
47 
48 ## Operations on entry lists
49 
50 - __Add__() - if the lists are for the same tree, adds all the entries of the second list
51  to the first list. If the lists are for different trees, creates a TEntryList
52  with 2 sublists for each TTree. If the lists are for TChains, merges the ones
53  for the same trees and adds new sublists for the TTrees that were not included
54  in the first TEntryList
55 - __Subtract__() - if the lists are for the same TTree, removes the entries of the second
56  list from the first list. If the lists are for TChains, loops over all
57  sub-lists
58 - __GetEntry(n)__ - returns the n-th entry number
59 - __Next__() - returns next entry number. Note, that this function is
60  much faster than GetEntry, and it's called when GetEntry() is called
61  for 2 or more indices in a row.
62 
63 ## TTree::Draw() and TChain::Draw()
64 
65 Use option __entrylist__ to write the results of TTree::Draw and TChain::Draw into
66 an entry list. Example:
67 ~~~ {.cpp}
68  tree->Draw(">>elist", "x<0 && y>0", "entrylist");
69  TEntryList *elist = (TEntryList*)gDirectory->Get("elist");
70 ~~~
71 ## Example of Loop on TEntryList with a TChain
72 ~~~ {.cpp}
73  void loopChain() {
74  TFile *fe = TFile::Open("myelist.root");
75  TEntryList *myelist = (TEntryList*)fe->Get("myelist");
76  TChain *chain = new TChain("ntuple");
77  chain->Add("hsimple.root");
78  chain->Add("hsimple2.root");
79  Long64_t listEntries = myelist->GetN();
80  Long64_t chainEntries = chain->GetEntries();
81  Int_t treenum = 0;
82  chain->SetEntryList(myelist);
83 
84  for (entry=start;entry < end;entry++) {
85  entryNumber = chain->GetEntryNumber(entry);
86  if (entryNumber < 0) break;
87  localEntry = chain->LoadTree(entryNumber);
88  if (localEntry < 0) break;
89  ....
90  then either call
91  branch->GetEntry(localEntry);
92  or
93  chain->GetEntry(entryNumber);
94  In the later case the LoadTree is then somewhat redudant.
95  ...
96  }
97  }
98 ~~~
99 When using the TEntryList interface directly, you can get the 'tree number' and entry in
100 the current tree (i.e. value similar to the return value of LoadTree) from calling
101 TEntryList::GetEntryAndTree:
102 ~~~ {.cpp}
103  Long64_t treeEntry = myelist->GetEntryAndTree(el,treenum);
104 ~~~
105 to obtain the entry number within the chain you need to add to it the value of
106 `treeEntry+ch->GetTreeOffset()[treenum]`
107 such that the loop in the previous example can also be written as:
108 ~~~ {.cpp}
109  for (Long64_t el = 0; el < listEntries; el++) {
110  Long64_t treeEntry = myelist->GetEntryAndTree(el,treenum);
111  Long64_t chainEntry = treeEntry+ch->GetTreeOffset()[treenum];
112  printf("el=%lld, treeEntry=%lld, chainEntry=%lld, treenum=%d\n", el, treeEntry, chainEntry, treenum);
113 
114  ch->LoadTree(chainEntry); // this also returns treeEntry
115  needed_branch->GetEntry(treeEntry);
116  }
117 ~~~
118 ## TSelectors
119 
120 To fill an TEntryList from a TSelector correctly, one must add the TEntryList object
121 to the output list of the selector (TSelector::fOutput). This is the only way to
122 make the sub-lists of the TEntryList switch when the current tree of the TChain is
123 changed.
124 
125 ## Using a TEntryList as input (TTree::SetEntryList() and TChain::SetEntryList())
126 
127 while the TTree::SetEntryList() function is only setting the TTree::fEntryList
128 data member, the same function in TChain also finds correspondence between
129 the TTrees of this TChain and the sub-lists of this TEntryList.
130 
131 ## TEntryList and the current directory
132 
133 TEntryList objects are automatically added to the current directory (like TTrees).
134 However, in case of a TEntryList for a chain, only the top-level entry list is added,
135 not the sub-lists for specific trees. Placing entry lists in the current directory
136 allows calling them as a part of a TTreeFormula expression, so if the user wants
137 to extract a sublist from a TChain entry list via the GetEntryList() or some other
138 function, they have to add it to the current directory to be able to use it in
139 TTreeFormula expressions.
140 
141 ## TEntryList and TEventList
142 
143 TTree::SetEventList() and TChain::SetEventList() transform a TEventList into a TEntryList
144 See comments to those functions for more details
145 */
146 
147 #include "TEntryList.h"
148 #include "TEntryListBlock.h"
149 #include "TError.h"
150 #include "TKey.h"
151 #include "TTree.h"
152 #include "TFile.h"
153 #include "TRegexp.h"
154 #include "TSystem.h"
155 
156 ClassImp(TEntryList);
157 
158 ////////////////////////////////////////////////////////////////////////////////
159 /// default c-tor
160 
161 TEntryList::TEntryList() : fEntriesToProcess(0)
162 {
163  fLists = 0;
164  fCurrent = 0;
165  fBlocks = 0;
166  fN = 0;
167  fNBlocks = 0;
168  fTreeName = "";
169  fFileName = "";
170  fStringHash = 0;
171  fTreeNumber = -1;
172  fDirectory = 0;
173  fReapply = kFALSE;
174  fLastIndexQueried = -1;
175  fLastIndexReturned = 0;
176  fShift = kFALSE;
177 }
178 
179 ////////////////////////////////////////////////////////////////////////////////
180 /// c-tor with name and title
181 
182 TEntryList::TEntryList(const char *name, const char *title) :
183  TNamed(name, title),
184  fEntriesToProcess(0)
185 {
186  fLists = 0;
187  fCurrent = 0;
188  fBlocks = 0;
189  fN = 0;
190  fNBlocks = 0;
191  fTreeName = "";
192  fFileName = "";
193  fStringHash = 0;
194  fTreeNumber = -1;
195  fReapply = kFALSE;
196 
197  fDirectory = gDirectory;
198  if (fDirectory) fDirectory->Append(this);
199 
200  fLastIndexQueried = -1;
201  fLastIndexReturned = 0;
202  fShift = kFALSE;
203 }
204 
205 ////////////////////////////////////////////////////////////////////////////////
206 /// constructor with name and title, which also sets the tree
207 
208 TEntryList::TEntryList(const char *name, const char *title, const TTree *tree):TNamed(name, title)
209 {
210  fLists = 0;
211  fCurrent = 0;
212  fBlocks = 0;
213  fN = 0;
214  fNBlocks = 0;
215  fTreeNumber = -1;
216  TEntryList::SetTree(tree);
217  fReapply = kFALSE;
218 
219  fDirectory = gDirectory;
220  if (fDirectory) fDirectory->Append(this);
221 
222  fLastIndexQueried = -1;
223  fLastIndexReturned = 0;
224  fShift = kFALSE;
225 }
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 /// c-tor with name and title, which also sets the treename and the filename
229 
230 TEntryList::TEntryList(const char *name, const char *title, const char *treename, const char *filename) : TNamed(name, title),fEntriesToProcess(0)
231 {
232  fLists = 0;
233  fCurrent = 0;
234  fBlocks = 0;
235  fNBlocks = 0;
236  fN = 0;
237  SetTree(treename, filename);
238  fTreeNumber = -1;
239  fReapply = kFALSE;
240 
241  fDirectory = gDirectory;
242  if (fDirectory) fDirectory->Append(this);
243 
244  fLastIndexQueried = -1;
245  fLastIndexReturned = 0;
246  fShift = kFALSE;
247 }
248 
249 ////////////////////////////////////////////////////////////////////////////////
250 /// c-tor, which sets the tree
251 
252 TEntryList::TEntryList(const TTree *tree) : fEntriesToProcess(0)
253 {
254  fLists = 0;
255  fCurrent = 0;
256  fBlocks = 0;
257  fNBlocks = 0;
258  fN = 0;
259 
260  SetTree(tree);
261  fTreeNumber = -1;
262 
263  fReapply = kFALSE;
264  fDirectory = gDirectory;
265  if (fDirectory) fDirectory->Append(this);
266 
267  fLastIndexQueried = -1;
268  fLastIndexReturned = 0;
269  fShift = kFALSE;
270 }
271 
272 ////////////////////////////////////////////////////////////////////////////////
273 /// copy c-tor
274 
275 TEntryList::TEntryList(const TEntryList &elist) : TNamed(elist)
276 {
277  fNBlocks = elist.fNBlocks;
278  fTreeName = elist.fTreeName;
279  fFileName = elist.fFileName;
280  fStringHash = elist.fStringHash;
281  fTreeNumber = elist.fTreeNumber;
282  fLastIndexQueried = -1;
283  fLastIndexReturned = 0;
284  fN = elist.fN;
285  fShift = elist.fShift;
286  fLists = 0;
287  fBlocks = 0;
288  fReapply = elist.fReapply;
289  fCurrent = 0;
290  fEntriesToProcess = elist.fEntriesToProcess;
291  if (elist.fLists){
292  fLists = new TList();
293  TEntryList *el1 = 0;
294  TEntryList *el2 = 0;
295  TIter next(elist.fLists);
296  while((el1 = (TEntryList*)next())){
297  el2 = new TEntryList(*el1);
298  if (el1==elist.fCurrent)
299  fCurrent = el2;
300  fLists->Add(el2);
301  }
302  } else {
303  if (elist.fBlocks){
304  TEntryListBlock *block1 = 0;
305  TEntryListBlock *block2 = 0;
306  //or just copy it as a TObjArray??
307  fBlocks = new TObjArray();
308  for (Int_t i=0; i<fNBlocks; i++){
309  block1 = (TEntryListBlock*)elist.fBlocks->UncheckedAt(i);
310  block2 = new TEntryListBlock(*block1);
311  fBlocks->Add(block2);
312  }
313  }
314  fCurrent = this;
315  }
316  fDirectory = 0;
317 
318 }
319 
320 ////////////////////////////////////////////////////////////////////////////////
321 /// Destructor.
322 
323 TEntryList::~TEntryList()
324 {
325  if (fBlocks){
326  fBlocks->Delete();
327  delete fBlocks;
328  }
329  fBlocks = 0;
330  if (fLists){
331  fLists->Delete();
332  delete fLists;
333  }
334 
335  fLists = 0;
336 
337  if (fDirectory) fDirectory->Remove(this);
338  fDirectory = 0;
339 
340 }
341 
342 ////////////////////////////////////////////////////////////////////////////////
343 /// Add 2 entry lists
344 
345 void TEntryList::Add(const TEntryList *elist)
346 {
347  if (fN==0){
348  if (!fLists && fTreeName=="" && fFileName==""){
349  //this list is empty. copy the other list completely
350  fNBlocks = elist->fNBlocks;
351  fTreeName = elist->fTreeName;
352  fFileName = elist->fFileName;
353  fStringHash = elist->fStringHash;
354  fTreeNumber = elist->fTreeNumber;
355  fLastIndexQueried = -1;
356  fLastIndexReturned = 0;
357  fN = elist->fN;
358  if (elist->fLists){
359  fLists = new TList();
360  TEntryList *el1 = 0;
361  TEntryList *el2 = 0;
362  TIter next(elist->fLists);
363  while((el1 = (TEntryList*)next())){
364  el2 = new TEntryList(*el1);
365  if (el1==elist->fCurrent)
366  fCurrent = el2;
367  fLists->Add(el2);
368  }
369  } else {
370  if (elist->fBlocks){
371  TEntryListBlock *block1 = 0;
372  TEntryListBlock *block2 = 0;
373  fBlocks = new TObjArray();
374  for (Int_t i=0; i<fNBlocks; i++){
375  block1 = (TEntryListBlock*)elist->fBlocks->UncheckedAt(i);
376  block2 = new TEntryListBlock(*block1);
377  fBlocks->Add(block2);
378  }
379  }
380  fCurrent = 0;
381  }
382  return;
383  }
384  }
385 
386  if (!fLists){
387  if (!elist->fLists){
388  if (!strcmp(elist->fTreeName.Data(),fTreeName.Data()) && !strcmp(elist->fFileName.Data(),fFileName.Data())){
389  //entry lists are for the same tree
390  if (!elist->fBlocks)
391  //the other list is empty list
392  return;
393  if (!fBlocks){
394  //this entry list is empty
395  TEntryListBlock *block1 = 0;
396  TEntryListBlock *block2 = 0;
397  fNBlocks = elist->fNBlocks;
398  fN = elist->fN;
399  fBlocks = new TObjArray();
400  for (Int_t i=0; i<fNBlocks; i++){
401  block1 = (TEntryListBlock*)elist->fBlocks->UncheckedAt(i);
402  block2 = new TEntryListBlock(*block1);
403  fBlocks->Add(block2);
404  }
405  return;
406  }
407  //both not empty, merge block by block
408  TEntryListBlock *block1=0;
409  TEntryListBlock *block2=0;
410  Int_t i;
411  Int_t nmin = TMath::Min(fNBlocks, elist->fNBlocks);
412  Long64_t nnew, nold;
413  for (i=0; i<nmin; i++){
414  block1 = (TEntryListBlock*)fBlocks->UncheckedAt(i);
415  block2 = (TEntryListBlock*)elist->fBlocks->UncheckedAt(i);
416  nold = block1->GetNPassed();
417  nnew = block1->Merge(block2);
418  fN = fN - nold + nnew;
419  }
420  if (fNBlocks<elist->fNBlocks){
421  Int_t nmax = elist->fNBlocks;
422  for (i=nmin; i<nmax; i++){
423  block2 = (TEntryListBlock*)elist->fBlocks->UncheckedAt(i);
424  block1 = new TEntryListBlock(*block2);
425  fBlocks->Add(block1);
426  fN+=block1->GetNPassed();
427  fNBlocks++;
428  }
429  }
430  fLastIndexQueried = -1;
431  fLastIndexReturned = 0;
432  } else {
433  //entry lists are for different trees. create a chain entry list with
434  //2 sub lists for the first and second entry lists
435  fLastIndexQueried = -1;
436  fLastIndexReturned = 0;
437  fLists = new TList();
438  TEntryList *el = new TEntryList();
439  el->fTreeName = fTreeName;
440  el->fFileName = fFileName;
441  el->fBlocks = fBlocks;
442  fBlocks = 0;
443  el->fNBlocks = fNBlocks;
444  el->fN = fN;
445  el->fLastIndexQueried = -1;
446  el->fLastIndexReturned = 0;
447  fLists->Add(el);
448  el = new TEntryList(*elist);
449  el->fLastIndexQueried = -1;
450  el->fLastIndexReturned = 0;
451  fLists->Add(el);
452  fN+=el->GetN();
453  fCurrent = 0;
454  }
455  } else {
456  //second list already has sublists. add one by one
457  TEntryList *el = 0;
458  TIter next(elist->fLists);
459  while ((el = (TEntryList*)next())){
460  Add(el);
461  }
462  fCurrent = 0;
463  }
464  } else {
465  //there are already some sublists in this list, just add another one
466  if (!elist->fLists){
467  //the other list doesn't have sublists
468  TIter next(fLists);
469  TEntryList *el = 0;
470  Bool_t found = kFALSE;
471  while ((el = (TEntryList*)next())){
472  if (!strcmp(el->fTreeName.Data(), elist->fTreeName.Data()) &&
473  !strcmp(el->fFileName.Data(), elist->fFileName.Data())){
474  // if (el->fStringHash == elist->fStringHash){
475  //found a list for the same tree
476  Long64_t oldn = el->GetN();
477  el->Add(elist);
478  found = kTRUE;
479  fN = fN - oldn + el->GetN();
480  break;
481  }
482  }
483  if (!found){
484  el = new TEntryList(*elist);
485  el->fLastIndexQueried = -1;
486  el->fLastIndexReturned = 0;
487  fLists->Add(el);
488  fN+=el->GetN();
489  }
490  } else {
491  //add all sublists from the other list
492  TEntryList *el = 0;
493  TIter next(elist->fLists);
494  while ((el = (TEntryList*)next())){
495  Add(el);
496  }
497  fCurrent = 0;
498  }
499  if (fCurrent){
500  if (fCurrent->fBlocks){
501  Int_t currentblock = (fCurrent->fLastIndexReturned)/kBlockSize;
502  TEntryListBlock *block = (TEntryListBlock*)fCurrent->fBlocks->UncheckedAt(currentblock);
503  block->ResetIndices();
504  fCurrent->fLastIndexReturned = 0;
505  fCurrent->fLastIndexQueried = -1;
506  }
507  }
508  fCurrent = 0;
509  }
510 
511 }
512 
513 ////////////////////////////////////////////////////////////////////////////////
514 /// - When tree = 0, returns from the current list
515 /// - When tree != 0, finds the list, corresponding to this tree
516 /// - When tree is a chain, the entry is assumed to be global index and the local
517 /// entry is recomputed from the treeoffset information of the chain
518 
519 Int_t TEntryList::Contains(Long64_t entry, TTree *tree)
520 {
521  if (!tree){
522  if (fBlocks) {
523  //this entry list doesn't contain any sub-lists
524  TEntryListBlock *block = 0;
525  Int_t nblock = entry/kBlockSize;
526  if (nblock >= fNBlocks) return 0;
527  block = (TEntryListBlock*)fBlocks->UncheckedAt(nblock);
528  return block->Contains(entry-nblock*kBlockSize);
529  }
530  if (fLists) {
531  if (!fCurrent) fCurrent = (TEntryList*)fLists->First();
532  return fCurrent->Contains(entry);
533  }
534  return 0;
535  } else {
536  Long64_t localEntry = tree->LoadTree(entry);
537  SetTree(tree->GetTree());
538  if (fCurrent)
539  return fCurrent->Contains(localEntry);
540  }
541  return 0;
542 
543 }
544 
545 ////////////////////////////////////////////////////////////////////////////////
546 /// Called by TKey and others to automatically add us to a directory when we are read from a file.
547 
548 void TEntryList::DirectoryAutoAdd(TDirectory* dir)
549 {
550  SetDirectory(dir);
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 /// Add entry \#entry to the list
555 /// - When tree = 0, adds to the current list
556 /// - When tree != 0, finds the list, corresponding to this tree
557 /// - When tree is a chain, the entry is assumed to be global index and the local
558 /// entry is recomputed from the treeoffset information of the chain
559 
560 Bool_t TEntryList::Enter(Long64_t entry, TTree *tree)
561 {
562  if (!tree){
563  if (!fLists) {
564  if (!fBlocks) fBlocks = new TObjArray();
565  TEntryListBlock *block = 0;
566  Long64_t nblock = entry/kBlockSize;
567  if (nblock >= fNBlocks) {
568  if (fNBlocks>0){
569  block = (TEntryListBlock*)fBlocks->UncheckedAt(fNBlocks-1);
570  if (!block) return 0;
571  block->OptimizeStorage();
572  }
573  for (Int_t i=fNBlocks; i<=nblock; i++){
574  block = new TEntryListBlock();
575  fBlocks->Add(block);
576  }
577  fNBlocks = nblock+1;
578  }
579  block = (TEntryListBlock*)fBlocks->UncheckedAt(nblock);
580  if (block->Enter(entry-nblock*kBlockSize)) {
581  fN++;
582  return 1;
583  }
584  } else {
585  //the entry in the current entry list
586  if (!fCurrent) fCurrent = (TEntryList*)fLists->First();
587  if (fCurrent->Enter(entry)) {
588  if (fLists)
589  fN++;
590  return 1;
591  }
592  }
593  } else {
594  Long64_t localentry = tree->LoadTree(entry);
595  SetTree(tree->GetTree());
596  if (fCurrent){
597  if (fCurrent->Enter(localentry)) {
598  if (fLists)
599  fN++;
600  return 1;
601  }
602  }
603  }
604  return 0;
605 
606 }
607 
608 ////////////////////////////////////////////////////////////////////////////////
609 /// Remove entry \#entry from the list
610 /// - When tree = 0, removes from the current list
611 /// - When tree != 0, finds the list, corresponding to this tree
612 /// - When tree is a chain, the entry is assumed to be global index and the local
613 /// entry is recomputed from the treeoffset information of the chain
614 
615 Bool_t TEntryList::Remove(Long64_t entry, TTree *tree)
616 {
617  if (entry < 0)
618  return kFALSE;
619  if (!tree) {
620  if (!fLists) {
621  if (!fBlocks) return 0;
622  TEntryListBlock *block = 0;
623  Long64_t nblock = entry/kBlockSize;
624  block = (TEntryListBlock*)fBlocks->UncheckedAt(nblock);
625  if (!block) return 0;
626  Long64_t blockindex = entry - nblock*kBlockSize;
627  if (block->Remove(blockindex)){
628  fN--;
629  return 1;
630  }
631  } else {
632  if (!fCurrent) fCurrent = (TEntryList*)fLists->First();
633  if (fCurrent->Remove(entry)){
634  if (fLists)
635  fN--;
636  return 1;
637  }
638  }
639  } else {
640  Int_t localentry = tree->LoadTree(entry);
641  SetTree(tree->GetTree());
642  if (fCurrent){
643  if (fCurrent->Remove(localentry)) {
644  if (fLists)
645  fN--;
646  return 1;
647  }
648  }
649  }
650  return 0;
651 }
652 
653 ////////////////////////////////////////////////////////////////////////////////
654 /// Return the number of the entry \#index of this TEntryList in the TTree or TChain
655 /// See also Next().
656 
657 Long64_t TEntryList::GetEntry(Int_t index)
658 {
659 
660  if (index>=fN){
661  return -1;
662  }
663  if (index==fLastIndexQueried+1){
664  //in a loop
665  return Next();
666  } else {
667  if (fBlocks) {
668  TEntryListBlock *block = 0;
669  Long64_t total_passed = 0;
670  Int_t i=0;
671  while (total_passed<=index && i<fNBlocks){
672  block=(TEntryListBlock*)fBlocks->UncheckedAt(i);
673  total_passed+=block->GetNPassed();
674  i++;
675  }
676  i--;
677  total_passed-=block->GetNPassed();
678  if (i!=fLastIndexReturned/kBlockSize){
679  block = (TEntryListBlock*)fBlocks->UncheckedAt(fLastIndexReturned/kBlockSize);
680  block->ResetIndices();
681  block = (TEntryListBlock*)fBlocks->UncheckedAt(i);
682  }
683 
684  Long64_t localindex = index - total_passed;
685  Long64_t blockindex = block->GetEntry(localindex);
686  if (blockindex < 0) return -1;
687  Long64_t res = i*kBlockSize + blockindex;
688  fLastIndexQueried = index;
689  fLastIndexReturned = res;
690  return res;
691  } else {
692  //find the corresponding list
693  if (!fCurrent) fCurrent = (TEntryList*)fLists->First();
694  TIter next(fLists);
695  TEntryList *templist;
696  Long64_t ntotal = 0;
697  if (fCurrent){
698  //reset all indices of the current list
699  if (fCurrent->fBlocks){
700  Int_t currentblock = (fCurrent->fLastIndexReturned)/kBlockSize;
701  TEntryListBlock *block = (TEntryListBlock*)fCurrent->fBlocks->UncheckedAt(currentblock);
702  block->ResetIndices();
703  fCurrent->fLastIndexReturned = 0;
704  fCurrent->fLastIndexQueried = -1;
705  }
706  }
707  while ((templist = (TEntryList*)next())){
708  if (!fShift){
709  ntotal += templist->GetN();
710  } else {
711  if (templist->GetTreeNumber() >= 0)
712  ntotal += templist->GetN();
713  }
714  if (ntotal > index)
715  break;
716  }
717  fCurrent = templist;
718  if (!fCurrent) return -1;
719  Long64_t localentry = index - (ntotal - fCurrent->GetN());
720  fLastIndexQueried = index;
721  fLastIndexReturned = fCurrent->GetEntry(localentry);
722  return fLastIndexReturned;
723  }
724 
725  }
726  return -1;
727 }
728 
729 ////////////////////////////////////////////////////////////////////////////////
730 /// Return the index of "index"-th non-zero entry in the TTree or TChain
731 /// and the # of the corresponding tree in the chain
732 
733 Long64_t TEntryList::GetEntryAndTree(Int_t index, Int_t &treenum)
734 {
735 //If shift is true, then when the requested entry is found in an entry list,
736 //for which there is no corresponding tree in the chain, this list is not
737 //taken into account, and entry from the next list with a tree is returned.
738 //Example:
739 //First sublist - 20 entries, second sublist - 5 entries, third sublist - 10 entries
740 //Second sublist doesn't correspond to any trees of the chain
741 //Then, when GetEntryAndTree(21, treenum, kTRUE) is called, first entry of the
742 //third sublist will be returned
743 
744  Long64_t result = GetEntry(index);
745  if (result < 0) {
746  treenum = -1;
747  return result;
748  }
749  R__ASSERT(fLists == nullptr || (fLists != nullptr && fCurrent != nullptr));
750  if (fCurrent)
751  treenum = fCurrent->fTreeNumber;
752  else
753  treenum = fTreeNumber;
754  if (treenum < 0)
755  return -1;
756 
757  return result;
758 }
759 
760 ////////////////////////////////////////////////////////////////////////////////
761 /// To be able to re-localize the entry-list we identify the file by just the
762 /// name and the anchor, i.e. we drop protocol, host, options, ...
763 /// The result in the form 'file#anchor' (or 'file', if no anchor is present)
764 /// is saved in 'fn'.
765 /// The function optionally (is 'local' is defined) checks file locality (i.e.
766 /// protocol 'file://') returning the result in '*local' .
767 
768 void TEntryList::GetFileName(const char *filename, TString &fn, Bool_t *local)
769 {
770  TUrl u(filename, kTRUE);
771  if (local) *local = (!strcmp(u.GetProtocol(), "file")) ? kTRUE : kFALSE;
772  if (strlen(u.GetAnchor()) > 0) {
773  fn.Form("%s#%s", u.GetFile(), u.GetAnchor());
774  } else {
775  fn = u.GetFile();
776  }
777  // Done
778  return;
779 }
780 
781 ////////////////////////////////////////////////////////////////////////////////
782 /// Return the entry list, corresponding to treename and filename
783 /// By default, the filename is first tried as is, and then, if the corresponding list
784 /// is not found, the filename is expanded to the absolute path, and compared again.
785 /// To avoid it, use option "ne"
786 
787 TEntryList *TEntryList::GetEntryList(const char *treename, const char *filename, Option_t *opt)
788 {
789  if (gDebug > 1)
790  Info("GetEntryList","tree: %s, file: %s",
791  (treename ? treename : "-"), (filename ? filename : "-"));
792 
793  if (!treename || !filename) return 0;
794  TString option = opt;
795  option.ToUpper();
796  Bool_t nexp = option.Contains("NE");
797 
798  TString fn;
799  Bool_t local;
800  GetFileName(filename, fn, &local);
801  if (nexp) local = kFALSE;
802 
803  if (gDebug > 1)
804  Info("GetEntryList", "file: %s, local? %d", filename, local);
805 
806  if (!fLists){
807  //there are no sublists
808  if (!strcmp(treename, fTreeName.Data()) && !(strcmp(fn.Data(), fFileName.Data()))){
809  return this;
810  } else {
811  //if the file is local, try the full name, unless "ne" option was specified
812  if (!nexp && local){
813  gSystem->ExpandPathName(fn);
814  if (!gSystem->IsAbsoluteFileName(fn))
815  gSystem->PrependPathName(gSystem->pwd(), fn);
816  fn = gSystem->UnixPathName(fn);
817  if (!strcmp(treename, fTreeName.Data()) && !(strcmp(fn.Data(), fFileName.Data())))
818  return this;
819  }
820  return 0;
821  }
822  }
823 
824  TString stotal = treename;
825  stotal.Append(fn);
826  ULong_t newhash = stotal.Hash();
827 
828  TIter next(fLists);
829  TEntryList *templist;
830  while ((templist = (TEntryList*)next())){
831  if (templist->fStringHash==0){
832  stotal = templist->fTreeName + templist->fFileName;
833  templist->fStringHash = stotal.Hash();
834  }
835  if (gDebug > 1)
836  Info("GetEntryList", "file: %s (fn: %s), hash: %lu, element hash: %lu",
837  filename, fn.Data(), newhash, templist->fStringHash);
838  if (newhash == templist->fStringHash){
839  if (!strcmp(templist->GetTreeName(), treename) && !strcmp(templist->GetFileName(), fn.Data())){
840  return templist;
841  }
842  }
843  }
844 
845  //didn't find anything for this filename, try the full name too
846  if (!nexp && local){
847  TString longname = fn;
848  gSystem->ExpandPathName(longname);
849  if (!gSystem->IsAbsoluteFileName(longname))
850  gSystem->PrependPathName(gSystem->pwd(), longname);
851  longname = gSystem->UnixPathName(longname);
852  stotal = treename;
853  stotal.Append(longname);
854  newhash = stotal.Hash();
855  next.Reset();
856  while ((templist = (TEntryList*)next())){
857  if (templist->fStringHash==0){
858  stotal = templist->fTreeName + templist->fFileName;
859  templist->fStringHash = stotal.Hash();
860  }
861  if (gDebug > 1)
862  Info("GetEntryList", "file: %s (longname: %s), hash: %lu, element hash: %lu",
863  filename, longname.Data(), newhash, templist->fStringHash);
864  if (newhash == templist->fStringHash){
865  if (templist->fTreeName == treename && templist->fFileName == longname){
866  return templist;
867  }
868  }
869  }
870  }
871  return 0;
872 }
873 
874 ////////////////////////////////////////////////////////////////////////////////
875 /// Merge this list with the lists from the collection
876 
877 Int_t TEntryList::Merge(TCollection *list)
878 {
879  if (!list) return -1;
880  TIter next(list);
881  TEntryList *elist = 0;
882  while ((elist = (TEntryList*)next())) {
883  if (!elist->InheritsFrom(TEntryList::Class())) {
884  Error("Add","Attempt to add object of class: %s to a %s",elist->ClassName(),this->ClassName());
885  return -1;
886  }
887  Add(elist);
888  }
889  return 0;
890 }
891 
892 ////////////////////////////////////////////////////////////////////////////////
893 /// Return the next non-zero entry index (next after fLastIndexQueried)
894 /// this function is faster than GetEntry()
895 
896 Long64_t TEntryList::Next()
897 {
898  Long64_t result;
899  if (fN == fLastIndexQueried+1 || fN==0){
900  return -1;
901  }
902  if (fBlocks){
903  Int_t iblock = fLastIndexReturned/kBlockSize;
904  TEntryListBlock *current_block = (TEntryListBlock*)fBlocks->UncheckedAt(iblock);
905  result = current_block->Next();
906  if (result>=0) {
907  fLastIndexQueried++;
908  fLastIndexReturned = result+kBlockSize*iblock;
909  return fLastIndexReturned;
910  }
911  else {
912  while (result<0 && iblock<fNBlocks-1) {
913  current_block->ResetIndices();
914  iblock++;
915  current_block = (TEntryListBlock*)fBlocks->UncheckedAt(iblock);
916  current_block->ResetIndices();
917  result = current_block->Next();
918  }
919  if (result<0) {
920  fLastIndexQueried = -1;
921  fLastIndexReturned = 0;
922  return -1;
923  }
924  fLastIndexQueried++;
925  fLastIndexReturned = result+kBlockSize*iblock;
926 
927  return fLastIndexReturned;
928  }
929  } else {
930  if (!fCurrent) {
931  fCurrent = (TEntryList*)fLists->First();
932  if (!fCurrent) return 0;
933  if (fShift) {
934  while (fCurrent->GetTreeNumber()<0) {
935  fCurrent = (TEntryList*)fLists->After(fCurrent);
936  if (!fCurrent) return 0;
937  }
938  }
939  }
940  result = fCurrent->Next();
941  if (result>=0) {
942  fLastIndexQueried++;
943  fLastIndexReturned = result;
944  return result;
945  } else {
946  if (fCurrent){
947  //reset all indices of the current list
948  if (fCurrent->fBlocks){
949  Int_t currentblock = (fCurrent->fLastIndexReturned)/kBlockSize;
950  TEntryListBlock *block = (TEntryListBlock*)fCurrent->fBlocks->UncheckedAt(currentblock);
951  block->ResetIndices();
952  fCurrent->fLastIndexReturned = 0;
953  fCurrent->fLastIndexQueried = -1;
954  }
955  }
956 
957  //find the list with the next non-zero entry
958  while (result<0 && fCurrent!=((TEntryList*)fLists->Last())){
959  if (!fCurrent) return 0;
960  fCurrent->fLastIndexQueried = -1;
961  fCurrent->fLastIndexReturned = 0;
962  fCurrent = (TEntryList*)fLists->After(fCurrent);
963  // fCurrent is guarantee to be non-zero because it is not the 'last'
964  // element of the list.
965  if (!fCurrent) return 0;
966  if (!fShift)
967  result = fCurrent->Next();
968  else {
969  if (fCurrent->GetTreeNumber() >= 0)
970  result = fCurrent->Next();
971  }
972  }
973  fLastIndexQueried++;
974  fLastIndexReturned = result;
975  return result;
976  }
977  }
978 }
979 
980 ////////////////////////////////////////////////////////////////////////////////
981 /// Checks if the array representation is more economical and if so, switches to it
982 
983 void TEntryList::OptimizeStorage()
984 {
985  if (fBlocks){
986  TEntryListBlock *block = 0;
987  for (Int_t i=0; i<fNBlocks; i++){
988  block = (TEntryListBlock*)fBlocks->UncheckedAt(i);
989  block->OptimizeStorage();
990  }
991  }
992 }
993 
994 ////////////////////////////////////////////////////////////////////////////////
995 /// Print this list
996 /// - option = "" - default - print the name of the tree and file
997 /// - option = "all" - print all the entry numbers
998 
999 void TEntryList::Print(const Option_t* option) const
1000 {
1001  TString opt = option;
1002  opt.ToUpper();
1003  if (fBlocks) {
1004  Printf("%s %s %lld", fTreeName.Data(), fFileName.Data(), fN);
1005  if (opt.Contains("A")){
1006  TEntryListBlock* block = 0;
1007  for (Int_t i=0; i<fNBlocks; i++){
1008  block = (TEntryListBlock*)fBlocks->UncheckedAt(i);
1009  Int_t shift = i*kBlockSize;
1010  block->PrintWithShift(shift);
1011  }
1012  }
1013  }
1014  else {
1015  TEntryList *elist = 0;
1016  if (fN>0){
1017  TIter next(fLists);
1018  while((elist = (TEntryList*)next())){
1019  elist->Print(option);
1020  }
1021  } else {
1022  if (!fLists) Printf("%s %s %lld", fTreeName.Data(), fFileName.Data(), fN);
1023  else {
1024  TIter next(fLists);
1025  while ((elist = (TEntryList*)next())){
1026  Printf("%s %s %lld", elist->GetTreeName(), elist->GetFileName(), elist->GetN());
1027  }
1028  }
1029  }
1030  }
1031 }
1032 
1033 ////////////////////////////////////////////////////////////////////////////////
1034 /// Reset this list
1035 
1036 void TEntryList::Reset()
1037 {
1038  //Maybe not delete, but just reset the number of blocks to 0????
1039 
1040  if (fBlocks){
1041  fBlocks->Delete();
1042  delete fBlocks;
1043  fBlocks = 0;
1044  }
1045  if (fLists){
1046  if (!((TEntryList*)fLists->First())->GetDirectory()){
1047  fLists->Delete();
1048  }
1049  delete fLists;
1050  fLists = 0;
1051  }
1052  fCurrent = 0;
1053  fBlocks = 0;
1054  fNBlocks = 0;
1055  fN = 0;
1056  fTreeName = "";
1057  fFileName = "";
1058  fStringHash = 0;
1059  fTreeNumber = -1;
1060  fLastIndexQueried = -1;
1061  fLastIndexReturned = 0;
1062  fReapply = kFALSE;
1063 }
1064 
1065 ////////////////////////////////////////////////////////////////////////////////
1066 /// Add reference to directory dir. dir can be 0.
1067 
1068 void TEntryList::SetDirectory(TDirectory *dir)
1069 {
1070  if (fDirectory == dir) return;
1071  if (fDirectory) fDirectory->Remove(this);
1072  fDirectory = dir;
1073  if (fDirectory) fDirectory->Append(this);
1074 }
1075 
1076 ////////////////////////////////////////////////////////////////////////////////
1077 /// If a list for a tree with such name and filename exists, sets it as the current sublist
1078 /// If not, creates this list and sets it as the current sublist
1079 ///
1080 /// ! the filename is taken as provided, no extensions to full path or url !
1081 
1082 void TEntryList::SetTree(const char *treename, const char *filename)
1083 {
1084  TEntryList *elist = 0;
1085 
1086  TString fn;
1087  GetFileName(filename, fn);
1088 
1089  TString stotal = treename;
1090  stotal.Append(fn.Data());
1091  //printf("setting tree %s\n", stotal.Data());
1092  ULong_t newhash = stotal.Hash();
1093  if (fLists) {
1094  //find the corresponding entry list and make it current
1095  if (!fCurrent) fCurrent = (TEntryList*)fLists->First();
1096  if (fCurrent->fStringHash == 0){
1097  stotal = fCurrent->fTreeName + fCurrent->fFileName;
1098  fCurrent->fStringHash = stotal.Hash();
1099  }
1100  if (newhash == fCurrent->fStringHash){
1101  //this list is current
1102  if (!strcmp(fCurrent->fTreeName, treename) && !strcmp(fCurrent->fFileName, fn.Data())){
1103  return;
1104  }
1105  }
1106  TIter next(fLists);
1107  while ((elist = (TEntryList*)next())){
1108  if (newhash == elist->fStringHash){
1109  if (elist->fTreeName == treename && elist->fFileName == fn.Data()) {
1110  //the current entry list was changed. reset the fLastIndexQueried,
1111  //so that Next() doesn't start with the wrong current list
1112  //Also, reset those indices in the previously current list
1113  if (fCurrent->fBlocks){
1114  Int_t currentblock = (fCurrent->fLastIndexReturned)/kBlockSize;
1115  TEntryListBlock *block = (TEntryListBlock*)fCurrent->fBlocks->UncheckedAt(currentblock);
1116  block->ResetIndices();
1117  fCurrent->fLastIndexReturned = 0;
1118  fCurrent->fLastIndexQueried = -1;
1119  }
1120  fCurrent = elist;
1121  fLastIndexQueried = -3;
1122  return;
1123  }
1124  }
1125  }
1126  //didn't find an entry list for this tree, create a new one
1127  elist = new TEntryList("", "", treename, fn.Data());
1128  if (elist->GetDirectory()) {
1129  //sub lists are not added to the current directory
1130  elist->GetDirectory()->Remove(elist);
1131  elist->SetDirectory(0);
1132  }
1133  fLists->Add(elist);
1134  fCurrent = elist;
1135  return;
1136  } else {
1137  if (fN==0 && fTreeName=="" && fFileName==""){
1138  //this is the first tree set to this list
1139  fTreeName = treename;
1140  fFileName = fn;
1141  stotal = fTreeName + fFileName;
1142  //fStringHash = stotal.Hash();
1143  fStringHash = newhash;
1144  fCurrent = this;
1145  } else {
1146  if (fStringHash == 0){
1147  stotal = fTreeName + fFileName;
1148  fStringHash = stotal.Hash();
1149  }
1150  if (newhash != fStringHash){
1151  //we have a chain and already have an entry list for the first tree
1152  //move the first entry list to the fLists
1153  fLists = new TList();
1154  elist = new TEntryList();
1155  elist->fTreeName = fTreeName;
1156  elist->fFileName = fFileName;
1157  elist->fStringHash = fStringHash;
1158  elist->fN = fN;
1159  elist->fTreeNumber = fTreeNumber;
1160  elist->fBlocks = fBlocks;
1161  fBlocks = 0;
1162  elist->fNBlocks = fNBlocks;
1163  fLists->Add(elist);
1164  elist = new TEntryList("", "", treename, fn.Data());
1165  if (elist->GetDirectory()) {
1166  //sub lists are not added to the current directory
1167  elist->GetDirectory()->Remove(elist);
1168  elist->SetDirectory(0);
1169  }
1170  fLists->Add(elist);
1171  fCurrent = elist;
1172  //the current entry list was changed. reset the fLastIndexQueried,
1173  //so that Next() doesn't start with the wrong current list
1174  fLastIndexQueried = -3;
1175 
1176  }
1177  else {
1178  //same tree as in the current entry list, don't do anything
1179  return;
1180  }
1181  }
1182  }
1183 }
1184 
1185 ////////////////////////////////////////////////////////////////////////////////
1186 /// If a list for a tree with such name and filename exists, sets it as the current sublist
1187 /// If not, creates this list and sets it as the current sublist
1188 /// The name of the file, where the tree is, is taken as
1189 /// `tree->GetTree()->GetCurrentFile()->GetName()`, and then expanded either to the absolute path,
1190 /// or to full url. If, for some reason, you want to provide
1191 /// the filename in a different format, use SetTree(const char *treename, const char *filename),
1192 /// where the filename is taken "as is".
1193 
1194 void TEntryList::SetTree(const TTree *tree)
1195 {
1196  if (!tree) return;
1197  auto thisTree = tree->GetTree();
1198  if (!thisTree) return;
1199 
1200  TString treename;
1201  if (tree->GetDirectory()->InheritsFrom("TFile")) {
1202  treename = thisTree->GetName();
1203  } else {
1204  treename = TString::Format("%s/%s",tree->GetDirectory()->GetName(),thisTree->GetName());
1205  }
1206 
1207  TString filename;
1208  if (tree->GetTree()->GetCurrentFile()){
1209  filename = tree->GetTree()->GetCurrentFile()->GetName();
1210  TUrl url(filename.Data(), kTRUE);
1211  if (!strcmp(url.GetProtocol(), "file")){
1212  gSystem->ExpandPathName(filename);
1213  if (!gSystem->IsAbsoluteFileName(filename))
1214  gSystem->PrependPathName(gSystem->pwd(), filename);
1215  filename = gSystem->UnixPathName(filename);
1216  url.SetFile(filename);
1217  }
1218  filename = url.GetUrl();
1219  } else {
1220  //memory-resident
1221  filename = "";
1222  }
1223  SetTree(treename, filename);
1224 
1225 }
1226 
1227 ////////////////////////////////////////////////////////////////////////////////
1228 /// Remove all the entries of this entry list, that are contained in elist
1229 
1230 void TEntryList::Subtract(const TEntryList *elist)
1231 {
1232  TEntryList *templist = 0;
1233  if (!fLists){
1234  if (!fBlocks) return;
1235  //check if lists are for the same tree
1236  if (!elist->fLists){
1237  //second list is also only for 1 tree
1238  if (!strcmp(elist->fTreeName.Data(),fTreeName.Data()) &&
1239  !strcmp(elist->fFileName.Data(),fFileName.Data())){
1240  //same tree
1241  Long64_t n2 = elist->GetN();
1242  Long64_t entry;
1243  for (Int_t i=0; i<n2; i++){
1244  entry = (const_cast<TEntryList*>(elist))->GetEntry(i);
1245  Remove(entry);
1246  }
1247  } else {
1248  //different trees
1249  return;
1250  }
1251  } else {
1252  //second list has sublists, try to find one for the same tree as this list
1253  TIter next1(elist->GetLists());
1254  templist = 0;
1255  Bool_t found = kFALSE;
1256  while ((templist = (TEntryList*)next1())){
1257  if (!strcmp(templist->fTreeName.Data(),fTreeName.Data()) &&
1258  !strcmp(templist->fFileName.Data(),fFileName.Data())){
1259  found = kTRUE;
1260  break;
1261  }
1262  }
1263  if (found) {
1264  Subtract(templist);
1265  }
1266  }
1267  } else {
1268  //this list has sublists
1269  TIter next2(fLists);
1270  templist = 0;
1271  Long64_t oldn=0;
1272  while ((templist = (TEntryList*)next2())){
1273  oldn = templist->GetN();
1274  templist->Subtract(elist);
1275  fN = fN - oldn + templist->GetN();
1276  }
1277  }
1278  return;
1279 }
1280 
1281 ////////////////////////////////////////////////////////////////////////////////
1282 
1283 TEntryList operator||(TEntryList &elist1, TEntryList &elist2)
1284 {
1285  TEntryList eresult = elist1;
1286  //eresult = elist1;
1287  // printf("internal in operator1\n");
1288  eresult.Print("all");
1289  eresult.Add(&elist2);
1290  // printf("internal in operator2\n");
1291  eresult.Print("all");
1292 
1293  return eresult;
1294 }
1295 
1296 ////////////////////////////////////////////////////////////////////////////////
1297 /// Relocate the file paths.
1298 /// If `oldroot` is defined, replace `oldroot` with `newroot` in all file names,
1299 /// i.e. `oldroot/re/st/of/the/path` will become `newroot`/re/st/of/the/path`.
1300 /// If `oldroot` is null, the new path will be just `newroot/path`.
1301 /// Relocation is mandatory to use the entry-list with the same dataset at a different
1302 /// location (i.e. on a different cluster, machine or disks).
1303 
1304 Int_t TEntryList::RelocatePaths(const char *newroot, const char *oldroot)
1305 {
1306  // At least newroot must be given
1307  if (!newroot || (newroot && strlen(newroot) <= 0)) {
1308  Warning("RelocatePaths", "the new location must be given!");
1309  return -1;
1310  }
1311 
1312  if (strlen(GetName()) > 0)
1313  Info("RelocatePaths", "'%s': relocating paths '%s' to '%s'",
1314  GetName(), oldroot ? oldroot : "*", newroot);
1315 
1316  Int_t nrl = 0, xnrl = 0;
1317  // Apply to all underlying lists, if any
1318  if (fLists) {
1319  TIter nxl(fLists);
1320  TEntryList *enl = 0;
1321  while ((enl = (TEntryList *) nxl())) {
1322  if ((xnrl = enl->RelocatePaths(newroot, oldroot)) < 0) {
1323  Warning("RelocatePaths", "problems relocating '%s'", enl->GetName());
1324  } else {
1325  nrl += xnrl;
1326  }
1327  }
1328  }
1329  // Apply to ourselves
1330  TString temp;
1331  Ssiz_t lo = 0;
1332  if (oldroot && (lo = strlen(oldroot)) > 0) {
1333  if (fFileName.BeginsWith(oldroot)) {
1334  fFileName.Replace(0, lo, newroot);
1335  nrl++;
1336  }
1337  } else {
1338  Ssiz_t ilst = fFileName.Last('/');
1339  if (ilst != kNPOS) {
1340  fFileName.Replace(0, ilst, newroot);
1341  } else {
1342  fFileName.Insert(0, TString::Format("%s/", newroot));
1343  }
1344  nrl++;
1345  }
1346  if (fStringHash != 0) {
1347  temp.Form("%s%s", fTreeName.Data(), fFileName.Data());
1348  fStringHash = temp.Hash();
1349  }
1350 
1351  // Done
1352  return nrl;
1353 }
1354 
1355 ////////////////////////////////////////////////////////////////////////////////
1356 /// Relocate entry list 'enlnm' in file 'fn' replacing 'oldroot' with 'newroot' in
1357 /// filenames. If 'enlnm' is null or '*' all entry lists in the file are relocated.
1358 /// Relocation is mandatory to use the entry-list with the same dataset at a different
1359 /// location (i.e. on a different cluster, machine or disks).
1360 /// This function can be called as many times as need to reach the desired result.
1361 /// The existing 'locations' can be checked qith TEntryList::Scan .
1362 
1363 Int_t TEntryList::Relocate(const char *fn,
1364  const char *newroot, const char *oldroot, const char *enlnm)
1365 {
1366  // Open the file for updating
1367  TFile *fl = TFile::Open(fn, "UPDATE");
1368  if (!fl || (fl&& fl->IsZombie())) {
1369  ::Error("TEntryList::Relocate", "file '%s' cannot be open for updating", fn);
1370  return -1;
1371  }
1372 
1373  Int_t nrl = 0;
1374  // Read the lists
1375  TString nm(enlnm);
1376  if (nm.IsNull()) nm = "*";
1377  TRegexp nmrg(nm, kTRUE);
1378  TIter nxk(fl->GetListOfKeys());
1379  TKey *key = 0;
1380  while ((key = (TKey *) nxk())) {
1381  if (!strcmp(key->GetClassName(), "TEntryList")) {
1382  TString knm(key->GetName());
1383  if (knm.Index(nmrg) != kNPOS) {
1384  TEntryList *enl = dynamic_cast<TEntryList *>(fl->Get(knm));
1385  if (enl) {
1386  Int_t xnrl = enl->RelocatePaths(newroot, oldroot);
1387  if (xnrl >= 0) {
1388  enl->Write(knm, TObject::kOverwrite);
1389  nrl += xnrl;
1390  } else {
1391  ::Error("TEntryList::Relocate", "problems relocating '%s' ...", enl->GetName());
1392  }
1393  }
1394  }
1395  }
1396  }
1397  // Close the file
1398  fl->Close();
1399  delete fl;
1400  // Done
1401  return nrl;
1402 }
1403 
1404 ////////////////////////////////////////////////////////////////////////////////
1405 /// Get in 'c' the string in common at the beginning of 'a' and 'b'
1406 ///
1407 /// Return:
1408 /// - 0 a and b are not contained in each other, i.e. c != a && c != b
1409 /// - 1 a is contained in b, i.e. c == a (includes a == empty)
1410 /// - 2 b is contained in a, i.e. c == b (includes b == empty)
1411 /// - 3 b is a, i.e. c == b == a (includes a == b == empty)
1412 /// Auxiliary function for path scans.
1413 
1414 static Int_t GetCommonString(TString a, TString b, TString &c)
1415 {
1416  if (a == b) {
1417  c = a;
1418  return 3;
1419  }
1420  if (a.IsNull()) {
1421  c = "";
1422  return 1;
1423  }
1424  if (b.IsNull()) {
1425  c = "";
1426  return 2;
1427  }
1428  Bool_t ashort = (a.Length() > b.Length()) ? kFALSE : kTRUE;
1429  Ssiz_t len = (ashort) ? a.Length() : b.Length();
1430  Int_t lcom = 0;
1431  for (Int_t i = 0; i < len; i++) {
1432  if (a[i] != b[i]) break;
1433  lcom++;
1434  }
1435  if (lcom == len) {
1436  c = ashort ? a : b;
1437  return ashort ? 1 : 2;
1438  }
1439  c = a(0,lcom);
1440  // Done
1441  return 0;
1442 }
1443 
1444 ////////////////////////////////////////////////////////////////////////////////
1445 /// Scan the paths to find the common roots. If 'roots' is defined, add
1446 /// the found roots to the list as TObjStrings.
1447 /// Return the number of roots found.
1448 
1449 Int_t TEntryList::ScanPaths(TList *roots, Bool_t notify)
1450 {
1451  TList *xrl = roots ? roots : new TList;
1452 
1453  Int_t nrl = 0;
1454  // Apply to all underlying lists, if any
1455  if (fLists) {
1456  TIter nxl(fLists);
1457  TEntryList *enl = 0;
1458  while ((enl = (TEntryList *) nxl()))
1459  nrl += enl->ScanPaths(xrl, kFALSE);
1460  }
1461  // Apply to ourselves
1462  Bool_t newobjs = kTRUE;
1463  TString path = gSystem->DirName(fFileName), com;
1464  TObjString *objs = 0;
1465  TIter nxr(xrl);
1466  while ((objs = (TObjString *) nxr())) {
1467  Int_t rc = 0;
1468  if ((rc = GetCommonString(path, objs->GetString(), com)) != 2) {
1469  TUrl ucom(com);
1470  if (strlen(ucom.GetFile()) > 0 && strcmp(ucom.GetFile(), "/")) {
1471  objs->SetString(com.Data());
1472  newobjs = kFALSE;
1473  break;
1474  }
1475  }
1476  }
1477  if (newobjs) xrl->Add(new TObjString(path));
1478 
1479  // Done
1480  nrl = xrl->GetSize();
1481  if (notify) {
1482  Printf(" * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *");
1483  Printf(" * Entry-list: %s", GetName());
1484  Printf(" * %d common root paths found", nrl);
1485  nxr.Reset();
1486  while ((objs = (TObjString *) nxr())) {
1487  Printf(" * %s", objs->GetName());
1488  }
1489  Printf(" * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ *");
1490  }
1491 
1492  if (xrl != roots) {
1493  xrl->SetOwner(kTRUE);
1494  SafeDelete(xrl);
1495  }
1496 
1497  // Done
1498  return nrl;
1499 }
1500 
1501 ////////////////////////////////////////////////////////////////////////////////
1502 /// Scan TEntryList in 'fn' to find the common parts of paths.
1503 /// If 'roots' is defined, add the found roots to the list as TObjStrings.
1504 /// Return the number of common root paths found.
1505 
1506 Int_t TEntryList::Scan(const char *fn, TList *roots)
1507 {
1508  // Open the file for updating
1509  TFile *fl = TFile::Open(fn);
1510  if (!fl || (fl&& fl->IsZombie())) {
1511  ::Error("TEntryList::Relocate", "file '%s' cannot be open for reading", fn);
1512  return -1;
1513  }
1514 
1515  Int_t nrs = 0;
1516  // Read the lists
1517  TIter nxk(fl->GetListOfKeys());
1518  TKey *key = 0;
1519  while ((key = (TKey *) nxk())) {
1520  if (!strcmp(key->GetClassName(), "TEntryList")) {
1521  TEntryList *enl = dynamic_cast<TEntryList *>(fl->Get(key->GetName()));
1522  if (enl) {
1523  nrs += enl->ScanPaths(roots);
1524  } else {
1525  ::Error("TEntryList::Scan", "object entry-list '%s' not found or not loadable!", key->GetName());
1526  }
1527  }
1528  }
1529  // Close the file
1530  fl->Close();
1531  delete fl;
1532 
1533  // Done
1534  return nrs;
1535 }
1536 
1537 ////////////////////////////////////////////////////////////////////////////////
1538 /// Custom streamer for class TEntryList to handle the different interpretation
1539 /// of fFileName between version 1 and >1 .
1540 
1541 void TEntryList::Streamer(TBuffer &b)
1542 {
1543  if (b.IsReading()) {
1544  UInt_t R__s, R__c;
1545  Version_t R__v = b.ReadVersion(&R__s, &R__c);
1546  b.ReadClassBuffer(TEntryList::Class(), this, R__v, R__s, R__c);
1547  if (R__v <= 1) {
1548  // The filename contained also the protocol and host: this was dropped
1549  // in version > 1 to allow re-localization
1550  GetFileName(fFileName.Data(), fFileName);
1551  }
1552  } else {
1553  b.WriteClassBuffer(TEntryList::Class(), this);
1554  }
1555 }