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 "TFolder.h"
20#include "TClass.h"
21#include "TRealData.h"
22#include "TDataMember.h"
23#include "TDataType.h"
24#include "TObjString.h"
25#include "TObjArray.h"
26#include "TUrl.h"
27#include "TVirtualMutex.h"
28#include "TRootSnifferStore.h"
29#include "THttpCallArg.h"
30
31#include <cstdlib>
32#include <memory>
33#include <vector>
34#include <cstring>
35
36const char *item_prop_kind = "_kind";
37const char *item_prop_more = "_more";
38const char *item_prop_title = "_title";
39const char *item_prop_hidden = "_hidden";
40const char *item_prop_typename = "_typename";
41const char *item_prop_arraydim = "_arraydim";
42const char *item_prop_realname = "_realname"; // real object name
43const char *item_prop_user = "_username";
44const char *item_prop_autoload = "_autoload";
45const char *item_prop_rootversion = "_root_version";
46
47/** \class TRootSnifferScanRec
48\ingroup http
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\ingroup http
400
401Sniffer of ROOT objects, data provider for THttpServer
402
403Provides methods to scan different structures like folders,
404directories, files and collections. Can locate objects (or its data member) per name.
405Can be extended to application-specific classes.
406
407Normally TRootSnifferFull class is used which able to access data from trees, canvases, histograms.
408*/
409
410
411////////////////////////////////////////////////////////////////////////////////
412/// constructor
413
414TRootSniffer::TRootSniffer(const char *name, const char *objpath)
415 : TNamed(name, "sniffer of root objects"), fObjectsPath(objpath)
416{
418}
419
420////////////////////////////////////////////////////////////////////////////////
421/// destructor
422
426
427////////////////////////////////////////////////////////////////////////////////
428/// set current http arguments, which then used in different process methods
429/// For instance, if user authorized with some user name,
430/// depending from restrictions some objects will be invisible
431/// or user get full access to the element
432/// Returns previous argument which was set before
433
435{
436 auto res = fCurrentArg;
437 fCurrentArg = arg;
440 return res;
441}
442
443////////////////////////////////////////////////////////////////////////////////
444/// Restrict access to the specified location
445///
446/// Hides or provides read-only access to different parts of the hierarchy
447/// Restriction done base on user-name specified with http requests
448/// Options can be specified in URL style (separated with &)
449/// Following parameters can be specified:
450///
451/// visible = [all|user(s)] - make item visible for all users or only specified user
452/// hidden = [all|user(s)] - make item hidden from all users or only specified user
453/// readonly = [all|user(s)] - make item read-only for all users or only specified user
454/// allow = [all|user(s)] - make full access for all users or only specified user
455/// allow_method = method(s) - allow method(s) execution even when readonly flag specified for the object
456///
457/// Like make command seen by all but can be executed only by admin
458///
459/// sniff->Restrict("/CmdReset","allow=admin");
460///
461/// Or fully hide command from guest account
462///
463/// sniff->Restrict("/CmdRebin","hidden=guest");
464
465void TRootSniffer::Restrict(const char *path, const char *options)
466{
467 const char *rslash = strrchr(path, '/');
468 if (rslash)
469 rslash++;
470 if (!rslash || (*rslash == 0))
471 rslash = path;
472
473 fRestrictions.Add(new TNamed(rslash, TString::Format("%s%s%s", path, "%%%", options).Data()));
474}
475
476////////////////////////////////////////////////////////////////////////////////
477/// When specified, _autoload attribute will be always add
478/// to top element of h.json/h.hml requests
479/// Used to instruct browser automatically load special code
480
482{
483 fAutoLoad = scripts ? scripts : "";
484}
485
486////////////////////////////////////////////////////////////////////////////////
487/// return name of configured autoload scripts (or 0)
488
489const char *TRootSniffer::GetAutoLoad() const
490{
491 return fAutoLoad.Length() > 0 ? fAutoLoad.Data() : nullptr;
492}
493
494////////////////////////////////////////////////////////////////////////////////
495/// Made fast check if item with specified name is in restriction list
496/// If returns true, requires precise check with CheckRestriction() method
497
499{
500 if (!item_name || (*item_name == 0) || !fCurrentArg)
501 return kFALSE;
502
503 return fRestrictions.FindObject(item_name) != nullptr;
504}
505
506////////////////////////////////////////////////////////////////////////////////
507/// return 2 when option match to current user name
508/// return 1 when option==all
509/// return 0 when option does not match user name
510
512{
513 const char *username = fCurrentArg ? fCurrentArg->GetUserName() : nullptr;
514
515 if (!username || !option || (*option == 0))
516 return 0;
517
518 if (strcmp(option, "all") == 0)
519 return 1;
520
521 if (strcmp(username, option) == 0)
522 return 2;
523
524 if (strstr(option, username) == 0)
525 return -1;
526
528
529 Bool_t find = arr->FindObject(username) != nullptr;
530
531 delete arr;
532
533 return find ? 2 : -1;
534}
535
536////////////////////////////////////////////////////////////////////////////////
537/// Checked if restriction is applied to the item full_item_name
538/// should have full path to the item. Returns:
539///
540/// * -1 - object invisible, cannot be accessed or listed
541/// * 0 - no explicit restrictions, use default
542/// * 1 - read-only access
543/// * 2 - full access
544
546{
547 if (!full_item_name || (*full_item_name == 0))
548 return 0;
549
550 const char *item_name = strrchr(full_item_name, '/');
551 if (item_name)
552 item_name++;
553 if (!item_name || (*item_name == 0))
555
556 TString pattern1 = TString("*/") + item_name + "%%%";
558
559 const char *options = nullptr;
560 TIter iter(&fRestrictions);
561
562 while (auto obj = iter()) {
563 const char *title = obj->GetTitle();
564
565 if (strstr(title, pattern1.Data()) == title) {
566 options = title + pattern1.Length();
567 break;
568 }
569 if (strstr(title, pattern2.Data()) == title) {
570 options = title + pattern2.Length();
571 break;
572 }
573 }
574
575 if (!options)
576 return 0;
577
578 TUrl url;
579 url.SetOptions(options);
580 url.ParseOptions();
581
582 Int_t can_see =
583 WithCurrentUserName(url.GetValueFromOptions("visible")) - WithCurrentUserName(url.GetValueFromOptions("hidden"));
584
586 WithCurrentUserName(url.GetValueFromOptions("allow")) - WithCurrentUserName(url.GetValueFromOptions("readonly"));
587
588 if (can_access > 0)
589 return 2; // first of all, if access enabled, provide it
590 if (can_see < 0)
591 return -1; // if object to be hidden, do it
592
593 const char *methods = url.GetValueFromOptions("allow_method");
594 if (methods)
596
597 if (can_access < 0)
598 return 1; // read-only access
599
600 return 0; // default behavior
601}
602
603////////////////////////////////////////////////////////////////////////////////
604/// scan object data members
605/// some members like enum or static members will be excluded
606
608{
609 if (!cl || !ptr || rec.Done())
610 return;
611
612 // ensure that real class data (including parents) exists
613 if (!(cl->Property() & kIsAbstract))
614 cl->BuildRealData();
615
616 // scan only real data
617 TIter iter(cl->GetListOfRealData());
618 while (auto obj = iter()) {
619 TRealData *rdata = dynamic_cast<TRealData *>(obj);
620 if (!rdata || strchr(rdata->GetName(), '.'))
621 continue;
622
623 TDataMember *member = rdata->GetDataMember();
624 // exclude enum or static variables
625 if (!member || (member->Property() & (kIsStatic | kIsEnum | kIsUnion)))
626 continue;
627 char *member_ptr = ptr + rdata->GetThisOffset();
628
629 if (member->IsaPointer())
630 member_ptr = *((char **)member_ptr);
631
633
634 if (chld.GoInside(rec, member, 0, this)) {
635
636 TClass *mcl = (member->IsBasic() || member->IsSTLContainer()) ? nullptr : gROOT->GetClass(member->GetTypeName());
637
638 Int_t coll_offset = mcl ? mcl->GetBaseClassOffset(TCollection::Class()) : -1;
639 if (coll_offset >= 0) {
640 chld.SetField(item_prop_more, "true", kFALSE);
641 chld.fHasMore = kTRUE;
642 }
643
644 if (chld.SetFoundResult(ptr, cl, member))
645 break;
646
647 const char *title = member->GetTitle();
648 if (title && *title)
649 chld.SetField(item_prop_title, title);
650
651 if (member->GetTypeName())
652 chld.SetField(item_prop_typename, member->GetTypeName());
653
654 if (member->GetArrayDim() > 0) {
655 // store array dimensions in form [N1,N2,N3,...]
656 TString dim("[");
657 for (Int_t n = 0; n < member->GetArrayDim(); n++) {
658 if (n > 0)
659 dim.Append(",");
660 dim.Append(TString::Format("%d", member->GetMaxIndex(n)));
661 }
662 dim.Append("]");
663 chld.SetField(item_prop_arraydim, dim, kFALSE);
664 } else if (member->GetArrayIndex() != 0) {
665 TRealData *idata = cl->GetRealData(member->GetArrayIndex());
666 TDataMember *imember = idata ? idata->GetDataMember() : nullptr;
667 if (imember && (strcmp(imember->GetTrueTypeName(), "int") == 0)) {
668 Int_t arraylen = *((int *)(ptr + idata->GetThisOffset()));
670 }
671 }
672
673 chld.SetRootClass(mcl);
674
675 if (chld.CanExpandItem()) {
676 if (coll_offset >= 0) {
677 // chld.SetField("#members", "true", kFALSE);
679 }
680 }
681
682 if (chld.SetFoundResult(ptr, cl, member))
683 break;
684 }
685 }
686}
687
688////////////////////////////////////////////////////////////////////////////////
689/// Scans object properties
690/// here such fields as _autoload or _icon properties depending on class or object name could be assigned
691/// By default properties, coded in the Class title are scanned. Example:
692///
693/// ClassDef(UserClassName, 1) // class comments *SNIFF* _field1=value _field2="string value"
694///
695/// Here *SNIFF* mark is important. After it all expressions like field=value are parsed
696/// One could use double quotes to code string values with spaces.
697/// Fields separated from each other with spaces
698
700{
701 TClass *cl = obj ? obj->IsA() : nullptr;
702
703 const char *pos = strstr(cl ? cl->GetTitle() : "", "*SNIFF*");
704 if (!pos)
705 return;
706
707 pos += 7;
708 while (*pos) {
709 if (*pos == ' ') {
710 pos++;
711 continue;
712 }
713 // first locate identifier
714 const char *pos0 = pos;
715 while (*pos && (*pos != '='))
716 pos++;
717 if (*pos == 0)
718 return;
719 TString name(pos0, pos - pos0);
720 pos++;
721 Bool_t quotes = (*pos == '\"');
722 if (quotes)
723 pos++;
724 pos0 = pos;
725 // then value with or without quotes
726 while (*pos && (*pos != (quotes ? '\"' : ' ')))
727 pos++;
728 TString value(pos0, pos - pos0);
729 rec.SetField(name, value);
730 if (quotes)
731 pos++;
732 pos++;
733 }
734}
735
736////////////////////////////////////////////////////////////////////////////////
737/// Scans TKey properties
738/// in special cases load objects from the file
739
741{
742 if (strcmp(key->GetClassName(), "TDirectoryFile") == 0) {
743 if (rec.fLevel == 0) {
744 auto dir = key->ReadObject<TDirectory>();
745 if (dir) {
746 obj = dir;
747 obj_class = dir->IsA();
748 }
749 } else {
750 rec.SetField(item_prop_more, "true", kFALSE);
751 rec.fHasMore = kTRUE;
752 }
753 }
754}
755
756////////////////////////////////////////////////////////////////////////////////
757/// scans object childs (if any)
758/// here one scans collection, branches, trees and so on
759
761{
762 if (obj->InheritsFrom(TFolder::Class())) {
763 ScanCollection(rec, ((TFolder *)obj)->GetListOfFolders());
764 } else if (obj->InheritsFrom(TDirectory::Class())) {
765 TDirectory *dir = (TDirectory *)obj;
766 ScanCollection(rec, dir->GetList(), nullptr, dir->GetListOfKeys());
767 } else if (rec.CanExpandItem()) {
768 ScanObjectMembers(rec, obj->IsA(), (char *)obj);
769 }
770}
771
772////////////////////////////////////////////////////////////////////////////////
773/// Scan collection content
774
777{
778 if ((!lst || (lst->GetSize() == 0)) && (!keys_lst || (keys_lst->GetSize() == 0)))
779 return;
780
782 if (foldername) {
783 if (!folderrec.GoInside(rec, nullptr, foldername, this))
784 return;
785 }
786
788
789 if (lst) {
790 TIter iter(lst);
791 TObject *next = iter();
793
794 while (next) {
795 if (IsItemField(next)) {
796 // special case - in the beginning one could have items for master folder
797 if (!isany && (next->GetName() != nullptr) && ((*(next->GetName()) == '_') || master.ScanOnlyFields()))
798 master.SetField(next->GetName(), next->GetTitle());
799 next = iter();
800 continue;
801 }
802
803 isany = kTRUE;
804 TObject *obj = next;
805
807 if (!chld.GoInside(master, obj, nullptr, this)) {
808 next = iter();
809 continue;
810 }
811
812 if (chld.SetResult(obj, obj->IsA()))
813 return;
814
816
818 // now properties, coded as TNamed objects, placed after object in the hierarchy
819 while ((next = iter()) != nullptr) {
820 if (!IsItemField(next))
821 break;
822 if ((next->GetName() != nullptr) && ((*(next->GetName()) == '_') || chld.ScanOnlyFields())) {
823 // only fields starting with _ are stored
824 chld.SetField(next->GetName(), next->GetTitle());
825 if (strcmp(next->GetName(), item_prop_kind) == 0)
826 has_kind = kTRUE;
827 if (strcmp(next->GetName(), item_prop_title) == 0)
829 }
830 }
831
832 if (!has_kind)
833 chld.SetRootClass(obj->IsA());
834 if (!has_title && obj->GetTitle())
835 chld.SetField(item_prop_title, obj->GetTitle());
836
838
839 if (chld.SetResult(obj, obj->IsA()))
840 return;
841 }
842 }
843
844 if (keys_lst) {
845 TIter iter(keys_lst);
846
847 while (auto kobj = iter()) {
848 TKey *key = dynamic_cast<TKey *>(kobj);
849 if (!key)
850 continue;
851 TObject *obj = lst ? lst->FindObject(key->GetName()) : nullptr;
852
853 // even object with the name exists, it should also match with class name
854 if (obj && (strcmp(obj->ClassName(), key->GetClassName()) != 0))
855 obj = nullptr;
856
857 // if object of that name and of that class already in the list, ignore appropriate key
858 if (obj && (master.fMask & TRootSnifferScanRec::kScan))
859 continue;
860
862 // if object not exists, provide key itself for the scan
863 if (!obj) {
864 obj = key;
865 iskey = kTRUE;
866 }
867
869 TString fullname = TString::Format("%s;%d", key->GetName(), key->GetCycle());
870
871 if (chld.GoInside(master, obj, fullname.Data(), this)) {
872
873 if (!chld.IsReadOnly(fReadOnly) && iskey && chld.IsReadyForResult()) {
874 TObject *keyobj = key->ReadObj();
875 if (keyobj)
876 if (chld.SetResult(keyobj, keyobj->IsA()))
877 return;
878 }
879
880 if (chld.SetResult(obj, obj->IsA()))
881 return;
882
883 TClass *obj_class = obj->IsA();
884
886
887 if (obj->GetTitle())
888 chld.SetField(item_prop_title, obj->GetTitle());
889
890 // special handling of TKey class - in non-readonly mode
891 // sniffer allowed to fetch objects
892 if (!chld.IsReadOnly(fReadOnly) && iskey)
893 ScanKeyProperties(chld, key, obj, obj_class);
894
895 rec.SetRootClass(obj_class);
896
898
899 // here we should know how many childs are accumulated
900 if (chld.SetResult(obj, obj_class))
901 return;
902 }
903 }
904 }
905}
906
907////////////////////////////////////////////////////////////////////////////////
908/// Create own TFolder structures independent from gROOT
909/// This allows to have many independent TRootSniffer instances
910/// At the same time such sniffer lost access to all global lists and folders
911
913{
914 if (fTopFolder) return;
915
917
918 // this only works with c++14, use ROOT wrapper
919 fTopFolder = std::make_unique<TFolder>("http","Dedicated instance");
920
921 // not sure if we have to add that private folder to global list of cleanups
922
923 // R__LOCKGUARD(gROOTMutex);
924 // gROOT->GetListOfCleanups()->Add(fTopFolder.get());
925
926}
927
928////////////////////////////////////////////////////////////////////////////////
929/// Returns top TFolder instance for the sniffer
930
932{
933 if (fTopFolder) return fTopFolder.get();
934
935 TFolder *topf = gROOT->GetRootFolder();
936
937 if (!topf) {
938 Error("RegisterObject", "Not found top ROOT folder!!!");
939 return nullptr;
940 }
941
942 TFolder *httpfold = dynamic_cast<TFolder *>(topf->FindObject("http"));
943 if (!httpfold) {
944 if (!force)
945 return nullptr;
946 httpfold = topf->AddFolder("http", "ROOT http server");
947 httpfold->SetBit(kCanDelete);
948 // register top folder in list of cleanups
950 gROOT->GetListOfCleanups()->Add(httpfold);
951 }
952
953 return httpfold;
954}
955
956////////////////////////////////////////////////////////////////////////////////
957/// scan complete ROOT objects hierarchy
958/// For the moment it includes objects in gROOT directory
959/// and list of canvases and files
960/// Also all registered objects are included.
961/// One could re-implement this method to provide alternative
962/// scan methods or to extend some collection kinds
963
965{
966 rec.SetField(item_prop_kind, "ROOT.Session");
968 rec.SetField(item_prop_user, fCurrentArg->GetUserName());
969
970 // should be on the top while //root/http folder could have properties for itself
972 if (topf) {
973 rec.SetField(item_prop_title, topf->GetTitle());
974 ScanCollection(rec, topf->GetListOfFolders());
975 }
976
977 if (HasStreamerInfo()) {
979 if (chld.GoInside(rec, nullptr, "StreamerInfo", this)) {
980 chld.SetField(item_prop_kind, "ROOT.TStreamerInfoList");
981 chld.SetField(item_prop_title, "List of streamer infos for binary I/O");
982 chld.SetField(item_prop_hidden, "true", kFALSE);
983 chld.SetField("_module", "hierarchy");
984 chld.SetField("_after_request", "markAsStreamerInfo");
985 }
986 }
987
988 if (IsScanGlobalDir()) {
989 ScanCollection(rec, gROOT->GetList());
990
991 ScanCollection(rec, gROOT->GetListOfCanvases(), "Canvases");
992
993 ScanCollection(rec, gROOT->GetListOfFiles(), "Files");
994 }
995}
996
997////////////////////////////////////////////////////////////////////////////////
998/// scan ROOT hierarchy with provided store object
999
1000void TRootSniffer::ScanHierarchy(const char *topname, const char *path, TRootSnifferStore *store,
1002{
1004 rec.fSearchPath = path;
1005 if (rec.fSearchPath) {
1006 while (*rec.fSearchPath == '/')
1007 rec.fSearchPath++;
1008 if (*rec.fSearchPath == 0)
1009 rec.fSearchPath = nullptr;
1010 }
1011
1012 // if path non-empty, we should find item first and than start scanning
1013 rec.fMask = !rec.fSearchPath ? TRootSnifferScanRec::kScan : TRootSnifferScanRec::kExpand;
1014 if (only_fields)
1016
1017 rec.fStore = store;
1018
1019 rec.CreateNode(topname);
1020
1021 if (!rec.fSearchPath)
1023
1024 if (!rec.fSearchPath && GetAutoLoad())
1025 rec.SetField(item_prop_autoload, GetAutoLoad());
1026
1027 ScanRoot(rec);
1028
1029 rec.CloseNode();
1030}
1031
1032////////////////////////////////////////////////////////////////////////////////
1033/// Search element with specified path
1034/// Returns pointer on element
1035/// Optionally one could obtain element class, member description
1036/// and number of childs. When chld!=nullptr, not only element is searched,
1037/// but also number of childs are counted. When member!=0, any object
1038/// will be scanned for its data members (disregard of extra options)
1039
1041{
1042 TRootSnifferStore store;
1043
1045 rec.fSearchPath = path;
1047 if (*rec.fSearchPath == '/')
1048 rec.fSearchPath++;
1049 rec.fStore = &store;
1050
1051 ScanRoot(rec);
1052
1054 TClass *res_cl = store.GetResClass();
1055 void *res = store.GetResPtr();
1056
1057 if (res_member && res_cl && !member) {
1058 res_cl = (res_member->IsBasic() || res_member->IsSTLContainer()) ? nullptr : gROOT->GetClass(res_member->GetTypeName());
1059 TRealData *rdata = res_cl ? res_cl->GetRealData(res_member->GetName()) : nullptr;
1060 if (rdata) {
1061 res = (char *)res + rdata->GetThisOffset();
1062 if (res_member->IsaPointer())
1063 res = *((char **)res);
1064 } else {
1065 res = nullptr; // should never happen
1066 }
1067 }
1068
1069 if (cl)
1070 *cl = res_cl;
1071 if (member)
1072 *member = res_member;
1073 if (chld)
1074 *chld = store.GetResNumChilds();
1075
1076 // remember current restriction
1078
1079 return res;
1080}
1081
1082////////////////////////////////////////////////////////////////////////////////
1083/// Search element in hierarchy, derived from TObject
1084
1086{
1087 TClass *cl = nullptr;
1088
1089 void *obj = FindInHierarchy(path, &cl);
1090
1091 return cl && (cl->GetBaseClassOffset(TObject::Class()) == 0) ? (TObject *)obj : nullptr;
1092}
1093
1094////////////////////////////////////////////////////////////////////////////////
1095/// Get hash function for specified item
1096/// used to detect any changes in the specified object
1097
1099{
1101
1102 return !obj ? 0 : TString::Hash(obj, obj->IsA()->Size());
1103}
1104
1105////////////////////////////////////////////////////////////////////////////////
1106/// Method verifies if object can be drawn
1107
1109{
1110 TClass *obj_cl = nullptr;
1111 void *res = FindInHierarchy(path, &obj_cl);
1112 return (res != nullptr) && CanDrawClass(obj_cl);
1113}
1114
1115////////////////////////////////////////////////////////////////////////////////
1116/// Method returns true when object has childs or
1117/// one could try to expand item
1118
1120{
1121 TClass *obj_cl = nullptr;
1122 Int_t obj_chld(-1);
1123 void *res = FindInHierarchy(path, &obj_cl, nullptr, &obj_chld);
1124 return res && (obj_chld > 0);
1125}
1126
1127////////////////////////////////////////////////////////////////////////////////
1128/// Produce JSON data for specified item
1129/// For object conversion TBufferJSON is used
1130
1131Bool_t TRootSniffer::ProduceJson(const std::string &path, const std::string &options, std::string &res)
1132{
1133 if (path.empty())
1134 return kFALSE;
1135
1136 const char *path_ = path.c_str();
1137 if (*path_ == '/')
1138 path_++;
1139
1140 TUrl url;
1141 url.SetOptions(options.c_str());
1142 url.ParseOptions();
1143 Int_t compact = -1;
1144 if (url.GetValueFromOptions("compact"))
1145 compact = url.GetIntValueFromOptions("compact");
1146
1147 TClass *obj_cl = nullptr;
1148 TDataMember *member = nullptr;
1150 if (!obj_ptr || (!obj_cl && !member))
1151 return kFALSE;
1152
1153 // TODO: implement direct storage into std::string
1154 TString buf = TBufferJSON::ConvertToJSON(obj_ptr, obj_cl, compact >= 0 ? compact : 0, member ? member->GetName() : nullptr);
1155 res = buf.Data();
1156
1157 return !res.empty();
1158}
1159
1160////////////////////////////////////////////////////////////////////////////////
1161/// Execute command marked as _kind=='Command'
1162
1163Bool_t TRootSniffer::ExecuteCmd(const std::string &path, const std::string &options, std::string &res)
1164{
1165 TFolder *parent = nullptr;
1166 TObject *obj = GetItem(path.c_str(), parent, kFALSE, kFALSE);
1167
1168 const char *kind = GetItemField(parent, obj, item_prop_kind);
1169 if ((kind == 0) || (strcmp(kind, "Command") != 0)) {
1170 if (gDebug > 0)
1171 Info("ExecuteCmd", "Entry %s is not a command", path.c_str());
1172 res = "false";
1173 return kTRUE;
1174 }
1175
1176 const char *cmethod = GetItemField(parent, obj, "method");
1177 if (!cmethod || (strlen(cmethod) == 0)) {
1178 if (gDebug > 0)
1179 Info("ExecuteCmd", "Entry %s do not defines method for execution", path.c_str());
1180 res = "false";
1181 return kTRUE;
1182 }
1183
1184 // if read-only specified for the command, it is not allowed for execution
1185 if (fRestrictions.GetLast() >= 0) {
1186 FindInHierarchy(path.c_str()); // one need to call method to check access rights
1187 if (fCurrentRestrict == 1) {
1188 if (gDebug > 0)
1189 Info("ExecuteCmd", "Entry %s not allowed for specified user", path.c_str());
1190 res = "false";
1191 return kTRUE;
1192 }
1193 }
1194
1196
1197 const char *cnumargs = GetItemField(parent, obj, "_numargs");
1199 if (numargs > 0) {
1200 TUrl url;
1201 url.SetOptions(options.c_str());
1202 url.ParseOptions();
1203
1204 for (Int_t n = 0; n < numargs; n++) {
1205 TString argname = TString::Format("arg%d", n + 1);
1206 const char *argvalue = url.GetValueFromOptions(argname);
1207 if (!argvalue) {
1208 if (gDebug > 0)
1209 Info("ExecuteCmd", "For command %s argument %s not specified in options %s", path.c_str(), argname.Data(),
1210 options.c_str());
1211 res = "false";
1212 return kTRUE;
1213 }
1214
1216 argname = TString("%") + argname + TString("%");
1217 method.ReplaceAll(argname, svalue);
1218 }
1219 }
1220
1221 if (gDebug > 0)
1222 Info("ExecuteCmd", "Executing command %s method:%s", path.c_str(), method.Data());
1223
1224 TObject *item_obj = nullptr;
1225 Ssiz_t separ = method.Index("/->");
1226
1227 if (method.Index("this->") == 0) {
1228 // if command name started with this-> means method of sniffer will be executed
1229 item_obj = this;
1230 separ = 3;
1231 } else if (separ != kNPOS) {
1233 }
1234
1235 if (item_obj) {
1236 method = TString::Format("((%s*)%zu)->%s", item_obj->ClassName(), (size_t)item_obj, method.Data() + separ + 3);
1237 if (gDebug > 2)
1238 Info("ExecuteCmd", "Executing %s", method.Data());
1239 }
1240
1241 auto v = gROOT->ProcessLineSync(method.Data());
1242
1243 res = std::to_string(v);
1244
1245 return kTRUE;
1246}
1247
1248////////////////////////////////////////////////////////////////////////////////
1249/// Produce JSON/XML for specified item
1250///
1251/// Contrary to h.json request, only fields for specified item are stored
1252
1253Bool_t TRootSniffer::ProduceItem(const std::string &path, const std::string &options, std::string &res, Bool_t asjson)
1254{
1255 TString buf; // TODO: implement direct storage into std::string
1256 if (asjson) {
1257 TRootSnifferStoreJson store(buf, options.find("compact") != std::string::npos);
1258 ScanHierarchy("top", path.c_str(), &store, kTRUE);
1259 } else {
1260 TRootSnifferStoreXml store(buf, options.find("compact") != std::string::npos);
1261 ScanHierarchy("top", path.c_str(), &store, kTRUE);
1262 }
1263 res = buf.Data();
1264 return !res.empty();
1265}
1266
1267////////////////////////////////////////////////////////////////////////////////
1268/// Produce XML data for specified item
1269///
1270/// For object conversion TBufferXML is used
1271/// Method implemented only in TRootSnifferFull class
1272
1273Bool_t TRootSniffer::ProduceXml(const std::string &/* path */, const std::string & /* options */, std::string & /* res */)
1274{
1275 return kFALSE;
1276}
1277
1278////////////////////////////////////////////////////////////////////////////////
1279/// Method replaces all kind of special symbols, which could appear in URL options
1280
1282{
1283 if (!value || (strlen(value) == 0))
1284 return TString();
1285
1286 TString res = value;
1287
1288 res.ReplaceAll("%27", "\'");
1289 res.ReplaceAll("%22", "\"");
1290 res.ReplaceAll("%3E", ">");
1291 res.ReplaceAll("%3C", "<");
1292 res.ReplaceAll("%20", " ");
1293 res.ReplaceAll("%5B", "[");
1294 res.ReplaceAll("%5D", "]");
1295 res.ReplaceAll("%3D", "=");
1296
1297 if (remove_quotes && (res.Length() > 1) && ((res[0] == '\'') || (res[0] == '\"')) &&
1298 (res[0] == res[res.Length() - 1])) {
1299 res.Remove(res.Length() - 1);
1300 res.Remove(0, 1);
1301 }
1302
1303 return res;
1304}
1305
1306////////////////////////////////////////////////////////////////////////////////
1307/// Execute command for specified object
1308///
1309/// Options include method and extra list of parameters
1310/// sniffer should be not-readonly to allow execution of the commands
1311/// reskind defines kind of result 0 - debug, 1 - json, 2 - binary
1312///
1313/// Method implemented only in TRootSnifferFull class
1314
1315Bool_t TRootSniffer::ProduceExe(const std::string & /*path*/, const std::string & /*options*/, Int_t /*reskind*/,
1316 std::string & /*res*/)
1317{
1318 return kFALSE;
1319}
1320
1321////////////////////////////////////////////////////////////////////////////////
1322/// Process several requests, packing all results into binary or JSON buffer
1323///
1324/// Input parameters should be coded in the POST block and has
1325/// individual request relative to current path, separated with '\n' symbol like
1326/// item1/root.bin\n
1327/// item2/exe.bin?method=GetList\n
1328/// item3/exe.bin?method=GetTitle\n
1329/// Request requires 'number' URL option which contains number of requested items
1330///
1331/// In case of binary request output buffer looks like:
1332///
1333/// 4bytes length + payload,
1334/// 4bytes length + payload, ...
1335///
1336/// In case of JSON request output is array with results for each item
1337/// multi.json request do not support binary requests for the items
1338
1339Bool_t TRootSniffer::ProduceMulti(const std::string &path, const std::string &options, std::string &str, Bool_t asjson)
1340{
1342 return kFALSE;
1343
1344 const char *args = (const char *)fCurrentArg->GetPostData();
1345 const char *ends = args + fCurrentArg->GetPostDataLength();
1346
1347 TUrl url;
1348 url.SetOptions(options.c_str());
1349
1350 Int_t number = 0;
1351 if (url.GetValueFromOptions("number"))
1352 number = url.GetIntValueFromOptions("number");
1353
1354 // binary buffers required only for binary requests, json output can be produced as is
1355 std::vector<std::string> mem;
1356
1357 if (asjson)
1358 str = "[";
1359
1360 for (Int_t n = 0; n < number; n++) {
1361 const char *next = args;
1362 while ((next < ends) && (*next != '\n'))
1363 next++;
1364 if (next == ends) {
1365 Error("ProduceMulti", "Not enough arguments in POST block");
1366 break;
1367 }
1368
1369 std::string file1(args, next - args);
1370 args = next + 1;
1371
1372 std::string path1, opt1;
1373
1374 // extract options
1375 std::size_t pos = file1.find_first_of('?');
1376 if (pos != std::string::npos) {
1377 opt1 = file1.substr(pos + 1, file1.length() - pos);
1378 file1.resize(pos);
1379 }
1380
1381 // extract extra path
1382 pos = file1.find_last_of('/');
1383 if (pos != std::string::npos) {
1384 path1 = file1.substr(0, pos);
1385 file1.erase(0, pos + 1);
1386 }
1387
1388 if (!path.empty())
1389 path1 = path + "/" + path1;
1390
1391 std::string res1;
1392
1393 // produce next item request
1395
1396 if (asjson) {
1397 if (n > 0)
1398 str.append(", ");
1399 if (res1.empty())
1400 str.append("null");
1401 else
1402 str.append(res1);
1403 } else {
1404 mem.emplace_back(std::move(res1));
1405 }
1406 }
1407
1408 if (asjson) {
1409 str.append("]");
1410 } else {
1411 Int_t length = 0;
1412 for (unsigned n = 0; n < mem.size(); n++)
1413 length += 4 + mem[n].length();
1414 str.resize(length);
1415 char *curr = (char *)str.data();
1416 for (unsigned n = 0; n < mem.size(); n++) {
1417 Long_t l = mem[n].length();
1418 *curr++ = (char)(l & 0xff);
1419 l = l >> 8;
1420 *curr++ = (char)(l & 0xff);
1421 l = l >> 8;
1422 *curr++ = (char)(l & 0xff);
1423 l = l >> 8;
1424 *curr++ = (char)(l & 0xff);
1425 if (!mem[n].empty())
1426 memcpy(curr, mem[n].data(), mem[n].length());
1427 curr += mem[n].length();
1428 }
1429 }
1430
1431 return kTRUE;
1432}
1433
1434////////////////////////////////////////////////////////////////////////////////
1435/// Produce binary data for specified item
1436///
1437/// If "zipped" option specified in query, buffer will be compressed
1438///
1439/// Implemented only in TRootSnifferFull class
1440
1441Bool_t TRootSniffer::ProduceBinary(const std::string & /*path*/, const std::string & /*query*/, std::string & /*res*/)
1442{
1443 return kFALSE;
1444}
1445
1446////////////////////////////////////////////////////////////////////////////////
1447/// Produce ROOT file for specified item
1448///
1449/// Implemented only in TRootSnifferFull class
1450
1451Bool_t TRootSniffer::ProduceRootFile(const std::string & /*path*/, const std::string & /*query*/, std::string & /*res*/)
1452{
1453 return kFALSE;
1454}
1455
1456////////////////////////////////////////////////////////////////////////////////
1457/// Method to produce image from specified object
1458///
1459/// Parameters:
1460///
1461/// kind - image kind TImage::kPng, TImage::kJpeg, TImage::kGif
1462/// path - path to object
1463/// options - extra options
1464///
1465/// By default, image 300x200 is produced
1466/// In options string one could provide following parameters:
1467///
1468/// w - image width
1469/// h - image height
1470/// opt - draw options
1471///
1472/// For instance:
1473///
1474/// http://localhost:8080/Files/hsimple.root/hpx/get.png?w=500&h=500&opt=lego1
1475///
1476/// Returns produced image in the res string
1477///
1478/// Method implemented only in TRootSnifferFull class
1479
1480Bool_t TRootSniffer::ProduceImage(Int_t /*kind*/, const std::string & /*path*/, const std::string & /*options*/, std::string & /*res*/)
1481{
1482 return kFALSE;
1483}
1484
1485////////////////////////////////////////////////////////////////////////////////
1486/// Invokes TRootSniffer::ProduceIamge, converting kind into TImage::EImageFileTypes type
1487
1488Bool_t TRootSniffer::CallProduceImage(const std::string &/*kind*/, const std::string &/*path*/, const std::string &/*options*/, std::string &/*res*/)
1489{
1490 return kFALSE;
1491}
1492
1493////////////////////////////////////////////////////////////////////////////////
1494/// Method produce different kind of data out of object
1495///
1496/// @param path specifies object or object member
1497/// @param file can be:
1498///
1499/// * "root.bin" - binary data
1500/// * "root.png" - png image
1501/// * "root.jpeg" - jpeg image
1502/// * "root.gif" - gif image
1503/// * "root.xml" - xml representation
1504/// * "root.json" - json representation
1505/// * "file.root" - ROOT file with stored object
1506/// * "exe.json" - method execution with json reply
1507/// * "exe.bin" - method execution with binary reply
1508/// * "exe.txt" - method execution with debug output
1509/// * "cmd.json" - execution of registered commands
1510///
1511/// @param options specific options
1512/// @param res returns result - binary or text.
1513
1514Bool_t TRootSniffer::Produce(const std::string &path, const std::string &file, const std::string &options, std::string &res)
1515{
1516 if (file.empty())
1517 return kFALSE;
1518
1519 if (file == "root.bin")
1520 return ProduceBinary(path, options, res);
1521
1522 if (file == "root.png")
1523 return CallProduceImage("png", path, options, res);
1524
1525 if (file == "root.jpeg")
1526 return CallProduceImage("jpeg", path, options, res);
1527
1528 if (file == "root.gif")
1529 return CallProduceImage("gif", path, options, res);
1530
1531 if (file == "exe.bin")
1532 return ProduceExe(path, options, 2, res);
1533
1534 if (file == "root.xml")
1535 return ProduceXml(path, options, res);
1536
1537 if (file == "root.json")
1538 return ProduceJson(path, options, res);
1539
1540 if (file == "file.root")
1541 return ProduceRootFile(path, options, res);
1542
1543 // used for debugging
1544 if (file == "exe.txt")
1545 return ProduceExe(path, options, 0, res);
1546
1547 if (file == "exe.json")
1548 return ProduceExe(path, options, 1, res);
1549
1550 if (file == "cmd.json")
1551 return ExecuteCmd(path, options, res);
1552
1553 if (file == "item.json")
1554 return ProduceItem(path, options, res, kTRUE);
1555
1556 if (file == "item.xml")
1557 return ProduceItem(path, options, res, kFALSE);
1558
1559 if (file == "multi.bin")
1560 return ProduceMulti(path, options, res, kFALSE);
1561
1562 if (file == "multi.json")
1563 return ProduceMulti(path, options, res, kTRUE);
1564
1565 return kFALSE;
1566}
1567
1568////////////////////////////////////////////////////////////////////////////////
1569/// Return item from the subfolders structure
1570
1572{
1574 if (!httpfold) return nullptr;
1575
1576 parent = httpfold;
1577 TObject *obj = httpfold;
1578
1579 if (!fullname)
1580 return httpfold;
1581
1582 // when full path started not with slash, "Objects" subfolder is appended
1583 TString path = fullname;
1584 if (within_objects && ((path.Length() == 0) || (path[0] != '/')))
1585 path = fObjectsPath + "/" + path;
1586
1587 TString tok;
1588 Ssiz_t from = 0;
1589
1590 while (path.Tokenize(tok, from, "/")) {
1591 if (tok.Length() == 0)
1592 continue;
1593
1594 TFolder *fold = dynamic_cast<TFolder *>(obj);
1595 if (!fold)
1596 return nullptr;
1597
1598 TIter iter(fold->GetListOfFolders());
1599 while ((obj = iter()) != nullptr) {
1600 if (IsItemField(obj))
1601 continue;
1602 if (tok.CompareTo(obj->GetName()) == 0)
1603 break;
1604 }
1605
1606 if (!obj) {
1607 if (!force)
1608 return nullptr;
1609 obj = fold->AddFolder(tok, "sub-folder");
1610 obj->SetBit(kCanDelete);
1611 }
1612
1613 parent = fold;
1614 }
1615
1616 return obj;
1617}
1618
1619////////////////////////////////////////////////////////////////////////////////
1620/// Creates subfolder where objects can be registered
1621
1623{
1624 TFolder *parent = nullptr;
1625
1626 return dynamic_cast<TFolder *>(GetItem(subfolder, parent, force));
1627}
1628
1629////////////////////////////////////////////////////////////////////////////////
1630/// Register object in subfolder structure
1631///
1632/// @param obj pointer to TObject
1633/// @param subfolder can have many levels like:
1634///
1635/// TRootSniffer* sniff = new TRootSniffer("sniff");
1636/// sniff->RegisterObject("my/sub/subfolder", h1);
1637///
1638/// Such objects can be later found in "Objects" folder of sniffer like
1639///
1640/// auto h1 = sniff->FindTObjectInHierarchy("/Objects/my/sub/subfolder/h1");
1641///
1642/// If subfolder name starts with '/', object will be registered starting from top folder.
1643///
1644/// One could provide additional fields for registered objects
1645/// For instance, setting "_more" field to true let browser
1646/// explore objects members. For instance:
1647///
1648/// TEvent* ev = new TEvent("ev");
1649/// sniff->RegisterObject("Events", ev);
1650/// sniff->SetItemField("Events/ev", "_more", "true");
1651
1653{
1655 if (!f)
1656 return kFALSE;
1657
1658 // If object will be destroyed, it will be removed from the folders automatically
1659 obj->SetBit(kMustCleanup);
1660
1661 f->Add(obj);
1662
1663 return kTRUE;
1664}
1665
1666////////////////////////////////////////////////////////////////////////////////
1667/// Unregister (remove) object from folders structures
1668///
1669/// Folder itself will remain even when it will be empty
1670
1672{
1673 if (!obj)
1674 return kTRUE;
1675
1677
1678 if (!topf) {
1679 Error("UnregisterObject", "Not found top folder");
1680 return kFALSE;
1681 }
1682
1683 // TODO - probably we should remove all set properties as well
1684 topf->RecursiveRemove(obj);
1685
1686 return kTRUE;
1687}
1688
1689////////////////////////////////////////////////////////////////////////////////
1690/// Create item element
1691
1692Bool_t TRootSniffer::CreateItem(const char *fullname, const char *title)
1693{
1694 TFolder *f = GetSubFolder(fullname, kTRUE);
1695 if (!f)
1696 return kFALSE;
1697
1698 if (title)
1699 f->SetTitle(title);
1700
1701 return kTRUE;
1702}
1703
1704////////////////////////////////////////////////////////////////////////////////
1705/// Return true when object is TNamed with kItemField bit set
1706///
1707/// such objects used to keep field values for item
1708
1710{
1711 return (obj != nullptr) && (obj->IsA() == TNamed::Class()) && obj->TestBit(kItemField);
1712}
1713
1714////////////////////////////////////////////////////////////////////////////////
1715/// Set or get field for the child
1716///
1717/// each field coded as TNamed object, placed after chld in the parent hierarchy
1718
1720{
1721 if (!parent)
1722 return kFALSE;
1723
1724 if (!chld) {
1725 Info("AccessField", "Should be special case for top folder, support later");
1726 return kFALSE;
1727 }
1728
1729 TIter iter(parent->GetListOfFolders());
1730
1731 Bool_t find = kFALSE, last_find = kFALSE;
1732 // this is special case of top folder - fields are on very top
1733 if (parent == chld)
1734 last_find = find = kTRUE;
1735
1736 TNamed *curr = nullptr;
1737 while (auto obj = iter()) {
1738 if (IsItemField(obj)) {
1739 if (last_find && obj->GetName() && !strcmp(name, obj->GetName()))
1740 curr = (TNamed *)obj;
1741 } else {
1742 last_find = (obj == chld);
1743 if (last_find)
1744 find = kTRUE;
1745 if (find && !last_find)
1746 break; // no need to continue
1747 }
1748 }
1749
1750 // object must be in childs list
1751 if (!find)
1752 return kFALSE;
1753
1754 if (only_get) {
1755 *only_get = curr;
1756 return curr != nullptr;
1757 }
1758
1759 if (curr) {
1760 if (value) {
1761 curr->SetTitle(value);
1762 } else {
1763 parent->Remove(curr);
1764 delete curr;
1765 }
1766 return kTRUE;
1767 }
1768
1769 curr = new TNamed(name, value);
1770 curr->SetBit(kItemField);
1771
1772 if (last_find) {
1773 // object is on last place, therefore just add property
1774 parent->Add(curr);
1775 return kTRUE;
1776 }
1777
1778 // only here we do dynamic cast to the TList to use AddAfter
1779 TList *lst = dynamic_cast<TList *>(parent->GetListOfFolders());
1780 if (!lst) {
1781 Error("AccessField", "Fail cast to TList");
1782 return kFALSE;
1783 }
1784
1785 if (parent == chld)
1786 lst->AddFirst(curr);
1787 else
1788 lst->AddAfter(chld, curr);
1789
1790 return kTRUE;
1791}
1792
1793////////////////////////////////////////////////////////////////////////////////
1794/// Set field for specified item
1795
1796Bool_t TRootSniffer::SetItemField(const char *fullname, const char *name, const char *value)
1797{
1798 if (!fullname || !name)
1799 return kFALSE;
1800
1801 TFolder *parent = nullptr;
1802 TObject *obj = GetItem(fullname, parent);
1803
1804 if (!parent || !obj)
1805 return kFALSE;
1806
1807 if (strcmp(name, item_prop_title) == 0) {
1808 TNamed *n = dynamic_cast<TNamed *>(obj);
1809 if (n) {
1810 n->SetTitle(value);
1811 return kTRUE;
1812 }
1813 }
1814
1815 return AccessField(parent, obj, name, value);
1816}
1817
1818////////////////////////////////////////////////////////////////////////////////
1819/// Return field for specified item
1820
1821const char *TRootSniffer::GetItemField(TFolder *parent, TObject *obj, const char *name)
1822{
1823 if (!parent || !obj || !name)
1824 return nullptr;
1825
1826 TNamed *field = nullptr;
1827
1828 if (!AccessField(parent, obj, name, nullptr, &field))
1829 return nullptr;
1830
1831 return field ? field->GetTitle() : nullptr;
1832}
1833
1834////////////////////////////////////////////////////////////////////////////////
1835/// Return field for specified item
1836
1837const char *TRootSniffer::GetItemField(const char *fullname, const char *name)
1838{
1839 if (!fullname)
1840 return nullptr;
1841
1842 TFolder *parent = nullptr;
1843 TObject *obj = GetItem(fullname, parent);
1844
1845 return GetItemField(parent, obj, name);
1846}
1847
1848////////////////////////////////////////////////////////////////////////////////
1849/// Register command which can be executed from web interface
1850///
1851/// As method one typically specifies string, which is executed with
1852/// gROOT->ProcessLine() method. For instance:
1853///
1854/// serv->RegisterCommand("Invoke","InvokeFunction()");
1855///
1856/// Or one could specify any method of the object which is already registered
1857/// to the server. For instance:
1858///
1859/// serv->Register("/", hpx);
1860/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()");
1861///
1862/// Here symbols '/->' separates item name from method to be executed
1863///
1864/// One could specify additional arguments in the command with
1865/// syntax like %arg1%, %arg2% and so on. For example:
1866///
1867/// serv->RegisterCommand("/ResetHPX", "/hpx/->SetTitle(\"%arg1%\")");
1868/// serv->RegisterCommand("/RebinHPXPY", "/hpxpy/->Rebin2D(%arg1%,%arg2%)");
1869///
1870/// Such parameter(s) will be requested when command clicked in the browser.
1871///
1872/// Once command is registered, one could specify icon which will appear in the browser:
1873///
1874/// serv->SetIcon("/ResetHPX", "rootsys/icons/ed_execute.png");
1875///
1876/// One also can set extra property '_fastcmd', that command appear as
1877/// tool button on the top of the browser tree:
1878///
1879/// serv->SetItemField("/ResetHPX", "_fastcmd", "true");
1880///
1881/// Or it is equivalent to specifying extra argument when register command:
1882///
1883/// serv->RegisterCommand("/ResetHPX", "/hpx/->Reset()", "button;rootsys/icons/ed_delete.png");
1884
1885Bool_t TRootSniffer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
1886{
1887 CreateItem(cmdname, TString::Format("command %s", method).Data());
1888 SetItemField(cmdname, "_kind", "Command");
1889 if (icon) {
1890 if (strncmp(icon, "button;", 7) == 0) {
1891 SetItemField(cmdname, "_fastcmd", "true");
1892 icon += 7;
1893 }
1894 if (*icon)
1895 SetItemField(cmdname, "_icon", icon);
1896 }
1897 SetItemField(cmdname, "method", method);
1898 Int_t numargs = 0;
1899 do {
1900 TString nextname = TString::Format("%sarg%d%s", "%", numargs + 1, "%");
1901 if (strstr(method, nextname.Data()) == nullptr)
1902 break;
1903 numargs++;
1904 } while (numargs < 100);
1905 if (numargs > 0)
1906 SetItemField(cmdname, "_numargs", TString::Format("%d", numargs));
1907
1908 return kTRUE;
1909}
#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:110
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:627
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:411
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:2036
TList * GetListOfRealData() const
Definition TClass.h:465
Int_t GetBaseClassOffset(const TClass *toBase, void *address=nullptr, bool isDerivedObject=true)
Definition TClass.cxx:2796
Long_t Property() const override
Returns the properties of the TClass as a bit field stored as a Long_t value.
Definition TClass.cxx:6128
TRealData * GetRealData(const char *name) const
Return pointer to TRealData element with name "name".
Definition TClass.cxx:3534
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:103
virtual const char * GetClassName() const
Definition TKey.h:75
Short_t GetCycle() const
Return cycle number associated to this key.
Definition TKey.cxx:579
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition TKey.cxx:760
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:575
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:41
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:457
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:202
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
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:864
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:543
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1071
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:501
virtual TClass * IsA() const
Definition TObject.h:246
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:68
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition TObject.h:70
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1045
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:712
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:693
TString & Append(const char *cs)
Definition TString.h:580
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