otsdaq_utilities  v2_05_02_indev
JSRootTree.js
1 
4 (function( factory ) {
5  if ( typeof define === "function" && define.amd ) {
6  define( ['JSRootCore', 'JSRootIOEvolution', 'JSRootMath'], factory );
7  } else if (typeof exports === 'object' && typeof module !== 'undefined') {
8  factory(require("./JSRootCore.js"), require("./JSRootIOEvolution.js"), require("./JSRootMath.js"));
9  } else {
10  if (typeof JSROOT == 'undefined')
11  throw new Error('JSROOT is not defined', 'JSRootTree.js');
12  if (typeof JSROOT.IO != 'object')
13  throw new Error('JSROOT.IO not defined', 'JSRootTree.js');
14  factory(JSROOT);
15  }
16 } (function(JSROOT) {
17 
18  "use strict";
19 
20  JSROOT.sources.push("tree");
21 
22  JSROOT.BranchType = { kLeafNode: 0, kBaseClassNode: 1, kObjectNode: 2, kClonesNode: 3,
23  kSTLNode: 4, kClonesMemberNode: 31, kSTLMemberNode: 41 };
24 
25  JSROOT.IO.BranchBits = {
26  kDoNotProcess: JSROOT.BIT(10), // Active bit for branches
27  kIsClone: JSROOT.BIT(11), // to indicate a TBranchClones
28  kBranchObject: JSROOT.BIT(12), // branch is a TObject*
29  kBranchAny: JSROOT.BIT(17), // branch is an object*
30  kAutoDelete: JSROOT.BIT(15),
31  kDoNotUseBufferMap: JSROOT.BIT(22) // If set, at least one of the entry in the branch will use the buffer's map of classname and objects.
32  }
33 
34 
41  function TSelector() {
42  this.branches = []; // list of branches to read
43  this.names = []; // list of member names for each branch in tgtobj
44  this.directs = []; // indication if only branch without any children should be read
45  this.break_execution = 0;
46  this.tgtobj = {};
47  }
48 
58  TSelector.prototype.AddBranch = function(branch, name, direct) {
59  if (!name)
60  name = (typeof branch === 'string') ? branch : ("br" + this.branches.length);
61  this.branches.push(branch);
62  this.names.push(name);
63  this.directs.push(direct);
64  return this.branches.length-1;
65  }
66 
67  TSelector.prototype.indexOfBranch = function(branch) {
68  return this.branches.indexOf(branch);
69  }
70 
71  TSelector.prototype.nameOfBranch = function(indx) {
72  return this.names[indx];
73  }
74 
75  TSelector.prototype.ShowProgress = function(value) {
76  // this function can be used to check current TTree progress
77  }
78 
80  TSelector.prototype.Abort = function() {
81  this.break_execution = -1111;
82  }
83 
88  TSelector.prototype.Begin = function(tree) {
89  }
90 
95  TSelector.prototype.Process = function(entry) {
96  }
97 
102  TSelector.prototype.Terminate = function(res) {
103  }
104 
105  // =================================================================
106 
107  JSROOT.CheckArrayPrototype = function(arr, check_content) {
108  // return 0 when not array
109  // 1 - when arbitrary array
110  // 2 - when plain (1-dim) array with same-type content
111  if (typeof arr !== 'object') return 0;
112  var proto = Object.prototype.toString.apply(arr);
113  if (proto.indexOf('[object')!==0) return 0;
114  var pos = proto.indexOf('Array]');
115  if (pos < 0) return 0;
116  if (pos > 8) return 2; // this is typed array like Int32Array
117 
118  if (!check_content) return 1; //
119  var typ, plain = true;
120  for (var k=0;k<arr.length;++k) {
121  var sub = typeof arr[k];
122  if (!typ) typ = sub;
123  if (sub!==typ) { plain = false; break; }
124  if ((sub=="object") && JSROOT.CheckArrayPrototype(arr[k])) { plain = false; break; }
125  }
126 
127  return plain ? 2 : 1;
128  }
129 
130  function ArrayIterator(arr, select, tgtobj) {
131  // class used to iterate over all array indexes until number value
132 
133  this.object = arr;
134  this.value = 0; // value always used in iterator
135  this.arr = []; // all arrays
136  this.indx = []; // all indexes
137  this.cnt = -1; // current index counter
138  this.tgtobj = tgtobj;
139 
140  if (typeof select === 'object')
141  this.select = select; // remember indexes for selection
142  else
143  this.select = []; // empty array, undefined for each dimension means iterate over all indexes
144  }
145 
146  ArrayIterator.prototype.next = function() {
147  var obj, typ, cnt = this.cnt, seltyp;
148 
149  if (cnt >= 0) {
150 
151  if (++this.fastindx < this.fastlimit) {
152  this.value = this.fastarr[this.fastindx];
153  return true;
154  }
155 
156  while (--cnt >= 0) {
157  if ((this.select[cnt]===undefined) && (++this.indx[cnt] < this.arr[cnt].length)) break;
158  }
159  if (cnt < 0) return false;
160  }
161 
162  while (true) {
163 
164  obj = (cnt < 0) ? this.object : (this.arr[cnt])[this.indx[cnt]];
165 
166  typ = obj ? typeof obj : "any";
167 
168  if (typ === "object") {
169  if (obj._typename !== undefined) {
170  if (JSROOT.IsRootCollection(obj)) { obj = obj.arr; typ = "array"; }
171  else typ = "any";
172  } else if (!isNaN(obj.length) && (JSROOT.CheckArrayPrototype(obj)>0)) {
173  typ = "array";
174  } else {
175  typ = "any";
176  }
177  }
178 
179  if (this.select[cnt+1]=="$self$") {
180  this.value = obj;
181  this.fastindx = this.fastlimit = 0;
182  this.cnt = cnt+1;
183  return true;
184  }
185 
186  if ((typ=="any") && (typeof this.select[cnt+1] === "string")) {
187  // this is extraction of the member from arbitrary class
188  this.arr[++cnt] = obj;
189  this.indx[cnt] = this.select[cnt]; // use member name as index
190  continue;
191  }
192 
193  if ((typ === "array") && ((obj.length > 0) || (this.select[cnt+1]==="$size$"))) {
194  this.arr[++cnt] = obj;
195  switch (this.select[cnt]) {
196  case undefined: this.indx[cnt] = 0; break;
197  case "$last$": this.indx[cnt] = obj.length-1; break;
198  case "$size$":
199  this.value = obj.length;
200  this.fastindx = this.fastlimit = 0;
201  this.cnt = cnt;
202  return true;
203  break;
204  default:
205  if (!isNaN(this.select[cnt])) {
206  this.indx[cnt] = this.select[cnt];
207  if (this.indx[cnt] < 0) this.indx[cnt] = obj.length-1;
208  } else {
209  // this is compile variable as array index - can be any expression
210  this.select[cnt].Produce(this.tgtobj);
211  this.indx[cnt] = Math.round(this.select[cnt].get(0));
212  }
213  }
214  } else {
215 
216  if (cnt<0) return false;
217 
218  this.value = obj;
219  if (this.select[cnt]===undefined) {
220  this.fastarr = this.arr[cnt];
221  this.fastindx = this.indx[cnt];
222  this.fastlimit = this.fastarr.length;
223  } else {
224  this.fastindx = this.fastlimit = 0; // no any iteration on that level
225  }
226 
227  this.cnt = cnt;
228  return true;
229  }
230  }
231 
232  // unreachable code
233  // return false;
234  }
235 
236  ArrayIterator.prototype.reset = function() {
237  this.arr = [];
238  this.indx = [];
239  delete this.fastarr;
240  this.cnt = -1;
241  this.value = 0;
242  }
243 
244  // ============================================================================
245 
246  function TDrawVariable(globals) {
247  // object with single variable in TTree::Draw expression
248  this.globals = globals;
249 
250  this.code = "";
251  this.brindex = []; // index of used branches from selector
252  this.branches = []; // names of branches in target object
253  this.brarray = []; // array specifier for each branch
254  this.func = null; // generic function for variable calculation
255 
256  this.kind = undefined;
257  this.buf = []; // buffer accumulates temporary values
258  }
259 
260  TDrawVariable.prototype.Parse = function(tree,selector,code,only_branch,branch_mode) {
261  // when only_branch specified, its placed in the front of the expression
262 
263  function is_start_symbol(symb) {
264  if ((symb >= "A") && (symb <= "Z")) return true;
265  if ((symb >= "a") && (symb <= "z")) return true;
266  return (symb === "_");
267  }
268 
269  function is_next_symbol(symb) {
270  if (is_start_symbol(symb)) return true;
271  if ((symb >= "0") && (symb <= "9")) return true;
272  return false;
273  }
274 
275  if (!code) code = ""; // should be empty string at least
276 
277  this.code = (only_branch ? only_branch.fName : "") + code;
278 
279  var pos = 0, pos2 = 0, br = null;
280  while ((pos < code.length) || only_branch) {
281 
282  var arriter = [];
283 
284  if (only_branch) {
285  br = only_branch;
286  only_branch = undefined;
287  } else {
288  // first try to find branch
289  while ((pos < code.length) && !is_start_symbol(code[pos])) pos++;
290  pos2 = pos;
291  while ((pos2 < code.length) && (is_next_symbol(code[pos2]) || code[pos2]===".")) pos2++;
292  if (code[pos2]=="$") {
293  var repl = "";
294  switch (code.substr(pos, pos2-pos)) {
295  case "LocalEntry":
296  case "Entry": repl = "arg.$globals.entry"; break;
297  case "Entries": repl = "arg.$globals.entries"; break;
298  }
299  if (repl) {
300  code = code.substr(0, pos) + repl + code.substr(pos2+1);
301  pos = pos + repl.length;
302  continue;
303  }
304  }
305 
306  br = tree.FindBranch(code.substr(pos, pos2-pos), true);
307  if (!br) { pos = pos2+1; continue; }
308 
309  // when full id includes branch name, replace only part of extracted expression
310  if (br.branch && (br.rest!==undefined)) {
311  pos2 -= br.rest.length;
312  branch_mode = br.read_mode; // maybe selection of the sub-object done
313  br = br.branch;
314  }
315 
316  // when code ends with the point - means object itself will be accessed
317  // sometime branch name itself ends with the point
318  if ((pos2>=code.length-1) && (code[code.length-1]===".")) {
319  arriter.push("$self$");
320  pos2 = code.length;
321  }
322  }
323 
324  // now extract all levels of iterators
325  while (pos2 < code.length) {
326 
327  if ((code[pos2]==="@") && (code.substr(pos2,5)=="@size") && (arriter.length==0)) {
328  pos2+=5;
329  branch_mode = true;
330  break;
331  }
332 
333  if (code[pos2] === ".") {
334  // this is object member
335  var prev = ++pos2;
336 
337  if ((code[prev]==="@") && (code.substr(prev,5)==="@size")) {
338  arriter.push("$size$");
339  pos2+=5;
340  break;
341  }
342 
343  if (!is_start_symbol(code[prev])) {
344  arriter.push("$self$"); // last point means extraction of object itself
345  break;
346  }
347 
348  while ((pos2 < code.length) && is_next_symbol(code[pos2])) pos2++;
349 
350  // this is looks like function call - do not need to extract member with
351  if (code[pos2]=="(") { pos2 = prev-1; break; }
352 
353  // this is selection of member, but probably we need to activate iterator for ROOT collection
354  if ((arriter.length===0) && br) {
355  // TODO: if selected member is simple data type - no need to make other checks - just break here
356  if ((br.fType === JSROOT.BranchType.kClonesNode) || (br.fType === JSROOT.BranchType.kSTLNode)) {
357  arriter.push(undefined);
358  } else {
359  var objclass = JSROOT.IO.GetBranchObjectClass(br, tree, false, true);
360  if (objclass && JSROOT.IsRootCollection(null, objclass)) arriter.push(undefined);
361  }
362  }
363  arriter.push(code.substr(prev, pos2-prev));
364  continue;
365  }
366 
367  if (code[pos2]!=="[") break;
368 
369  // simple []
370  if (code[pos2+1]=="]") { arriter.push(undefined); pos2+=2; continue; }
371 
372  var prev = pos2++, cnt = 0;
373  while ((pos2 < code.length) && ((code[pos2]!="]") || (cnt>0))) {
374  if (code[pos2]=='[') cnt++; else if (code[pos2]==']') cnt--;
375  pos2++;
376  }
377  var sub = code.substr(prev+1, pos2-prev-1);
378  switch(sub) {
379  case "":
380  case "$all$": arriter.push(undefined); break;
381  case "$last$": arriter.push("$last$"); break;
382  case "$size$": arriter.push("$size$"); break;
383  case "$first$": arriter.push(0); break;
384  default:
385  if (!isNaN(parseInt(sub))) {
386  arriter.push(parseInt(sub));
387  } else {
388  // try to compile code as draw variable
389  var subvar = new TDrawVariable(this.globals);
390  if (!subvar.Parse(tree,selector, sub)) return false;
391  arriter.push(subvar);
392  }
393  }
394  pos2++;
395  }
396 
397  if (arriter.length===0) arriter = undefined; else
398  if ((arriter.length===1) && (arriter[0]===undefined)) arriter = true;
399 
400  var indx = selector.indexOfBranch(br);
401  if (indx<0) indx = selector.AddBranch(br, undefined, branch_mode);
402 
403  branch_mode = undefined;
404 
405  this.brindex.push(indx);
406  this.branches.push(selector.nameOfBranch(indx));
407  this.brarray.push(arriter);
408 
409  // this is simple case of direct usage of the branch
410  if ((pos===0) && (pos2 === code.length) && (this.branches.length===1)) {
411  this.direct_branch = true; // remember that branch read as is
412  return true;
413  }
414 
415  var replace = "arg.var" + (this.branches.length-1);
416 
417  code = code.substr(0, pos) + replace + code.substr(pos2);
418 
419  pos = pos + replace.length;
420  }
421 
422  // support usage of some standard TMath functions
423  code = code.replace(/TMath::Exp\(/g, 'Math.exp(')
424  .replace(/TMath::Abs\(/g, 'Math.abs(')
425  .replace(/TMath::Prob\(/g, 'arg.$math.Prob(')
426  .replace(/TMath::Gaus\(/g, 'arg.$math.Gaus(');
427 
428  this.func = new Function("arg", "return (" + code + ")");
429 
430  return true;
431  }
432 
433  TDrawVariable.prototype.is_dummy = function() {
434  return (this.branches.length === 0) && !this.func;
435  }
436 
437  TDrawVariable.prototype.Produce = function(obj) {
438  // after reading tree braches into the object, calculate variable value
439 
440  this.length = 1;
441  this.isarray = false;
442 
443  if (this.is_dummy()) {
444  this.value = 1.; // used as dummy weight variable
445  this.kind = "number";
446  return;
447  }
448 
449  var arg = { $globals: this.globals, $math: JSROOT.Math }, usearrlen = -1, arrs = [];
450  for (var n=0;n<this.branches.length;++n) {
451  var name = "var" + n;
452  arg[name] = obj[this.branches[n]];
453 
454  // try to check if branch is array and need to be iterated
455  if (this.brarray[n]===undefined)
456  this.brarray[n] = (JSROOT.CheckArrayPrototype(arg[name]) > 0) || JSROOT.IsRootCollection(arg[name]);
457 
458  // no array - no pain
459  if (this.brarray[n]===false) continue;
460 
461  // check if array can be used as is - one dimension and normal values
462  if ((this.brarray[n]===true) && (JSROOT.CheckArrayPrototype(arg[name], true) === 2)) {
463  // plain array, can be used as is
464  arrs[n] = arg[name];
465  } else {
466  var iter = new ArrayIterator(arg[name], this.brarray[n], obj);
467  arrs[n] = [];
468  while (iter.next()) arrs[n].push(iter.value);
469  }
470  if ((usearrlen < 0) || (usearrlen < arrs[n].length)) usearrlen = arrs[n].length;
471  }
472 
473  if (usearrlen < 0) {
474  this.value = this.direct_branch ? arg.var0 : this.func(arg);
475  if (!this.kind) this.kind = typeof this.value;
476  return;
477  }
478 
479  if (usearrlen == 0) {
480  // empty array - no any histogram should be filled
481  this.length = 0;
482  this.value = 0;
483  return;
484  }
485 
486  this.length = usearrlen;
487  this.isarray = true;
488 
489  if (this.direct_branch) {
490  this.value = arrs[0]; // just use array
491  } else {
492  this.value = new Array(usearrlen);
493 
494  for (var k=0;k<usearrlen;++k) {
495  for (var n=0;n<this.branches.length;++n) {
496  if (arrs[n]) arg["var"+n] = arrs[n][k];
497  }
498  this.value[k] = this.func(arg);
499  }
500  }
501 
502  if (!this.kind) this.kind = typeof this.value[0];
503  }
504 
505  TDrawVariable.prototype.get = function(indx) {
506  return this.isarray ? this.value[indx] : this.value;
507  }
508 
509  TDrawVariable.prototype.AppendArray = function(tgtarr) {
510  // append array to the buffer
511 
512  this.buf = this.buf.concat(tgtarr[this.branches[0]]);
513  }
514 
515  // =============================================================================
516 
524  function TDrawSelector(callback) {
525  TSelector.call(this);
526 
527  this.ndim = 0;
528  this.vars = []; // array of expression variables
529  this.cut = null; // cut variable
530  this.hist = null;
531  this.histo_callback = callback;
532  this.histo_drawopt = "";
533  this.hist_name = "$htemp";
534  this.hist_title = "Result of TTree::Draw";
535  this.graph = false;
536  this.hist_args = []; // arguments for histogram creation
537  this.arr_limit = 1000; // number of accumulated items before create histogram
538  this.htype = "F";
539  this.monitoring = 0;
540  this.globals = {}; // object with global parameters, which could be used in any draw expression
541  this.last_progress = 0;
542  this.aver_diff = 0;
543  }
544 
545  TDrawSelector.prototype = Object.create(TSelector.prototype);
546 
547  TDrawSelector.prototype.ParseParameters = function(tree, args, expr) {
548 
549  if (!expr || (typeof expr !== "string")) return "";
550 
551  // parse parameters which defined at the end as expression;par1name:par1value;par2name:par2value
552  var pos = expr.lastIndexOf(";");
553  while (pos>=0) {
554  var parname = expr.substr(pos+1), parvalue = undefined;
555  expr = expr.substr(0,pos);
556  pos = expr.lastIndexOf(";");
557 
558  var separ = parname.indexOf(":");
559  if (separ>0) { parvalue = parname.substr(separ+1); parname = parname.substr(0, separ); }
560 
561  var intvalue = parseInt(parvalue);
562  if (!parvalue || isNaN(intvalue)) intvalue = undefined;
563 
564  switch (parname) {
565  case "num":
566  case "entries":
567  case "numentries":
568  if (parvalue==="all") args.numentries = tree.fEntries; else
569  if (parvalue==="half") args.numentries = Math.round(tree.fEntries/2); else
570  if (intvalue !== undefined) args.numentries = intvalue;
571  break;
572  case "first":
573  if (intvalue !== undefined) args.firstentry = intvalue;
574  break;
575  case "mon":
576  case "monitor":
577  args.monitoring = (intvalue !== undefined) ? intvalue : 5000;
578  break;
579  case "player":
580  args.player = true;
581  break;
582  case "dump":
583  args.dump = true;
584  break;
585  case "maxseg":
586  case "maxrange":
587  if (intvalue) tree.$file.fMaxRanges = intvalue;
588  break;
589  case "accum":
590  if (intvalue) this.arr_limit = intvalue;
591  break;
592  case "htype":
593  if (parvalue && (parvalue.length===1)) {
594  this.htype = parvalue.toUpperCase();
595  if ((this.htype!=="C") && (this.htype!=="S") && (this.htype!=="I")
596  && (this.htype!=="F") && (this.htype!=="L") && (this.htype!=="D")) this.htype = "F";
597  }
598  break;
599  case "hbins" :
600  this.hist_nbins = parseInt(parvalue);
601  if (isNaN(this.hist_nbins) || (this.hist_nbins<=3)) delete this.hist_nbins;
602  break;
603  case "drawopt":
604  args.drawopt = parvalue;
605  break;
606  case "graph":
607  args.graph = intvalue || true;
608  break;
609  }
610  }
611 
612  pos = expr.lastIndexOf(">>");
613  if (pos>=0) {
614  var harg = expr.substr(pos+2).trim();
615  expr = expr.substr(0,pos).trim();
616  pos = harg.indexOf("(");
617  if (pos>0) {
618  this.hist_name = harg.substr(0, pos);
619  harg = harg.substr(pos);
620  }
621  if (harg === "dump") {
622  args.dump = true;
623  } else if (harg.indexOf("Graph") == 0) {
624  args.graph = true;
625  } else if (pos<0) {
626  this.hist_name = harg;
627  } else if ((harg[0]=="(") && (harg[harg.length-1]==")")) {
628  harg = harg.substr(1,harg.length-2).split(",");
629  var isok = true;
630  for (var n=0;n<harg.length;++n) {
631  harg[n] = (n%3===0) ? parseInt(harg[n]) : parseFloat(harg[n]);
632  if (isNaN(harg[n])) isok = false;
633  }
634  if (isok) this.hist_args = harg;
635  }
636  }
637 
638  if (args.dump) {
639  this.dump_values = true;
640  args.reallocate_objects = true;
641  if (args.numentries===undefined) args.numentries = 10;
642  }
643 
644  return expr;
645  }
646 
647  TDrawSelector.prototype.ParseDrawExpression = function(tree, args) {
648 
649  // parse complete expression
650  var expr = this.ParseParameters(tree, args, args.expr), cut = "";
651 
652  // parse option for histogram creation
653  this.hist_title = "drawing '" + expr + "' from " + tree.fName;
654 
655  var pos = 0;
656  if (args.cut) {
657  cut = args.cut;
658  } else {
659  pos = expr.replace(/TMath::/g, 'TMath__').lastIndexOf("::"); // avoid confusion due-to :: in the namespace
660  if (pos>0) {
661  cut = expr.substr(pos+2).trim();
662  expr = expr.substr(0,pos).trim();
663  }
664  }
665 
666  args.parse_expr = expr;
667  args.parse_cut = cut;
668 
669  // var names = expr.split(":"); // to allow usage of ? operator, we need to handle : as well
670  var names = [], nbr1 = 0, nbr2 = 0, prev = 0;
671  for (pos=0; pos < expr.length; ++pos) {
672  switch (expr[pos]) {
673  case "(" : nbr1++; break;
674  case ")" : nbr1--; break;
675  case "[" : nbr2++; break;
676  case "]" : nbr2--; break;
677  case ":" :
678  if (expr[pos+1]==":") { pos++; continue; }
679  if (!nbr1 && !nbr2 && (pos>prev)) names.push(expr.substr(prev,pos-prev));
680  prev = pos+1;
681  break;
682  }
683  }
684  if (!nbr1 && !nbr2 && (pos>prev)) names.push(expr.substr(prev,pos-prev));
685 
686  if ((names.length < 1) || (names.length > 3)) return false;
687 
688  this.ndim = names.length;
689 
690  var is_direct = !cut;
691 
692  for (var n=0;n<this.ndim;++n) {
693  this.vars[n] = new TDrawVariable(this.globals);
694  if (!this.vars[n].Parse(tree, this, names[n])) return false;
695  if (!this.vars[n].direct_branch) is_direct = false;
696  }
697 
698  this.cut = new TDrawVariable(this.globals);
699  if (cut)
700  if (!this.cut.Parse(tree, this, cut)) return false;
701 
702  if (!this.branches.length) {
703  console.warn('no any branch is selected');
704  return false;
705  }
706 
707  if (is_direct) this.ProcessArrays = this.ProcessArraysFunc;
708 
709  this.monitoring = args.monitoring;
710 
711  this.graph = args.graph;
712 
713  if (args.drawopt !== undefined)
714  this.histo_drawopt = args.drawopt;
715  else
716  this.histo_drawopt = (this.ndim===2) ? "col" : "";
717 
718  return true;
719  }
720 
721  TDrawSelector.prototype.DrawOnlyBranch = function(tree, branch, expr, args) {
722  this.ndim = 1;
723 
724  if (expr.indexOf("dump")==0) expr = ";" + expr;
725 
726  expr = this.ParseParameters(tree, args, expr);
727 
728  this.monitoring = args.monitoring;
729 
730  if (args.dump) {
731  this.dump_values = true;
732  args.reallocate_objects = true;
733  }
734 
735  if (this.dump_values) {
736 
737  this.hist = []; // array of dump objects
738 
739  this.leaf = args.leaf;
740 
741  // branch object remains, therefore we need to copy fields to see them all
742  this.copy_fields = ((args.branch.fLeaves && (args.branch.fLeaves.arr.length > 1)) ||
743  (args.branch.fBranches && (args.branch.fBranches.arr.length > 0))) && !args.leaf;
744 
745  this.AddBranch(branch, "br0", args.direct_branch); // add branch
746 
747  this.Process = this.ProcessDump;
748 
749  return true;
750  }
751 
752  this.vars[0] = new TDrawVariable(this.globals);
753  if (!this.vars[0].Parse(tree, this, expr, branch, args.direct_branch)) return false;
754  this.hist_title = "drawing branch '" + branch.fName + (expr ? "' expr:'" + expr : "") + "' from " + tree.fName;
755 
756  this.cut = new TDrawVariable(this.globals);
757 
758  if (this.vars[0].direct_branch) this.ProcessArrays = this.ProcessArraysFunc;
759 
760  return true;
761  }
762 
763  TDrawSelector.prototype.Begin = function(tree) {
764  this.globals.entries = tree.fEntries;
765 
766  if (this.monitoring)
767  this.lasttm = new Date().getTime();
768  }
769 
770  TDrawSelector.prototype.ShowProgress = function(value) {
771  // this function should be defined not here
772 
773  if (typeof document == 'undefined' || !JSROOT.progress) return;
774 
775  if ((value===undefined) || isNaN(value)) return JSROOT.progress();
776 
777  if (this.last_progress !== value) {
778  var diff = value - this.last_progress;
779  if (!this.aver_diff) this.aver_diff = diff;
780  this.aver_diff = diff*0.3 + this.aver_diff*0.7;
781  }
782 
783  var ndig = 0;
784  if (this.aver_diff <= 0) ndig = 0; else
785  if (this.aver_diff < 0.0001) ndig = 3; else
786  if (this.aver_diff < 0.001) ndig = 2; else
787  if (this.aver_diff < 0.01) ndig = 1;
788 
789  var main_box = document.createElement("p"),
790  text_node = document.createTextNode("TTree draw " + (value*100).toFixed(ndig) + " % "),
791  selector = this;
792 
793  main_box.appendChild(text_node);
794  main_box.title = "Click on element to break drawing";
795 
796  main_box.onclick = function() {
797  if (++selector.break_execution<3) {
798  main_box.title = "Tree draw will break after next I/O operation";
799  return text_node.nodeValue = "Breaking ... ";
800  }
801  selector.Abort();
802  JSROOT.progress();
803  }
804 
805  JSROOT.progress(main_box);
806  this.last_progress = value;
807  }
808 
809  TDrawSelector.prototype.GetBitsBins = function(nbits, res) {
810  res.nbins = res.max = nbits;
811  res.fLabels = JSROOT.Create("THashList");
812  for (var k=0;k<nbits;++k) {
813  var s = JSROOT.Create("TObjString");
814  s.fString = k.toString();
815  s.fUniqueID = k+1;
816  res.fLabels.Add(s);
817  }
818  return res;
819  }
820 
821  TDrawSelector.prototype.GetMinMaxBins = function(axisid, nbins) {
822 
823  var res = { min: 0, max: 0, nbins: nbins, k: 1., fLabels: null, title: "" };
824 
825  if (axisid >= this.ndim) return res;
826 
827  var arr = this.vars[axisid].buf;
828 
829  res.title = this.vars[axisid].code || "";
830 
831  if (this.vars[axisid].kind === "object") {
832  // this is any object type
833  var typename, similar = true, maxbits = 8;
834  for (var k=0;k<arr.length;++k) {
835  if (!arr[k]) continue;
836  if (!typename) typename = arr[k]._typename;
837  if (typename !== arr[k]._typename) similar = false; // check all object types
838  if (arr[k].fNbits) maxbits = Math.max(maxbits, arr[k].fNbits+1);
839  }
840 
841  if (typename && similar) {
842  if ((typename==="TBits") && (axisid===0)) {
843  this.Fill1DHistogram = this.FillTBitsHistogram;
844  if (maxbits % 8) maxbits = (maxbits & 0xfff0) + 8;
845 
846  if ((this.hist_name === "bits") && (this.hist_args.length == 1) && this.hist_args[0])
847  maxbits = this.hist_args[0];
848 
849  return this.GetBitsBins(maxbits, res);
850  }
851  }
852  }
853 
854  if (this.vars[axisid].kind === "string") {
855  res.lbls = []; // all labels
856 
857  for (var k=0;k<arr.length;++k)
858  if (res.lbls.indexOf(arr[k])<0)
859  res.lbls.push(arr[k]);
860 
861  res.lbls.sort();
862  res.max = res.nbins = res.lbls.length;
863 
864  res.fLabels = JSROOT.Create("THashList");
865  for (var k=0;k<res.lbls.length;++k) {
866  var s = JSROOT.Create("TObjString");
867  s.fString = res.lbls[k];
868  s.fUniqueID = k+1;
869  if (s.fString === "") s.fString = "<empty>";
870  res.fLabels.Add(s);
871  }
872  } else if ((axisid === 0) && (this.hist_name === "bits") && (this.hist_args.length <= 1)) {
873  this.Fill1DHistogram = this.FillBitsHistogram;
874  return this.GetBitsBins(this.hist_args[0] || 32, res);
875  } else if (axisid*3 + 2 < this.hist_args.length) {
876  res.nbins = this.hist_args[axisid*3];
877  res.min = this.hist_args[axisid*3+1];
878  res.max = this.hist_args[axisid*3+2];
879  } else {
880 
881 
882 
883  res.min = Math.min.apply(null, arr);
884  res.max = Math.max.apply(null, arr);
885 
886  if (this.hist_nbins)
887  nbins = res.nbins = this.hist_nbins;
888 
889  res.isinteger = (Math.round(res.min)===res.min) && (Math.round(res.max)===res.max);
890  if (res.isinteger)
891  for (var k=0;k<arr.length;++k)
892  if (arr[k]!==Math.round(arr[k])) { res.isinteger = false; break; }
893 
894  if (res.isinteger) {
895  res.min = Math.round(res.min);
896  res.max = Math.round(res.max);
897  if (res.max-res.min < nbins*5) {
898  res.min -= 1;
899  res.max += 2;
900  res.nbins = Math.round(res.max - res.min);
901  } else {
902  var range = (res.max - res.min + 2), step = Math.floor(range / nbins);
903  while (step*nbins < range) step++;
904  res.max = res.min + nbins*step;
905  }
906  } else
907  if (res.min >= res.max) {
908  res.max = res.min;
909  if (Math.abs(res.min)<100) { res.min-=1; res.max+=1; } else
910  if (res.min>0) { res.min*=0.9; res.max*=1.1; } else { res.min*=1.1; res.max*=0.9; }
911  } else {
912  res.max += (res.max-res.min)/res.nbins;
913  }
914  }
915 
916  res.k = res.nbins/(res.max-res.min);
917 
918  res.GetBin = function(value) {
919  var bin = this.lbls ? this.lbls.indexOf(value) : Math.floor((value-this.min)*this.k);
920  return (bin<0) ? 0 : ((bin>this.nbins) ? this.nbins+1 : bin+1);
921  }
922 
923  return res;
924  }
925 
926  TDrawSelector.prototype.CreateHistogram = function() {
927  if (this.hist || !this.vars[0].buf) return;
928 
929  if (this.dump_values) {
930  // just create array where dumped valus will be collected
931  this.hist = [];
932 
933  // reassign fill method
934  this.Fill1DHistogram = this.Fill2DHistogram = this.Fill3DHistogram = this.DumpValue;
935  } else if (this.graph) {
936  var N = this.vars[0].buf.length;
937 
938  if(this.ndim == 1) {
939  // A 1-dimensional graph will just have the x axis as an index
940  this.hist = JSROOT.CreateTGraph(N, Array.from(Array(N).keys()), this.vars[0].buf);
941  } else if(this.ndim == 2) {
942  this.hist = JSROOT.CreateTGraph(N,this.vars[0].buf, this.vars[1].buf);
943  delete this.vars[1].buf;
944  }
945 
946  this.hist.fTitle = this.hist_title;
947  this.hist.fName = "Graph";
948 
949  } else {
950 
951  this.x = this.GetMinMaxBins(0, (this.ndim > 1) ? 50 : 200);
952 
953  this.y = this.GetMinMaxBins(1, 50);
954 
955  this.z = this.GetMinMaxBins(2, 50);
956 
957  switch (this.ndim) {
958  case 1: this.hist = JSROOT.CreateHistogram("TH1"+this.htype, this.x.nbins); break;
959  case 2: this.hist = JSROOT.CreateHistogram("TH2"+this.htype, this.x.nbins, this.y.nbins); break;
960  case 3: this.hist = JSROOT.CreateHistogram("TH3"+this.htype, this.x.nbins, this.y.nbins, this.z.nbins); break;
961  }
962 
963  this.hist.fXaxis.fTitle = this.x.title;
964  this.hist.fXaxis.fXmin = this.x.min;
965  this.hist.fXaxis.fXmax = this.x.max;
966  this.hist.fXaxis.fLabels = this.x.fLabels;
967 
968  if (this.ndim > 1) this.hist.fYaxis.fTitle = this.y.title;
969  this.hist.fYaxis.fXmin = this.y.min;
970  this.hist.fYaxis.fXmax = this.y.max;
971  this.hist.fYaxis.fLabels = this.y.fLabels;
972 
973  if (this.ndim > 2) this.hist.fZaxis.fTitle = this.z.title;
974  this.hist.fZaxis.fXmin = this.z.min;
975  this.hist.fZaxis.fXmax = this.z.max;
976  this.hist.fZaxis.fLabels = this.z.fLabels;
977 
978  this.hist.fName = this.hist_name;
979  this.hist.fTitle = this.hist_title;
980  this.hist.$custom_stat = (this.hist_name == "$htemp") ? 111110 : 111111;
981  }
982 
983  var var0 = this.vars[0].buf, cut = this.cut.buf, len = var0.length;
984 
985  if (!this.graph) {
986  switch (this.ndim) {
987  case 1:
988  for (var n=0;n<len;++n)
989  this.Fill1DHistogram(var0[n], cut ? cut[n] : 1.);
990  break;
991  case 2:
992  var var1 = this.vars[1].buf;
993  for (var n=0;n<len;++n)
994  this.Fill2DHistogram(var0[n], var1[n], cut ? cut[n] : 1.);
995  delete this.vars[1].buf;
996  break;
997  case 3:
998  var var1 = this.vars[1].buf, var2 = this.vars[2].buf;
999  for (var n=0;n<len;++n)
1000  this.Fill3DHistogram(var0[n], var1[n], var2[n], cut ? cut[n] : 1.);
1001  delete this.vars[1].buf;
1002  delete this.vars[2].buf;
1003  break;
1004  }
1005  }
1006 
1007  delete this.vars[0].buf;
1008  delete this.cut.buf;
1009  }
1010 
1011  TDrawSelector.prototype.FillTBitsHistogram = function(xvalue, weight) {
1012  if (!weight || !xvalue || !xvalue.fNbits || !xvalue.fAllBits) return;
1013 
1014  var sz = Math.min(xvalue.fNbits+1, xvalue.fNbytes*8);
1015 
1016  for (var bit=0,mask=1,b=0;bit<sz;++bit) {
1017  if (xvalue.fAllBits[b] && mask) {
1018  if (bit <= this.x.nbins)
1019  this.hist.fArray[bit+1] += weight;
1020  else
1021  this.hist.fArray[this.x.nbins+1] += weight;
1022  }
1023 
1024  mask*=2;
1025  if (mask>=0x100) { mask = 1; ++b; }
1026  }
1027  }
1028 
1029  TDrawSelector.prototype.FillBitsHistogram = function(xvalue, weight) {
1030  if (!weight) return;
1031 
1032  for (var bit=0,mask=1;bit<this.x.nbins;++bit) {
1033  if (xvalue & mask) this.hist.fArray[bit+1] += weight;
1034  mask*=2;
1035  }
1036  }
1037 
1038  TDrawSelector.prototype.Fill1DHistogram = function(xvalue, weight) {
1039  var bin = this.x.GetBin(xvalue);
1040  this.hist.fArray[bin] += weight;
1041 
1042  if (!this.x.lbls) {
1043  this.hist.fTsumw += weight;
1044  this.hist.fTsumwx += weight*xvalue;
1045  this.hist.fTsumwx2 += weight*xvalue*xvalue;
1046  }
1047  }
1048 
1049  TDrawSelector.prototype.Fill2DHistogram = function(xvalue, yvalue, weight) {
1050  var xbin = this.x.GetBin(xvalue),
1051  ybin = this.y.GetBin(yvalue);
1052 
1053  this.hist.fArray[xbin+(this.x.nbins+2)*ybin] += weight;
1054  if (!this.x.lbls && !this.y.lbls) {
1055  this.hist.fTsumw += weight;
1056  this.hist.fTsumwx += weight*xvalue;
1057  this.hist.fTsumwy += weight*yvalue;
1058  this.hist.fTsumwx2 += weight*xvalue*xvalue;
1059  this.hist.fTsumwxy += weight*xvalue*yvalue;
1060  this.hist.fTsumwy2 += weight*yvalue*yvalue;
1061  }
1062  }
1063 
1064  TDrawSelector.prototype.Fill3DHistogram = function(xvalue, yvalue, zvalue, weight) {
1065  var xbin = this.x.GetBin(xvalue),
1066  ybin = this.y.GetBin(yvalue),
1067  zbin = this.z.GetBin(zvalue);
1068 
1069  this.hist.fArray[xbin + (this.x.nbins+2) * (ybin + (this.y.nbins+2)*zbin) ] += weight;
1070  if (!this.x.lbls && !this.y.lbls && !this.z.lbls) {
1071  this.hist.fTsumw += weight;
1072  this.hist.fTsumwx += weight*xvalue;
1073  this.hist.fTsumwy += weight*yvalue;
1074  this.hist.fTsumwz += weight*zvalue;
1075  this.hist.fTsumwx2 += weight*xvalue*xvalue;
1076  this.hist.fTsumwy2 += weight*yvalue*yvalue;
1077  this.hist.fTsumwz2 += weight*zvalue*zvalue;
1078  this.hist.fTsumwxy += weight*xvalue*yvalue;
1079  this.hist.fTsumwxz += weight*xvalue*zvalue;
1080  this.hist.fTsumwyz += weight*yvalue*zvalue;
1081  }
1082  }
1083 
1084  TDrawSelector.prototype.DumpValue = function(v1, v2, v3, v4) {
1085  var obj;
1086  switch (this.ndim) {
1087  case 1: obj = { x: v1, weight: v2 }; break;
1088  case 2: obj = { x: v1, y: v2, weight: v3 }; break;
1089  case 3: obj = { x: v1, y: v2, z: v3, weight: v4 }; break;
1090  }
1091 
1092  if (this.cut.is_dummy()) {
1093  if (this.ndim===1) obj = v1; else delete obj.weight;
1094  }
1095 
1096  this.hist.push(obj);
1097  }
1098 
1099  TDrawSelector.prototype.ProcessArraysFunc = function(entry) {
1100  // function used when all branches can be read as array
1101  // most typical usage - histogramming of single branch
1102 
1103  if (this.arr_limit || this.graph) {
1104  var var0 = this.vars[0], len = this.tgtarr.br0.length,
1105  var1 = this.vars[1], var2 = this.vars[2];
1106  if ((var0.buf.length===0) && (len>=this.arr_limit) && !this.graph) {
1107  // special use case - first array large enough to create histogram directly base on it
1108  var0.buf = this.tgtarr.br0;
1109  if (var1) var1.buf = this.tgtarr.br1;
1110  if (var2) var2.buf = this.tgtarr.br2;
1111  } else
1112  for (var k=0;k<len;++k) {
1113  var0.buf.push(this.tgtarr.br0[k]);
1114  if (var1) var1.buf.push(this.tgtarr.br1[k]);
1115  if (var2) var2.buf.push(this.tgtarr.br2[k]);
1116  }
1117  var0.kind = "number";
1118  if (var1) var1.kind = "number";
1119  if (var2) var2.kind = "number";
1120  this.cut.buf = null; // do not create buffer for cuts
1121  if (!this.graph && (var0.buf.length >= this.arr_limit)) {
1122  this.CreateHistogram();
1123  this.arr_limit = 0;
1124  }
1125  } else {
1126  var br0 = this.tgtarr.br0, len = br0.length;
1127  switch(this.ndim) {
1128  case 1:
1129  for (var k=0;k<len;++k)
1130  this.Fill1DHistogram(br0[k], 1.);
1131  break;
1132  case 2:
1133  var br1 = this.tgtarr.br1;
1134  for (var k=0;k<len;++k)
1135  this.Fill2DHistogram(br0[k], br1[k], 1.);
1136  break;
1137  case 3:
1138  var br1 = this.tgtarr.br1, br2 = this.tgtarr.br2;
1139  for (var k=0;k<len;++k)
1140  this.Fill3DHistogram(br0[k], br1[k], br2[k], 1.);
1141  break;
1142  }
1143  }
1144  }
1145 
1146  TDrawSelector.prototype.ProcessDump = function(entry) {
1147  // simple dump of the branch - no need to analyze something
1148 
1149  var res = this.leaf ? this.tgtobj.br0[this.leaf] : this.tgtobj.br0;
1150 
1151  if (res && this.copy_fields) {
1152  if (JSROOT.CheckArrayPrototype(res)===0) {
1153  this.hist.push(JSROOT.extend({}, res));
1154  } else {
1155  this.hist.push(res);
1156  }
1157  } else {
1158  this.hist.push(res);
1159  }
1160  }
1161 
1162  TDrawSelector.prototype.Process = function(entry) {
1163 
1164  this.globals.entry = entry; // can be used in any expression
1165 
1166  this.cut.Produce(this.tgtobj);
1167  if (!this.dump_values && !this.cut.value) return;
1168 
1169  for (var n=0;n<this.ndim;++n)
1170  this.vars[n].Produce(this.tgtobj);
1171 
1172  var var0 = this.vars[0], var1 = this.vars[1], var2 = this.vars[2], cut = this.cut;
1173 
1174  if (this.graph || this.arr_limit) {
1175  switch(this.ndim) {
1176  case 1:
1177  for (var n0=0;n0<var0.length;++n0) {
1178  var0.buf.push(var0.get(n0));
1179  cut.buf.push(cut.value);
1180  }
1181  break;
1182  case 2:
1183  for (var n0=0;n0<var0.length;++n0)
1184  for (var n1=0;n1<var1.length;++n1) {
1185  var0.buf.push(var0.get(n0));
1186  var1.buf.push(var1.get(n1));
1187  cut.buf.push(cut.value);
1188  }
1189  break;
1190  case 3:
1191  for (var n0=0;n0<var0.length;++n0)
1192  for (var n1=0;n1<var1.length;++n1)
1193  for (var n2=0;n2<var2.length;++n2) {
1194  var0.buf.push(var0.get(n0));
1195  var1.buf.push(var1.get(n1));
1196  var2.buf.push(var2.get(n2));
1197  cut.buf.push(cut.value);
1198  }
1199  break;
1200  }
1201  if (!this.graph && var0.buf.length >= this.arr_limit) {
1202  this.CreateHistogram();
1203  this.arr_limit = 0;
1204  }
1205  } else if (this.hist) {
1206  switch(this.ndim) {
1207  case 1:
1208  for (var n0=0;n0<var0.length;++n0)
1209  this.Fill1DHistogram(var0.get(n0), cut.value);
1210  break;
1211  case 2:
1212  for (var n0=0;n0<var0.length;++n0)
1213  for (var n1=0;n1<var1.length;++n1)
1214  this.Fill2DHistogram(var0.get(n0), var1.get(n1), cut.value);
1215  break;
1216  case 3:
1217  for (var n0=0;n0<var0.length;++n0)
1218  for (var n1=0;n1<var1.length;++n1)
1219  for (var n2=0;n2<var2.length;++n2)
1220  this.Fill3DHistogram(var0.get(n0), var1.get(n1), var2.get(n2), cut.value);
1221  break;
1222  }
1223  }
1224 
1225  if (this.monitoring && this.hist && !this.dump_values) {
1226  var now = new Date().getTime();
1227  if (now - this.lasttm > this.monitoring) {
1228  this.lasttm = now;
1229  JSROOT.CallBack(this.histo_callback, this.hist, this.histo_drawopt, true);
1230  }
1231  }
1232  }
1233 
1234  TDrawSelector.prototype.Terminate = function(res) {
1235  if (res && !this.hist) this.CreateHistogram();
1236 
1237  this.ShowProgress();
1238 
1239  return JSROOT.CallBack(this.histo_callback, this.hist, this.dump_values ? "inspect" : this.histo_drawopt);
1240  }
1241 
1242  // ======================================================================
1243 
1244  JSROOT.IO.FindBrachStreamerElement = function(branch, file) {
1245  // return TStreamerElement associated with the branch - if any
1246  // unfortunately, branch.fID is not number of element in streamer info
1247 
1248  if (!branch || !file || (branch._typename!=="TBranchElement") || (branch.fID<0) || (branch.fStreamerType<0)) return null;
1249 
1250  var s_i = file.FindStreamerInfo(branch.fClassName, branch.fClassVersion, branch.fCheckSum),
1251  arr = (s_i && s_i.fElements) ? s_i.fElements.arr : null;
1252  if (!arr) return null;
1253 
1254  var match_name = branch.fName,
1255  pos = match_name.indexOf("[");
1256  if (pos>0) match_name = match_name.substr(0, pos);
1257  pos = match_name.lastIndexOf(".");
1258  if (pos>0) match_name = match_name.substr(pos+1);
1259 
1260  function match_elem(elem) {
1261  if (!elem) return false;
1262  if (elem.fName !== match_name) return false;
1263  if (elem.fType === branch.fStreamerType) return true;
1264  if ((elem.fType === JSROOT.IO.kBool) && (branch.fStreamerType === JSROOT.IO.kUChar)) return true;
1265  if (((branch.fStreamerType===JSROOT.IO.kSTL) || (branch.fStreamerType===JSROOT.IO.kSTL + JSROOT.IO.kOffsetL) ||
1266  (branch.fStreamerType===JSROOT.IO.kSTLp) || (branch.fStreamerType===JSROOT.IO.kSTLp + JSROOT.IO.kOffsetL))
1267  && (elem.fType === JSROOT.IO.kStreamer)) return true;
1268  console.warn('Should match element', elem.fType, 'with branch', branch.fStreamerType);
1269  return false;
1270  }
1271 
1272  // first check branch fID - in many cases gut guess
1273  if (match_elem(arr[branch.fID])) return arr[branch.fID];
1274 
1275  // console.warn('Missmatch with branch name and extracted element', branch.fName, match_name, (s_elem ? s_elem.fName : "---"));
1276 
1277  for (var k=0;k<arr.length;++k)
1278  if ((k!==branch.fID) && match_elem(arr[k])) return arr[k];
1279 
1280  console.error('Did not found/match element for branch', branch.fName, 'class', branch.fClassName);
1281 
1282  return null;
1283  }
1284 
1285 
1286  JSROOT.IO.DefineMemberTypeName = function(file, parent_class, member_name) {
1287  // return type name of given member in the class
1288 
1289  var s_i = file.FindStreamerInfo(parent_class),
1290  arr = (s_i && s_i.fElements) ? s_i.fElements.arr : null,
1291  elem = null;
1292  if (!arr) return "";
1293 
1294  for (var k=0;k<arr.length;++k) {
1295  if (arr[k].fTypeName === "BASE") {
1296  var res = JSROOT.IO.DefineMemberTypeName(file, arr[k].fName, member_name);
1297  if (res) return res;
1298  } else
1299  if (arr[k].fName === member_name) { elem = arr[k]; break; }
1300  }
1301 
1302  if (!elem) return "";
1303 
1304  var clname = elem.fTypeName;
1305  if (clname[clname.length-1]==="*") clname = clname.substr(0, clname.length-1);
1306 
1307  return clname;
1308  }
1309 
1310  JSROOT.IO.GetBranchObjectClass = function(branch, tree, with_clones, with_leafs) {
1311  // return class name of the object, stored in the branch
1312 
1313  if (!branch || (branch._typename!=="TBranchElement")) return "";
1314 
1315  if ((branch.fType === JSROOT.BranchType.kLeafNode) && (branch.fID===-2) && (branch.fStreamerType===-1)) {
1316  // object where all sub-branches will be collected
1317  return branch.fClassName;
1318  }
1319 
1320  if (with_clones && branch.fClonesName && ((branch.fType === JSROOT.BranchType.kClonesNode) || (branch.fType === JSROOT.BranchType.kSTLNode)))
1321  return branch.fClonesName;
1322 
1323  var s_elem = JSROOT.IO.FindBrachStreamerElement(branch, tree.$file);
1324 
1325  if ((branch.fType === JSROOT.BranchType.kBaseClassNode) && s_elem && (s_elem.fTypeName==="BASE"))
1326  return s_elem.fName;
1327 
1328  if (branch.fType === JSROOT.BranchType.kObjectNode) {
1329  if (s_elem && ((s_elem.fType === JSROOT.IO.kObject) || (s_elem.fType === JSROOT.IO.kAny)))
1330  return s_elem.fTypeName;
1331  return "TObject";
1332  }
1333 
1334  if ((branch.fType === JSROOT.BranchType.kLeafNode) && s_elem && with_leafs) {
1335  if ((s_elem.fType === JSROOT.IO.kObject) || (s_elem.fType === JSROOT.IO.kAny)) return s_elem.fTypeName;
1336  if (s_elem.fType === JSROOT.IO.kObjectp) return s_elem.fTypeName.substr(0, s_elem.fTypeName.length-1);
1337  }
1338 
1339  return "";
1340  }
1341 
1342  JSROOT.IO.MakeMethodsList = function(typename) {
1343  // create fast list to assign all methods to the object
1344 
1345  var methods = JSROOT.getMethods(typename);
1346 
1347  var res = {
1348  names : [],
1349  values : [],
1350  Create : function() {
1351  var obj = {};
1352  for (var n=0;n<this.names.length;++n)
1353  obj[this.names[n]] = this.values[n];
1354  return obj;
1355  }
1356  }
1357 
1358  res.names.push("_typename"); res.values.push(typename);
1359  for (var key in methods) {
1360  res.names.push(key);
1361  res.values.push(methods[key]);
1362  }
1363  return res;
1364  }
1365 
1366  JSROOT.IO.DetectBranchMemberClass = function(brlst, prefix, start) {
1367  // try to define classname for the branch member, scanning list of branches
1368  var clname = "";
1369  for (var kk=(start || 0); kk<brlst.arr.length; ++kk)
1370  if ((brlst.arr[kk].fName.indexOf(prefix)===0) && brlst.arr[kk].fClassName) clname = brlst.arr[kk].fClassName;
1371  return clname;
1372  }
1373 
1376  JSROOT.TreeMethods = {};
1377 
1381  JSROOT.TreeMethods.Process = function(selector, args) {
1382  // function similar to the TTree::Process
1383 
1384  if (!args) args = {};
1385 
1386  if (!selector || !this.$file || !selector.branches) {
1387  console.error('required parameter missing for TTree::Process');
1388  if (selector) selector.Terminate(false);
1389  return false;
1390  }
1391 
1392  // central handle with all information required for reading
1393  var handle = {
1394  tree: this, // keep tree reference
1395  file: this.$file, // keep file reference
1396  selector: selector, // reference on selector
1397  arr: [], // list of branches
1398  curr: -1, // current entry ID
1399  current_entry: -1, // current processed entry
1400  simple_read: true, // all baskets in all used branches are in sync,
1401  process_arrays: true // one can process all branches as arrays
1402  };
1403 
1404  var namecnt = 0;
1405 
1406  function CreateLeafElem(leaf, name) {
1407  // function creates TStreamerElement which corresponds to the elementary leaf
1408  var datakind = 0;
1409  switch (leaf._typename) {
1410  case 'TLeafF': datakind = JSROOT.IO.kFloat; break;
1411  case 'TLeafD': datakind = JSROOT.IO.kDouble; break;
1412  case 'TLeafO': datakind = JSROOT.IO.kBool; break;
1413  case 'TLeafB': datakind = leaf.fIsUnsigned ? JSROOT.IO.kUChar : JSROOT.IO.kChar; break;
1414  case 'TLeafS': datakind = leaf.fIsUnsigned ? JSROOT.IO.kUShort : JSROOT.IO.kShort; break;
1415  case 'TLeafI': datakind = leaf.fIsUnsigned ? JSROOT.IO.kUInt : JSROOT.IO.kInt; break;
1416  case 'TLeafL': datakind = leaf.fIsUnsigned ? JSROOT.IO.kULong64 : JSROOT.IO.kLong64; break;
1417  case 'TLeafC': datakind = JSROOT.IO.kTString; break; // datakind = leaf.fIsUnsigned ? JSROOT.IO.kUChar : JSROOT.IO.kChar; break;
1418  default: return null;
1419  }
1420  return JSROOT.IO.CreateStreamerElement(name || leaf.fName, datakind);
1421  }
1422 
1423  function FindInHandle(branch) {
1424  for (var k=0;k<handle.arr.length;++k)
1425  if (handle.arr[k].branch === branch) return handle.arr[k];
1426  return null;
1427  }
1428 
1429  function AddBranchForReading(branch, target_object, target_name, read_mode) {
1430  // central method to add branch for reading
1431  // read_mode == true - read only this branch
1432  // read_mode == '$child$' is just member of object from for STL or clonesarray
1433  // read_mode == '<any class name>' is sub-object from STL or clonesarray, happens when such new object need to be created
1434  // read_mode == '.member_name' select only reading of member_name instead of complete object
1435 
1436  if (typeof branch === 'string')
1437  branch = handle.tree.FindBranch(branch);
1438 
1439  if (!branch) { console.error('Did not found branch'); return null; }
1440 
1441  var item = FindInHandle(branch);
1442 
1443  if (item) {
1444  console.error('Branch already configured for reading', branch.fName);
1445  if (item.tgt !== target_object) console.error('Target object differs');
1446  return elem;
1447  }
1448 
1449  if (!branch.fEntries) {
1450  console.warn('Branch ', branch.fName, ' does not have entries');
1451  return null;
1452  }
1453 
1454  // console.log('Add branch', branch.fName);
1455 
1456  item = {
1457  branch: branch,
1458  tgt: target_object, // used target object - can be differ for object members
1459  name: target_name,
1460  index: -1, // index in the list of read branches
1461  member: null, // member to read branch
1462  type: 0, // keep type identifier
1463  curr_entry: -1, // last processed entry
1464  raw : null, // raw buffer for reading
1465  basket : null, // current basket object
1466  curr_basket: 0, // number of basket used for processing
1467  read_entry: -1, // last entry which is already read
1468  staged_entry: -1, // entry which is staged for reading
1469  first_readentry: -1, // first entry to read
1470  staged_basket: 0, // last basket staged for reading
1471  numentries: branch.fEntries,
1472  numbaskets: branch.fWriteBasket, // number of baskets which can be read from the file
1473  counters: null, // branch indexes used as counters
1474  ascounter: [], // list of other branches using that branch as counter
1475  baskets: [], // array for read baskets,
1476  staged_prev: 0, // entry limit of previous I/O request
1477  staged_now: 0, // entry limit of current I/O request
1478  progress_showtm: 0, // last time when progress was showed
1479  GetBasketEntry: function(k) {
1480  if (!this.branch || (k > this.branch.fMaxBaskets)) return 0;
1481  var res = (k < this.branch.fMaxBaskets) ? this.branch.fBasketEntry[k] : 0;
1482  if (res) return res;
1483  var bskt = (k>0) ? this.branch.fBaskets.arr[k-1] : null;
1484  return bskt ? (this.branch.fBasketEntry[k-1] + bskt.fNevBuf) : 0;
1485  },
1486  GetTarget: function(tgtobj) {
1487  // returns target object which should be used for the branch reading
1488  if (!this.tgt) return tgtobj;
1489  for (var k=0;k<this.tgt.length;++k) {
1490  var sub = this.tgt[k];
1491  if (!tgtobj[sub.name]) tgtobj[sub.name] = sub.lst.Create();
1492  tgtobj = tgtobj[sub.name];
1493  }
1494  return tgtobj;
1495  },
1496  GetEntry: function(entry) {
1497  // This should be equivalent to TBranch::GetEntry() method
1498  var shift = entry - this.first_entry, off;
1499  if (!this.branch.TestBit(JSROOT.IO.BranchBits.kDoNotUseBufferMap))
1500  this.raw.ClearObjectMap();
1501  if (this.basket.fEntryOffset) {
1502  off = this.basket.fEntryOffset[shift];
1503  if (this.basket.fDisplacement)
1504  this.raw.fDisplacement = this.basket.fDisplacement[shift];
1505  } else {
1506  off = this.basket.fKeylen + this.basket.fNevBufSize * shift;
1507  }
1508  this.raw.locate(off - this.raw.raw_shift);
1509 
1510  // this.member.func(this.raw, this.GetTarget(tgtobj));
1511  }
1512  };
1513 
1514  // last basket can be stored directly with the branch
1515  while (item.GetBasketEntry(item.numbaskets+1)) item.numbaskets++;
1516 
1517  // check all counters if we
1518  var nb_branches = branch.fBranches ? branch.fBranches.arr.length : 0,
1519  nb_leaves = branch.fLeaves ? branch.fLeaves.arr.length : 0,
1520  leaf = (nb_leaves>0) ? branch.fLeaves.arr[0] : null,
1521  elem = null, // TStreamerElement used to create reader
1522  member = null, // member for actual reading of the branch
1523  is_brelem = (branch._typename==="TBranchElement"),
1524  child_scan = 0, // scan child branches after main branch is appended
1525  item_cnt = null, item_cnt2 = null, object_class = "";
1526 
1527  if (branch.fBranchCount) {
1528 
1529  item_cnt = FindInHandle(branch.fBranchCount);
1530 
1531  if (!item_cnt)
1532  item_cnt = AddBranchForReading(branch.fBranchCount, target_object, "$counter" + namecnt++, true);
1533 
1534  if (!item_cnt) { console.error('Cannot add counter branch', branch.fBranchCount.fName); return null; }
1535 
1536  var BranchCount2 = branch.fBranchCount2;
1537 
1538  if (!BranchCount2 && (branch.fBranchCount.fStreamerType===JSROOT.IO.kSTL) &&
1539  ((branch.fStreamerType === JSROOT.IO.kStreamLoop) || (branch.fStreamerType === JSROOT.IO.kOffsetL+JSROOT.IO.kStreamLoop))) {
1540  // special case when count member from kStreamLoop not assigned as fBranchCount2
1541  var elemd = JSROOT.IO.FindBrachStreamerElement(branch, handle.file),
1542  arrd = branch.fBranchCount.fBranches.arr;
1543 
1544  if (elemd && elemd.fCountName && arrd)
1545  for(var k=0;k<arrd.length;++k)
1546  if (arrd[k].fName === branch.fBranchCount.fName + "." + elemd.fCountName) {
1547  BranchCount2 = arrd[k];
1548  break;
1549  }
1550 
1551  if (!BranchCount2) console.error('Did not found branch for second counter of kStreamLoop element');
1552  }
1553 
1554  if (BranchCount2) {
1555  item_cnt2 = FindInHandle(BranchCount2);
1556 
1557  if (!item_cnt2) item_cnt2 = AddBranchForReading(BranchCount2, target_object, "$counter" + namecnt++, true);
1558 
1559  if (!item_cnt2) { console.error('Cannot add counter branch2', BranchCount2.fName); return null; }
1560  }
1561  } else
1562  if (nb_leaves===1 && leaf && leaf.fLeafCount) {
1563  var br_cnt = handle.tree.FindBranch(leaf.fLeafCount.fName);
1564 
1565  if (br_cnt) {
1566  item_cnt = FindInHandle(br_cnt);
1567 
1568  if (!item_cnt) item_cnt = AddBranchForReading(br_cnt, target_object, "$counter" + namecnt++, true);
1569 
1570  if (!item_cnt) { console.error('Cannot add counter branch', br_cnt.fName); return null; }
1571  }
1572  }
1573 
1574  function ScanBranches(lst, master_target, chld_kind) {
1575  if (!lst || !lst.arr.length) return true;
1576 
1577  var match_prefix = branch.fName;
1578  if (match_prefix[match_prefix.length-1] === ".") match_prefix = match_prefix.substr(0,match_prefix.length-1);
1579  if ((typeof read_mode=== "string") && (read_mode[0]==".")) match_prefix += read_mode;
1580  match_prefix+=".";
1581 
1582  for (var k=0;k<lst.arr.length;++k) {
1583  var br = lst.arr[k];
1584  if ((chld_kind>0) && (br.fType!==chld_kind)) continue;
1585 
1586  if (br.fType === JSROOT.BranchType.kBaseClassNode) {
1587  if (!ScanBranches(br.fBranches, master_target, chld_kind)) return false;
1588  continue;
1589  }
1590 
1591  var elem = JSROOT.IO.FindBrachStreamerElement(br, handle.file);
1592  if (elem && (elem.fTypeName==="BASE")) {
1593  // if branch is data of base class, map it to original target
1594  if (br.fTotBytes && !AddBranchForReading(br, target_object, target_name, read_mode)) return false;
1595  if (!ScanBranches(br.fBranches, master_target, chld_kind)) return false;
1596  continue;
1597  }
1598 
1599  var subname = br.fName, chld_direct = 1;
1600 
1601  if (br.fName.indexOf(match_prefix)===0) {
1602  subname = subname.substr(match_prefix.length);
1603  } else {
1604  if (chld_kind>0) continue; // for defined children names prefix must be present
1605  }
1606 
1607  var p = subname.indexOf('[');
1608  if (p>0) subname = subname.substr(0,p);
1609  p = subname.indexOf('<');
1610  if (p>0) subname = subname.substr(0,p);
1611 
1612  if (chld_kind > 0) {
1613  chld_direct = "$child$";
1614  var pp = subname.indexOf(".");
1615  if (pp>0) chld_direct = JSROOT.IO.DetectBranchMemberClass(lst, branch.fName + "." + subname.substr(0,pp+1), k) || "TObject";
1616  }
1617 
1618  if (!AddBranchForReading(br, master_target, subname, chld_direct)) return false;
1619  }
1620 
1621  return true;
1622  }
1623 
1624  if (branch._typename === "TBranchObject") {
1625  member = {
1626  name: target_name,
1627  typename: branch.fClassName,
1628  virtual: leaf.fVirtual,
1629  func: function(buf,obj) {
1630  var clname = this.typename;
1631  if (this.virtual) clname = buf.ReadFastString(buf.ntou1()+1);
1632  obj[this.name] = buf.ClassStreamer({}, clname);
1633  }
1634  };
1635  } else
1636 
1637  if ((branch.fType === JSROOT.BranchType.kClonesNode) || (branch.fType === JSROOT.BranchType.kSTLNode)) {
1638 
1639  elem = JSROOT.IO.CreateStreamerElement(target_name, JSROOT.IO.kInt);
1640 
1641  if (!read_mode || ((typeof read_mode==="string") && (read_mode[0]===".")) || (read_mode===1)) {
1642  handle.process_arrays = false;
1643 
1644  member = {
1645  name: target_name,
1646  conttype: branch.fClonesName || "TObject",
1647  reallocate: args.reallocate_objects,
1648  func: function(buf,obj) {
1649  var size = buf.ntoi4(), n = 0;
1650  if (!obj[this.name] || this.reallocate) {
1651  obj[this.name] = new Array(size);
1652  } else {
1653  n = obj[this.name].length;
1654  obj[this.name].length = size; // reallocate array
1655  }
1656 
1657  while (n<size) obj[this.name][n++] = this.methods.Create(); // create new objects
1658  }
1659  }
1660 
1661  if ((typeof read_mode==="string") && (read_mode[0]===".")) {
1662  member.conttype = JSROOT.IO.DetectBranchMemberClass(branch.fBranches, branch.fName+read_mode);
1663  if (!member.conttype) {
1664  console.error('Cannot select object', read_mode, "in the branch", branch.fName);
1665  return null;
1666  }
1667  }
1668 
1669  member.methods = JSROOT.IO.MakeMethodsList(member.conttype);
1670 
1671  child_scan = (branch.fType === JSROOT.BranchType.kClonesNode) ? JSROOT.BranchType.kClonesMemberNode : JSROOT.BranchType.kSTLMemberNode;
1672  }
1673  } else
1674 
1675  if ((object_class = JSROOT.IO.GetBranchObjectClass(branch, handle.tree))) {
1676 
1677  if (read_mode === true) {
1678  console.warn('Object branch ' + object_class + ' can not have data to be read directly');
1679  return null;
1680  }
1681 
1682  handle.process_arrays = false;
1683 
1684  var newtgt = new Array(target_object ? (target_object.length + 1) : 1);
1685  for (var l=0;l<newtgt.length-1;++l) newtgt[l] = target_object[l];
1686  newtgt[newtgt.length-1] = { name: target_name, lst: JSROOT.IO.MakeMethodsList(object_class) };
1687 
1688  if (!ScanBranches(branch.fBranches, newtgt, 0)) return null;
1689 
1690  return item; // this kind of branch does not have baskets and not need to be read
1691 
1692  } else if (is_brelem && (nb_leaves === 1) && (leaf.fName === branch.fName) && (branch.fID==-1)) {
1693 
1694  elem = JSROOT.IO.CreateStreamerElement(target_name, branch.fClassName);
1695 
1696  if (elem.fType === JSROOT.IO.kAny) {
1697 
1698  var streamer = handle.file.GetStreamer(branch.fClassName, { val: branch.fClassVersion, checksum: branch.fCheckSum });
1699  if (!streamer) { elem = null; console.warn('not found streamer!'); } else
1700  member = {
1701  name: target_name,
1702  typename: branch.fClassName,
1703  streamer: streamer,
1704  func: function(buf,obj) {
1705  var res = { _typename: this.typename };
1706  for (var n = 0; n < this.streamer.length; ++n)
1707  this.streamer[n].func(buf, res);
1708  obj[this.name] = res;
1709  }
1710  };
1711  }
1712 
1713  // elem.fType = JSROOT.IO.kAnyP;
1714 
1715  // only STL containers here
1716  // if (!elem.fSTLtype) elem = null;
1717  } else if (is_brelem && (nb_leaves <= 1)) {
1718 
1719  elem = JSROOT.IO.FindBrachStreamerElement(branch, handle.file);
1720 
1721  // this is basic type - can try to solve problem differently
1722  if (!elem && branch.fStreamerType && (branch.fStreamerType < 20))
1723  elem = JSROOT.IO.CreateStreamerElement(target_name, branch.fStreamerType);
1724 
1725  } else if (nb_leaves === 1) {
1726  // no special constrains for the leaf names
1727 
1728  elem = CreateLeafElem(leaf, target_name);
1729 
1730  } else if ((branch._typename === "TBranch") && (nb_leaves > 1)) {
1731  // branch with many elementary leaves
1732 
1733  var arr = new Array(nb_leaves), isok = true;
1734  for (var l=0;l<nb_leaves;++l) {
1735  arr[l] = CreateLeafElem(branch.fLeaves.arr[l]);
1736  arr[l] = JSROOT.IO.CreateMember(arr[l], handle.file);
1737  if (!arr[l]) isok = false;
1738  }
1739 
1740  if (isok)
1741  member = {
1742  name: target_name,
1743  leaves: arr,
1744  func: function(buf, obj) {
1745  var tgt = obj[this.name], l = 0;
1746  if (!tgt) obj[this.name] = tgt = {};
1747  while (l<this.leaves.length)
1748  this.leaves[l++].func(buf,tgt);
1749  }
1750  }
1751  }
1752 
1753  if (!elem && !member) {
1754  console.warn('Not supported branch kind', branch.fName, branch._typename);
1755  return null;
1756  }
1757 
1758  if (!member) {
1759  member = JSROOT.IO.CreateMember(elem, handle.file);
1760 
1761  if ((member.base !== undefined) && member.basename) {
1762  // when element represent base class, we need handling which differ from normal IO
1763  member.func = function(buf, obj) {
1764  if (!obj[this.name]) obj[this.name] = { _typename: this.basename };
1765  buf.ClassStreamer(obj[this.name], this.basename);
1766  };
1767  }
1768  }
1769 
1770  if (item_cnt && (typeof read_mode === "string")) {
1771 
1772  member.name0 = item_cnt.name;
1773 
1774  var snames = target_name.split(".");
1775 
1776  if (snames.length === 1) {
1777  // no point in the name - just plain array of objects
1778  member.get = function(arr,n) { return arr[n]; }
1779  } else if (read_mode === "$child$") {
1780  console.error('target name contains point, but suppose to be direct child', target_name);
1781  return null;
1782  } else if (snames.length === 2) {
1783  target_name = member.name = snames[1];
1784  member.name1 = snames[0];
1785  member.subtype1 = read_mode;
1786  member.methods1 = JSROOT.IO.MakeMethodsList(member.subtype1);
1787  member.get = function(arr,n) {
1788  var obj1 = arr[n][this.name1];
1789  if (!obj1) obj1 = arr[n][this.name1] = this.methods1.Create();
1790  return obj1;
1791  }
1792  } else {
1793  // very complex task - we need to reconstruct several embedded members with their types
1794  // try our best - but not all data types can be reconstructed correctly
1795  // while classname is not enough - there can be different versions
1796 
1797  if (!branch.fParentName) {
1798  console.error('Not possible to provide more than 2 parts in the target name', target_name);
1799  return null;
1800  }
1801 
1802  target_name = member.name = snames.pop(); // use last element
1803  member.snames = snames; // remember all sub-names
1804  member.smethods = []; // and special handles to create missing objects
1805 
1806  var parent_class = branch.fParentName; // unfortunately, without version
1807 
1808  for (var k=0;k<snames.length;++k) {
1809  var chld_class = JSROOT.IO.DefineMemberTypeName(handle.file, parent_class, snames[k]);
1810  member.smethods[k] = JSROOT.IO.MakeMethodsList(chld_class || "AbstractClass");
1811  parent_class = chld_class;
1812  }
1813  member.get = function(arr,n) {
1814  var obj1 = arr[n][this.snames[0]];
1815  if (!obj1) obj1 = arr[n][this.snames[0]] = this.smethods[0].Create();
1816  for (var k=1;k<this.snames.length;++k) {
1817  var obj2 = obj1[this.snames[k]];
1818  if (!obj2) obj2 = obj1[this.snames[k]] = this.smethods[k].Create();
1819  obj1 = obj2;
1820  }
1821  return obj1;
1822  }
1823  }
1824 
1825  // case when target is sub-object and need to be created before
1826 
1827 
1828  if (member.objs_branch_func) {
1829  // STL branch provides special function for the reading
1830  member.func = member.objs_branch_func;
1831  } else {
1832  member.func0 = member.func;
1833 
1834  member.func = function(buf,obj) {
1835  var arr = obj[this.name0], n = 0; // objects array where reading is done
1836  while(n<arr.length)
1837  this.func0(buf,this.get(arr,n++)); // read all individual object with standard functions
1838  }
1839  }
1840 
1841  } else if (item_cnt) {
1842 
1843  handle.process_arrays = false;
1844 
1845  if ((elem.fType === JSROOT.IO.kDouble32) || (elem.fType === JSROOT.IO.kFloat16)) {
1846  // special handling for compressed floats
1847 
1848  member.stl_size = item_cnt.name;
1849  member.func = function(buf, obj) {
1850  obj[this.name] = this.readarr(buf, obj[this.stl_size]);
1851  }
1852 
1853  } else
1854  if (((elem.fType === JSROOT.IO.kOffsetP+JSROOT.IO.kDouble32) || (elem.fType === JSROOT.IO.kOffsetP+JSROOT.IO.kFloat16)) && branch.fBranchCount2) {
1855  // special handling for variable arrays of compressed floats in branch - not tested
1856 
1857  member.stl_size = item_cnt.name;
1858  member.arr_size = item_cnt2.name;
1859  member.func = function(buf, obj) {
1860  var sz0 = obj[this.stl_size], sz1 = obj[this.arr_size], arr = new Array(sz0);
1861  for (var n=0;n<sz0;++n)
1862  arr[n] = (buf.ntou1() === 1) ? this.readarr(buf, sz1[n]) : [];
1863  obj[this.name] = arr;
1864  }
1865 
1866  } else
1867  // special handling of simple arrays
1868  if (((elem.fType > 0) && (elem.fType < JSROOT.IO.kOffsetL)) || (elem.fType === JSROOT.IO.kTString) ||
1869  (((elem.fType > JSROOT.IO.kOffsetP) && (elem.fType < JSROOT.IO.kOffsetP + JSROOT.IO.kOffsetL)) && branch.fBranchCount2)) {
1870 
1871  member = {
1872  name: target_name,
1873  stl_size: item_cnt.name,
1874  type: elem.fType,
1875  func: function(buf, obj) {
1876  obj[this.name] = buf.ReadFastArray(obj[this.stl_size], this.type);
1877  }
1878  };
1879 
1880  if (branch.fBranchCount2) {
1881  member.type -= JSROOT.IO.kOffsetP;
1882  member.arr_size = item_cnt2.name;
1883  member.func = function(buf, obj) {
1884  var sz0 = obj[this.stl_size], sz1 = obj[this.arr_size], arr = new Array(sz0);
1885  for (var n=0;n<sz0;++n)
1886  arr[n] = (buf.ntou1() === 1) ? buf.ReadFastArray(sz1[n], this.type) : [];
1887  obj[this.name] = arr;
1888  }
1889  }
1890 
1891  } else
1892  if ((elem.fType > JSROOT.IO.kOffsetP) && (elem.fType < JSROOT.IO.kOffsetP + JSROOT.IO.kOffsetL) && member.cntname) {
1893 
1894  member.cntname = item_cnt.name;
1895  } else
1896  if (elem.fType == JSROOT.IO.kStreamer) {
1897  // with streamers one need to extend existing array
1898 
1899  if (item_cnt2)
1900  throw new Error('Second branch counter not supported yet with JSROOT.IO.kStreamer');
1901 
1902  // function provided by normal I/O
1903  member.func = member.branch_func;
1904  member.stl_size = item_cnt.name;
1905  } else
1906  if ((elem.fType === JSROOT.IO.kStreamLoop) || (elem.fType === JSROOT.IO.kOffsetL+JSROOT.IO.kStreamLoop)) {
1907  if (item_cnt2) {
1908  // special solution for kStreamLoop
1909  member.stl_size = item_cnt.name;
1910  member.cntname = item_cnt2.name;
1911  member.func = member.branch_func; // this is special function, provided by base I/O
1912  } else {
1913  member.cntname = item_cnt.name;
1914  }
1915  } else {
1916 
1917  member.name = "$stl_member";
1918 
1919  var loop_size_name;
1920 
1921  if (item_cnt2) {
1922  if (member.cntname) {
1923  loop_size_name = item_cnt2.name;
1924  member.cntname = "$loop_size";
1925  } else {
1926  throw new Error('Second branch counter not used - very BAD');
1927  }
1928  }
1929 
1930  var stlmember = {
1931  name: target_name,
1932  stl_size: item_cnt.name,
1933  loop_size: loop_size_name,
1934  member0: member,
1935  func: function(buf, obj) {
1936  var cnt = obj[this.stl_size], arr = new Array(cnt), n = 0;
1937  for (var n=0;n<cnt;++n) {
1938  if (this.loop_size) obj.$loop_size = obj[this.loop_size][n];
1939  this.member0.func(buf, obj);
1940  arr[n] = obj.$stl_member;
1941  }
1942  delete obj.$stl_member;
1943  delete obj.$loop_size;
1944  obj[this.name] = arr;
1945  }
1946  };
1947 
1948  member = stlmember;
1949  }
1950  }
1951 
1952  // set name used to store result
1953  member.name = target_name;
1954 
1955  item.member = member; // member for reading
1956  if (elem) item.type = elem.fType;
1957  item.index = handle.arr.length; // index in the global list of branches
1958 
1959  if (item_cnt) {
1960  item.counters = [ item_cnt.index ];
1961  item_cnt.ascounter.push(item.index);
1962 
1963  if (item_cnt2) {
1964  item.counters.push(item_cnt2.index);
1965  item_cnt2.ascounter.push(item.index);
1966  }
1967  }
1968 
1969  handle.arr.push(item);
1970 
1971  // now one should add all other child branches
1972  if (child_scan)
1973  if (!ScanBranches(branch.fBranches, target_object, child_scan)) return null;
1974 
1975  return item;
1976  }
1977 
1978  // main loop to add all branches from selector for reading
1979  for (var nn=0; nn<selector.branches.length; ++nn) {
1980 
1981  var item = AddBranchForReading(selector.branches[nn], undefined, selector.names[nn], selector.directs[nn]);
1982 
1983  if (!item) {
1984  selector.Terminate(false);
1985  return false;
1986  }
1987  }
1988 
1989  // check if simple reading can be performed and there are direct data in branch
1990 
1991  for (var h=1; (h < handle.arr.length) && handle.simple_read; ++h) {
1992 
1993  var item = handle.arr[h], item0 = handle.arr[0];
1994 
1995  if ((item.numentries !== item0.numentries) || (item.numbaskets !== item0.numbaskets)) handle.simple_read = false;
1996  for (var n=0;n<item.numbaskets;++n)
1997  if (item.GetBasketEntry(n) !== item0.GetBasketEntry(n)) handle.simple_read = false;
1998  }
1999 
2000  // now calculate entries range
2001 
2002  handle.firstentry = handle.lastentry = 0;
2003  for (var nn = 0; nn < handle.arr.length; ++nn) {
2004  var branch = handle.arr[nn].branch, e1 = branch.fFirstEntry;
2005  if (e1 === undefined) e1 = (branch.fBasketBytes[0] ? branch.fBasketEntry[0] : 0);
2006  handle.firstentry = Math.max(handle.firstentry, e1);
2007  handle.lastentry = (nn===0) ? (e1 + branch.fEntries) : Math.min(handle.lastentry, e1 + branch.fEntries);
2008  }
2009 
2010  if (handle.firstentry >= handle.lastentry) {
2011  console.warn('No any common events for selected branches');
2012  selector.Terminate(false);
2013  return false;
2014  }
2015 
2016  handle.process_min = handle.firstentry;
2017  handle.process_max = handle.lastentry;
2018 
2019  if (!isNaN(args.firstentry) && (args.firstentry>handle.firstentry) && (args.firstentry < handle.lastentry))
2020  handle.process_min = args.firstentry;
2021 
2022  handle.current_entry = handle.staged_now = handle.process_min;
2023 
2024  if (!isNaN(args.numentries) && (args.numentries>0)) {
2025  var max = handle.process_min + args.numentries;
2026  if (max<handle.process_max) handle.process_max = max;
2027  }
2028 
2029  if ((typeof selector.ProcessArrays === 'function') && handle.simple_read) {
2030  // this is indication that selector can process arrays of values
2031  // only strictly-matched tree structure can be used for that
2032 
2033  for (var k=0;k<handle.arr.length;++k) {
2034  var elem = handle.arr[k];
2035  if ((elem.type<=0) || (elem.type >= JSROOT.IO.kOffsetL) || (elem.type === JSROOT.IO.kCharStar)) handle.process_arrays = false;
2036  }
2037 
2038  if (handle.process_arrays) {
2039  // create other members for fast processing
2040 
2041  selector.tgtarr = {}; // object with arrays
2042 
2043  for(var nn=0;nn<handle.arr.length;++nn) {
2044  var item = handle.arr[nn],
2045  elem = JSROOT.IO.CreateStreamerElement(item.name, item.type);
2046 
2047  elem.fType = item.type + JSROOT.IO.kOffsetL;
2048  elem.fArrayLength = 10;
2049  elem.fArrayDim = 1;
2050  elem.fMaxIndex[0] = 10; // 10 if artificial number, will be replaced during reading
2051 
2052  item.arrmember = JSROOT.IO.CreateMember(elem, handle.file);
2053  }
2054  }
2055  } else {
2056  handle.process_arrays = false;
2057  }
2058 
2059  function ReadBaskets(bitems, baskets_call_back) {
2060  // read basket with tree data, selecting different files
2061 
2062  var places = [], filename = "";
2063 
2064  function ExtractPlaces() {
2065  // extract places to read and define file name
2066 
2067  places = []; filename = "";
2068 
2069  for (var n=0;n<bitems.length;++n) {
2070  if (bitems[n].done) continue;
2071 
2072  var branch = bitems[n].branch;
2073 
2074  if (places.length===0)
2075  filename = branch.fFileName;
2076  else
2077  if (filename !== branch.fFileName) continue;
2078 
2079  bitems[n].selected = true; // mark which item was selected for reading
2080 
2081  places.push(branch.fBasketSeek[bitems[n].basket], branch.fBasketBytes[bitems[n].basket]);
2082  }
2083 
2084  return places.length > 0;
2085  }
2086 
2087  function ReadProgress(value) {
2088 
2089  if ((handle.staged_prev === handle.staged_now) ||
2090  (handle.process_max <= handle.process_min)) return;
2091 
2092  var tm = new Date().getTime();
2093 
2094  if (tm - handle.progress_showtm < 500) return; // no need to show very often
2095 
2096  handle.progress_showtm = tm;
2097 
2098  var portion = (handle.staged_prev + value * (handle.staged_now - handle.staged_prev)) /
2099  (handle.process_max - handle.process_min);
2100 
2101  handle.selector.ShowProgress(portion);
2102  }
2103 
2104  function ProcessBlobs(blobs) {
2105  if (!blobs || ((places.length>2) && (blobs.length*2 !== places.length)))
2106  return JSROOT.CallBack(baskets_call_back, null);
2107 
2108  var baskets = [], n = 0;
2109 
2110  for (var k=0;k<bitems.length;++k) {
2111  if (!bitems[k].selected) continue;
2112 
2113  bitems[k].selected = false;
2114  bitems[k].done = true;
2115 
2116  var blob = (places.length > 2) ? blobs[n++] : blobs,
2117  buf = JSROOT.CreateTBuffer(blob, 0, handle.file),
2118  basket = buf.ClassStreamer({}, "TBasket");
2119 
2120  if (basket.fNbytes !== bitems[k].branch.fBasketBytes[bitems[k].basket])
2121  console.error('mismatch in read basket sizes', bitems[k].branch.fBasketBytes[bitems[k].basket]);
2122 
2123  // items[k].obj = basket; // keep basket object itself if necessary
2124 
2125  bitems[k].bskt_obj = basket; // only number of entries in the basket are relevant for the moment
2126 
2127  if (basket.fKeylen + basket.fObjlen === basket.fNbytes) {
2128  // use data from original blob
2129  buf.raw_shift = 0;
2130  } else {
2131  // unpack data and create new blob
2132  var objblob = JSROOT.R__unzip(blob, basket.fObjlen, false, buf.o);
2133 
2134  if (objblob) {
2135  buf = JSROOT.CreateTBuffer(objblob, 0, handle.file);
2136  buf.raw_shift = basket.fKeylen;
2137  buf.fTagOffset = basket.fKeylen;
2138  } else {
2139  throw new Error('FAIL TO UNPACK');
2140  }
2141  }
2142 
2143  bitems[k].raw = buf; // here already unpacked buffer
2144 
2145  if (bitems[k].branch.fEntryOffsetLen > 0)
2146  buf.ReadBasketEntryOffset(basket, buf.raw_shift);
2147  }
2148 
2149  if (ExtractPlaces())
2150  handle.file.ReadBuffer(places, ProcessBlobs, filename, ReadProgress);
2151  else
2152  JSROOT.CallBack(baskets_call_back, bitems);
2153  }
2154 
2155  // extract places where to read
2156  if (ExtractPlaces())
2157  handle.file.ReadBuffer(places, ProcessBlobs, filename, ReadProgress);
2158  else
2159  JSROOT.CallBack(baskets_call_back, null);
2160  }
2161 
2162  function ReadNextBaskets() {
2163 
2164  var totalsz = 0, bitems = [], isany = true, is_direct = false, min_staged = handle.process_max;
2165 
2166  while ((totalsz < 1e6) && isany) {
2167  isany = false;
2168  // very important, loop over branches in reverse order
2169  // let check counter branch after reading of normal branch is prepared
2170  for (var n=handle.arr.length-1; n>=0; --n) {
2171  var elem = handle.arr[n];
2172 
2173  while (elem.staged_basket < elem.numbaskets) {
2174 
2175  var k = elem.staged_basket++;
2176 
2177  // no need to read more baskets, process_max is not included
2178  if (elem.GetBasketEntry(k) >= handle.process_max) break;
2179 
2180  // check which baskets need to be read
2181  if (elem.first_readentry < 0) {
2182  var lmt = elem.GetBasketEntry(k+1),
2183  not_needed = (lmt <= handle.process_min);
2184 
2185  //for (var d=0;d<elem.ascounter.length;++d) {
2186  // var dep = handle.arr[elem.ascounter[d]]; // dependent element
2187  // if (dep.first_readentry < lmt) not_needed = false; // check that counter provide required data
2188  //}
2189 
2190  if (not_needed) continue; // if that basket not required, check next
2191 
2192  elem.curr_basket = k; // basket where reading will start
2193 
2194  elem.first_readentry = elem.GetBasketEntry(k); // remember which entry will be read first
2195  }
2196 
2197  // check if basket already loaded in the branch
2198 
2199  var bitem = {
2200  id: n, // to find which element we are reading
2201  branch: elem.branch,
2202  basket: k,
2203  raw: null // here should be result
2204  };
2205 
2206  var bskt = elem.branch.fBaskets.arr[k];
2207  if (bskt) {
2208  bitem.raw = bskt.fBufferRef;
2209  if (bitem.raw)
2210  bitem.raw.locate(0); // reset pointer - same branch may be read several times
2211  else
2212  bitem.raw = JSROOT.CreateTBuffer(null, 0, handle.file); // create dummy buffer - basket has no data
2213  bitem.raw.raw_shift = bskt.fKeylen;
2214 
2215  if (bskt.fBufferRef && (elem.branch.fEntryOffsetLen > 0))
2216  bitem.raw.ReadBasketEntryOffset(bskt, bitem.raw.raw_shift);
2217 
2218  bitem.bskt_obj = bskt;
2219  is_direct = true;
2220  elem.baskets[k] = bitem;
2221  } else {
2222  bitems.push(bitem);
2223  totalsz += elem.branch.fBasketBytes[k];
2224  isany = true;
2225  }
2226 
2227  elem.staged_entry = elem.GetBasketEntry(k+1);
2228 
2229  min_staged = Math.min(min_staged, elem.staged_entry);
2230 
2231  break;
2232  }
2233  }
2234  }
2235 
2236  if ((totalsz === 0) && !is_direct)
2237  return handle.selector.Terminate(true);
2238 
2239  handle.staged_prev = handle.staged_now;
2240  handle.staged_now = min_staged;
2241 
2242  var portion = 0;
2243  if (handle.process_max > handle.process_min)
2244  portion = (handle.staged_prev - handle.process_min)/ (handle.process_max - handle.process_min);
2245 
2246  handle.selector.ShowProgress(portion);
2247 
2248  handle.progress_showtm = new Date().getTime();
2249 
2250  if (totalsz > 0) return ReadBaskets(bitems, ProcessBaskets);
2251 
2252  if (is_direct) return ProcessBaskets([]); // directly process baskets
2253 
2254  throw new Error("No any data is requested - never come here");
2255  }
2256 
2257  function ProcessBaskets(bitems) {
2258  // this is call-back when next baskets are read
2259 
2260  if ((handle.selector.break_execution !== 0) || (bitems===null))
2261  return handle.selector.Terminate(false);
2262 
2263  // redistribute read baskets over branches
2264  for(var n=0;n<bitems.length;++n)
2265  handle.arr[bitems[n].id].baskets[bitems[n].basket] = bitems[n];
2266 
2267  // now process baskets
2268 
2269  var isanyprocessed = false;
2270 
2271  while(true) {
2272 
2273  var loopentries = 100000000, min_curr = handle.process_max, n, elem;
2274 
2275  // first loop used to check if all required data exists
2276  for (n=0;n<handle.arr.length;++n) {
2277 
2278  elem = handle.arr[n];
2279 
2280  if (!elem.raw || !elem.basket || (elem.first_entry + elem.basket.fNevBuf <= handle.current_entry)) {
2281  delete elem.raw;
2282  delete elem.basket;
2283 
2284  if ((elem.curr_basket >= elem.numbaskets)) {
2285  if (n==0) return handle.selector.Terminate(true);
2286  continue; // ignore non-master branch
2287  }
2288 
2289  // this is single response from the tree, includes branch, bakset number, raw data
2290  var bitem = elem.baskets[elem.curr_basket];
2291 
2292  // basket not read
2293  if (!bitem) {
2294  // no data, but no any event processed - problem
2295  if (!isanyprocessed) {
2296  console.warn('no data?', elem.branch.fName, elem.curr_basket);
2297  return handle.selector.Terminate(false);
2298  }
2299 
2300  // try to read next portion of tree data
2301  return ReadNextBaskets();
2302  }
2303 
2304  elem.raw = bitem.raw;
2305  elem.basket = bitem.bskt_obj;
2306  // elem.nev = bitem.fNevBuf; // number of entries in raw buffer
2307  elem.first_entry = elem.GetBasketEntry(bitem.basket);
2308 
2309  bitem.raw = null; // remove reference on raw buffer
2310  bitem.branch = null; // remove reference on the branch
2311  bitem.bskt_obj = null; // remove reference on the branch
2312  elem.baskets[elem.curr_basket++] = undefined; // remove from array
2313  }
2314 
2315  // define how much entries can be processed before next raw buffer will be finished
2316  loopentries = Math.min(loopentries, elem.first_entry + elem.basket.fNevBuf - handle.current_entry);
2317  }
2318 
2319  // second loop extracts all required data
2320 
2321  // do not read too much
2322  if (handle.current_entry + loopentries > handle.process_max)
2323  loopentries = handle.process_max - handle.current_entry;
2324 
2325  if (handle.process_arrays && (loopentries>1)) {
2326  // special case - read all data from baskets as arrays
2327 
2328  for (n=0;n<handle.arr.length;++n) {
2329  elem = handle.arr[n];
2330 
2331  elem.GetEntry(handle.current_entry);
2332 
2333  elem.arrmember.arrlength = loopentries;
2334  elem.arrmember.func(elem.raw, handle.selector.tgtarr);
2335 
2336  elem.raw = null;
2337  }
2338 
2339  handle.selector.ProcessArrays(handle.current_entry);
2340 
2341  handle.current_entry += loopentries;
2342 
2343  isanyprocessed = true;
2344  } else
2345 
2346  // main processing loop
2347  while(loopentries--) {
2348 
2349  for (n=0;n<handle.arr.length;++n) {
2350  elem = handle.arr[n];
2351 
2352  // locate buffer offset at proper place
2353  elem.GetEntry(handle.current_entry);
2354 
2355  elem.member.func(elem.raw, elem.GetTarget(handle.selector.tgtobj));
2356  }
2357 
2358  handle.selector.Process(handle.current_entry);
2359 
2360  handle.current_entry++;
2361 
2362  isanyprocessed = true;
2363  }
2364 
2365  if (handle.current_entry >= handle.process_max)
2366  return handle.selector.Terminate(true);
2367  }
2368  }
2369 
2370  // call begin before first entry is read
2371  handle.selector.Begin(this);
2372 
2373  ReadNextBaskets();
2374 
2375  return true; // indicate that reading of tree will be performed
2376  }
2377 
2381  JSROOT.TreeMethods.FindBranch = function(name, complex, lst) {
2382 
2383  var top_search = false, search = name, res = null;
2384 
2385  if (lst===undefined) {
2386  top_search = true;
2387  lst = this.fBranches;
2388  var pos = search.indexOf("[");
2389  if (pos>0) search = search.substr(0,pos);
2390  }
2391 
2392  if (!lst || (lst.arr.length===0)) return null;
2393 
2394  for (var n=0;n<lst.arr.length;++n) {
2395  var brname = lst.arr[n].fName;
2396  if (brname[brname.length-1] == "]")
2397  brname = brname.substr(0, brname.indexOf("["));
2398 
2399  // special case when branch name includes STL map name
2400  if ((search.indexOf(brname)!==0) && (brname.indexOf("<")>0)) {
2401  var p1 = brname.indexOf("<"), p2 = brname.lastIndexOf(">");
2402  brname = brname.substr(0, p1) + brname.substr(p2+1);
2403  }
2404 
2405  if (brname === search) { res = { branch: lst.arr[n], rest:"" }; break; }
2406 
2407  if (search.indexOf(brname)!==0) continue;
2408 
2409  // this is a case when branch name is in the begin of the search string
2410 
2411  // check where point is
2412  var pnt = brname.length;
2413  if (brname[pnt-1] === '.') pnt--;
2414  if (search[pnt] !== '.') continue;
2415 
2416  res = this.FindBranch(search, complex, lst.arr[n].fBranches);
2417  if (!res) res = this.FindBranch(search.substr(pnt+1), complex, lst.arr[n].fBranches);
2418 
2419  if (!res) res = { branch: lst.arr[n], rest: search.substr(pnt) };
2420 
2421  break;
2422  }
2423 
2424  if (!top_search || !res) return res;
2425 
2426  if (name.length > search.length) res.rest += name.substr(search.length);
2427 
2428  if (!complex && (res.rest.length>0)) return null;
2429 
2430  return complex ? res : res.branch;
2431  }
2432 
2444  JSROOT.TreeMethods.Draw = function(args, result_callback) {
2445 
2446  if (typeof args === 'string') args = { expr: args };
2447 
2448  if (!args.expr) args.expr = "";
2449 
2450  // special debugging code
2451  if (args.expr === "testio")
2452  return this.IOTest(args, result_callback);
2453 
2454  var selector = new TDrawSelector(result_callback);
2455 
2456  if (args.branch) {
2457  if (!selector.DrawOnlyBranch(this, args.branch, args.expr, args)) selector = null;
2458  } else {
2459  if (!selector.ParseDrawExpression(this, args)) selector = null;
2460  }
2461 
2462  if (!selector)
2463  return JSROOT.CallBack(result_callback, null);
2464 
2465  return this.Process(selector, args);
2466  }
2467 
2468  JSROOT.TreeMethods.IOTest = function(args, result_callback) {
2469  // generic I/O test for all branches in the tree
2470 
2471  if (!args.names && !args.bracnhes) {
2472 
2473  args.branches = [];
2474  args.names = [];
2475  args.nchilds = [];
2476  args.nbr = 0;
2477 
2478  function CollectBranches(obj, prntname) {
2479  if (!obj || !obj.fBranches) return 0;
2480 
2481  var cnt = 0;
2482 
2483  for (var n=0;n<obj.fBranches.arr.length;++n) {
2484  var br = obj.fBranches.arr[n],
2485  name = (prntname ? prntname + "/" : "") + br.fName;
2486  args.branches.push(br);
2487  args.names.push(name);
2488  args.nchilds.push(0);
2489  var pos = args.nchilds.length-1;
2490  cnt += br.fLeaves ? br.fLeaves.arr.length : 0;
2491  var nchld = CollectBranches(br, name);
2492 
2493  cnt += nchld;
2494  args.nchilds[pos] = nchld;
2495 
2496  }
2497  return cnt;
2498  }
2499 
2500  var numleaves = CollectBranches(this);
2501 
2502  args.names.push("Total are " + args.branches.length + " branches with " + numleaves + " leaves");
2503  }
2504 
2505  args.lasttm = new Date().getTime();
2506  args.lastnbr = args.nbr;
2507 
2508  var tree = this;
2509 
2510  function TestNextBranch() {
2511 
2512  var selector = new TSelector;
2513 
2514  selector.AddBranch(args.branches[args.nbr], "br0");
2515 
2516  selector.Process = function() {
2517  if (this.tgtobj.br0 === undefined)
2518  this.fail = true;
2519  }
2520 
2521  selector.Terminate = function(res) {
2522  if (typeof res !== 'string')
2523  res = (!res || this.fails) ? "FAIL" : "ok";
2524 
2525  args.names[args.nbr] = res + " " + args.names[args.nbr];
2526  args.nbr++;
2527 
2528  if (args.nbr >= args.branches.length) {
2529  JSROOT.progress();
2530  return JSROOT.CallBack(result_callback, args.names, "inspect");
2531  }
2532 
2533  var now = new Date().getTime();
2534 
2535  if ((now - args.lasttm > 5000) || (args.nbr - args.lastnbr > 50))
2536  setTimeout(tree.IOTest.bind(tree,args,result_callback), 100); // use timeout to avoid deep recursion
2537  else
2538  TestNextBranch();
2539  }
2540 
2541  JSROOT.progress("br " + args.nbr + "/" + args.branches.length + " " + args.names[args.nbr]);
2542 
2543  var br = args.branches[args.nbr],
2544  object_class = JSROOT.IO.GetBranchObjectClass(br, tree),
2545  num = br.fEntries,
2546  skip_branch = (!br.fLeaves || (br.fLeaves.arr.length === 0));
2547 
2548  if (object_class) skip_branch = (args.nchilds[args.nbr]>100);
2549 
2550  // skip_branch = args.nchilds[args.nbr]>1;
2551 
2552  if (skip_branch || (num<=0)) {
2553  // ignore empty branches or objects with too-many subbranch
2554  // if (object_class) console.log('Ignore branch', br.fName, 'class', object_class, 'with', args.nchilds[args.nbr],'subbranches');
2555  selector.Terminate("ignore");
2556  } else {
2557 
2558  var drawargs = { numentries: 10 },
2559  first = br.fFirstEntry || 0,
2560  last = br.fEntryNumber || (first+num);
2561 
2562  if (num < drawargs.numentries) {
2563  drawargs.numentries = num;
2564  } else {
2565  // select randomly first entry to test I/O
2566  drawargs.firstentry = first + Math.round((last-first-drawargs.numentries)*Math.random());
2567  }
2568 
2569  // keep console output for debug purposes
2570  console.log('test branch', br.fName, 'first', (drawargs.firstentry || 0), "num", drawargs.numentries);
2571 
2572  tree.Process(selector, drawargs);
2573  }
2574  }
2575 
2576  TestNextBranch();
2577  }
2578 
2579 
2580  JSROOT.TSelector = TSelector;
2581  JSROOT.TDrawVariable = TDrawVariable;
2582  JSROOT.TDrawSelector = TDrawSelector;
2583 
2584  return JSROOT;
2585 
2586 }));