<commit_before>// $Id$
// Author: <NAME>   22/12/2013

#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 "TMethodArg.h"
#include "TMethodCall.h"
#include "TDataMember.h"
#include "TDataType.h"
#include "TBaseClass.h"
#include "TObjString.h"
#include "TUrl.h"
#include "TImage.h"
#ifdef COMPILED_WITH_DABC
extern "C" void R__zip(int cxlevel, int *srcsize, char* src, int *tgtsize, char* tgt, int* irep);
#else
#include "RZip.h"
#endif
#include "TRootSnifferStore.h"

#include <stdlib.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"; // real object name

// ============================================================================

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootSnifferScanRec                                                  //
//                                                                      //
// Structure used to scan hierarchies of ROOT objects                   //
// Represents single level of hierarchy                                 //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
TRootSnifferScanRec::TRootSnifferScanRec() :
   fParent(0),
   fMask(0),
   fSearchPath(0),
   fLevel(0),
   fItemsNames(),
   fStore(0),
   fHasMore(kFALSE),
   fNodeStarted(kFALSE),
   fNumFields(0),
   fNumChilds(0)
{
   // constructor

   fItemsNames.SetOwner(kTRUE);
}

//______________________________________________________________________________
TRootSnifferScanRec::~TRootSnifferScanRec()
{
   // destructor

   CloseNode();
}

//______________________________________________________________________________
void TRootSnifferScanRec::SetField(const char *name, const char *value, Bool_t with_quotes)
{
   // record field for current element

   if (CanSetFields()) fStore->SetField(fLevel, name, value, with_quotes);
   fNumFields++;
}

//______________________________________________________________________________
void TRootSnifferScanRec::BeforeNextChild()
{
   // indicates that new child for current element will be started

   if (CanSetFields()) fStore->BeforeNextChild(fLevel, fNumChilds, fNumFields);
   fNumChilds++;
}

