#include "TRootSniffer.h"
#include "TH1.h"
#include "TGraph.h"
#include "TProfile.h"
#include "TCanvas.h"
#include "TFile.h"
#include "TKey.h"
#include "TList.h"
#include "TMemFile.h"
#include "TStreamerInfo.h"
#include "TBufferFile.h"
#include "TBufferJSON.h"
#include "TBufferXML.h"
#include "TROOT.h"
#include "TTimer.h"
#include "TFolder.h"
#include "TTree.h"
#include "TBranch.h"
#include "TLeaf.h"
#include "TClass.h"
#include "TMethod.h"
#include "TFunction.h"
#include "TMethodArg.h"
#include "TMethodCall.h"
#include "TRealData.h"
#include "TDataMember.h"
#include "TDataType.h"
#include "TBaseClass.h"
#include "TObjString.h"
#include "TUrl.h"
#include "TImage.h"
#include "RZip.h"
#include "TRootSnifferStore.h"
#include "THttpCallArg.h"
#include <stdlib.h>
#include <vector>
#include <string.h>
const char *item_prop_kind = "_kind";
const char *item_prop_more = "_more";
const char *item_prop_title = "_title";
const char *item_prop_hidden = "_hidden";
const char *item_prop_typename = "_typename";
const char *item_prop_arraydim = "_arraydim";
const char *item_prop_realname = "_realname";
const char *item_prop_user = "_username";
const char *item_prop_autoload = "_autoload";
TRootSnifferScanRec::TRootSnifferScanRec() :
fParent(0),
fMask(0),
fSearchPath(0),
fLevel(0),
fItemName(),
fItemsNames(),
fRestriction(0),
fStore(0),
fHasMore(kFALSE),
fNodeStarted(kFALSE),
fNumFields(0),
fNumChilds(0)
{
fItemsNames.SetOwner(kTRUE);
}
TRootSnifferScanRec::~TRootSnifferScanRec()
{
CloseNode();
}
void TRootSnifferScanRec::SetField(const char *name, const char *value, Bool_t with_quotes)
{
if (CanSetFields()) fStore->SetField(fLevel, name, value, with_quotes);
fNumFields++;
}
void TRootSnifferScanRec::BeforeNextChild()
{
if (CanSetFields()) fStore->BeforeNextChild(fLevel, fNumChilds, fNumFields);
fNumChilds++;
}
void TRootSnifferScanRec::MakeItemName(const char *objname, TString &itemname)
{
std::string nnn = objname;
size_t pos;
while ((pos = nnn.find_first_of("- []<>#:&?/\'\"\\")) != std::string::npos)
nnn.replace(pos, 1, "_");
itemname = nnn.c_str();
Int_t cnt = 0;
while (fItemsNames.FindObject(itemname.Data())) {
itemname.Form("%s_%d", nnn.c_str(), cnt++);
}
fItemsNames.Add(new TObjString(itemname.Data()));
}
void TRootSnifferScanRec::BuildFullName(TString& buf, TRootSnifferScanRec* prnt)
{
if (!prnt) prnt = fParent;
if (prnt) {
prnt->BuildFullName(buf);
buf.Append("/");
buf.Append(fItemName);
}
}
void TRootSnifferScanRec::CreateNode(const char *_node_name)
{
if (!CanSetFields()) return;
fNodeStarted = kTRUE;
if (fParent) fParent->BeforeNextChild();
if (fStore) fStore->CreateNode(fLevel, _node_name);
}
void TRootSnifferScanRec::CloseNode()
{
if (fStore && fNodeStarted) {
fStore->CloseNode(fLevel, fNumChilds);
fNodeStarted = kFALSE;
}
}
void TRootSnifferScanRec::SetRootClass(TClass *cl)
{
if ((cl != 0) && CanSetFields())
SetField(item_prop_kind, TString::Format("ROOT.%s", cl->GetName()));
}
Bool_t TRootSnifferScanRec::Done() const
{
if (fStore == 0)
return kFALSE;
if ((fMask & kSearch) && fStore->GetResPtr())
return kTRUE;
if ((fMask & kCheckChilds) && fStore->GetResPtr() &&
(fStore->GetResNumChilds() >= 0))
return kTRUE;
return kFALSE;
}
Bool_t TRootSnifferScanRec::IsReadyForResult() const
{
if (Done()) return kFALSE;
if ((fMask & (kSearch | kCheckChilds)) == 0) return kFALSE;
if (fSearchPath != 0) return kFALSE;
if (fStore == 0) return kFALSE;
return kTRUE;
}
Bool_t TRootSnifferScanRec::SetResult(void *obj, TClass *cl, TDataMember *member)
{
if (member==0) return SetFoundResult(obj, cl);
fStore->Error("SetResult", "When member specified, pointer on object (not member) should be provided; use SetFoundResult");
return kFALSE;
}
Bool_t TRootSnifferScanRec::SetFoundResult(void *obj, TClass *cl, TDataMember *member)
{
if (Done()) return kTRUE;
if (!IsReadyForResult()) return kFALSE;
fStore->SetResult(obj, cl, member, fNumChilds, fRestriction);
return Done();
}
Int_t TRootSnifferScanRec::Depth() const
{
Int_t cnt = 0;
const TRootSnifferScanRec *rec = this;
while (rec->fParent) {
rec = rec->fParent;
cnt++;
}
return cnt;
}
Bool_t TRootSnifferScanRec::CanExpandItem()
{
if (fMask & (kExpand | kSearch | kCheckChilds)) return kTRUE;
if (!fHasMore) return kFALSE;
if (fParent && (fParent->fMask & kExpand)) return kTRUE;
return kFALSE;
}
Bool_t TRootSnifferScanRec::IsReadOnly(Bool_t dflt)
{
if (fRestriction==0) return dflt;
return fRestriction!=2;
}
Bool_t TRootSnifferScanRec::GoInside(TRootSnifferScanRec &super, TObject *obj,
const char *obj_name, TRootSniffer* sniffer)
{
if (super.Done()) return kFALSE;
if ((obj != 0) && (obj_name == 0)) obj_name = obj->GetName();
if ((obj_name == 0) || (*obj_name == 0)) return kFALSE;
const char *full_name = 0;
if (obj && obj->InheritsFrom(TDirectoryFile::Class())) {
const char *slash = strrchr(obj_name, '/');
if (slash != 0) {
full_name = obj_name;
obj_name = slash + 1;
if (*obj_name == 0) obj_name = "file";
}
}
super.MakeItemName(obj_name, fItemName);
if (sniffer && sniffer->HasRestriction(fItemName.Data())) {
TString fullname;
BuildFullName(fullname, &super);
fRestriction = sniffer->CheckRestriction(fullname.Data());
if (fRestriction<0) return kFALSE;
}
fParent = &super;
fLevel = super.fLevel;
fStore = super.fStore;
fSearchPath = super.fSearchPath;
fMask = super.fMask & kActions;
if (fRestriction==0) fRestriction = super.fRestriction;
Bool_t topelement = kFALSE;
if (fMask & kScan) {
if (super.ScanOnlyFields()) return kFALSE;
fLevel++;
} else {
if (fSearchPath == 0) return kFALSE;
if (strncmp(fSearchPath, fItemName.Data(), fItemName.Length()) != 0)
return kFALSE;
const char *separ = fSearchPath + fItemName.Length();
Bool_t isslash = kFALSE;
while (*separ == '/') {
separ++;
isslash = kTRUE;
}
if (*separ == 0) {
fSearchPath = 0;
if (fMask & kExpand) {
topelement = kTRUE;
fMask = (fMask & kOnlyFields) | kScan;
fHasMore = (fMask & kOnlyFields) == 0;
}
} else {
if (!isslash) return kFALSE;
fSearchPath = separ;
}
}
CreateNode(fItemName.Data());
if ((obj_name != 0) && (fItemName != obj_name))
SetField(item_prop_realname, obj_name);
if (full_name != 0)
SetField("_fullname", full_name);
if (topelement && sniffer->GetAutoLoad())
SetField(item_prop_autoload, sniffer->GetAutoLoad());
return kTRUE;
}
ClassImp(TRootSniffer)
TRootSniffer::TRootSniffer(const char *name, const char *objpath) :
TNamed(name, "sniffer of root objects"),
fObjectsPath(objpath),
fMemFile(0),
fSinfo(0),
fReadOnly(kTRUE),
fScanGlobalDir(kTRUE),
fCurrentArg(0),
fCurrentRestrict(0),
fCurrentAllowedMethods(0),
fRestrictions(),
fAutoLoad()
{
fRestrictions.SetOwner(kTRUE);
}
TRootSniffer::~TRootSniffer()
{
if (fSinfo) {
delete fSinfo;
fSinfo = 0;
}
if (fMemFile) {
delete fMemFile;
fMemFile = 0;
}
}
void TRootSniffer::SetCurrentCallArg(THttpCallArg* arg)
{
fCurrentArg = arg;
fCurrentRestrict = 0;
fCurrentAllowedMethods = "";
}
void TRootSniffer::SetAutoLoad(const char* scripts)
{
fAutoLoad = scripts!=0 ? scripts : "";
}
const char* TRootSniffer::GetAutoLoad() const
{
return fAutoLoad.Length() > 0 ? fAutoLoad.Data() : 0;
}
void TRootSniffer::Restrict(const char* path, const char* options)
{
const char* rslash = strrchr(path,'/');
if (rslash) rslash++;
if ((rslash==0) || (*rslash==0)) rslash = path;
fRestrictions.Add(new TNamed(rslash, TString::Format("%s%s%s", path,"%%%",options).Data()));
}
Bool_t TRootSniffer::HasRestriction(const char* item_name)
{
if ((item_name==0) || (*item_name==0) || (fCurrentArg==0)) return kFALSE;
return fRestrictions.FindObject(item_name)!=0;
}
Int_t TRootSniffer::WithCurrentUserName(const char* option)
{
const char* username = fCurrentArg ? fCurrentArg->GetUserName() : 0;
if ((username==0) || (option == 0) || (*option==0)) return 0;
if (strcmp(option,"all") == 0) return 1;
if (strcmp(username, option) == 0) return 2;
if (strstr(option, username) == 0) return -1;
TObjArray* arr = TString(option).Tokenize(",");
Bool_t find = arr->FindObject(username) != 0;
delete arr;
return find ? 2 : -1;
}
Int_t TRootSniffer::CheckRestriction(const char* full_item_name)
{
if ((full_item_name==0) || (*full_item_name==0)) return 0;
const char* item_name = strrchr(full_item_name,'/');
if (item_name) item_name++;
if ((item_name==0) || (*item_name==0)) item_name = full_item_name;
TString pattern1 = TString("*/") + item_name + "%%%";
TString pattern2 = TString(full_item_name) + "%%%";
const char* options = 0;
TIter iter(&fRestrictions);
TObject* obj;
while ((obj = iter()) != 0) {
const char* title = obj->GetTitle();
if (strstr(title,pattern1.Data())==title) { options = title + pattern1.Length(); break; }
if (strstr(title,pattern2.Data())==title) { options = title + pattern2.Length(); break; }
}
if (options==0) return 0;
TUrl url;
url.SetOptions(options);
url.ParseOptions();
Int_t can_see = WithCurrentUserName(url.GetValueFromOptions("visible")) -
WithCurrentUserName(url.GetValueFromOptions("hidden"));
Int_t can_access = WithCurrentUserName(url.GetValueFromOptions("allow")) -
WithCurrentUserName(url.GetValueFromOptions("readonly"));
if (can_access > 0) return 2;
if (can_see < 0) return -1;
const char* methods = url.GetValueFromOptions("allow_method");
if (methods!=0) fCurrentAllowedMethods = methods;
if (can_access < 0) return 1;
return 0;
}
void TRootSniffer::ScanObjectMembers(TRootSnifferScanRec &rec, TClass *cl, char *ptr)
{
if ((cl == 0) || (ptr == 0) || rec.Done()) return;
if (!(cl->Property() & kIsAbstract)) cl->BuildRealData();
TObject *obj = 0;
TIter iter(cl->GetListOfRealData());
while ((obj = iter()) != 0) {
TRealData *rdata = dynamic_cast<TRealData *>(obj);
if ((rdata == 0) || strchr(rdata->GetName(),'.')) continue;
TDataMember *member = rdata->GetDataMember();
if ((member == 0) || (member->Property() & (kIsStatic | kIsEnum | kIsUnion))) continue;
char *member_ptr = ptr + rdata->GetThisOffset();
if (member->IsaPointer()) member_ptr = *((char **) member_ptr);
TRootSnifferScanRec chld;
if (chld.GoInside(rec, member, 0, this)) {
TClass *mcl = (member->IsBasic() || member->IsSTLContainer()) ? 0 :
gROOT->GetClass(member->GetTypeName());
Int_t coll_offset = mcl ? mcl->GetBaseClassOffset(TCollection::Class()) : -1;
if (coll_offset >= 0) {
chld.SetField(item_prop_more, "true", kFALSE);
chld.fHasMore = kTRUE;
}
if (chld.SetFoundResult(ptr, cl, member)) break;
const char *title = member->GetTitle();
if ((title != 0) && (strlen(title) != 0))
chld.SetField(item_prop_title, title);
if (member->GetTypeName())
chld.SetField(item_prop_typename, member->GetTypeName());
if (member->GetArrayDim() > 0) {
TString dim("[");
for (Int_t n = 0; n < member->GetArrayDim(); n++) {
if (n > 0) dim.Append(",");
dim.Append(TString::Format("%d", member->GetMaxIndex(n)));
}
dim.Append("]");
chld.SetField(item_prop_arraydim, dim, kFALSE);
} else
if (member->GetArrayIndex()!=0) {
TRealData *idata = cl->GetRealData(member->GetArrayIndex());
TDataMember *imember = (idata!=0) ? idata->GetDataMember() : 0;
if ((imember!=0) && (strcmp(imember->GetTrueTypeName(),"int")==0)) {
Int_t arraylen = *((int *) (ptr + idata->GetThisOffset()));
chld.SetField(item_prop_arraydim, TString::Format("[%d]", arraylen), kFALSE);
}
}
chld.SetRootClass(mcl);
if (chld.CanExpandItem()) {
if (coll_offset >= 0) {
ScanCollection(chld, (TCollection *)(member_ptr + coll_offset));
}
}
if (chld.SetFoundResult(ptr, cl, member)) break;
}
}
}
void TRootSniffer::ScanObjectProperties(TRootSnifferScanRec &rec, TObject *obj)
{
TClass* cl = obj ? obj->IsA() : 0;
const char* pos = strstr(cl ? cl->GetTitle() : "", "*SNIFF*");
if (pos==0) return;
pos += 7;
while (*pos != 0) {
if (*pos == ' ') { pos++; continue; }
const char* pos0 = pos;
while ((*pos != 0) && (*pos != '=')) pos++;
if (*pos == 0) return;
TString name(pos0, pos-pos0);
pos++;
Bool_t quotes = (*pos == '\"');
if (quotes) pos++;
pos0 = pos;
while ((*pos != 0) && (*pos != (quotes ? '\"' : ' '))) pos++;
TString value(pos0, pos-pos0);
rec.SetField(name, value);
if (quotes) pos++;
pos++;
}
}
void TRootSniffer::ScanObjectChilds(TRootSnifferScanRec &rec, TObject *obj)
{
if (obj->InheritsFrom(TFolder::Class())) {
ScanCollection(rec, ((TFolder *) obj)->GetListOfFolders());
} else if (obj->InheritsFrom(TDirectory::Class())) {
TDirectory *dir = (TDirectory *) obj;
ScanCollection(rec, dir->GetList(), 0, dir->GetListOfKeys());
} else if (obj->InheritsFrom(TTree::Class())) {
if (!rec.IsReadOnly(fReadOnly)) {
rec.SetField("_player", "JSROOT.drawTreePlayer");
rec.SetField("_prereq", "jq2d");
}
ScanCollection(rec, ((TTree *) obj)->GetListOfLeaves());
} else if (obj->InheritsFrom(TBranch::Class())) {
ScanCollection(rec, ((TBranch *) obj)->GetListOfLeaves());
} else if (rec.CanExpandItem()) {
ScanObjectMembers(rec, obj->IsA(), (char *)obj);
}
}
void TRootSniffer::ScanCollection(TRootSnifferScanRec &rec, TCollection *lst,
const char *foldername, TCollection *keys_lst)
{
if (((lst == 0) || (lst->GetSize() == 0)) && ((keys_lst == 0) || (keys_lst->GetSize() == 0))) return;
TRootSnifferScanRec folderrec;
if (foldername) {
if (!folderrec.GoInside(rec, 0, foldername, this)) return;
}
TRootSnifferScanRec &master = foldername ? folderrec : rec;
if (lst != 0) {
TIter iter(lst);
TObject *next = iter();
Bool_t isany = kFALSE;
while (next!=0) {
if (IsItemField(next)) {
if (!isany && (next->GetName() != 0) && ((*(next->GetName()) == '_') || master.ScanOnlyFields()))
master.SetField(next->GetName(), next->GetTitle());
next = iter();
continue;
}
isany = kTRUE;
TObject* obj = next;
TRootSnifferScanRec chld;
if (!chld.GoInside(master, obj, 0, this)) { next = iter(); continue; }
if (chld.SetResult(obj, obj->IsA())) return;
Bool_t has_kind(kFALSE), has_title(kFALSE);
ScanObjectProperties(chld, obj);
while ((next = iter()) != 0) {
if (!IsItemField(next)) break;
if ((next->GetName() != 0) && ((*(next->GetName()) == '_') || chld.ScanOnlyFields())) {
chld.SetField(next->GetName(), next->GetTitle());
if (strcmp(next->GetName(), item_prop_kind)==0) has_kind = kTRUE;
if (strcmp(next->GetName(), item_prop_title)==0) has_title = kTRUE;
}
}
if (!has_kind) chld.SetRootClass(obj->IsA());
if (!has_title && (obj->GetTitle()!=0)) chld.SetField(item_prop_title, obj->GetTitle());
ScanObjectChilds(chld, obj);
if (chld.SetResult(obj, obj->IsA())) return;
}
}
if (keys_lst != 0) {
TIter iter(keys_lst);
TObject *kobj(0);
while ((kobj = iter()) != 0) {
TKey *key = dynamic_cast<TKey *>(kobj);
if (key == 0) continue;
TObject *obj = (lst == 0) ? 0 : lst->FindObject(key->GetName());
if ((obj!=0) && (strcmp(obj->ClassName(),key->GetClassName())!=0)) obj = 0;
if ((obj != 0) && (master.fMask & TRootSnifferScanRec::kScan)) continue;
Bool_t iskey = kFALSE;
if (obj == 0) { obj = key; iskey = kTRUE; }
TRootSnifferScanRec chld;
TString fullname = TString::Format("%s;%d", key->GetName(), key->GetCycle());
if (chld.GoInside(master, obj, fullname.Data(), this)) {
if (!chld.IsReadOnly(fReadOnly) && iskey && chld.IsReadyForResult()) {
TObject *keyobj = key->ReadObj();
if (keyobj != 0)
if (chld.SetResult(keyobj, keyobj->IsA())) return;
}
if (chld.SetResult(obj, obj->IsA())) return;
TClass *obj_class = obj->IsA();
ScanObjectProperties(chld, obj);
if (obj->GetTitle()!=0) chld.SetField(item_prop_title, obj->GetTitle());
if (!chld.IsReadOnly(fReadOnly) && iskey) {
if (strcmp(key->GetClassName(), "TDirectoryFile") == 0) {
if (chld.fLevel == 0) {
TDirectory *dir = dynamic_cast<TDirectory *>(key->ReadObj());
if (dir != 0) {
obj = dir;
obj_class = dir->IsA();
}
} else {
chld.SetField(item_prop_more, "true", kFALSE);
chld.fHasMore = kTRUE;
}
} else {
obj_class = TClass::GetClass(key->GetClassName());
if (obj_class && obj_class->InheritsFrom(TTree::Class())) {
if (rec.CanExpandItem()) {
obj = key->ReadObj();
if (obj) obj_class = obj->IsA();
} else {
rec.SetField("_player", "JSROOT.drawTreePlayerKey");
rec.SetField("_prereq", "jq2d");
}
}
}
}
rec.SetRootClass(obj_class);
ScanObjectChilds(chld, obj);
if (chld.SetResult(obj, obj_class)) return;
}
}
}
}
void TRootSniffer::ScanRoot(TRootSnifferScanRec &rec)
{
rec.SetField(item_prop_kind, "ROOT.Session");
if (fCurrentArg && fCurrentArg->GetUserName())
rec.SetField(item_prop_user, fCurrentArg->GetUserName());
TFolder *topf = dynamic_cast<TFolder *>(gROOT->FindObject("//root/http"));
if (topf) {
rec.SetField(item_prop_title, topf->GetTitle());
ScanCollection(rec, topf->GetListOfFolders());
}
{
TRootSnifferScanRec chld;
if (chld.GoInside(rec, 0, "StreamerInfo", this)) {
chld.SetField(item_prop_kind, "ROOT.TStreamerInfoList");
chld.SetField(item_prop_title, "List of streamer infos for binary I/O");
chld.SetField(item_prop_hidden, "true");
chld.SetField("_after_request", "JSROOT.MarkAsStreamerInfo");
}
}
if (IsScanGlobalDir()) {
ScanCollection(rec, gROOT->GetList());
ScanCollection(rec, gROOT->GetListOfCanvases(), "Canvases");
ScanCollection(rec, gROOT->GetListOfFiles(), "Files");
}
}
Bool_t TRootSniffer::IsDrawableClass(TClass *cl)
{
if (cl == 0) return kFALSE;
if (cl->InheritsFrom(TH1::Class())) return kTRUE;
if (cl->InheritsFrom(TGraph::Class())) return kTRUE;
if (cl->InheritsFrom(TCanvas::Class())) return kTRUE;
if (cl->InheritsFrom(TProfile::Class())) return kTRUE;
return kFALSE;
}
void TRootSniffer::ScanHierarchy(const char *topname, const char *path,
TRootSnifferStore *store, Bool_t only_fields)
{
TRootSnifferScanRec rec;
rec.fSearchPath = path;
if (rec.fSearchPath) {
while(*rec.fSearchPath == '/') rec.fSearchPath++;
if (*rec.fSearchPath == 0) rec.fSearchPath = 0;
}
rec.fMask = (rec.fSearchPath == 0) ? TRootSnifferScanRec::kScan : TRootSnifferScanRec::kExpand;
if (only_fields) rec.fMask |= TRootSnifferScanRec::kOnlyFields;
rec.fStore = store;
rec.CreateNode(topname);
if ((rec.fSearchPath == 0) && (GetAutoLoad() != 0))
rec.SetField(item_prop_autoload, GetAutoLoad());
ScanRoot(rec);
rec.CloseNode();
}
void *TRootSniffer::FindInHierarchy(const char *path, TClass **cl,
TDataMember **member, Int_t *chld)
{
if (IsStreamerInfoItem(path)) {
CreateMemFile();
if (cl && fSinfo) *cl = fSinfo->IsA();
return fSinfo;
}
TRootSnifferStore store;
TRootSnifferScanRec rec;
rec.fSearchPath = path;
rec.fMask = (chld != 0) ? TRootSnifferScanRec::kCheckChilds : TRootSnifferScanRec::kSearch;
if (*rec.fSearchPath == '/') rec.fSearchPath++;
rec.fStore = &store;
ScanRoot(rec);
TDataMember *res_member = store.GetResMember();
TClass *res_cl = store.GetResClass();
void *res = store.GetResPtr();
if ((res_member!=0) && (res_cl!=0) && (member==0)) {
res_cl = (res_member->IsBasic() || res_member->IsSTLContainer()) ? 0 :
gROOT->GetClass(res_member->GetTypeName());
TRealData *rdata = res_cl->GetRealData(res_member->GetName());
if (rdata) {
res = (char *) res + rdata->GetThisOffset();
if (res_member->IsaPointer()) res = *((char **) res);
} else {
res = 0;
}
}
if (cl) *cl = res_cl;
if (member) *member = res_member;
if (chld) *chld = store.GetResNumChilds();
fCurrentRestrict = store.GetResRestrict();
return res;
}
TObject *TRootSniffer::FindTObjectInHierarchy(const char *path)
{
TClass *cl(0);
void *obj = FindInHierarchy(path, &cl);
return (cl != 0) && (cl->GetBaseClassOffset(TObject::Class()) == 0) ? (TObject *) obj : 0;
}
ULong_t TRootSniffer::GetStreamerInfoHash()
{
return fSinfo ? fSinfo->GetSize() : 0;
}
ULong_t TRootSniffer::GetItemHash(const char *itemname)
{
if (IsStreamerInfoItem(itemname)) return GetStreamerInfoHash();
TObject *obj = FindTObjectInHierarchy(itemname);
return obj == 0 ? 0 : TString::Hash(obj, obj->IsA()->Size());
}
Bool_t TRootSniffer::CanDrawItem(const char *path)
{
TClass *obj_cl(0);
void *res = FindInHierarchy(path, &obj_cl);
return (res != 0) && IsDrawableClass(obj_cl);
}
Bool_t TRootSniffer::CanExploreItem(const char *path)
{
TClass *obj_cl(0);
Int_t obj_chld(-1);
void *res = FindInHierarchy(path, &obj_cl, 0, &obj_chld);
return (res != 0) && (obj_chld > 0);
}
void TRootSniffer::CreateMemFile()
{
if (fMemFile != 0) return;
TDirectory *olddir = gDirectory;
gDirectory = 0;
TFile *oldfile = gFile;
gFile = 0;
fMemFile = new TMemFile("dummy.file", "RECREATE");
gROOT->GetListOfFiles()->Remove(fMemFile);
TH1F *d = new TH1F("d", "d", 10, 0, 10);
fMemFile->WriteObject(d, "h1");
delete d;
TGraph *gr = new TGraph(10);
gr->SetName("abc");
fMemFile->WriteObject(gr, "gr1");
delete gr;
fMemFile->WriteStreamerInfo();
TList *l = new TList();
l->Add(gROOT->GetListOfStreamerInfo()->FindObject("TGraph"));
l->Add(gROOT->GetListOfStreamerInfo()->FindObject("TH1F"));
l->Add(gROOT->GetListOfStreamerInfo()->FindObject("TH1"));
l->Add(gROOT->GetListOfStreamerInfo()->FindObject("TNamed"));
l->Add(gROOT->GetListOfStreamerInfo()->FindObject("TObject"));
fMemFile->WriteObject(l, "ll");
delete l;
fMemFile->WriteStreamerInfo();
fSinfo = fMemFile->GetStreamerInfoList();
gDirectory = olddir;
gFile = oldfile;
}
Bool_t TRootSniffer::ProduceJson(const char *path, const char *options,
TString &res)
{
if ((path == 0) || (*path == 0)) return kFALSE;
if (*path == '/') path++;
TUrl url;
url.SetOptions(options);
url.ParseOptions();
Int_t compact = -1;
if (url.GetValueFromOptions("compact"))
compact = url.GetIntValueFromOptions("compact");
TClass *obj_cl(0);
TDataMember *member(0);
void *obj_ptr = FindInHierarchy(path, &obj_cl, &member);
if ((obj_ptr == 0) || ((obj_cl == 0) && (member == 0))) return kFALSE;
res = TBufferJSON::ConvertToJSON(obj_ptr, obj_cl, compact >= 0 ? compact : 0, member ? member->GetName() : 0);
return res.Length() > 0;
}
Bool_t TRootSniffer::ExecuteCmd(const char *path, const char *options,
TString &res)
{
TFolder *parent(0);
TObject *obj = GetItem(path, parent, kFALSE, kFALSE);
const char *kind = GetItemField(parent, obj, item_prop_kind);
if ((kind == 0) || (strcmp(kind, "Command") != 0)) {
if (gDebug > 0) Info("ExecuteCmd", "Entry %s is not a command", path);
res = "false";
return kTRUE;
}
const char *cmethod = GetItemField(parent, obj, "method");
if ((cmethod==0) || (strlen(cmethod)==0)) {
if (gDebug > 0) Info("ExecuteCmd", "Entry %s do not defines method for execution", path);
res = "false";
return kTRUE;
}
if (fRestrictions.GetLast()>=0) {
FindInHierarchy(path);
if (fCurrentRestrict==1) {
if (gDebug > 0) Info("ExecuteCmd", "Entry %s not allowed for specified user", path);
res = "false";
return kTRUE;
}
}
TString method = cmethod;
const char *cnumargs = GetItemField(parent, obj, "_numargs");
Int_t numargs = cnumargs ? TString(cnumargs).Atoi() : 0;
if (numargs > 0) {
TUrl url;
url.SetOptions(options);
url.ParseOptions();
for (Int_t n=0; n<numargs;n++) {
TString argname = TString::Format("arg%d", n+1);
const char* argvalue = url.GetValueFromOptions(argname);
if (argvalue==0) {
if (gDebug > 0) Info("ExecuteCmd", "For command %s argument %s not specified in options %s", path, argname.Data(), options);
res = "false";
return kTRUE;
}
TString svalue = DecodeUrlOptionValue(argvalue, kTRUE);
argname = TString("%") + argname + TString("%");
method.ReplaceAll(argname, svalue);
}
}
if (gDebug > 0) Info("ExecuteCmd", "Executing command %s method:%s", path, method.Data());
TObject *item_obj = 0;
Ssiz_t separ = method.Index("/->");
if (method.Index("this->") == 0) {
item_obj = this;
separ = 3;
} else
if (separ != kNPOS) {
item_obj = FindTObjectInHierarchy(TString(method.Data(), separ).Data());
}
if (item_obj != 0) {
method = TString::Format("((%s*)%lu)->%s", item_obj->ClassName(), (long unsigned) item_obj, method.Data() + separ + 3);
if (gDebug > 2) Info("ExecuteCmd", "Executing %s", method.Data());
}
Long_t v = gROOT->ProcessLineSync(method.Data());
res.Form("%ld", v);
return kTRUE;
}
Bool_t TRootSniffer::ProduceItem(const char *path, const char *options, TString &res, Bool_t asjson)
{
if (asjson) {
TRootSnifferStoreJson store(res, strstr(options, "compact")!=0);
ScanHierarchy("top", path, &store, kTRUE);
} else {
TRootSnifferStoreXml store(res, strstr(options, "compact")!=0);
ScanHierarchy("top", path, &store, kTRUE);
}
return res.Length() > 0;
}
Bool_t TRootSniffer::ProduceXml(const char *path, const char * ,
TString &res)
{
if ((path == 0) || (*path == 0)) return kFALSE;
if (*path == '/') path++;
TClass *obj_cl(0);
void *obj_ptr = FindInHierarchy(path, &obj_cl);
if ((obj_ptr == 0) || (obj_cl == 0)) return kFALSE;
res = TBufferXML::ConvertToXML(obj_ptr, obj_cl);
return res.Length() > 0;
}
TString TRootSniffer::DecodeUrlOptionValue(const char *value, Bool_t remove_quotes)
{
if ((value == 0) || (strlen(value) == 0)) return TString();
TString res = value;
res.ReplaceAll("%27", "\'");
res.ReplaceAll("%22", "\"");
res.ReplaceAll("%3E", ">");
res.ReplaceAll("%3C", "<");
res.ReplaceAll("%20", " ");
res.ReplaceAll("%5B", "[");
res.ReplaceAll("%5D", "]");
if (remove_quotes && (res.Length() > 1) &&
((res[0] == '\'') || (res[0] == '\"')) && (res[0] == res[res.Length() - 1])) {
res.Remove(res.Length() - 1);
res.Remove(0, 1);
}
return res;
}
Bool_t TRootSniffer::ProduceExe(const char *path, const char *options, Int_t reskind, TString *res_str, void **res_ptr, Long_t *res_length)
{
TString *debug = (reskind == 0) ? res_str : 0;
if ((path == 0) || (*path == 0)) {
if (debug) debug->Append("Item name not specified\n");
return debug != 0;
}
if (*path == '/') path++;
TClass *obj_cl(0);
void *obj_ptr = FindInHierarchy(path, &obj_cl);
if (debug) debug->Append(TString::Format("Item:%s found:%s\n", path, obj_ptr ? "true" : "false"));
if ((obj_ptr == 0) || (obj_cl == 0)) return debug != 0;
TUrl url;
url.SetOptions(options);
const char *method_name = url.GetValueFromOptions("method");
TString prototype = DecodeUrlOptionValue(url.GetValueFromOptions("prototype"), kTRUE);
TString funcname = DecodeUrlOptionValue(url.GetValueFromOptions("func"), kTRUE);
TMethod *method = 0;
TFunction *func = 0;
if (method_name != 0) {
if (prototype.Length() == 0) {
if (debug) debug->Append(TString::Format("Search for any method with name \'%s\'\n", method_name));
method = obj_cl->GetMethodAllAny(method_name);
} else {
if (debug) debug->Append(TString::Format("Search for method \'%s\' with prototype \'%s\'\n", method_name, prototype.Data()));
method = obj_cl->GetMethodWithPrototype(method_name, prototype);
}
}
if (method != 0) {
if (debug) debug->Append(TString::Format("Method: %s\n", method->GetPrototype()));
} else {
if (funcname.Length() > 0) {
if (prototype.Length() == 0) {
if (debug) debug->Append(TString::Format("Search for any function with name \'%s\'\n", funcname.Data()));
func = gROOT->GetGlobalFunction(funcname);
} else {
if (debug) debug->Append(TString::Format("Search for function \'%s\' with prototype \'%s\'\n", funcname.Data(), prototype.Data()));
func = gROOT->GetGlobalFunctionWithPrototype(funcname, prototype);
}
}
if (func != 0) {
if (debug) debug->Append(TString::Format("Function: %s\n", func->GetPrototype()));
}
}
if ((method == 0) && (func==0)) {
if (debug) debug->Append("Method not found\n");
return debug != 0;
}
if ((fReadOnly && (fCurrentRestrict == 0)) || (fCurrentRestrict == 1)) {
if ((method!=0) && (fCurrentAllowedMethods.Index(method_name) == kNPOS)) {
if (debug) debug->Append("Server runs in read-only mode, method cannot be executed\n");
return debug != 0;
} else
if ((func!=0) && (fCurrentAllowedMethods.Index(funcname) == kNPOS)) {
if (debug) debug->Append("Server runs in read-only mode, function cannot be executed\n");
return debug != 0;
} else {
if (debug) debug->Append("For that special method server allows access even read-only mode is specified\n");
}
}
TList *args = method ? method->GetListOfMethodArgs() : func->GetListOfMethodArgs();
TList garbage;
garbage.SetOwner(kTRUE);
TObject *post_obj = 0;
TString call_args;
TIter next(args);
TMethodArg *arg = 0;
while ((arg = (TMethodArg *) next()) != 0) {
if ((strcmp(arg->GetName(), "rest_url_opt") == 0) &&
(strcmp(arg->GetFullTypeName(), "const char*") == 0) && (args->GetSize() == 1)) {
const char *pos = strstr(options, "method=");
if ((pos == 0) || (strlen(pos) < strlen(method_name) + 8)) return debug != 0;
call_args.Form("\"%s\"", pos + strlen(method_name) + 8);
break;
}
TString sval;
const char *val = url.GetValueFromOptions(arg->GetName());
if (val) {
sval = DecodeUrlOptionValue(val, kFALSE);
val = sval.Data();
}
if ((val!=0) && (strcmp(val,"_this_")==0)) {
sval.Form("(%s*)0x%lx", obj_cl->GetName(), (long unsigned) obj_ptr);
val = sval.Data();
} else
if ((val!=0) && (fCurrentArg!=0) && (fCurrentArg->GetPostData()!=0)) {
if (strcmp(val,"_post_object_xml_")==0) {
post_obj = TBufferXML::ConvertFromXML((const char*) fCurrentArg->GetPostData());
if (post_obj == 0) {
sval = "0";
} else {
sval.Form("(%s*)0x%lx", post_obj->ClassName(), (long unsigned) post_obj);
if (url.HasOption("_destroy_post_")) garbage.Add(post_obj);
}
val = sval.Data();
} else
if ((strcmp(val,"_post_object_")==0) && url.HasOption("_post_class_")) {
TString clname = url.GetValueFromOptions("_post_class_");
TClass* arg_cl = gROOT->GetClass(clname, kTRUE, kTRUE);
if ((arg_cl!=0) && (arg_cl->GetBaseClassOffset(TObject::Class()) == 0) && (post_obj==0)) {
post_obj = (TObject*) arg_cl->New();
if (post_obj==0) {
if (debug) debug->Append(TString::Format("Fail to create object of class %s\n", clname.Data()));
} else {
if (debug) debug->Append(TString::Format("Reconstruct object of class %s from POST data\n", clname.Data()));
TBufferFile buf(TBuffer::kRead, fCurrentArg->GetPostDataLength(), fCurrentArg->GetPostData(), kFALSE);
buf.MapObject(post_obj, arg_cl);
post_obj->Streamer(buf);
if (url.HasOption("_destroy_post_")) garbage.Add(post_obj);
}
}
sval.Form("(%s*)0x%lx", clname.Data(), (long unsigned) post_obj);
val = sval.Data();
} else
if (strcmp(val,"_post_data_")==0) {
sval.Form("(void*)0x%lx", (long unsigned) *res_ptr);
val = sval.Data();
} else
if (strcmp(val,"_post_length_")==0) {
sval.Form("%ld", (long) *res_length);
val = sval.Data();
}
}
if (val == 0) val = arg->GetDefault();
if (debug) debug->Append(TString::Format(" Argument:%s Type:%s Value:%s \n", arg->GetName(), arg->GetFullTypeName(), val ? val : "<missed>"));
if (val == 0) return debug != 0;
if (call_args.Length() > 0) call_args += ", ";
if ((strcmp(arg->GetFullTypeName(), "const char*") == 0) || (strcmp(arg->GetFullTypeName(), "Option_t*") == 0)) {
int len = strlen(val);
if ((strlen(val) < 2) || (*val != '\"') || (val[len - 1] != '\"'))
call_args.Append(TString::Format("\"%s\"", val));
else
call_args.Append(val);
} else {
call_args.Append(val);
}
}
TMethodCall *call = 0;
if (method!=0) {
call = new TMethodCall(obj_cl, method_name, call_args.Data());
if (debug) debug->Append(TString::Format("Calling obj->%s(%s);\n", method_name, call_args.Data()));
} else {
call = new TMethodCall(funcname.Data(), call_args.Data());
if (debug) debug->Append(TString::Format("Calling %s(%s);\n", funcname.Data(), call_args.Data()));
}
garbage.Add(call);
if (!call->IsValid()) {
if (debug) debug->Append("Fail: invalid TMethodCall\n");
return debug != 0;
}
Int_t compact = 0;
if (url.GetValueFromOptions("compact"))
compact = url.GetIntValueFromOptions("compact");
TString res = "null";
void *ret_obj = 0;
TClass *ret_cl = 0;
TBufferFile *resbuf = 0;
if (reskind==2) {
resbuf = new TBufferFile(TBuffer::kWrite, 10000);
garbage.Add(resbuf);
}
switch (call->ReturnType()) {
case TMethodCall::kLong: {
Long_t l(0);
if (method)
call->Execute(obj_ptr, l);
else
call->Execute(l);
if (resbuf) resbuf->WriteLong(l);
else res.Form("%ld", l);
break;
}
case TMethodCall::kDouble : {
Double_t d(0.);
if (method)
call->Execute(obj_ptr, d);
else
call->Execute(d);
if (resbuf) resbuf->WriteDouble(d);
else res.Form(TBufferJSON::GetFloatFormat(), d);
break;
}
case TMethodCall::kString : {
char *txt(0);
if (method)
call->Execute(obj_ptr, &txt);
else
call->Execute(0, &txt);
if (txt != 0) {
if (resbuf) resbuf->WriteString(txt);
else res.Form("\"%s\"", txt);
}
break;
}
case TMethodCall::kOther : {
std::string ret_kind = func ? func->GetReturnTypeNormalizedName() : method->GetReturnTypeNormalizedName();
if ((ret_kind.length() > 0) && (ret_kind[ret_kind.length() - 1] == '*')) {
ret_kind.resize(ret_kind.length() - 1);
ret_cl = gROOT->GetClass(ret_kind.c_str(), kTRUE, kTRUE);
}
if (ret_cl != 0) {
Long_t l(0);
if (method)
call->Execute(obj_ptr, l);
else
call->Execute(l);
if (l != 0) ret_obj = (void *) l;
} else {
if (method)
call->Execute(obj_ptr);
else
call->Execute();
}
break;
}
case TMethodCall::kNone : {
if (method)
call->Execute(obj_ptr);
else
call->Execute();
break;
}
}
const char *_ret_object_ = url.GetValueFromOptions("_ret_object_");
if (_ret_object_ != 0) {
TObject *obj = 0;
if (gDirectory != 0) obj = gDirectory->Get(_ret_object_);
if (debug) debug->Append(TString::Format("Return object %s found %s\n", _ret_object_, obj ? "true" : "false"));
if (obj == 0) {
res = "null";
} else {
ret_obj = obj;
ret_cl = obj->IsA();
}
}
if ((ret_obj != 0) && (ret_cl != 0)) {
if ((resbuf != 0) && (ret_cl->GetBaseClassOffset(TObject::Class()) == 0)) {
TObject *obj = (TObject *) ret_obj;
resbuf->MapObject(obj);
obj->Streamer(*resbuf);
if (fCurrentArg) fCurrentArg->SetExtraHeader("RootClassName", ret_cl->GetName());
} else {
res = TBufferJSON::ConvertToJSON(ret_obj, ret_cl, compact);
}
}
if ((resbuf != 0) && (resbuf->Length() > 0) && (res_ptr != 0) && (res_length != 0)) {
*res_ptr = malloc(resbuf->Length());
memcpy(*res_ptr, resbuf->Buffer(), resbuf->Length());
*res_length = resbuf->Length();
}
if (debug) debug->Append(TString::Format("Result = %s\n", res.Data()));
if ((reskind == 1) && res_str) *res_str = res;
if (url.HasOption("_destroy_result_") && (ret_obj != 0) && (ret_cl != 0)) {
ret_cl->Destructor(ret_obj);
if (debug) debug->Append("Destroy result object at the end\n");
}
garbage.Delete();
return kTRUE;
}
Bool_t TRootSniffer::ProduceMulti(const char *path, const char *options, void *&ptr, Long_t &length, TString &str, Bool_t asjson)
{
if ((fCurrentArg==0) || (fCurrentArg->GetPostDataLength()<=0) || (fCurrentArg->GetPostData()==0)) return kFALSE;
const char* args = (const char*) fCurrentArg->GetPostData();
const char* ends = args + fCurrentArg->GetPostDataLength();
TUrl url;
url.SetOptions(options);
Int_t number = 0;
if (url.GetValueFromOptions("number"))
number = url.GetIntValueFromOptions("number");
std::vector<void*> mem;
std::vector<Long_t> memlen;
if (asjson) str = "[";
for (Int_t n=0;n<number;n++) {
const char* next = args;
while ((next < ends) && (*next != '\n')) next++;
if (next==ends) {
Error("ProduceMulti", "Not enough arguments in POST block");
break;
}
TString file1(args, next - args);
args = next + 1;
TString path1, opt1;
Int_t pos = file1.First('?');
if (pos != kNPOS) {
opt1 = file1(pos+1, file1.Length() - pos);
file1.Resize(pos);
}
pos = file1.Last('/');
if (pos != kNPOS) {
path1 = file1(0, pos);
file1.Remove(0, pos+1);
}
if ((path!=0) && (*path!=0)) path1 = TString(path) + "/" + path1;
void* ptr1 = 0;
Long_t len1 = 0;
TString str1;
Produce(path1, file1, opt1, ptr1, len1, str1);
if (asjson) {
if (n>0) str.Append(", ");
if (ptr1!=0) { str.Append("\"<non-supported binary>\""); free(ptr1); } else
if (str1.Length()>0) str.Append(str1); else str.Append("null");
} else {
if ((str1.Length()>0) && (ptr1==0)) {
len1 = str1.Length();
ptr1 = malloc(len1);
memcpy(ptr1, str1.Data(), len1);
}
mem.push_back(ptr1);
memlen.push_back(len1);
}
}
if (asjson) {
str.Append("]");
} else {
length = 0;
for (unsigned n=0;n<mem.size();n++) {
length += 4 + memlen[n];
}
ptr = malloc(length);
char* curr = (char*) ptr;
for (unsigned n=0;n<mem.size();n++) {
Long_t l = memlen[n];
*curr++ = (char) (l & 0xff); l = l >> 8;
*curr++ = (char) (l & 0xff); l = l >> 8;
*curr++ = (char) (l & 0xff); l = l >> 8;
*curr++ = (char) (l & 0xff);
if ((mem[n]!=0) && (memlen[n]>0)) memcpy(curr, mem[n], memlen[n]);
curr+=memlen[n];
}
}
for (unsigned n=0;n<mem.size();n++) free(mem[n]);
return kTRUE;
}
Bool_t TRootSniffer::IsStreamerInfoItem(const char *itemname)
{
if ((itemname == 0) || (*itemname == 0)) return kFALSE;
return (strcmp(itemname, "StreamerInfo") == 0) || (strcmp(itemname, "StreamerInfo/") == 0);
}
Bool_t TRootSniffer::ProduceBinary(const char *path, const char * , void *&ptr,
Long_t &length)
{
if ((path == 0) || (*path == 0)) return kFALSE;
if (*path == '/') path++;
TClass *obj_cl(0);
void *obj_ptr = FindInHierarchy(path, &obj_cl);
if ((obj_ptr == 0) || (obj_cl == 0)) return kFALSE;
if (obj_cl->GetBaseClassOffset(TObject::Class()) != 0) {
Info("ProduceBinary", "Non-TObject class not supported");
return kFALSE;
}
CreateMemFile();
TDirectory *olddir = gDirectory;
gDirectory = 0;
TFile *oldfile = gFile;
gFile = 0;
TObject *obj = (TObject *) obj_ptr;
TBufferFile *sbuf = new TBufferFile(TBuffer::kWrite, 100000);
sbuf->SetParent(fMemFile);
sbuf->MapObject(obj);
obj->Streamer(*sbuf);
if (fCurrentArg) fCurrentArg->SetExtraHeader("RootClassName", obj_cl->GetName());
delete fSinfo;
fMemFile->WriteStreamerInfo();
fSinfo = fMemFile->GetStreamerInfoList();
gDirectory = olddir;
gFile = oldfile;
ptr = malloc(sbuf->Length());
memcpy(ptr, sbuf->Buffer(), sbuf->Length());
length = sbuf->Length();
delete sbuf;
return kTRUE;
}
Bool_t TRootSniffer::ProduceImage(Int_t kind, const char *path,
const char *options, void *&ptr,
Long_t &length)
{
ptr = 0;
length = 0;
if ((path == 0) || (*path == 0)) return kFALSE;
if (*path == '/') path++;
TClass *obj_cl(0);
void *obj_ptr = FindInHierarchy(path, &obj_cl);
if ((obj_ptr == 0) || (obj_cl == 0)) return kFALSE;
if (obj_cl->GetBaseClassOffset(TObject::Class()) != 0) {
Error("TRootSniffer", "Only derived from TObject classes can be drawn");
return kFALSE;
}
TObject *obj = (TObject *) obj_ptr;
TImage *img = TImage::Create();
if (img == 0) return kFALSE;
if (obj->InheritsFrom(TPad::Class())) {
if (gDebug > 1)
Info("TRootSniffer", "Crate IMAGE directly from pad");
img->FromPad((TPad *) obj);
} else if (IsDrawableClass(obj->IsA())) {
if (gDebug > 1)
Info("TRootSniffer", "Crate IMAGE from object %s", obj->GetName());
Int_t width(300), height(200);
TString drawopt = "";
if ((options != 0) && (*options != 0)) {
TUrl url;
url.SetOptions(options);
url.ParseOptions();
Int_t w = url.GetIntValueFromOptions("w");
if (w > 10) width = w;
Int_t h = url.GetIntValueFromOptions("h");
if (h > 10) height = h;
const char *opt = url.GetValueFromOptions("opt");
if (opt != 0) drawopt = opt;
}
Bool_t isbatch = gROOT->IsBatch();
TVirtualPad *save_gPad = gPad;
if (!isbatch) gROOT->SetBatch(kTRUE);
TCanvas *c1 = new TCanvas("__online_draw_canvas__", "title", width, height);
obj->Draw(drawopt.Data());
img->FromPad(c1);
delete c1;
if (!isbatch) gROOT->SetBatch(kFALSE);
gPad = save_gPad;
} else {
delete img;
return kFALSE;
}
TImage *im = TImage::Create();
im->Append(img);
char *png_buffer(0);
int size(0);
im->GetImageBuffer(&png_buffer, &size, (TImage::EImageFileTypes) kind);
if ((png_buffer != 0) && (size > 0)) {
ptr = malloc(size);
length = size;
memcpy(ptr, png_buffer, length);
}
delete [] png_buffer;
delete im;
return ptr != 0;
}
Bool_t TRootSniffer::Produce(const char *path, const char *file,
const char *options, void *&ptr, Long_t &length, TString &str)
{
if ((file == 0) || (*file == 0)) return kFALSE;
if (strcmp(file, "root.bin") == 0)
return ProduceBinary(path, options, ptr, length);
if (strcmp(file, "root.png") == 0)
return ProduceImage(TImage::kPng, path, options, ptr, length);
if (strcmp(file, "root.jpeg") == 0)
return ProduceImage(TImage::kJpeg, path, options, ptr, length);
if (strcmp(file, "root.gif") == 0)
return ProduceImage(TImage::kGif, path, options, ptr, length);
if (strcmp(file, "exe.bin") == 0)
return ProduceExe(path, options, 2, 0, &ptr, &length);
if (strcmp(file, "root.xml") == 0)
return ProduceXml(path, options, str);
if (strcmp(file, "root.json") == 0)
return ProduceJson(path, options, str);
if (strcmp(file, "exe.txt") == 0)
return ProduceExe(path, options, 0, &str);
if (strcmp(file, "exe.json") == 0)
return ProduceExe(path, options, 1, &str);
if (strcmp(file, "cmd.json") == 0)
return ExecuteCmd(path, options, str);
if (strcmp(file, "item.json") == 0)
return ProduceItem(path, options, str, kTRUE);
if (strcmp(file, "item.xml") == 0)
return ProduceItem(path, options, str, kFALSE);
if (strcmp(file, "multi.bin") == 0)
return ProduceMulti(path, options, ptr, length, str, kFALSE);
if (strcmp(file, "multi.json") == 0)
return ProduceMulti(path, options, ptr, length, str, kTRUE);
return kFALSE;
}
TObject *TRootSniffer::GetItem(const char *fullname, TFolder *&parent, Bool_t force, Bool_t within_objects)
{
TFolder *topf = gROOT->GetRootFolder();
if (topf == 0) {
Error("RegisterObject", "Not found top ROOT folder!!!");
return 0;
}
TFolder *httpfold = dynamic_cast<TFolder *>(topf->FindObject("http"));
if (httpfold == 0) {
if (!force) return 0;
httpfold = topf->AddFolder("http", "ROOT http server");
httpfold->SetBit(kCanDelete);
gROOT->GetListOfCleanups()->Add(httpfold);
}
parent = httpfold;
TObject *obj = httpfold;
if (fullname==0) return httpfold;
TString path = fullname;
if (within_objects && ((path.Length()==0) || (path[0]!='/')))
path = fObjectsPath + "/" + path;
TString tok;
Ssiz_t from(0);
while (path.Tokenize(tok,from,"/")) {
if (tok.Length()==0) continue;
TFolder *fold = dynamic_cast<TFolder *> (obj);
if (fold == 0) return 0;
TIter iter(fold->GetListOfFolders());
while ((obj = iter()) != 0) {
if (IsItemField(obj)) continue;
if (tok.CompareTo(obj->GetName())==0) break;
}
if (obj == 0) {
if (!force) return 0;
obj = fold->AddFolder(tok, "sub-folder");
obj->SetBit(kCanDelete);
}
parent = fold;
}
return obj;
}
TFolder *TRootSniffer::GetSubFolder(const char *subfolder, Bool_t force)
{
TFolder *parent = 0;
return dynamic_cast<TFolder *> (GetItem(subfolder, parent, force));
}
Bool_t TRootSniffer::RegisterObject(const char *subfolder, TObject *obj)
{
TFolder *f = GetSubFolder(subfolder, kTRUE);
if (f == 0) return kFALSE;
obj->SetBit(kMustCleanup);
f->Add(obj);
return kTRUE;
}
Bool_t TRootSniffer::UnregisterObject(TObject *obj)
{
if (obj == 0) return kTRUE;
TFolder *topf = dynamic_cast<TFolder *>(gROOT->FindObject("//root/http"));
if (topf == 0) {
Error("UnregisterObject", "Not found //root/http folder!!!");
return kFALSE;
}
if (topf) topf->RecursiveRemove(obj);
return kTRUE;
}
Bool_t TRootSniffer::CreateItem(const char *fullname, const char *title)
{
TFolder *f = GetSubFolder(fullname, kTRUE);
if (f == 0) return kFALSE;
if (title) f->SetTitle(title);
return kTRUE;
}
Bool_t TRootSniffer::IsItemField(TObject* obj) const
{
return (obj!=0) && (obj->IsA() == TNamed::Class()) && obj->TestBit(kItemField);
}
Bool_t TRootSniffer::AccessField(TFolder *parent, TObject *chld,
const char *name, const char *value, TNamed **only_get)
{
if (parent==0) return kFALSE;
if (chld==0) {
Info("SetField", "Should be special case for top folder, support later");
return kFALSE;
}
TIter iter(parent->GetListOfFolders());
TObject* obj = 0;
Bool_t find(kFALSE), last_find(kFALSE);
if (parent == chld) { last_find = find = kTRUE; }
TNamed* curr = 0;
while ((obj = iter()) != 0) {
if (IsItemField(obj)) {
if (last_find && (obj->GetName()!=0) && !strcmp(name, obj->GetName())) curr = (TNamed*) obj;
} else {
last_find = (obj == chld);
if (last_find) find = kTRUE;
if (find && !last_find) break;
}
}
if (!find) return kFALSE;
if (only_get!=0) {
*only_get = curr;
return curr!=0;
}
if (curr!=0) {
if (value!=0) { curr->SetTitle(value); }
else { parent->Remove(curr); delete curr; }
return kTRUE;
}
curr = new TNamed(name, value);
curr->SetBit(kItemField);
if (last_find) {
parent->Add(curr);
return kTRUE;
}
TList *lst = dynamic_cast<TList *> (parent->GetListOfFolders());
if (lst==0) {
Error("SetField", "Fail cast to TList");
return kFALSE;
}
if (parent==chld)
lst->AddFirst(curr);
else
lst->AddAfter(chld, curr);
return kTRUE;
}
Bool_t TRootSniffer::SetItemField(const char *fullname, const char *name, const char *value)
{
if ((fullname==0) || (name==0)) return kFALSE;
TFolder *parent(0);
TObject *obj = GetItem(fullname, parent);
if ((parent==0) || (obj==0)) return kFALSE;
if (strcmp(name, item_prop_title)==0) {
TNamed* n = dynamic_cast<TNamed*> (obj);
if (n!=0) { n->SetTitle(value); return kTRUE; }
}
return AccessField(parent, obj, name, value);
}
const char *TRootSniffer::GetItemField(TFolder *parent, TObject *obj, const char *name)
{
if ((parent==0) || (obj==0) || (name==0)) return 0;
TNamed *field(0);
if (!AccessField(parent, obj, name, 0, &field)) return 0;
return field ? field->GetTitle() : 0;
}
const char *TRootSniffer::GetItemField(const char *fullname, const char *name)
{
if (fullname==0) return 0;
TFolder *parent(0);
TObject *obj = GetItem(fullname, parent);
return GetItemField(parent, obj, name);
}
Bool_t TRootSniffer::RegisterCommand(const char *cmdname, const char *method, const char *icon)
{
CreateItem(cmdname, Form("command %s", method));
SetItemField(cmdname, "_kind", "Command");
if (icon != 0) {
if (strncmp(icon, "button;", 7) == 0) {
SetItemField(cmdname, "_fastcmd", "true");
icon += 7;
}
if (*icon != 0) SetItemField(cmdname, "_icon", icon);
}
SetItemField(cmdname, "method", method);
Int_t numargs = 0;
do {
TString nextname = TString::Format("%sarg%d%s","%",numargs+1,"%");
if (strstr(method,nextname.Data())==0) break;
numargs++;
} while (numargs<100);
if (numargs>0)
SetItemField(cmdname, "_numargs", TString::Format("%d", numargs));
return kTRUE;
}