Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TRootSniffer.cxx
Go to the documentation of this file.
1// $Id$
2// Author: Sergey Linev 22/12/2013
3
4/*************************************************************************
5 * Copyright (C) 1995-2013, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TRootSniffer.h"
13
14#include "TDirectoryFile.h"
15#include "TKey.h"
16#include "TList.h"
17#include "TBufferJSON.h"
18#include "TROOT.h"
19#include "TInterpreter.h"
20#include "TFolder.h"
21#include "TClass.h"
22#include "TRealData.h"
23#include "TDataMember.h"
24#include "TDataType.h"
25#include "TObjString.h"
26#include "TObjArray.h"
27#include "TUrl.h"
28#include "TVirtualMutex.h"
29#include "TRootSnifferStore.h"
30#include "THttpCallArg.h"
31
32#include <cstdlib>
33#include <memory>
34#include <vector>
35#include <cstring>
36
37const char *item_prop_kind = "_kind";
38const char *item_prop_more = "_more";
39const char *item_prop_title = "_title";
40const char *item_prop_hidden = "_hidden";
41const char *item_prop_typename = "_typename";
42const char *item_prop_arraydim = "_arraydim";
43const char *item_prop_realname = "_realname"; // real object name
44const char *item_prop_user = "_username";
45const char *item_prop_autoload = "_autoload";
46const char *item_prop_rootversion = "_root_version";
47
48/** \class TRootSnifferScanRec
49
50Structure used to scan hierarchies of ROOT objects
51
52Represents single level of hierarchy
53*/
54
55////////////////////////////////////////////////////////////////////////////////
56/// constructor
57
62
63////////////////////////////////////////////////////////////////////////////////
64/// destructor
65
70
71////////////////////////////////////////////////////////////////////////////////
72/// record field for current element
73
80
81////////////////////////////////////////////////////////////////////////////////
82/// Indicates that new child for current element will be started
83
90
91////////////////////////////////////////////////////////////////////////////////
92/// Constructs item name from object name
93/// if special symbols like '/', '#', ':', '&', '?' are used in object name
94/// they will be replaced with '_'.
95/// To avoid item name duplication, additional id number can be appended
96
98{
99 std::string nnn = objname;
100
101 size_t pos;
102
103 // replace all special symbols which can make problem to navigate in hierarchy
104 while ((pos = nnn.find_first_of("- []<>#:&?/\'\"\\")) != std::string::npos)
105 nnn.replace(pos, 1, "_");
106
107 itemname = nnn.c_str();
108 Int_t cnt = 0;
109
110 while (fItemsNames.FindObject(itemname.Data())) {
111 itemname.Form("%s_%d", nnn.c_str(), cnt++);
112 }
113
114 fItemsNames.Add(new TObjString(itemname.Data()));
115}
116
117////////////////////////////////////////////////////////////////////////////////
118/// Produce full name, including all parents
119
121{
122 if (!prnt)
123 prnt = fParent;
124
125 if (prnt) {
126 prnt->BuildFullName(buf);
127
128 buf.Append("/");
129 buf.Append(fItemName);
130 }
131}
132
133////////////////////////////////////////////////////////////////////////////////
134/// Creates new node with specified name
135/// if special symbols like "[]&<>" are used, node name
136/// will be replaced by default name like "extra_item_N" and
137/// original node name will be recorded as "_original_name" field
138/// Optionally, object name can be recorded as "_realname" field
139
141{
142 if (!CanSetFields())
143 return;
144
146
147 if (fParent)
149
150 if (fStore)
152}
153
154////////////////////////////////////////////////////////////////////////////////
155/// Close started node
156
164
165////////////////////////////////////////////////////////////////////////////////
166/// set root class name as node kind
167/// in addition, path to master item (streamer info) specified
168/// Such master item required to correctly unstream data on JavaScript
169
171{
172 if (cl && CanSetFields())
174}
175
176////////////////////////////////////////////////////////////////////////////////
177/// returns true if scanning is done
178/// Can happen when searched element is found
179
181{
182 if (!fStore)
183 return kFALSE;
184
185 if ((fMask & kSearch) && fStore->GetResPtr())
186 return kTRUE;
187
188 if ((fMask & kCheckChilds) && fStore->GetResPtr() && (fStore->GetResNumChilds() >= 0))
189 return kTRUE;
190
191 return kFALSE;
192}
193
194////////////////////////////////////////////////////////////////////////////////
195/// Checks if result will be accepted.
196/// Used to verify if sniffer should read object from the file
197
199{
200 if (Done())
201 return kFALSE;
202
203 // only when doing search, result will be propagated
204 if ((fMask & (kSearch | kCheckChilds)) == 0)
205 return kFALSE;
206
207 // only when full search path is scanned
208 if (fSearchPath)
209 return kFALSE;
210
211 if (!fStore)
212 return kFALSE;
213
214 return kTRUE;
215}
216
217////////////////////////////////////////////////////////////////////////////////
218/// set results of scanning
219/// when member should be specified, use SetFoundResult instead
220
222{
223 if (!member)
224 return SetFoundResult(obj, cl);
225
226 fStore->Error("SetResult",
227 "When member specified, pointer on object (not member) should be provided; use SetFoundResult");
228 return kFALSE;
229}
230
231////////////////////////////////////////////////////////////////////////////////
232/// set results of scanning
233/// when member specified, obj is pointer on object to which member belongs
234
236{
237 if (Done())
238 return kTRUE;
239
240 if (!IsReadyForResult())
241 return kFALSE;
242
244
245 return Done();
246}
247
248////////////////////////////////////////////////////////////////////////////////
249/// returns current depth of scanned hierarchy
250
252{
253 Int_t cnt = 0;
254 const TRootSnifferScanRec *rec = this;
255 while (rec->fParent) {
256 rec = rec->fParent;
257 cnt++;
258 }
259
260 return cnt;
261}
262
263////////////////////////////////////////////////////////////////////////////////
264/// returns true if current item can be expanded - means one could explore
265/// objects members
266
268{
269 if (fMask & (kExpand | kSearch | kCheckChilds))
270 return kTRUE;
271
272 if (!fHasMore)
273 return kFALSE;
274
275 // if parent has expand mask, allow to expand item
276 if (fParent && (fParent->fMask & kExpand))
277 return kTRUE;
278
279 return kFALSE;
280}
281
282////////////////////////////////////////////////////////////////////////////////
283/// returns read-only flag for current item
284/// Depends from default value and current restrictions
285
287{
288 if (fRestriction == 0)
289 return dflt;
290
291 return fRestriction != 2;
292}
293
294////////////////////////////////////////////////////////////////////////////////
295/// Method verifies if new level of hierarchy
296/// should be started with provided object.
297/// If required, all necessary nodes and fields will be created
298/// Used when different collection kinds should be scanned
299
301{
302 if (super.Done())
303 return kFALSE;
304
305 if (obj && !obj_name)
306 obj_name = obj->GetName();
307
308 // exclude zero names
309 if (!obj_name || (*obj_name == 0))
310 return kFALSE;
311
312 const char *full_name = nullptr;
313
314 // remove slashes from file names
315 if (obj && obj->InheritsFrom(TDirectoryFile::Class())) {
316 const char *slash = strrchr(obj_name, '/');
317 if (slash) {
319 obj_name = slash + 1;
320 if (*obj_name == 0)
321 obj_name = "file";
322 }
323 }
324
325 super.MakeItemName(obj_name, fItemName);
326
327 if (sniffer && sniffer->HasRestriction(fItemName.Data())) {
328 // check restriction more precisely
329 TString fullname;
330 BuildFullName(fullname, &super);
331 fRestriction = sniffer->CheckRestriction(fullname.Data());
332 if (fRestriction < 0)
333 return kFALSE;
334 }
335
336 fParent = &super;
337 fLevel = super.fLevel;
338 fStore = super.fStore;
339 fSearchPath = super.fSearchPath;
340 fMask = super.fMask & kActions;
341 if (fRestriction == 0)
342 fRestriction = super.fRestriction; // get restriction from parent
344
345 if (fMask & kScan) {
346 // if scanning only fields, ignore all childs
347 if (super.ScanOnlyFields())
348 return kFALSE;
349 // only when doing scan, increment level, used for text formatting
350 fLevel++;
351 } else {
352 if (!fSearchPath)
353 return kFALSE;
354
356 return kFALSE;
357
358 const char *separ = fSearchPath + fItemName.Length();
359
361 while (*separ == '/') {
362 separ++;
363 isslash = kTRUE;
364 }
365
366 if (*separ == 0) {
367 fSearchPath = nullptr;
368 if (fMask & kExpand) {
371 fHasMore = (fMask & kOnlyFields) == 0;
372 }
373 } else {
374 if (!isslash)
375 return kFALSE;
377 }
378 }
379
381
382 if (obj_name && (fItemName != obj_name))
384
385 if (full_name)
386 SetField("_fullname", full_name);
387
388 if (topelement)
389 SetField(item_prop_rootversion, TString::Format("%d", gROOT->GetVersionCode()), kFALSE);
390
391 if (topelement && sniffer->GetAutoLoad())
392 SetField(item_prop_autoload, sniffer->GetAutoLoad());
393
394 return kTRUE;
395}
396
397
398/** \class TRootSniffer
399
400Sniffer of ROOT objects, data provider for THttpServer
401
402Provides methods to scan different structures like folders,
403directories, files and collections. Can locate objects (or its data member) per name.
404Can be extended to application-specific classes.
405
406Normally TRootSnifferFull class is used which able to access data from trees, canvases, histograms.
407*/
408
409
410////////////////////////////////////////////////////////////////////////////////
411/// constructor
412
413TRootSniffer::TRootSniffer(const char *name, const char *objpath)
414 : TNamed(name, "sniffer of root objects"), fObjectsPath(objpath)
415{
417}
418
419////////////////////////////////////////////////////////////////////////////////
420/// destructor
421
425
426////////////////////////////////////////////////////////////////////////////////
427/// set current http arguments, which then used in different process methods
428/// For instance, if user authorized with some user name,
429/// depending from restrictions some objects will be invisible
430/// or user get full access to the element
431/// Returns previous argument which was set before
432
434{
435 auto res = fCurrentArg;
436 fCurrentArg = arg;
439 return res;
440}
441
442////////////////////////////////////////////////////////////////////////////////
443/// Restrict access to the specified location
444///
445/// Hides or provides read-only access to different parts of the hierarchy
446/// Restriction done base on user-name specified with http requests
447/// Options can be specified in URL style (separated with &)
448/// Following parameters can be specified:
449///
450/// visible = [all|user(s)] - make item visible for all users or only specified user
451/// hidden = [all|user(s)] - make item hidden from all users or only specified user
452/// readonly = [all|user(s)] - make item read-only for all users or only specified user
453/// allow = [all|user(s)] - make full access for all users or only specified user
454/// allow_method = method(s) - allow method(s) execution even when readonly flag specified for the object
455///
456/// Like make command seen by all but can be executed only by admin
457///
458/// sniff->Restrict("/CmdReset","allow=admin");
459///
460/// Or fully hide command from guest account
461///
462/// sniff->Restrict("/CmdRebin","hidden=guest");
463
464void TRootSniffer::Restrict(const char *path, const char *options)
465{
466 const char *rslash = strrchr(path, '/');
467 if (rslash)
468 rslash++;
469 if (!rslash || (*rslash == 0))
470 rslash = path;
471
472 fRestrictions.Add(new TNamed(rslash, TString::Format("%s%s%s", path, "%%%", options).Data()));
473}
474
475////////////////////////////////////////////////////////////////////////////////
476/// When specified, _autoload attribute will be always add
477/// to top element of h.json/h.hml requests
478/// Used to instruct browser automatically load special code
479
481{
482 fAutoLoad = scripts ? scripts : "";
483}
484
485////////////////////////////////////////////////////////////////////////////////
486/// return name of configured autoload scripts (or 0)
487
488const char *TRootSniffer::GetAutoLoad() const
489{
490 return fAutoLoad.Length() > 0 ? fAutoLoad.Data() : nullptr;
491}
492
493////////////////////////////////////////////////////////////////////////////////
494/// Made fast check if item with specified name is in restriction list
495/// If returns true, requires precise check with CheckRestriction() method
496
498{
499 if (!item_name || (*item_name == 0) || !fCurrentArg)
500 return kFALSE;
501
502 return fRestrictions.FindObject(item_name) != nullptr;
503}
504
505////////////////////////////////////////////////////////////////////////////////
506/// return 2 when option match to current user name
507/// return 1 when option==all
508/// return 0 when option does not match user name
509
511{
512 const char *username = fCurrentArg ? fCurrentArg->GetUserName() : nullptr;
513
514 if (!username || !option || (*option == 0))
515 return 0;
516
517 if (strcmp(option, "all") == 0)
518 return 1;
519
520 if (strcmp(username, option) == 0)
521 return 2;
522
523 if (strstr(option, username) == 0)
524 return -1;
525
527
528 Bool_t find = arr->FindObject(username) != nullptr;
529
530 delete arr;
531
532 return find ? 2 : -1;
533}
534
535////////////////////////////////////////////////////////////////////////////////
536/// Checked if restriction is applied to the item full_item_name
537/// should have full path to the item. Returns:
538///
539/// * -1 - object invisible, cannot be accessed or listed
540/// * 0 - no explicit restrictions, use default
541/// * 1 - read-only access
542/// * 2 - full access
543
545{
546 if (!full_item_name || (*full_item_name == 0))
547 return 0;
548
549 const char *item_name = strrchr(full_item_name, '/');
550 if (item_name)
551 item_name++;
552 if (!item_name || (*item_name == 0))
554
555 TString pattern1 = TString("*/") + item_name + "%%%";
557
558 const char *options = nullptr;
559 TIter iter(&fRestrictions);
560
561 while (auto obj = iter()) {
562 const char *title = obj->GetTitle();
563
564 if (strstr(title, pattern1.Data()) == title) {
565 options = title + pattern1.Length();
566 break;
567 }
568 if (strstr(title, pattern2.Data()) == title) {
569 options = title + pattern2.Length();
570 break;
571 }
572 }
573
574 if (!options)
575 return 0;
576
577 TUrl url;
578 url.SetOptions(options);
579 url.ParseOptions();
580
581 Int_t can_see =
582 WithCurrentUserName(url.GetValueFromOptions("visible")) - WithCurrentUserName(url.GetValueFromOptions("hidden"));
583
585 WithCurrentUserName(url.GetValueFromOptions("allow")) - WithCurrentUserName(url.GetValueFromOptions("readonly"));
586
587 if (can_access > 0)
588 return 2; // first of all, if access enabled, provide it
589 if (can_see < 0)
590 return -1; // if object to be hidden, do it
591
592 const char *methods = url.GetValueFromOptions("allow_method");
593 if (methods)
595
596 if (can_access < 0)
597 return 1; // read-only access
598
599 return 0; // default behavior
600}
601
602////////////////////////////////////////////////////////////////////////////////
603/// scan object data members
604/// some members like enum or static members will be excluded
605
607{
608 if (!cl || !ptr || rec.Done())
609 return;
610
611 // ensure that real class data (including parents) exists
612 if (!(cl->Property() & kIsAbstract))
613 cl->BuildRealData();
614
615 // scan only real data
616 TIter iter(cl->GetListOfRealData());
617 while (auto obj = iter()) {
618 TRealData *rdata = dynamic_cast<TRealData *>(obj);
619 if (!rdata || strchr(rdata->GetName(), '.'))
620 continue;
621
622 TDataMember *member = rdata->GetDataMember();
623 // exclude enum or static variables
624 if (!member || (member->Property() & (kIsStatic | kIsEnum | kIsUnion)))
625 continue;
626 char *member_ptr = ptr + rdata->GetThisOffset();
627
628 if (member->IsaPointer())
629 member_ptr = *((char **)member_ptr);
630
632
633 if (chld.GoInside(rec, member, 0, this)) {
634
635 TClass *mcl = (member->IsBasic() || member->IsSTLContainer()) ? nullptr : gROOT->GetClass(member->GetTypeName());
636
637 Int_t coll_offset = mcl ? mcl->GetBaseClassOffset(TCollection::Class()) : -1;
638 if (coll_offset >= 0) {
639 chld.SetField(item_prop_more, "true", kFALSE);
640 chld.fHasMore = kTRUE;
641 }
642
643 if (chld.SetFoundResult(ptr, cl, member))
644 break;
645
646 const char *title = member->GetTitle();
647 if (title && *title)
648 chld.SetField(item_prop_title, title);
649
650 if (member->GetTypeName())
651 chld.SetField(item_prop_typename, member->GetTypeName());
652
653 if (member->GetArrayDim() > 0) {
654 // store array dimensions in form [N1,N2,N3,...]
655 TString dim("[");
656 for (Int_t n = 0; n < member->GetArrayDim(); n++) {
657 if (n > 0)
658 dim.Append(",");
659 dim.Append(TString::Format("%d", member->GetMaxIndex(n)));
660 }
661 dim.Append("]");
662 chld.SetField(item_prop_arraydim, dim, kFALSE);
663 } else if (member->GetArrayIndex() != 0) {
664 TRealData *idata = cl->GetRealData(member->GetArrayIndex());
665 TDataMember *imember = idata ? idata->GetDataMember() : nullptr;
666 if (imember && (strcmp(imember->GetTrueTypeName(), "int") == 0)) {
667 Int_t arraylen = *((int *)(ptr + idata->GetThisOffset()));
669 }
670 }
671
672 chld.SetRootClass(mcl);
673
674 if (chld.CanExpandItem()) {
675 if (coll_offset >= 0) {
676 // chld.SetField("#members", "true", kFALSE);
678 }
679 }
680
681 if (chld.SetFoundResult(ptr, cl, member))
682 break;
683 }
684 }
685}
686
687////////////////////////////////////////////////////////////////////////////////
688/// Scans object properties
689/// here such fields as _autoload or _icon properties depending on class or object name could be assigned
690/// By default properties, coded in the Class title are scanned. Example:
691///
692/// ClassDef(UserClassName, 1) // class comments *SNIFF* _field1=value _field2="string value"
693///
694/// Here *SNIFF* mark is important. After it all expressions like field=value are parsed
695/// One could use double quotes to code string values with spaces.
696/// Fields separated from each other with spaces
697
699{
700 TClass *cl = obj ? obj->IsA() : nullptr;
701
702 const char *pos = strstr(cl ? cl->GetTitle() : "", "*SNIFF*");
703 if (!pos)
704 return;
705
706 pos += 7;
707 while (*pos) {
708 if (*pos == ' ') {
709 pos++;
710 continue;
711 }
712 // first locate identifier
713 const char *pos0 = pos;
714 while (*pos && (*pos != '='))
715 pos++;
716 if (*pos == 0)
717 return;
718 TString name(pos0, pos - pos0);
719 pos++;
720 Bool_t quotes = (*pos == '\"');
721 if (quotes)
722 pos++;
723 pos0 = pos;
724 // then value with or without quotes
725 while (*pos && (*pos != (quotes ? '\"' : ' ')))
726 pos++;
727 TString value(pos0, pos - pos0);
728 rec.SetField(name, value);
729 if (quotes)
730 pos++;
731 pos++;
732 }
733}
734
735////////////////////////////////////////////////////////////////////////////////
736/// Scans TKey properties
737/// in special cases load objects from the file
738
740{
741 if (strcmp(key->GetClassName(), "TDirectoryFile") == 0) {
742 if (rec.fLevel == 0) {
743 auto dir = key->ReadObject<TDirectory>();
744 if (dir) {
745 obj = dir;
746 obj_class = dir->IsA();
747 }
748 } else {
749 rec.SetField(item_prop_more, "true", kFALSE);
750 rec.fHasMore = kTRUE;
751 }
752 }
753}
754
755////////////////////////////////////////////////////////////////////////////////
756/// scans object childs (if any)
757/// here one scans collection, branches, trees and so on
758
760{
761 if (obj->InheritsFrom(TFolder::Class())) {
762 ScanCollection(rec, ((TFolder *)obj)->GetListOfFolders());
763 } else if (obj->InheritsFrom(TDirectory::Class())) {
764 TDirectory *dir = (TDirectory *)obj;
765 ScanCollection(rec, dir->GetList(), nullptr, dir->GetListOfKeys());
766 } else if (rec.CanExpandItem()) {
767 ScanObjectMembers(rec, obj->IsA(), (char *)obj);
768 }
769}
770
771////////////////////////////////////////////////////////////////////////////////
772/// Scan collection content
773
776{
777 if ((!lst || (lst->GetSize() == 0)) && (!keys_lst || (keys_lst->GetSize() == 0)))
778 return;
779
781 if (foldername) {
782 if (!folderrec.GoInside(rec, nullptr, foldername, this))
783 return;
784 }
785
787
788 if (lst) {
789 TIter iter(lst);
790 TObject *next = iter();
792
793 while (next) {
794 if (IsItemField(next)) {
795 // special case - in the beginning one could have items for master folder
796 if (!isany && (next->GetName() != nullptr) && ((*(next->GetName()) == '_') || master.ScanOnlyFields()))
797 master.SetField(next->GetName(), next->GetTitle());
798 next = iter();
799 continue;
800 }
801
802 isany = kTRUE;
803 TObject *obj = next;
804
806 if (!chld.GoInside(master, obj, nullptr, this)) {
807 next = iter();
808 continue;
809 }
810
811 if (chld.SetResult(obj, obj->IsA()))
812 return;
813
815
817 // now properties, coded as TNamed objects, placed after object in the hierarchy
818 while ((next = iter()) != nullptr) {
819 if (!IsItemField(next))
820 break;
821 if ((next->GetName() != nullptr) && ((*(next->GetName()) == '_') || chld.ScanOnlyFields())) {
822 // only fields starting with _ are stored
823 chld.SetField(next->GetName(), next->GetTitle());
824 if (strcmp(next->GetName(), item_prop_kind) == 0)
825 has_kind = kTRUE;
826 if (strcmp(next->GetName(), item_prop_title) == 0)
828 }
829 }
830
831 if (!has_kind)
832 chld.SetRootClass(obj->IsA());
833 if (!has_title && obj->GetTitle())
834 chld.SetField(item_prop_title, obj->GetTitle());
835
837
838 if (chld.SetResult(obj, obj->IsA()))
839 return;
840 }
841 }
842
843 if (keys_lst) {
844 TIter iter(keys_lst);
845
846 while (auto kobj = iter()) {
847 TKey *key = dynamic_cast<TKey *>(kobj);
848 if (!key)
849 continue;
850 TObject *obj = lst ? lst->FindObject(key->GetName()) : nullptr;
851
852 // even object with the name exists, it should also match with class name
853 if (obj && (strcmp(obj->ClassName(), key->GetClassName()) != 0))
854 obj = nullptr;
855
856 // if object of that name and of that class already in the list, ignore appropriate key
857 if (obj && (master.fMask & TRootSnifferScanRec::kScan))
858 continue;
859
861 // if object not exists, provide key itself for the scan
862 if (!obj) {
863 obj = key;
864 iskey = kTRUE;
865 }
866
868 TString fullname = TString::Format("%s;%d", key->GetName(), key->GetCycle());
869
870 if (chld.GoInside(master, obj, fullname.Data(), this)) {
871
872 if (!chld.IsReadOnly(fReadOnly) && iskey && chld.IsReadyForResult()) {
873 TObject *keyobj = key->ReadObj();
874 if (keyobj)
875 if (chld.SetResult(keyobj, keyobj->IsA()))
876 return;
877 }
878
879 if (chld.SetResult(obj, obj->IsA()))
880 return;
881
882 TClass *obj_class = obj->IsA();
883
885
886 if (obj->GetTitle())
887 chld.SetField(item_prop_title, obj->GetTitle());
888
889 // special handling of TKey class - in non-readonly mode
890 // sniffer allowed to fetch objects
891 if (!chld.IsReadOnly(fReadOnly) && iskey)
892 ScanKeyProperties(chld, key, obj, obj_class);
893
894 rec.SetRootClass(obj_class);
895
897
898 // here we should know how many childs are accumulated
899 if (chld.SetResult(obj, obj_class))
900 return;
901 }
902 }
903 }
904}
905
906////////////////////////////////////////////////////////////////////////////////
907/// Create own TFolder structures independent from gROOT
908/// This allows to have many independent TRootSniffer instances
909/// At the same time such sniffer lost access to all global lists and folders
910
912{
913 if (fTopFolder) return;
914
916
917 // this only works with c++14, use ROOT wrapper
918 fTopFolder = std::make_unique<TFolder>("http","Dedicated instance");
919
920 // not sure if we have to add that private folder to global list of cleanups
921
922 // R__LOCKGUARD(gROOTMutex);
923 // gROOT->GetListOfCleanups()->Add(fTopFolder.get());
924
925}
926
927////////////////////////////////////////////////////////////////////////////////
928/// Returns top TFolder instance for the sniffer
929
931{
932 if (fTopFolder) return fTopFolder.get();
933
934 TFolder *topf = gROOT->GetRootFolder();
935
936 if (!topf) {
937 Error("RegisterObject", "Not found top ROOT folder!!!");
938 return nullptr;
939 }
940
941 TFolder *httpfold = dynamic_cast<TFolder *>(topf->FindObject("http"));
942 if (!httpfold) {
943 if (!force)
944 return nullptr;
945 httpfold = topf->AddFolder("http", "ROOT http server");
946 httpfold->SetBit(kCanDelete);
947 // register top folder in list of cleanups
949 gROOT->GetListOfCleanups()->Add(httpfold);
950 }
951
952 return httpfold;
953}
954
955////////////////////////////////////////////////////////////////////////////////
956/// scan complete ROOT objects hierarchy
957/// For the moment it includes objects in gROOT directory
958/// and list of canvases and files
959/// Also all registered objects are included.
960/// One could re-implement this method to provide alternative
961/// scan methods or to extend some collection kinds
962
964{
965 rec.SetField(item_prop_kind, "ROOT.Session");
968
969 // should be on the top while //root/http folder could have properties for itself
971 if (topf) {
972 rec.SetField(item_prop_title, topf->GetTitle());
973 ScanCollection(rec, topf->GetListOfFolders());
974 }
975
976 if (HasStreamerInfo()) {
978 if (chld.GoInside(rec, nullptr, "StreamerInfo", this)) {
979 chld.SetField(item_prop_kind, "ROOT.TStreamerInfoList");
980 chld.SetField(item_prop_title, "List of streamer infos for binary I/O");
981 chld.SetField(item_prop_hidden, "true", kFALSE);
982 chld.SetField("_module", "hierarchy");
983 chld.SetField("_after_request", "markAsStreamerInfo");
984 }
985 }
986
987 if (IsScanGlobalDir()) {
988 ScanCollection(rec, gROOT->GetList());
989
990 ScanCollection(rec, gROOT->GetListOfCanvases(), "Canvases");
991
992 ScanCollection(rec, gROOT->GetListOfFiles(), "Files");
993 }
994}
995
996////////////////////////////////////////////////////////////////////////////////
997/// scan ROOT hierarchy with provided store object
998
999void TRootSniffer::ScanHierarchy(const char *topname, const char *path, TRootSnifferStore *store,
1001{
1003 rec.fSearchPath = path;
1004 if (rec.fSearchPath) {
1005 while (*rec.fSearchPath == '/')
1006 rec.fSearchPath++;
1007 if (*rec.fSearchPath == 0)
1008 rec.fSearchPath = nullptr;
1009 }
1010
1011 // if path non-empty, we should find item first and than start scanning
1013 if (only_fields)
1015
1016 rec.fStore = store;
1017
1018 rec.CreateNode(topname);
1019
1020 if (!rec.fSearchPath)
1022
1023 if (!rec.fSearchPath && GetAutoLoad())
1024 rec.SetField(item_prop_autoload, GetAutoLoad());
1025
1026 ScanRoot(rec);
1027
1028 rec.CloseNode();
1029}
1030
1031////////////////////////////////////////////////////////////////////////////////
1032/// Search element with specified path
1033/// Returns pointer on element
1034/// Optionally one could obtain element class, member description
1035/// and number of childs. When chld!=nullptr, not only element is searched,
1036/// but also number of childs are counted. When member!=0, any object
1037/// will be scanned for its data members (disregard of extra options)
1038
1040{
1041 TRootSnifferStore store;
1042
1044 rec.fSearchPath = path;
1046 if (*rec.fSearchPath == '/')
1047 rec.fSearchPath++;
1048 rec.fStore = &store;
1049
1050 ScanRoot(rec);
1051
1053 TClass *res_cl = store.GetResClass();
1054 void *res = store.GetResPtr();
1055
1056 if (res_member && res_cl && !member) {
1057 res_cl = (res_member->IsBasic() || res_member->IsSTLContainer()) ? nullptr : gROOT->GetClass(res_member->GetTypeName());
1058 TRealData *rdata = res_cl ? res_cl->GetRealData(res_member->GetName()) : nullptr;
1059 if (rdata) {
1060 res = (char *)res + rdata->GetThisOffset();
1061 if (res_member->IsaPointer())
1062 res = *((char **)res);
1063 } else {
1064 res = nullptr; // should never happen
1065 }
1066 }
1067
1068 if (cl)
1069 *cl = res_cl;
1070 if (member)
1071 *member = res_member;
1072 if (chld)
1073 *chld = store.GetResNumChilds();
1074
1075 // remember current restriction
1077
1078 return res;
1079}
1080
1081////////////////////////////////////////////////////////////////////////////////
1082/// Search element in hierarchy, derived from TObject
1083
1085{
1086 TClass *cl = nullptr;
1087
1088 void *obj = FindInHierarchy(path, &cl);
1089
1090 return cl && (cl->GetBaseClassOffset(TObject::Class()) == 0) ? (TObject *)obj : nullptr;
1091}
1092
1093////////////////////////////////////////////////////////////////////////////////
1094/// Get hash function for specified item
1095/// used to detect any changes in the specified object
1096
1098{
1100
1101 return !obj ? 0 : TString::Hash(obj, obj->IsA()->Size());
1102}
1103
1104////////////////////////////////////////////////////////////////////////////////
1105/// Method verifies if object can be drawn
1106
1108{
1109 TClass *obj_cl = nullptr;
1110 void *res = FindInHierarchy(path, &obj_cl);
1111 return (res != nullptr) && CanDrawClass(obj_cl);
1112}
1113
1114////////////////////////////////////////////////////////////////////////////////
1115/// Method returns true when object has childs or
1116/// one could try to expand item
1117
1119{
1120 TClass *obj_cl = nullptr;
1121 Int_t obj_chld(-1);
1122 void *res = FindInHierarchy(path, &obj_cl, nullptr, &obj_chld);
1123 return res && (obj_chld > 0);
1124}
1125
1126////////////////////////////////////////////////////////////////////////////////
1127/// Produce JSON data for specified item
1128/// For object conversion TBufferJSON is used
1129
1130Bool_t TRootSniffer::ProduceJson(const std::string &path, const std::string &options, std::string &res)
1131{
1132 if (path.empty())
1133 return kFALSE;
1134
1135 const char *path_ = path.c_str();
1136 if (*path_ == '/')
1137 path_++;
1138
1139 TUrl url;
1140 url.SetOptions(options.c_str());
1141 url.ParseOptions();
1142 Int_t compact = -1;
1143 if (url.GetValueFromOptions("compact"))
1144 compact = url.GetIntValueFromOptions("compact");
1145
1146 TClass *obj_cl = nullptr;
1147 TDataMember *member = nullptr;
1149 if (!obj_ptr || (!obj_cl && !member))
1150 return kFALSE;
1151
1152 // TODO: implement direct storage into std::string
1153 TString buf = TBufferJSON::ConvertToJSON(obj_ptr, obj_cl, compact >= 0 ? compact : 0, member ? member->GetName() : nullptr);
1154 res = buf.Data();
1155
1156 return !res.empty();
1157}
1158
1159////////////////////////////////////////////////////////////////////////////////
1160/// Execute command marked as _kind=='Command'
1161
1162Bool_t TRootSniffer::ExecuteCmd(const std::string &path, const std::string &options, std::string &res)
1163{
1164 TFolder *parent = nullptr;
1165 TObject *obj = GetItem(path.c_str(), parent, kFALSE, kFALSE);
1166
1167 const char *kind = GetItemField(parent, obj, item_prop_kind);
1168 if ((kind == 0) || (strcmp(kind, "Command") != 0)) {
1169 if (gDebug > 0)
1170 Info("ExecuteCmd", "Entry %s is not a command", path.c_str());
1171 res = "false";
1172 return kTRUE;
1173 }
1174
1175 const char *cmethod = GetItemField(parent, obj, "method");
1176 if (!cmethod || (strlen(cmethod) == 0)) {
1177 if (gDebug > 0)
1178 Info("ExecuteCmd", "Entry %s do not defines method for execution", path.c_str());
1179 res = "false";
1180 return kTRUE;
1181 }
1182
1183 // if read-only specified for the command, it is not allowed for execution
1184 if (fRestrictions.GetLast() >= 0) {
1185 FindInHierarchy(path.c_str()); // one need to call method to check access rights
1186 if (fCurrentRestrict == 1) {
1187 if (gDebug > 0)
1188 Info("ExecuteCmd", "Entry %s not allowed for specified user", path.c_str());
1189 res = "false";
1190 return kTRUE;
1191 }
1192 }
1193
1195
1196 const char *cnumargs = GetItemField(parent, obj, "_numargs");
1198 if (numargs > 0) {
1199 TUrl url;
1200 url.SetOptions(options.c_str());
1201 url.ParseOptions();
1202
1203 for (Int_t n = 0; n < numargs; n++) {
1204 TString argname = TString::Format("arg%d", n + 1);
1205 const char *argvalue = url.GetValueFromOptions(argname);
1206 if (!argvalue) {
1207 if (gDebug > 0)
1208 Info("ExecuteCmd", "For command %s argument %s not specified in options %s", path.c_str(), argname.Data(),
1209 options.c_str());
1210 res = "false";
1211 return kTRUE;
1212 }
1213
1215 argname = TString("%") + argname + TString("%");
1216 method.ReplaceAll(argname, svalue);
1217 }
1218 }
1219
1220 if (gDebug > 0)
1221 Info("ExecuteCmd", "Executing command %s method:%s", path.c_str(), method.Data());
1222
1223 TObject *item_obj = nullptr;
1224 Ssiz_t separ = method.Index("/->");
1225
1226 if (method.Index("this->") == 0) {
1227 // if command name started with this-> means method of sniffer will be executed
1228 item_obj = this;
1229 separ = 3;
1230 } else if (separ != kNPOS) {
1232 }
1233
1234 if (item_obj) {
1235 method = TString::Format("((%s*)%zu)->%s", item_obj->ClassName(), (size_t)item_obj, method.Data() + separ + 3);
1236 if (gDebug > 2)
1237 Info("ExecuteCmd", "Executing %s", method.Data());
1238 }
1239
1240 Int_t err = 0;
1241 auto v = gROOT->ProcessLineSync(method.Data(), &err);
1242 if (err == TInterpreter::kProcessing) {
1243 gInterpreter->ProcessLine(".@");
1244 if (gDebug > 0)
1245 Info("ExecuteCmd", "Unbalanced braces in %s", method.Data());
1246 res = "false";
1247 return kTRUE;
1248 }
1249
1250 res = std::to_string(v);
1251
1252 return kTRUE;
1253}
1254
1255////////////////////////////////////////////////////////////////////////////////
1256/// Produce JSON/XML for specified item
1257///
1258/// Contrary to h.json request, only fields for specified item are stored
1259
1260Bool_t TRootSniffer::ProduceItem(const std::string &path, const std::string &options, std::string &res, Bool_t asjson)
1261{
1262 TString buf; // TODO: implement direct storage into std::string
1263 if (asjson) {
1264 TRootSnifferStoreJson store(buf, options.find("compact") != std::string::npos);
1265 ScanHierarchy("top", path.c_str(), &store, kTRUE);
1266 } else {
1267 TRootSnifferStoreXml store(buf, options.find("compact") != std::string::npos);
1268 ScanHierarchy("top", path.c_str(), &store, kTRUE);
1269 }
1270 res = buf.Data();
1271 return !res.empty();
1272}
1273
1274////////////////////////////////////////////////////////////////////////////////
1275/// Produce XML data for specified item
1276///
1277/// For object conversion TBufferXML is used
1278/// Method implemented only in TRootSnifferFull class
1279
1280Bool_t TRootSniffer::ProduceXml(const std::string &/* path */, const std::string & /* options */, std::string & /* res */)
1281{
1282 return kFALSE;
1283}
1284
1285////////////////////////////////////////////////////////////////////////////////
1286/// Method replaces all kind of special symbols, which could appear in URL options
1287
1289{
1290 if (!value || (strlen(value) == 0))
1291 return TString();
1292
1293 TString res = value;
1294
1295 res.ReplaceAll("%27", "\'");
1296 res.ReplaceAll("%22", "\"");
1297 res.ReplaceAll("%3E", ">");
1298 res.ReplaceAll("%3C", "<");
1299 res.ReplaceAll("%20", " ");
1300 res.ReplaceAll("%5B", "[");
1301 res.ReplaceAll("%5D", "]");
1302 res.ReplaceAll("%3D", "=");
1303
1304 if (remove_quotes && (res.Length() > 1) && ((res[0] == '\'') || (res[0] == '\"')) &&
1305 (res[0] == res[res.Length() - 1])) {
1306 res.Remove(res.Length() - 1);
1307 res.Remove(0, 1);
1308 }
1309
1310 return res;
1311}
1312
1313////////////////////////////////////////////////////////////////////////////////
1314/// Execute command for specified object
1315///
1316/// Options include method and extra list of parameters
1317/// sniffer should be not-readonly to allow execution of the commands
1318/// reskind defines kind of result 0 - debug, 1 - json, 2 - binary
1319///
1320/// Method implemented only in TRootSnifferFull class
1321
1322Bool_t TRootSniffer::ProduceExe(const std::string & /*path*/, const std::string & /*options*/, Int_t /*reskind*/,
1323 std::string & /*res*/)
1324{
1325 return kFALSE;
1326}
1327
1328////////////////////////////////////////////////////////////////////////////////
1329/// Process several requests, packing all results into binary or JSON buffer
1330///
1331/// Input parameters should be coded in the POST block and has
1332/// individual request relative to current path, separated with '\n' symbol like
1333/// item1/root.bin\n
1334/// item2/exe.bin?method=GetList\n
1335/// item3/exe.bin?method=GetTitle\n
1336/// Request requires 'number' URL option which contains number of requested items
1337///
1338/// In case of binary request output buffer looks like:
1339///
1340/// 4bytes length + payload,
1341/// 4bytes length + payload, ...
1342///
1343/// In case of JSON request output is array with results for each item
1344/// multi.json request do not support binary requests for the items
1345
1346Bool_t TRootSniffer::ProduceMulti(const std::string &path, const std::string &options, std::string &str, Bool_t asjson)
1347{
1349 return kFALSE;
1350
1351 const char *args = (const char *)fCurrentArg->GetPostData();
1352 const char *ends = args + fCurrentArg->GetPostDataLength();
1353
1354 TUrl url;
1355 url.SetOptions(options.c_str());
1356
1357 Int_t number = 0;
1358 if (url.GetValueFromOptions("number"))
1359 number = url.GetIntValueFromOptions("number");
1360
1361 // binary buffers required only for binary requests, json output can be produced as is
1362 std::vector<std::string> mem;
1363
1364 if (asjson)
1365 str = "[";
1366
1367 for (Int_t n = 0; n < number; n++) {
1368 const char *next = args;
1369 while ((next < ends) && (*next != '\n'))
1370 next++;
1371 if (next == ends) {
1372 Error("ProduceMulti", "Not enough arguments in POST block");
1373 break;
1374 }
1375
1376 std::string file1(args, next - args);
1377 args = next + 1;
1378
1379 std::string path1, opt1;
1380
1381 // extract options
1382 std::size_t pos = file1.find_first_of('?');
1383 if (pos != std::string::npos) {
1384 opt1 = file1.substr(pos + 1, file1.length() - pos);
1385 file1.resize(pos);
1386 }
1387
1388 // extract extra path
1389 pos = file1.find_last_of('/');
1390 if (pos != std::string::npos) {
1391 path1 = file1.substr(0, pos);
1392 file1.erase(0, pos + 1);
1393 }
1394
1395 if (!path.empty())
1396 path1 = path + "/" + path1;
1397
1398 std::string res1;
1399
1400 // produce next item request
1402
1403 if (asjson) {
1404 if (n > 0)
1405 str.append(", ");
1406 if (res1.empty())
1407 str.append("null");
1408 else
1409 str.append(res1);
1410 } else {
1411 mem.emplace_back(std::move(res1));
1412 }
1413 }
1414
1415 if (asjson) {
1416 str.append("]");
1417 } else {
1418 Int_t length = 0;
1419 for (unsigned n = 0; n < mem.size(); n++)
1420 length += 4 + mem[n].length();
1421 str.resize(length);
1422 char *curr = (char *)str.data();
1423 for (unsigned n = 0; n < mem.size(); n++) {
1424 Long_t l = mem[n].length();
1425 *curr++ = (char)(l & 0xff);
1426 l = l >> 8;
1427 *curr++ = (char)(l & 0xff);
1428 l = l >> 8;
1429 *curr++ = (char)(l & 0xff);
1430 l = l >> 8;
1431 *curr++ = (char)(l & 0xff);
1432 if (!mem[n].empty())
1433 memcpy(curr, mem[n].data(), mem[n].length());
1434 curr += mem[n].length();
1435 }
1436 }
1437
1438 return kTRUE;
1439}
1440
1441////////////////////////////////////////////////////////////////////////////////
1442/// Produce binary data for specified item
1443///
1444/// If "zipped" option specified in query, buffer will be compressed
1445///
1446/// Implemented only in TRootSnifferFull class
1447
1448Bool_t TRootSniffer::ProduceBinary(const std::string & /*path*/, const std::string & /*query*/, std::string & /*res*/)
1449{
1450 return kFALSE;
1451}
1452
1453////////////////////////////////////////////////////////////////////////////////
1454/// Produce ROOT file for specified item
1455///
1456/// Implemented only in TRootSnifferFull class
1457
1458Bool_t TRootSniffer::ProduceRootFile(const std::string & /*path*/, const std::string & /*query*/, std::string & /*res*/)
1459{
1460 return kFALSE;
1461}
1462
1463////////////////////////////////////////////////////////////////////////////////
1464/// Method to produce image from specified object
1465///
1466/// Parameters:
1467///
1468/// kind - image kind TImage::kPng, TImage::kJpeg, TImage::kGif
1469/// path - path to object
1470/// options - extra options
1471///
1472/// By default, image 300x200 is produced
1473/// In options string one could provide following parameters:
1474///
1475/// w - image width
1476/// h - image height
1477/// opt - draw options
1478///
1479/// For instance:
1480///
1481/// http://localhost:8080/Files/hsimple.root/hpx/get.png?w=500&h=500&opt=lego1
1482///
1483/// Returns produced image in the res string
1484///
1485/// Method implemented only in TRootSnifferFull class
1486
1487Bool_t TRootSniffer::ProduceImage(Int_t /*kind*/, const std::string & /*path*/, const std::string & /*options*/, std::string & /*res*/)
1488{
1489 return kFALSE;
1490}
1491
1492////////////////////////////////////////////////////////////////////////////////
1493/// Invokes TRootSniffer::ProduceIamge, converting kind into TImage::EImageFileTypes type
1494
1495Bool_t TRootSniffer::CallProduceImage(const std::string &/*kind*/, const std::string &/*path*/, const std::string &/*options*/, std::string &/*res*/)
1496{
1497 return kFALSE;
1498}
1499
1500////////////////////////////////////////////////////////////////////////////////
1501/// Method produce different kind of data out of object
1502///
1503/// @param path specifies object or object member
1504/// @param file can be:
1505///
1506/// * "root.bin" - binary data
1507/// * "root.png" - png image
1508/// * "root.jpeg" - jpeg image
1509/// * "root.gif" - gif image
1510/// * "root.xml" - xml representation
1511/// * "root.json" - json representation
1512/// * "file.root" - ROOT file with stored object
1513/// * "exe.json" - method execution with json reply
1514/// * "exe.bin" - method execution with binary reply
1515/// * "exe.txt" - method execution with debug output
1516/// * "cmd.json" - execution of registered commands
1517///
1518/// @param options specific options
1519/// @param res returns result - binary or text.
1520
1521Bool_t TRootSniffer::Produce(const std::string &path, const std::string &file, const std::string &options, std::string &res)
1522{
1523 if (file.empty())
1524 return kFALSE;
1525
1526 if (file == "root.bin")
1527 return ProduceBinary(path, options, res);
1528
1529 if (file == "root.png")
1530 return CallProduceImage("png", path, options, res);
1531
1532 if (file == "root.jpeg")
1533 return CallProduceImage("jpeg", path, options, res);
1534
1535 if (file == "root.gif")
1536 return CallProduceImage("gif", path, options, res);
1537
1538 if (file == "exe.bin")
1539 return ProduceExe(path, options, 2, res);
1540
1541 if (file == "root.xml")
1542 return ProduceXml(path, options, res);
1543
1544 if (file == "root.json")
1545 return ProduceJson(path, options, res);
1546
1547 if (file == "file.root")
1548 return ProduceRootFile(path, options, res);
1549
1550 // used for debugging
1551 if (file == "exe.txt")
1552 return ProduceExe(path, options, 0, res);
1553
1554 if (file == "exe.json")
1555 return ProduceExe(path, options, 1, res);
1556
1557 if (file == "cmd.json")
1558 return ExecuteCmd(path, options, res);
1559
1560 if (file == "item.json")
1561 return ProduceItem(path, options, res, kTRUE);
1562
1563 if (file == "item.xml")
1564 return ProduceItem(path, options, res, kFALSE);
1565
1566 if (file == "multi.bin")
1567 return ProduceMulti(path, options, res, kFALSE);
1568
1569 if (file == "multi.json")
1570 return ProduceMulti(path, options, res, kTRUE);
1571
1572 return kFALSE;
1573}
1574
1575////////////////////////////////////////////////////////////////////////////////
1576/// Return item from the subfolders structure
1577
1579{
1581 if (!httpfold) return nullptr;
1582
1583 parent = httpfold;
1584 TObject *obj = httpfold;
1585
1586 if (!fullname)
1587 return httpfold;
1588
1589 // when full path started not with slash, "Objects" subfolder is appended
1590 TString path = fullname;
1591 if (within_objects && ((path.Length() == 0) || (path[0] != '/')))
1592 path = fObjectsPath + "/" + path;
1593
1594 TString tok;
1595 Ssiz_t from = 0;
1596
1597 while (path.Tokenize(tok, from, "/")) {
1598 if (tok.Length() == 0)
1599 continue;
1600
1601 TFolder *fold = dynamic_cast<TFolder *>(obj);
1602 if (!fold)
1603 return nullptr;
1604
1605 TIter iter(fold->GetListOfFolders());
1606 while ((obj = iter()) != nullptr) {
1607 if (IsItemField(obj))
1608 continue;
1609 if (tok.CompareTo(obj->GetName()) == 0)
1610 break;
1611 }
1612
1613 if (!obj) {
1614 if (!force)
1615 return nullptr;
1616 obj = fold->AddFolder(tok, "sub-folder");
1617 obj->SetBit(kCanDelete);
1618 }
1619
1620 parent = fold;
1621 }
1622
1623 return obj;
1624}
1625
1626////////////////////////////////////////////////////////////////////////////////
1627/// Creates subfolder where objects can be registered
1628
1630{
1631 TFolder *parent = nullptr;
1632
1633 return dynamic_cast<TFolder *>(GetItem(subfolder, parent, force));
1634}
1635
1636////////////////////////////////////////////////////////////////////////////////
1637/// Register object in subfolder structure
1638///
1639/// @param obj pointer to TObject
1640/// @param subfolder can have many levels like:
1641///
1642/// TRootSniffer* sniff = new TRootSniffer("sniff");
1643/// sniff->RegisterObject("my/sub/subfolder", h1);
1644///
1645/// Such objects can be later found in "Objects" folder of sniffer like
1646///
1647/// auto h1 = sniff->FindTObjectInHierarchy("/Objects/my/sub/subfolder/h1");
1648///
1649/// If subfolder name starts with '/', object will be registered starting from top folder.
1650///
1651/// One could provide additional fields for registered objects
1652/// For instance, setting "_more" field to true let browser
1653/// explore objects members. For instance:
1654///
1655/// TEvent* ev = new TEvent("ev");
1656/// sniff->RegisterObject("Events", ev);
1657/// sniff->SetItemField("Events/ev", "_more", "true");
1658
1660{
1662 if (!f)
1663 return kFALSE;
1664
1665 // If object will be destroyed, it will be removed from the folders automatically
1666 obj->SetBit(kMustCleanup);
1667
1668 f->Add(obj);
1669
1670 return kTRUE;
1671}
1672
1673////////////////////////////////////////////////////////////////////////////////
1674/// Unregister (remove) object from folders structures
1675///
1676/// Folder itself will remain even when it will be empty
1677
1679{
1680 if (!obj)
1681 return kTRUE;
1682
1684
1685 if (!topf) {
1686 Error("UnregisterObject", "Not found top folder");
1687 return kFALSE;
1688 }
1689
1690 // TODO - probably we should remove all set properties as well
1691 topf->RecursiveRemove(obj);
1692
1693 return kTRUE;
1694}
1695
1696////////////////////////////////////////////////////////////////////////////////
1697/// Create item element
1698
1699Bool_t TRootSniffer::CreateItem(const char *fullname, const char *title)
1700{
1701 TFolder *f = GetSubFolder(fullname, kTRUE);
1702 if (!f)
1703 return kFALSE;
1704
1705 if (title)
1706 f->SetTitle(title);
1707
1708 return kTRUE;
1709}
1710
1711////////////////////////////////////////////////////////////////////////////////
1712/// Return true when object is TNamed with kItemField bit set
1713///
1714/// such objects used to keep field values for item
1715
1717{
1718 return (obj != nullptr) && (obj->IsA() == TNamed::Class()) && obj->TestBit(kItemField);
1719}
1720
1721////////////////////////////////////////////////////////////////////////////////
1722/// Set or get field for the child
1723///
1724/// each field coded as TNamed object, placed after chld in the parent hierarchy
1725
1727{
1728 if (!parent)
1729 return kFALSE;
1730
1731 if (!chld) {
1732 Info("AccessField", "Should be special case for top folder, support later");
1733 return kFALSE;
1734 }
1735
1736 TIter iter(parent->GetListOfFolders());
1737
1738 Bool_t find = kFALSE, last_find = kFALSE;
1739 // this is special case of top folder - fields are on very top
1740 if (parent == chld)
1741 last_find = find = kTRUE;
1742
1743 TNamed *curr = nullptr;
1744 while (auto obj = iter()) {
1745 if (IsItemField(obj)) {
1746 if (last_find && obj->GetName() && !strcmp(name, obj->GetName()))
1747 curr = (TNamed *)obj;
1748 } else {
1749 last_find = (obj == chld);
1750 if (last_find)
1751 find = kTRUE;
1752 if (find && !last_find)
1753 break; // no need to continue
1754 }
1755 }
1756
1757 // object must be in childs list
1758 if (!find)
1759 return kFALSE;
1760
1761 if (only_get) {
1762 *only_get = curr;
1763 return curr != nullptr;
1764 }
1765
1766 if (curr) {
1767 if (value) {
1768 curr->SetTitle(value);
1769 } else {
1770 parent->Remove(curr);
1771 delete curr;
1772 }
1773 return kTRUE;
1774 }
1775
1776 curr = new TNamed(name, value);
1777 curr->SetBit(kItemField);
1778
1779 if (last_find) {
1780 // object is on last place, therefore just add property
1781 parent->Add(curr);
1782 return kTRUE;
1783 }
1784
1785 // only here we do dynamic cast to the TList to use AddAfter
1786 TList *lst = dynamic_cast<TList *>(parent->GetListOfFolders());
1787 if (!lst) {
1788 Error("AccessField", "Fail cast to TList");
1789 return kFALSE;
1790 }
1791
1792 if (parent == chld)
1793 lst->AddFirst(curr);
1794 else
1795 lst->AddAfter(chld, curr);
1796
1797 return kTRUE;
1798}
1799
1800////////////////////////////////////////////////////////////////////////////////
1801/// Set field for specified item
1802
1803Bool_t TRootSniffer::SetItemField(const char *fullname, const char *name, const char *value)
1804{
1805 if (!fullname || !name)
1806 return kFALSE;
1807
1808 TFolder *parent = nullptr;
1809 TObject *obj = GetItem(fullname, parent);
1810
1811 if (!parent || !obj)
1812 return kFALSE;
1813
1814 if (strcmp(name, item_prop_title) == 0) {
1815 TNamed *n = dynamic_cast<TNamed *>(obj);
1816 if (n) {
1817 n->SetTitle(value);
1818 return kTRUE;
1819 }
1820 }
1821
1822 return AccessField(parent, obj, name, value);
1823}
1824
1825////////////////////////////////////////////////////////////////////////////////
1826/// Return field for specified item
1827
1828const char *TRootSniffer::GetItemField(TFolder *parent, TObject *obj, const char *name)
1829{
1830 if (!parent || !obj || !name)
1831 return nullptr;
1832
1833 TNamed *field = nullptr;
1834
1835 if (!AccessField(parent, obj, name, nullptr, &field))
1836 return nullptr;
1837
1838 return field ? field->GetTitle() : nullptr;
1839}
1840
1841////////////////////////////////////////////////////////////////////////////////
1842/// Return field for specified item
1843
1844const char *TRootSniffer::GetItemField(const char *fullname, const char *name)
1845{
1846 if (!fullname)
1847 return nullptr;
1848
1849 TFolder *parent = nullptr;
1850 TObject *obj = GetItem(fullname, parent);
1851
1852 return GetItemField(parent, obj, name);
1853}
1854
1855////////////////////////////////////////////////////////////////////////////////
1856/// Register command which can be executed from web interface
1857///
1858/// As method one typically specifies string, which is executed with
1859/// gROOT->ProcessLine() method. For instance:
1860///
1861/// serv->RegisterCommand("Invoke","InvokeFunction()");
1862///
1863/// Or one could specify any method of the object which is already registered
1864/// to the server. For instance:
1865///
1866/// serv->Register("/", hpx);
1867/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1868///
1869/// Here symbols '/->' separates item name from method to be executed
1870///
1871/// One could specify additional arguments in the command with
1872/// syntax like %arg1%, %arg2% and so on. For example:
1873///
1874/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1875/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1876///
1877/// Such parameter(s) will be requested when command clicked in the browser.
1878///
1879/// Once command is registered, one could specify icon which will appear in the browser:
1880///
1881/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1882///
1883/// One also can set extra property '_fastcmd', that command appear as
1884/// tool button on the top of the browser tree:
1885///
1886/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1887///
1888/// Or it is equivalent to specifying extra argument when register command:
1889///
1890/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1891
1892Bool_t TRootSniffer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1893{
1894 CreateItem(cmdname, TString::Format("command %s", method).Data());
1895 SetItemField(cmdname, "_kind", "Command");
1896 if (icon) {
1897 if (strncmp(icon, "button;", 7) == 0) {
1898 SetItemField(cmdname, "_fastcmd", "true");
1899 icon += 7;
1900 }
1901 if (*icon)
1902 SetItemField(cmdname, "_icon", icon);
1903 }
1904 SetItemField(cmdname, "method", method);
1905 Int_t numargs = 0;
1906 do {
1907 TString nextname = TString::Format("%sarg%d%s", "%", numargs + 1, "%");
1908 if (strstr(method, nextname.Data()) == nullptr)
1909 break;
1910 numargs++;
1911 } while (numargs < 100);
1912 if (numargs > 0)
1913 SetItemField(cmdname, "_numargs", TString::Format("%d", numargs));
1914
1915 return kTRUE;
1916}
#define f(i)
Definition RSha256.hxx:104
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
unsigned long ULong_t
Unsigned long integer 4 bytes (unsigned long). Size depends on architecture.
Definition RtypesCore.h:69
long Long_t
Signed long integer 4 bytes (long). Size depends on architecture.
Definition RtypesCore.h:68
constexpr Bool_t kFALSE
Definition RtypesCore.h:108
constexpr Ssiz_t kNPOS
The equivalent of std::string::npos for the ROOT class TString.
Definition RtypesCore.h:131
constexpr Bool_t kTRUE
Definition RtypesCore.h:107
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
@ kIsEnum
Definition TDictionary.h:68
@ kIsAbstract
Definition TDictionary.h:71
@ kIsStatic
Definition TDictionary.h:80
@ kIsUnion
Definition TDictionary.h:67
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
char name[80]
Definition TGX11.cxx:148
#define gInterpreter
Int_t gDebug
Global variable setting the debug level. Set to 0 to disable, increase it in steps of 1 to increase t...
Definition TROOT.cxx:777
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:417
const char * item_prop_typename
const char * item_prop_realname
const char * item_prop_user
const char * item_prop_autoload
const char * item_prop_more
const char * item_prop_kind
const char * item_prop_arraydim
const char * item_prop_title
const char * item_prop_rootversion
const char * item_prop_hidden
#define R__LOCKGUARD(mutex)
static TString ConvertToJSON(const TObject *obj, Int_t compact=0, const char *member_name=nullptr)
Converts object, inherited from TObject class, to JSON string Lower digit of compact parameter define...
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
void BuildRealData(void *pointer=nullptr, Bool_t isTransient=kFALSE)
Build a full list of persistent data members.
Definition TClass.cxx:2038
TList * GetListOfRealData() const
Definition TClass.h:468
Int_t GetBaseClassOffset(const TClass *toBase, void *address=nullptr, bool isDerivedObject=true)
Definition TClass.cxx:2812
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6191
TRealData * GetRealData(const char *name) const
Return pointer to TRealData element with name "name".
Definition TClass.cxx:3565
Collection abstract base class.
Definition TCollection.h:65
static TClass * Class()
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
All ROOT classes may have RTTI (run time type identification) support added.
Definition TDataMember.h:31
static TClass * Class()
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual TList * GetList() const
Definition TDirectory.h:223
virtual TList * GetListOfKeys() const
Definition TDirectory.h:224
<div class="legacybox"><h2>Legacy Code</h2> TFolder is a legacy interface: there will be no bug fixes...
Definition TFolder.h:30
TCollection * GetListOfFolders() const
Definition TFolder.h:55
virtual void Add(TObject *obj)
Add object to this folder. obj must be a TObject or a TFolder.
Definition TFolder.cxx:165
static TClass * Class()
virtual void Remove(TObject *obj)
Remove object from this folder. obj must be a TObject or a TFolder.
Definition TFolder.cxx:456
Contains arguments for single HTTP call.
const char * GetUserName() const
return authenticated user name (0 - when no authentication)
const void * GetPostData() const
return pointer on posted with request data
Long_t GetPostDataLength() const
return length of posted with request data
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
T * ReadObject()
To read an object (non deriving from TObject) from the file.
Definition TKey.h:105
virtual const char * GetClassName() const
Definition TKey.h:77
Short_t GetCycle() const
Return cycle number associated to this key.
Definition TKey.cxx:611
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition TKey.cxx:792
A doubly linked list.
Definition TList.h:38
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:708
void Add(TObject *obj) override
Definition TList.h:81
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
TNamed()
Definition TNamed.h:38
static TClass * Class()
An array of TObjects.
Definition TObjArray.h:31
Collectable string class.
Definition TObjString.h:28
Mother of all ROOT objects.
Definition TObject.h:42
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:459
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:204
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:224
static TClass * Class()
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:885
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:546
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1095
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:504
virtual TClass * IsA() const
Definition TObject.h:248
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:71
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition TObject.h:73
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1069
The TRealData class manages the effective list of all data members for a given class.
Definition TRealData.h:30
Structure used to scan hierarchies of ROOT objects.
TString fItemName
! name of current item
Int_t fLevel
! current level of hierarchy
Int_t fRestriction
! restriction 0 - default, 1 - read-only, 2 - full access
Bool_t CanExpandItem()
Returns true when item can be expanded.
TRootSnifferStore * fStore
! object to store results
virtual ~TRootSnifferScanRec()
destructor
void SetField(const char *name, const char *value, Bool_t with_quotes=kTRUE)
Set item field only when creating is specified.
void CloseNode()
Close started node.
Bool_t CanSetFields() const
return true when fields could be set to the hierarchy item
UInt_t fMask
! defines operation kind
void MakeItemName(const char *objname, TString &itemname)
Construct item name, using object name as basis.
Bool_t IsReadyForResult() const
Checks if result will be accepted.
Bool_t SetResult(void *obj, TClass *cl, TDataMember *member=nullptr)
Obsolete, use SetFoundResult instead.
Bool_t fHasMore
! indicates that potentially there are more items can be found
@ kSearch
search for specified item (only objects and collections)
@ kOnlyFields
if set, only fields for specified item will be set (but all fields)
@ kExpand
expand of specified item - allowed to scan object members
@ kCheckChilds
check if there childs, very similar to search
@ kScan
normal scan of hierarchy
@ kActions
mask for actions, only actions copied to child rec
Bool_t IsReadOnly(Bool_t dflt=kTRUE)
Returns read-only flag for current item.
Bool_t GoInside(TRootSnifferScanRec &super, TObject *obj, const char *obj_name=nullptr, TRootSniffer *sniffer=nullptr)
Method verifies if new level of hierarchy should be started with provided object.
void BeforeNextChild()
Indicates that new child for current element will be started.
TRootSnifferScanRec * fParent
! pointer on parent record
void SetRootClass(TClass *cl)
Mark item with ROOT class and correspondent streamer info.
void CreateNode(const char *_node_name)
Starts new node, must be closed at the end.
Int_t fNumChilds
! number of childs
Int_t fNumFields
! number of fields
Bool_t fNodeStarted
! indicate if node was started
Bool_t SetFoundResult(void *obj, TClass *cl, TDataMember *member=nullptr)
Set found element with class and datamember (optional)
const char * fSearchPath
! current path searched
Int_t Depth() const
Returns depth of hierarchy.
TList fItemsNames
! list of created items names, need to avoid duplication
Bool_t Done() const
Method indicates that scanning can be interrupted while result is set.
void BuildFullName(TString &buf, TRootSnifferScanRec *prnt=nullptr)
Produces full name for the current item.
TRootSnifferScanRec()
constructor
Storage of hierarchy scan in TRootSniffer in JSON format.
Storage of hierarchy scan in TRootSniffer in XML format.
Abstract interface for storage of hierarchy scan in TRootSniffer.
Int_t GetResNumChilds() const
TDataMember * GetResMember() const
virtual void CreateNode(Int_t, const char *)
void * GetResPtr() const
void SetResult(void *_res, TClass *_rescl, TDataMember *_resmemb, Int_t _res_chld, Int_t restr=0)
set pointer on found element, class and number of childs
virtual void CloseNode(Int_t, Int_t)
TClass * GetResClass() const
virtual void BeforeNextChild(Int_t, Int_t, Int_t)
Int_t GetResRestrict() const
virtual void SetField(Int_t, const char *, const char *, Bool_t)
Sniffer of ROOT objects, data provider for THttpServer.
void ScanObjectMembers(TRootSnifferScanRec &rec, TClass *cl, char *ptr)
scan object data members some members like enum or static members will be excluded
const char * GetAutoLoad() const
return name of configured autoload scripts (or 0)
TString fObjectsPath
! default path for registered objects
void ScanHierarchy(const char *topname, const char *path, TRootSnifferStore *store, Bool_t only_fields=kFALSE)
Method scans normal objects, registered in ROOT.
TRootSniffer(const char *name="sniff", const char *objpath="Objects")
constructor
TList fRestrictions
! list of restrictions for different locations
Bool_t RegisterObject(const char *subfolder, TObject *obj)
Register object in subfolder structure.
virtual void ScanObjectChilds(TRootSnifferScanRec &rec, TObject *obj)
scans object childs (if any) here one scans collection, branches, trees and so on
TString fCurrentAllowedMethods
! list of allowed methods, extracted when analyzed object restrictions
virtual Bool_t HasStreamerInfo() const
Bool_t UnregisterObject(TObject *obj)
Unregister (remove) object from folders structures.
virtual void ScanKeyProperties(TRootSnifferScanRec &rec, TKey *key, TObject *&obj, TClass *&obj_class)
Scans TKey properties in special cases load objects from the file.
Bool_t CreateItem(const char *fullname, const char *title)
Create item element.
virtual Bool_t ExecuteCmd(const std::string &path, const std::string &options, std::string &res)
Execute command marked as _kind=='Command'.
Bool_t HasRestriction(const char *item_name)
Made fast check if item with specified name is in restriction list If returns true,...
virtual void ScanObjectProperties(TRootSnifferScanRec &rec, TObject *obj)
Scans object properties here such fields as _autoload or _icon properties depending on class or objec...
virtual void ScanRoot(TRootSnifferScanRec &rec)
scan complete ROOT objects hierarchy For the moment it includes objects in gROOT directory and list o...
Bool_t Produce(const std::string &path, const std::string &file, const std::string &options, std::string &res)
Method produce different kind of data out of object.
virtual Bool_t ProduceJson(const std::string &path, const std::string &options, std::string &res)
Produce JSON data for specified item For object conversion TBufferJSON is used.
void CreateOwnTopFolder()
Create own TFolder structures independent from gROOT This allows to have many independent TRootSniffe...
virtual Bool_t ProduceExe(const std::string &path, const std::string &options, Int_t reskind, std::string &res)
Execute command for specified object.
virtual Bool_t ProduceXml(const std::string &path, const std::string &options, std::string &res)
Produce XML data for specified item.
TString DecodeUrlOptionValue(const char *value, Bool_t remove_quotes=kTRUE)
Method replaces all kind of special symbols, which could appear in URL options.
THttpCallArg * SetCurrentCallArg(THttpCallArg *arg)
set current http arguments, which then used in different process methods For instance,...
void Restrict(const char *path, const char *options)
Restrict access to the specified location.
virtual ULong_t GetItemHash(const char *itemname)
Get hash function for specified item used to detect any changes in the specified object.
Bool_t fReadOnly
! indicate if sniffer allowed to change ROOT structures - like read objects from file
void SetScanGlobalDir(Bool_t on=kTRUE)
When enabled (default), sniffer scans gROOT for files, canvases, histograms.
TObject * GetItem(const char *fullname, TFolder *&parent, Bool_t force=kFALSE, Bool_t within_objects=kTRUE)
Return item from the subfolders structure.
THttpCallArg * fCurrentArg
! current http arguments (if any)
virtual Bool_t ProduceImage(Int_t kind, const std::string &path, const std::string &options, std::string &res)
Method to produce image from specified object.
TObject * FindTObjectInHierarchy(const char *path)
Search element in hierarchy, derived from TObject.
void SetAutoLoad(const char *scripts="")
When specified, _autoload attribute will be always add to top element of h.json/h....
virtual void * FindInHierarchy(const char *path, TClass **cl=nullptr, TDataMember **member=nullptr, Int_t *chld=nullptr)
Search element with specified path Returns pointer on element Optionally one could obtain element cla...
virtual Bool_t ProduceItem(const std::string &path, const std::string &options, std::string &res, Bool_t asjson=kTRUE)
Produce JSON/XML for specified item.
Int_t CheckRestriction(const char *item_name)
Checked if restriction is applied to the item full_item_name should have full path to the item.
virtual Bool_t CallProduceImage(const std::string &kind, const std::string &path, const std::string &options, std::string &res)
Invokes TRootSniffer::ProduceIamge, converting kind into TImage::EImageFileTypes type.
Bool_t IsItemField(TObject *obj) const
Return true when object is TNamed with kItemField bit set.
virtual ~TRootSniffer()
destructor
virtual Bool_t CanDrawClass(TClass *)
Int_t fCurrentRestrict
! current restriction for last-found object
TFolder * GetTopFolder(Bool_t force=kFALSE)
Returns top TFolder instance for the sniffer.
const char * GetItemField(TFolder *parent, TObject *item, const char *name)
Return field for specified item.
std::unique_ptr< TFolder > fTopFolder
! own top TFolder object, used for registering objects
Bool_t CanExploreItem(const char *path)
Method returns true when object has childs or one could try to expand item.
Bool_t SetItemField(const char *fullname, const char *name, const char *value)
Set field for specified item.
Int_t WithCurrentUserName(const char *option)
return 2 when option match to current user name return 1 when option==all return 0 when option does n...
Bool_t RegisterCommand(const char *cmdname, const char *method, const char *icon)
Register command which can be executed from web interface.
TString fAutoLoad
! scripts names, which are add as _autoload parameter to h.json request
Bool_t IsScanGlobalDir() const
Returns true when sniffer allowed to scan global directories.
virtual Bool_t ProduceBinary(const std::string &path, const std::string &options, std::string &res)
Produce binary data for specified item.
TFolder * GetSubFolder(const char *foldername, Bool_t force=kFALSE)
Creates subfolder where objects can be registered.
virtual Bool_t ProduceRootFile(const std::string &path, const std::string &options, std::string &res)
Produce ROOT file for specified item.
void ScanCollection(TRootSnifferScanRec &rec, TCollection *lst, const char *foldername=nullptr, TCollection *keys_lst=nullptr)
Scan collection content.
Bool_t AccessField(TFolder *parent, TObject *item, const char *name, const char *value, TNamed **only_get=nullptr)
Set or get field for the child.
virtual Bool_t ProduceMulti(const std::string &path, const std::string &options, std::string &res, Bool_t asjson=kTRUE)
Process several requests, packing all results into binary or JSON buffer.
Bool_t CanDrawItem(const char *path)
Method verifies if object can be drawn.
virtual Int_t GetLast() const
Returns index of last object in collection.
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
const char * Data() const
Definition TString.h:384
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:713
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition TString.cxx:2270
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition TString.cxx:684
TString & Remove(Ssiz_t pos)
Definition TString.h:694
TString & Append(const char *cs)
Definition TString.h:581
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2384
This class represents a WWW compatible URL.
Definition TUrl.h:33
const Int_t n
Definition legend1.C:16
TCanvas * slash()
Definition slash.C:1
TLine l
Definition textangle.C:4