//______________________________________________________________________________
void TRootSnifferScanRec::MakeItemName(const char *objname, TString& itemname)
{
   // constructs item name from object name
   // if special symbols like '/', '#', ':', '&', '?'  are used in object name
   // they will be replaced with '_'.
   // To avoid item name duplication, additional id number can be appended

   std::string nnn = objname;

   size_t pos;

   // replace all special symbols which can make problem to navigate in hierarchy
   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::CreateNode(const char *_node_name)
{
   // creates new node with specified name
   // if special symbols like "[]&<>" are used, node name
   // will be replaced by default name like "extra_item_N" and
   // original node name will be recorded as "_original_name" field
   // Optionally, object name can be recorded as "_realname" field

   if (!CanSetFields()) return;

   fNodeStarted = kTRUE;

   if (fParent) fParent->BeforeNextChild();

   if (fStore) fStore->CreateNode(fLevel, _node_name);
}

//______________________________________________________________________________
void TRootSnifferScanRec::CloseNode()
{
   // close started node

   if (fStore && fNodeStarted) {
      fStore->CloseNode(fLevel, fNumChilds);
      fNodeStarted = kFALSE;
   }
}

//______________________________________________________________________________
void TRootSnifferScanRec::SetRootClass(TClass *cl)
{
   // set root class name as node kind
   // in addition, path to master item (streamer info) specified
   // Such master item required to correctly unstream data on JavaScript

   if ((cl != 0) && CanSetFields())
      SetField(item_prop_kind, TString::Format("ROOT.%s", cl->GetName()));
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::Done() const
{
   // returns true if scanning is done
   // Can happen when searched element is found

   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
{
   // Checks if result will be accepted.
   // Used to verify if sniffer should read object from the file

   if (Done()) return kFALSE;

   // only when doing search, result will be propagated
   if ((fMask & (kSearch | kCheckChilds)) == 0) return kFALSE;

   // only when full search path is scanned
   if (fSearchPath != 0) return kFALSE;

   if (fStore == 0) return kFALSE;

   return kTRUE;
}


//______________________________________________________________________________
Bool_t TRootSnifferScanRec::SetResult(void *obj, TClass *cl, TDataMember *member, Int_t chlds)
{
   // set results of scanning

   if (Done()) return kTRUE;

   if (!IsReadyForResult()) return kFALSE;

   fStore->SetResult(obj, cl, member, chlds);

   return Done();
}

//______________________________________________________________________________
Int_t TRootSnifferScanRec::Depth() const
{
   // returns current depth of scanned hierarchy

   Int_t cnt = 0;
   const TRootSnifferScanRec *rec = this;
   while (rec->fParent) {
      rec = rec->fParent;
      cnt++;
   }

   return cnt;
}

//______________________________________________________________________________
Int_t TRootSnifferScanRec::ExtraFolderLevel()
{
   // return level depth till folder, marked with extra flag
   // Objects in such folder can be 'expanded' -
   // one can get access to all class members
   // If no extra folder found, -1 is returned

   TRootSnifferScanRec *rec = this;
   Int_t cnt = 0;
   while (rec) {
      if (rec->fMask & kExtraFolder) return cnt;
      rec = rec->fParent;
      cnt++;
   }

   return -1;
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::CanExpandItem()
{
   // returns true if current item can be expanded - means one could explore
   // objects members

   if (fMask & (kExpand | kSearch | kCheckChilds)) return kTRUE;

   if (!fHasMore) return kFALSE;

   // if parent has expand mask, allow to expand item
   if (fParent && (fParent->fMask & kExpand)) return kTRUE;

   return kFALSE;
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::GoInside(TRootSnifferScanRec &super, TObject *obj,
                                     const char *obj_name)
{
   // Method verifies if new level of hierarchy
   // should be started with provided object.
   // If required, all necessary nodes and fields will be created
   // Used when different collection kinds should be scanned

   if (super.Done()) return kFALSE;

   if ((obj != 0) && (obj_name == 0)) obj_name = obj->GetName();

   // exclude zero names
   if ((obj_name == 0) || (*obj_name == 0)) return kFALSE;

   TString obj_item_name;

   const char* full_name = 0;

   // remove slashes from file names
   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, obj_item_name);

   fLevel = super.fLevel;
   fStore = super.fStore;
   fSearchPath = super.fSearchPath;
   fMask = super.fMask & kActions;
   fParent = &super;

   if (fMask & kScan) {
      // only when doing scan, increment level, used for text formatting
      fLevel++;
   } else {
      if (fSearchPath == 0) return kFALSE;

      if (strncmp(fSearchPath, obj_item_name.Data(), obj_item_name.Length()) != 0)
         return kFALSE;

      const char *separ = fSearchPath + obj_item_name.Length();

      Bool_t isslash = kFALSE;
      while (*separ == '/') {
         separ++;
         isslash = kTRUE;
      }

      if (*separ == 0) {
         fSearchPath = 0;
         if (fMask & kExpand) {
            fMask = kScan;
            fSearchPath = 0;
            fHasMore = kTRUE; // when found selected object, allow to scan it (and only it)
         }

      } else {
         if (!isslash) return kFALSE;
         fSearchPath = separ;
      }
   }

   CreateNode(obj_item_name.Data());

   if ((obj_name!=0) && (obj_item_name != obj_name))
      SetField(item_prop_realname, obj_name);

   if (full_name != 0)
      SetField("_fullname", full_name);

   return kTRUE;
}


// ====================================================================

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootSniffer                                                         //
//                                                                      //
// Sniffer of ROOT objects, data provider for THttpServer               //
// Provides methods to scan different structures like folders,          //
// directories, files, trees, collections                               //
// Can locate objects (or its data member) per name                     //
// Can be extended to application-specific classes                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
TRootSniffer::TRootSniffer(const char *name, const char *objpath) :
   TNamed(name, "sniffer of root objects"),
   fObjectsPath(objpath),
   fMemFile(0),
   fSinfoSize(0),
   fReadOnly(kTRUE)
{
   // constructor
}

//______________________________________________________________________________
TRootSniffer::~TRootSniffer()
{
   // destructor

   if (fMemFile) {
      delete fMemFile;
      fMemFile = 0;
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanObjectMemebers(TRootSnifferScanRec &rec, TClass *cl,
                                      char *ptr, unsigned long int cloffset)
{
   // scan object data members
   // some members like enum or static members will be excluded

   if ((cl == 0) || (ptr == 0) || rec.Done()) return;

   // first of all expand base classes
   TIter cliter(cl->GetListOfBases());
   TObject *obj = 0;
   while ((obj = cliter()) != 0) {
      TBaseClass *baseclass = dynamic_cast<TBaseClass *>(obj);
      if (baseclass == 0) continue;
      TClass *bclass = baseclass->GetClassPointer();
      if (bclass == 0) continue;

      // all parent classes scanned within same hierarchy level
      // this is how normal object streaming works
      ScanObjectMemebers(rec, bclass, ptr, cloffset + baseclass->GetDelta());
      if (rec.Done()) break;

//    this code was used when each base class creates its own sub level

//      TRootSnifferScanRec chld;
//      if (chld.GoInside(rec, baseclass)) {
//         ScanObjectMemebers(chld, bclass, ptr, cloffset + baseclass->GetDelta());
//         if (chld.Done()) break;
//      }
   }

   // than expand data members
   TIter iter(cl->GetListOfDataMembers());
   while ((obj = iter()) != 0) {
      TDataMember *member = dynamic_cast<TDataMember *>(obj);
      // exclude enum or static variables
      if ((member == 0) || (member->Property() & (kIsStatic | kIsEnum | kIsUnion))) continue;

      char *member_ptr = ptr + cloffset + member->GetOffset();
      if (member->IsaPointer()) member_ptr = *((char **) member_ptr);

      TRootSnifferScanRec chld;

      if (chld.GoInside(rec, member)) {
         TClass *mcl = (member->IsBasic() || member->IsSTLContainer()) ? 0 :
                       gROOT->GetClass(member->GetTypeName());

         Int_t coll_offset = mcl ? mcl->GetBaseClassOffset(TCollection::Class()) : -1;

         Bool_t iscollection = (coll_offset >= 0);
         if (iscollection) {
            chld.SetField(item_prop_more, "true", kFALSE);
            chld.fHasMore = kTRUE;
         }

         if (chld.SetResult(member_ptr, mcl, 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) {
            // store array dimensions in form [N1,N2,N3,...]
            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);
         }

         chld.SetRootClass(mcl);

         if (chld.CanExpandItem()) {
            if (iscollection) {
               // chld.SetField("#members", "true", kFALSE);
               ScanCollection(chld, (TCollection *)(member_ptr + coll_offset));
            }
         }

         if (chld.SetResult(member_ptr, mcl, member, chld.fNumChilds)) break;
      }
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanObject(TRootSnifferScanRec &rec, TObject *obj)
{
   // scan object content
   // if object is known collection class (like TFolder),
   // collection content will be scanned as well

   if (obj == 0) return;

   if (!fReadOnly && obj->InheritsFrom(TKey::Class()) && rec.IsReadyForResult()) {
      TObject* keyobj = ((TKey*) obj)->ReadObj();
      if (keyobj!=0)
         if (rec.SetResult(keyobj, keyobj->IsA())) return;
   }

   if (rec.SetResult(obj, obj->IsA())) return;

   TClass* obj_class = obj->IsA();

   if ((obj_class == TFolder::Class()) && obj->TestBit(kItemFolder)) {
      ScanItem(rec, (TFolder*) obj);
   } else {
      ScanObjectProperties(rec, obj, obj_class);
      rec.SetRootClass(obj_class);
      ScanObjectChilds(rec, obj);
   }

   // here we should know how many childs are accumulated
   rec.SetResult(obj, obj_class, 0, rec.fNumChilds);
}

//_____________________________________________________________________
void TRootSniffer::ScanObjectProperties(TRootSnifferScanRec &rec,
                               TObject* &obj, TClass* &obj_class)
{
   // scans basic object properties
   // here such fields as _typename, _title, _more properties can be specified

   const char* title = obj->GetTitle();
   if ((title!=0) && (*title!=0))
      rec.SetField(item_prop_title, title);

   int isextra = rec.ExtraFolderLevel();

   if ((isextra == 1) || ((isextra > 1) && !IsDrawableClass(obj->IsA()))) {
      rec.SetField(item_prop_more, "true", kFALSE);
      rec.fHasMore = kTRUE;
   }

   // special handling of TKey class - in non-readonly mode
   // sniffer allowed to fetch objects

   if (!fReadOnly && obj->InheritsFrom(TKey::Class())) {
      TKey* key = (TKey *) obj;
      if (strcmp(key->GetClassName(),"TDirectoryFile")==0) {
         if (rec.fLevel==0) {
            TDirectory* dir = dynamic_cast<TDirectory*> (key->ReadObj());
            if (dir!=0) {
               obj = dir;
               obj_class = dir->IsA();
            }
         } else {
            rec.SetField(item_prop_more, "true", kFALSE);
            rec.fHasMore = kTRUE;
         }
      } else {
         obj_class = TClass::GetClass(key->GetClassName());
      }
   }
}

//_____________________________________________________________________
void TRootSniffer::ScanItem(TRootSnifferScanRec &rec, TFolder* item)
{
   // scans basic object properties
   // here such fields as _typename, _title, _more properties can be specified

   const char* title = item->GetTitle();
   if ((title!=0) && (*title!=0))
      rec.SetField(item_prop_title, title);

   // first all properties
   TIter iter(item->GetListOfFolders());
   TObject* obj = 0;
   while ((obj = iter()) != 0) {
      if (obj->IsA()!=TNamed::Class()) continue;
      if (obj->GetName() || (*(obj->GetName()) == '_'))
         rec.SetField(obj->GetName(), obj->GetTitle());
   }

   iter.Reset();

   while ((obj = iter()) != 0) {
      if (obj->IsA()==TNamed::Class()) continue;
      TRootSnifferScanRec chld;
      if (chld.GoInside(rec, obj)) {
         ScanObject(chld, obj);
         if (chld.Done()) break;
      }
   }
}

//_____________________________________________________________________
void TRootSniffer::ScanObjectChilds(TRootSnifferScanRec &rec, TObject *obj)
{
   // scans object childs (if any)
   // here one scans collection, branches, trees and so on

   if (obj->InheritsFrom(TFolder::Class())) {
      // starting from special folder, we automatically scan members

      TFolder *fold = (TFolder *) obj;
      if (fold->TestBit(kMoreFolder)) rec.fMask = rec.fMask | TRootSnifferScanRec::kExtraFolder;

      ScanCollection(rec, fold->GetListOfFolders());
   } else if (obj->InheritsFrom(TDirectory::Class())) {
      TDirectory* dir = (TDirectory *) obj;
      ScanCollection(rec, dir->GetList(), 0, kFALSE, dir->GetListOfKeys());
   } else if (obj->InheritsFrom(TTree::Class())) {
      if (!fReadOnly) rec.SetField("_player", "JSROOT.drawTreePlayer");
      ScanCollection(rec, ((TTree *) obj)->GetListOfLeaves());
   } else if (obj->InheritsFrom(TBranch::Class())) {
      ScanCollection(rec, ((TBranch *) obj)->GetListOfLeaves());
   } else if (rec.CanExpandItem()) {
      ScanObjectMemebers(rec, obj->IsA(), (char *) obj, 0);
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanCollection(TRootSnifferScanRec &rec, TCollection *lst,
                                  const char *foldername, Bool_t extra, TCollection* keys_lst)
{
   // scan collection content

   if (((lst == 0) || (lst->GetSize() == 0)) && ((keys_lst==0) || (keys_lst->GetSize()==0))) return;

   TRootSnifferScanRec folderrec;
   if (foldername) {
      if (!folderrec.GoInside(rec, 0, foldername)) return;
      if (extra) folderrec.fMask = folderrec.fMask | TRootSnifferScanRec::kExtraFolder;
   }

   {
      TRootSnifferScanRec &master = foldername ? folderrec : rec;

      if (lst!=0) {
         TIter iter(lst);
         TObject *obj(0);

         while ((obj = iter()) != 0) {
            TRootSnifferScanRec chld;
            if (chld.GoInside(master, obj)) {
               ScanObject(chld, obj);
               if (chld.Done()) break;
            }
         }
      }

      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) && (master.fMask & TRootSnifferScanRec::kScan)) continue;

            if (obj==0) obj = key; // if object exists, provide it to for scan instead of  key

            TRootSnifferScanRec chld;
            TString fullname = TString::Format("%s;%d", key->GetName(), key->GetCycle());

            if (chld.GoInside(master, obj, fullname.Data())) {
               ScanObject(chld, obj);
               if (chld.Done()) break;
            }
         }
      }
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanRoot(TRootSnifferScanRec &rec)
{
   // scan complete ROOT objects hierarchy
   // For the moment it includes objects in gROOT directory
   // and list of canvases and files
   // Also all registered objects are included.
   // One could reimplement this method to provide alternative
   // scan methods or to extend some collection kinds

   rec.SetField(item_prop_kind, "ROOT.Session");

   {
      TRootSnifferScanRec chld;
      if (chld.GoInside(rec, 0, "StreamerInfo")) {
         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");
      }
   }

   ScanCollection(rec, gROOT->GetList());

   ScanCollection(rec, gROOT->GetListOfCanvases(), "Canvases");

   ScanCollection(rec, gROOT->GetListOfFiles(), "Files");

   TFolder *topf = dynamic_cast<TFolder *>(gROOT->FindObject("//root/http"));
   if (topf) ScanCollection(rec, topf->GetListOfFolders());
}

//______________________________________________________________________________
Bool_t TRootSniffer::IsDrawableClass(TClass *cl)
{
   // return true if object can be drawn

   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)
{
   // scan ROOT hierarchy with provided store object

   TRootSnifferScanRec rec;
   rec.fSearchPath = path;
   if (rec.fSearchPath) {
      if (*rec.fSearchPath == '/') rec.fSearchPath++;
      if (*rec.fSearchPath == 0) rec.fSearchPath = 0;
   }

   // if path non-empty, we should find item first and than start scanning
   rec.fMask = rec.fSearchPath == 0 ? TRootSnifferScanRec::kScan : TRootSnifferScanRec::kExpand;

   rec.fStore = store;

   rec.CreateNode(topname);

   ScanRoot(rec);

   rec.CloseNode();
}

//______________________________________________________________________________
void *TRootSniffer::FindInHierarchy(const char *path, TClass **cl,
                                    TDataMember **member, Int_t *chld)
{
   // Search element with specified path
   // Returns pointer on element
   // Optionally one could obtain element class, member description
   // and number of childs. When chld!=0, not only element is searched,
   // but also number of childs are counted. When member!=0, any object
   // will be scanned for its data members (disregard of extra options)

   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);

   if (cl) *cl = store.GetResClass();
   if (member) *member = store.GetResMember();
   if (chld) *chld = store.GetResNumChilds();

   return store.GetResPtr();
}

//______________________________________________________________________________
TObject *TRootSniffer::FindTObjectInHierarchy(const char *path)
{
   // Search element in hierarchy, derived from TObject

   TClass *cl(0);

   void *obj = FindInHierarchy(path, &cl);

   return (cl != 0) && (cl->GetBaseClassOffset(TObject::Class()) == 0) ? (TObject *) obj : 0;
}

//______________________________________________________________________________
ULong_t TRootSniffer::GetStreamerInfoHash()
{
   // Returns hash value for streamer infos
   // At the moment - just number of items in streamer infos list.

   return fSinfoSize;
}

//______________________________________________________________________________
ULong_t TRootSniffer::GetItemHash(const char *itemname)
{
   // Get hash function for specified item
   // used to detect any changes in the specified object

   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)
{
   // Method verifies if object can be drawn

   TClass *obj_cl(0);
   void *res = FindInHierarchy(path, &obj_cl);
   return (res != 0) && IsDrawableClass(obj_cl);
}

//______________________________________________________________________________
Bool_t TRootSniffer::CanExploreItem(const char *path)
{
   // Method returns true when object has childs or
   // one could try to expand item

   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()
{
   // Creates TMemFile instance, which used for objects streaming
   // One could not use TBufferFile directly,
   // while one also require streamer infos list

   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");
   //      // gr->SetDrawOptions("AC*");
   fMemFile->WriteObject(gr, "gr1");
   delete gr;

   fMemFile->WriteStreamerInfo();

   // make primary list of streamer infos
   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();

   l = fMemFile->GetStreamerInfoList();
   // l->Print("*");
   fSinfoSize = l->GetSize();
   delete l;

   gDirectory = olddir;
   gFile = oldfile;
}

//______________________________________________________________________________
Bool_t TRootSniffer::ProduceJson(const char *path, const char *options,
                                 TString &res)
{
   // produce JSON data for specified item
   // For object conversion TBufferJSON is used

   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");

   if (IsStreamerInfoItem(path)) {

      CreateMemFile();

      TDirectory *olddir = gDirectory;
      gDirectory = 0;
      TFile *oldfile = gFile;
      gFile = 0;

      fMemFile->WriteStreamerInfo();
      TList *l = fMemFile->GetStreamerInfoList();
      fSinfoSize = l->GetSize();

      res = TBufferJSON::ConvertToJSON(l, compact);

      delete l;
      gDirectory = olddir;
      gFile = oldfile;
   } else {

      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;

      if (member == 0)
         res = TBufferJSON::ConvertToJSON(obj_ptr, obj_cl, compact >= 0 ? compact : 0);
      else
         res = TBufferJSON::ConvertToJSON(obj_ptr, member, compact >= 0 ? compact : 1);
   }

   return res.Length() > 0;
}

//______________________________________________________________________________
Bool_t TRootSniffer::ExecuteCmd(const char *path, const char */*options*/,
                                TString& res)
{
   // execute command marked as _kind=='Command'

   TFolder* item = FindItem(path);

   const char* kind = GetItemField(item, item_prop_kind);

   if ((item==0) || (kind==0) || (strcmp(kind,"Command")!=0)) { res = "false"; return kTRUE; }

   const char* method = GetItemField(item, "method");

   if (gDebug>0) Info("ExecuteCmd","Executing command %s method:%s", path, method);

   TString item_method;

   const char* separ = strstr(method,"/->");
   if (separ!=0) {
      TString itemname(method, separ - method);
      TObject* obj = FindTObjectInHierarchy(itemname.Data());
      if (obj!=0) {
         item_method.Form("((%s*)%lu)->%s", obj->ClassName(), (long unsigned) obj, separ + 3);
         method = item_method.Data();
         if (gDebug>2) Info("ExecuteCmd","Executing %s", method);
      }
   }

   Long_t v = gROOT->ProcessLineSync(method);

   res.Form("%ld", v);
<commit_msg>Fix warning C4138: '*/' found outside of comment<commit_after>// $Id$
// Author: <NAME>   22/12/2013

#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 "TMethodArg.h"
#include "TMethodCall.h"
#include "TDataMember.h"
#include "TDataType.h"
#include "TBaseClass.h"
#include "TObjString.h"
#include "TUrl.h"
#include "TImage.h"
#ifdef COMPILED_WITH_DABC
extern "C" void R__zip(int cxlevel, int *srcsize, char* src, int *tgtsize, char* tgt, int* irep);
#else
#include "RZip.h"
#endif
#include "TRootSnifferStore.h"

#include <stdlib.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"; // real object name

// ============================================================================

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootSnifferScanRec                                                  //
//                                                                      //
// Structure used to scan hierarchies of ROOT objects                   //
// Represents single level of hierarchy                                 //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
TRootSnifferScanRec::TRootSnifferScanRec() :
   fParent(0),
   fMask(0),
   fSearchPath(0),
   fLevel(0),
   fItemsNames(),
   fStore(0),
   fHasMore(kFALSE),
   fNodeStarted(kFALSE),
   fNumFields(0),
   fNumChilds(0)
{
   // constructor

   fItemsNames.SetOwner(kTRUE);
}

//______________________________________________________________________________
TRootSnifferScanRec::~TRootSnifferScanRec()
{
   // destructor

   CloseNode();
}

//______________________________________________________________________________
void TRootSnifferScanRec::SetField(const char *name, const char *value, Bool_t with_quotes)
{
   // record field for current element

   if (CanSetFields()) fStore->SetField(fLevel, name, value, with_quotes);
   fNumFields++;
}

//______________________________________________________________________________
void TRootSnifferScanRec::BeforeNextChild()
{
   // indicates that new child for current element will be started

   if (CanSetFields()) fStore->BeforeNextChild(fLevel, fNumChilds, fNumFields);
   fNumChilds++;
}

//______________________________________________________________________________
void TRootSnifferScanRec::MakeItemName(const char *objname, TString& itemname)
{
   // constructs item name from object name
   // if special symbols like '/', '#', ':', '&', '?'  are used in object name
   // they will be replaced with '_'.
   // To avoid item name duplication, additional id number can be appended

   std::string nnn = objname;

   size_t pos;

   // replace all special symbols which can make problem to navigate in hierarchy
   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::CreateNode(const char *_node_name)
{
   // creates new node with specified name
   // if special symbols like "[]&<>" are used, node name
   // will be replaced by default name like "extra_item_N" and
   // original node name will be recorded as "_original_name" field
   // Optionally, object name can be recorded as "_realname" field

   if (!CanSetFields()) return;

   fNodeStarted = kTRUE;

   if (fParent) fParent->BeforeNextChild();

   if (fStore) fStore->CreateNode(fLevel, _node_name);
}

//______________________________________________________________________________
void TRootSnifferScanRec::CloseNode()
{
   // close started node

   if (fStore && fNodeStarted) {
      fStore->CloseNode(fLevel, fNumChilds);
      fNodeStarted = kFALSE;
   }
}

//______________________________________________________________________________
void TRootSnifferScanRec::SetRootClass(TClass *cl)
{
   // set root class name as node kind
   // in addition, path to master item (streamer info) specified
   // Such master item required to correctly unstream data on JavaScript

   if ((cl != 0) && CanSetFields())
      SetField(item_prop_kind, TString::Format("ROOT.%s", cl->GetName()));
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::Done() const
{
   // returns true if scanning is done
   // Can happen when searched element is found

   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
{
   // Checks if result will be accepted.
   // Used to verify if sniffer should read object from the file

   if (Done()) return kFALSE;

   // only when doing search, result will be propagated
   if ((fMask & (kSearch | kCheckChilds)) == 0) return kFALSE;

   // only when full search path is scanned
   if (fSearchPath != 0) return kFALSE;

   if (fStore == 0) return kFALSE;

   return kTRUE;
}


//______________________________________________________________________________
Bool_t TRootSnifferScanRec::SetResult(void *obj, TClass *cl, TDataMember *member, Int_t chlds)
{
   // set results of scanning

   if (Done()) return kTRUE;

   if (!IsReadyForResult()) return kFALSE;

   fStore->SetResult(obj, cl, member, chlds);

   return Done();
}

//______________________________________________________________________________
Int_t TRootSnifferScanRec::Depth() const
{
   // returns current depth of scanned hierarchy

   Int_t cnt = 0;
   const TRootSnifferScanRec *rec = this;
   while (rec->fParent) {
      rec = rec->fParent;
      cnt++;
   }

   return cnt;
}

//______________________________________________________________________________
Int_t TRootSnifferScanRec::ExtraFolderLevel()
{
   // return level depth till folder, marked with extra flag
   // Objects in such folder can be 'expanded' -
   // one can get access to all class members
   // If no extra folder found, -1 is returned

   TRootSnifferScanRec *rec = this;
   Int_t cnt = 0;
   while (rec) {
      if (rec->fMask & kExtraFolder) return cnt;
      rec = rec->fParent;
      cnt++;
   }

   return -1;
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::CanExpandItem()
{
   // returns true if current item can be expanded - means one could explore
   // objects members

   if (fMask & (kExpand | kSearch | kCheckChilds)) return kTRUE;

   if (!fHasMore) return kFALSE;

   // if parent has expand mask, allow to expand item
   if (fParent && (fParent->fMask & kExpand)) return kTRUE;

   return kFALSE;
}

//______________________________________________________________________________
Bool_t TRootSnifferScanRec::GoInside(TRootSnifferScanRec &super, TObject *obj,
                                     const char *obj_name)
{
   // Method verifies if new level of hierarchy
   // should be started with provided object.
   // If required, all necessary nodes and fields will be created
   // Used when different collection kinds should be scanned

   if (super.Done()) return kFALSE;

   if ((obj != 0) && (obj_name == 0)) obj_name = obj->GetName();

   // exclude zero names
   if ((obj_name == 0) || (*obj_name == 0)) return kFALSE;

   TString obj_item_name;

   const char* full_name = 0;

   // remove slashes from file names
   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, obj_item_name);

   fLevel = super.fLevel;
   fStore = super.fStore;
   fSearchPath = super.fSearchPath;
   fMask = super.fMask & kActions;
   fParent = &super;

   if (fMask & kScan) {
      // only when doing scan, increment level, used for text formatting
      fLevel++;
   } else {
      if (fSearchPath == 0) return kFALSE;

      if (strncmp(fSearchPath, obj_item_name.Data(), obj_item_name.Length()) != 0)
         return kFALSE;

      const char *separ = fSearchPath + obj_item_name.Length();

      Bool_t isslash = kFALSE;
      while (*separ == '/') {
         separ++;
         isslash = kTRUE;
      }

      if (*separ == 0) {
         fSearchPath = 0;
         if (fMask & kExpand) {
            fMask = kScan;
            fSearchPath = 0;
            fHasMore = kTRUE; // when found selected object, allow to scan it (and only it)
         }

      } else {
         if (!isslash) return kFALSE;
         fSearchPath = separ;
      }
   }

   CreateNode(obj_item_name.Data());

   if ((obj_name!=0) && (obj_item_name != obj_name))
      SetField(item_prop_realname, obj_name);

   if (full_name != 0)
      SetField("_fullname", full_name);

   return kTRUE;
}


// ====================================================================

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TRootSniffer                                                         //
//                                                                      //
// Sniffer of ROOT objects, data provider for THttpServer               //
// Provides methods to scan different structures like folders,          //
// directories, files, trees, collections                               //
// Can locate objects (or its data member) per name                     //
// Can be extended to application-specific classes                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
TRootSniffer::TRootSniffer(const char *name, const char *objpath) :
   TNamed(name, "sniffer of root objects"),
   fObjectsPath(objpath),
   fMemFile(0),
   fSinfoSize(0),
   fReadOnly(kTRUE)
{
   // constructor
}

//______________________________________________________________________________
TRootSniffer::~TRootSniffer()
{
   // destructor

   if (fMemFile) {
      delete fMemFile;
      fMemFile = 0;
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanObjectMemebers(TRootSnifferScanRec &rec, TClass *cl,
                                      char *ptr, unsigned long int cloffset)
{
   // scan object data members
   // some members like enum or static members will be excluded

   if ((cl == 0) || (ptr == 0) || rec.Done()) return;

   // first of all expand base classes
   TIter cliter(cl->GetListOfBases());
   TObject *obj = 0;
   while ((obj = cliter()) != 0) {
      TBaseClass *baseclass = dynamic_cast<TBaseClass *>(obj);
      if (baseclass == 0) continue;
      TClass *bclass = baseclass->GetClassPointer();
      if (bclass == 0) continue;

      // all parent classes scanned within same hierarchy level
      // this is how normal object streaming works
      ScanObjectMemebers(rec, bclass, ptr, cloffset + baseclass->GetDelta());
      if (rec.Done()) break;

//    this code was used when each base class creates its own sub level

//      TRootSnifferScanRec chld;
//      if (chld.GoInside(rec, baseclass)) {
//         ScanObjectMemebers(chld, bclass, ptr, cloffset + baseclass->GetDelta());
//         if (chld.Done()) break;
//      }
   }

   // than expand data members
   TIter iter(cl->GetListOfDataMembers());
   while ((obj = iter()) != 0) {
      TDataMember *member = dynamic_cast<TDataMember *>(obj);
      // exclude enum or static variables
      if ((member == 0) || (member->Property() & (kIsStatic | kIsEnum | kIsUnion))) continue;

      char *member_ptr = ptr + cloffset + member->GetOffset();
      if (member->IsaPointer()) member_ptr = *((char **) member_ptr);

      TRootSnifferScanRec chld;

      if (chld.GoInside(rec, member)) {
         TClass *mcl = (member->IsBasic() || member->IsSTLContainer()) ? 0 :
                       gROOT->GetClass(member->GetTypeName());

         Int_t coll_offset = mcl ? mcl->GetBaseClassOffset(TCollection::Class()) : -1;

         Bool_t iscollection = (coll_offset >= 0);
         if (iscollection) {
            chld.SetField(item_prop_more, "true", kFALSE);
            chld.fHasMore = kTRUE;
         }

         if (chld.SetResult(member_ptr, mcl, 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) {
            // store array dimensions in form [N1,N2,N3,...]
            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);
         }

         chld.SetRootClass(mcl);

         if (chld.CanExpandItem()) {
            if (iscollection) {
               // chld.SetField("#members", "true", kFALSE);
               ScanCollection(chld, (TCollection *)(member_ptr + coll_offset));
            }
         }

         if (chld.SetResult(member_ptr, mcl, member, chld.fNumChilds)) break;
      }
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanObject(TRootSnifferScanRec &rec, TObject *obj)
{
   // scan object content
   // if object is known collection class (like TFolder),
   // collection content will be scanned as well

   if (obj == 0) return;

   if (!fReadOnly && obj->InheritsFrom(TKey::Class()) && rec.IsReadyForResult()) {
      TObject* keyobj = ((TKey*) obj)->ReadObj();
      if (keyobj!=0)
         if (rec.SetResult(keyobj, keyobj->IsA())) return;
   }

   if (rec.SetResult(obj, obj->IsA())) return;

   TClass* obj_class = obj->IsA();

   if ((obj_class == TFolder::Class()) && obj->TestBit(kItemFolder)) {
      ScanItem(rec, (TFolder*) obj);
   } else {
      ScanObjectProperties(rec, obj, obj_class);
      rec.SetRootClass(obj_class);
      ScanObjectChilds(rec, obj);
   }

   // here we should know how many childs are accumulated
   rec.SetResult(obj, obj_class, 0, rec.fNumChilds);
}

//_____________________________________________________________________
void TRootSniffer::ScanObjectProperties(TRootSnifferScanRec &rec,
                               TObject* &obj, TClass* &obj_class)
{
   // scans basic object properties
   // here such fields as _typename, _title, _more properties can be specified

   const char* title = obj->GetTitle();
   if ((title!=0) && (*title!=0))
      rec.SetField(item_prop_title, title);

   int isextra = rec.ExtraFolderLevel();

   if ((isextra == 1) || ((isextra > 1) && !IsDrawableClass(obj->IsA()))) {
      rec.SetField(item_prop_more, "true", kFALSE);
      rec.fHasMore = kTRUE;
   }

   // special handling of TKey class - in non-readonly mode
   // sniffer allowed to fetch objects

   if (!fReadOnly && obj->InheritsFrom(TKey::Class())) {
      TKey* key = (TKey *) obj;
      if (strcmp(key->GetClassName(),"TDirectoryFile")==0) {
         if (rec.fLevel==0) {
            TDirectory* dir = dynamic_cast<TDirectory*> (key->ReadObj());
            if (dir!=0) {
               obj = dir;
               obj_class = dir->IsA();
            }
         } else {
            rec.SetField(item_prop_more, "true", kFALSE);
            rec.fHasMore = kTRUE;
         }
      } else {
         obj_class = TClass::GetClass(key->GetClassName());
      }
   }
}

//_____________________________________________________________________
void TRootSniffer::ScanItem(TRootSnifferScanRec &rec, TFolder* item)
{
   // scans basic object properties
   // here such fields as _typename, _title, _more properties can be specified

   const char* title = item->GetTitle();
   if ((title!=0) && (*title!=0))
      rec.SetField(item_prop_title, title);

   // first all properties
   TIter iter(item->GetListOfFolders());
   TObject* obj = 0;
   while ((obj = iter()) != 0) {
      if (obj->IsA()!=TNamed::Class()) continue;
      if (obj->GetName() || (*(obj->GetName()) == '_'))
         rec.SetField(obj->GetName(), obj->GetTitle());
   }

   iter.Reset();

   while ((obj = iter()) != 0) {
      if (obj->IsA()==TNamed::Class()) continue;
      TRootSnifferScanRec chld;
      if (chld.GoInside(rec, obj)) {
         ScanObject(chld, obj);
         if (chld.Done()) break;
      }
   }
}

//_____________________________________________________________________
void TRootSniffer::ScanObjectChilds(TRootSnifferScanRec &rec, TObject *obj)
{
   // scans object childs (if any)
   // here one scans collection, branches, trees and so on

   if (obj->InheritsFrom(TFolder::Class())) {
      // starting from special folder, we automatically scan members

      TFolder *fold = (TFolder *) obj;
      if (fold->TestBit(kMoreFolder)) rec.fMask = rec.fMask | TRootSnifferScanRec::kExtraFolder;

      ScanCollection(rec, fold->GetListOfFolders());
   } else if (obj->InheritsFrom(TDirectory::Class())) {
      TDirectory* dir = (TDirectory *) obj;
      ScanCollection(rec, dir->GetList(), 0, kFALSE, dir->GetListOfKeys());
   } else if (obj->InheritsFrom(TTree::Class())) {
      if (!fReadOnly) rec.SetField("_player", "JSROOT.drawTreePlayer");
      ScanCollection(rec, ((TTree *) obj)->GetListOfLeaves());
   } else if (obj->InheritsFrom(TBranch::Class())) {
      ScanCollection(rec, ((TBranch *) obj)->GetListOfLeaves());
   } else if (rec.CanExpandItem()) {
      ScanObjectMemebers(rec, obj->IsA(), (char *) obj, 0);
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanCollection(TRootSnifferScanRec &rec, TCollection *lst,
                                  const char *foldername, Bool_t extra, TCollection* keys_lst)
{
   // scan collection content

   if (((lst == 0) || (lst->GetSize() == 0)) && ((keys_lst==0) || (keys_lst->GetSize()==0))) return;

   TRootSnifferScanRec folderrec;
   if (foldername) {
      if (!folderrec.GoInside(rec, 0, foldername)) return;
      if (extra) folderrec.fMask = folderrec.fMask | TRootSnifferScanRec::kExtraFolder;
   }

   {
      TRootSnifferScanRec &master = foldername ? folderrec : rec;

      if (lst!=0) {
         TIter iter(lst);
         TObject *obj(0);

         while ((obj = iter()) != 0) {
            TRootSnifferScanRec chld;
            if (chld.GoInside(master, obj)) {
               ScanObject(chld, obj);
               if (chld.Done()) break;
            }
         }
      }

      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) && (master.fMask & TRootSnifferScanRec::kScan)) continue;

            if (obj==0) obj = key; // if object exists, provide it to for scan instead of  key

            TRootSnifferScanRec chld;
            TString fullname = TString::Format("%s;%d", key->GetName(), key->GetCycle());

            if (chld.GoInside(master, obj, fullname.Data())) {
               ScanObject(chld, obj);
               if (chld.Done()) break;
            }
         }
      }
   }
}

//______________________________________________________________________________
void TRootSniffer::ScanRoot(TRootSnifferScanRec &rec)
{
   // scan complete ROOT objects hierarchy
   // For the moment it includes objects in gROOT directory
   // and list of canvases and files
   // Also all registered objects are included.
   // One could reimplement this method to provide alternative
   // scan methods or to extend some collection kinds

   rec.SetField(item_prop_kind, "ROOT.Session");

   {
      TRootSnifferScanRec chld;
      if (chld.GoInside(rec, 0, "StreamerInfo")) {
         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");
      }
   }

   ScanCollection(rec, gROOT->GetList());

   ScanCollection(rec, gROOT->GetListOfCanvases(), "Canvases");

   ScanCollection(rec, gROOT->GetListOfFiles(), "Files");

   TFolder *topf = dynamic_cast<TFolder *>(gROOT->FindObject("//root/http"));
   if (topf) ScanCollection(rec, topf->GetListOfFolders());
}

//______________________________________________________________________________
Bool_t TRootSniffer::IsDrawableClass(TClass *cl)
{
   // return true if object can be drawn

   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)
{
   // scan ROOT hierarchy with provided store object

   TRootSnifferScanRec rec;
   rec.fSearchPath = path;
   if (rec.fSearchPath) {
      if (*rec.fSearchPath == '/') rec.fSearchPath++;
      if (*rec.fSearchPath == 0) rec.fSearchPath = 0;
   }

   // if path non-empty, we should find item first and than start scanning
   rec.fMask = rec.fSearchPath == 0 ? TRootSnifferScanRec::kScan : TRootSnifferScanRec::kExpand;

   rec.fStore = store;

   rec.CreateNode(topname);

   ScanRoot(rec);

   rec.CloseNode();
}

//______________________________________________________________________________
void *TRootSniffer::FindInHierarchy(const char *path, TClass **cl,
                                    TDataMember **member, Int_t *chld)
{
   // Search element with specified path
   // Returns pointer on element
   // Optionally one could obtain element class, member description
   // and number of childs. When chld!=0, not only element is searched,
   // but also number of childs are counted. When member!=0, any object
   // will be scanned for its data members (disregard of extra options)

   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);

   if (cl) *cl = store.GetResClass();
   if (member) *member = store.GetResMember();
   if (chld) *chld = store.GetResNumChilds();

   return store.GetResPtr();
}

//______________________________________________________________________________
TObject *TRootSniffer::FindTObjectInHierarchy(const char *path)
{
   // Search element in hierarchy, derived from TObject

   TClass *cl(0);

   void *obj = FindInHierarchy(path, &cl);

   return (cl != 0) && (cl->GetBaseClassOffset(TObject::Class()) == 0) ? (TObject *) obj : 0;
}

//______________________________________________________________________________
ULong_t TRootSniffer::GetStreamerInfoHash()
{
   // Returns hash value for streamer infos
   // At the moment - just number of items in streamer infos list.

   return fSinfoSize;
}

//______________________________________________________________________________
ULong_t TRootSniffer::GetItemHash(const char *itemname)
{
   // Get hash function for specified item
   // used to detect any changes in the specified object

   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)
{
   // Method verifies if object can be drawn

   TClass *obj_cl(0);
   void *res = FindInHierarchy(path, &obj_cl);
   return (res != 0) && IsDrawableClass(obj_cl);
}

//______________________________________________________________________________
Bool_t TRootSniffer::CanExploreItem(const char *path)
{
   // Method returns true when object has childs or
   // one could try to expand item

   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()
{
   // Creates TMemFile instance, which used for objects streaming
   // One could not use TBufferFile directly,
   // while one also require streamer infos list

   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");
   //      // gr->SetDrawOptions("AC*");
   fMemFile->WriteObject(gr, "gr1");
   delete gr;

   fMemFile->WriteStreamerInfo();

   // make primary list of streamer infos
   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();

   l = fMemFile->GetStreamerInfoList();
   // l->Print("*");
   fSinfoSize = l->GetSize();
   delete l;

   gDirectory = olddir;
   gFile = oldfile;
}

//______________________________________________________________________________
Bool_t TRootSniffer::ProduceJson(const char *path, const char *options,
                                 TString &res)
{
   // produce JSON data for specified item
   // For object conversion TBufferJSON is used

   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");

   if (IsStreamerInfoItem(path)) {

      CreateMemFile();

      TDirectory *olddir = gDirectory;
      gDirectory = 0;
      TFile *oldfile = gFile;
      gFile = 0;

      fMemFile->WriteStreamerInfo();
      TList *l = fMemFile->GetStreamerInfoList();
      fSinfoSize = l->GetSize();

      res = TBufferJSON::ConvertToJSON(l, compact);

      delete l;
      gDirectory = olddir;
      gFile = oldfile;
   } else {

      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;

      if (member == 0)
         res = TBufferJSON::ConvertToJSON(obj_ptr, obj_cl, compact >= 0 ? compact : 0);
      else
         res = TBufferJSON::ConvertToJSON(obj_ptr, member, compact >= 0 ? compact : 1);
   }

   return res.Length() > 0;
}

//______________________________________________________________________________
Bool_t TRootSniffer::ExecuteCmd(const char *path, const char * /*options*/,
                                TString& res)
{
   // execute command marked as _kind=='Command'

   TFolder* item = FindItem(path);

   const char* kind = GetItemField(item, item_prop_kind);

   if ((item==0) || (kind==0) || (strcmp(kind,"Command")!=0)) { res = "false"; return kTRUE; }

   const char* method = GetItemField(item, "method");

   if (gDebug>0) Info("ExecuteCmd","Executing command %s method:%s", path, method);

   TString item_method;

   const char* separ = strstr(method,"/->");
   if (separ!=0) {
      TString itemname(method, separ - method);
      TObject* obj = FindTObjectInHierarchy(itemname.Data());
      if (obj!=0) {
         item_method.Form("((%s*)%lu)->%s", obj->ClassName(), (long unsigned) obj, separ + 3);
         method = item_method.Data();
         if (gDebug>2) Info("ExecuteCmd","Executing %s", method);
      }
   }

   Long_t v = gROOT->ProcessLineSync(method);

   res.Form("%ld", v);

{'fraction_non_alphanumeric': 0.1398042908063757, 'fraction_numerical': 0.005447575492635685, 'mean_word_length': 2.887893842332331, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 283, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 58, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '791050', 'n_tokens_mistral': 17719, 'n_tokens_neox': 17068, 'n_words': 4386}