/************************************************************************* * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ #include <ROOT/Browsable/RAnyObjectHolder.hxx> #include <ROOT/Browsable/RLevelIter.hxx> #include <ROOT/Browsable/RProvider.hxx> #include <ROOT/Browsable/TObjectHolder.hxx> #include <ROOT/Browsable/TKeyItem.hxx> #include <ROOT/RLogger.hxx> #include "TSystem.h" #include "TKey.h" #include "TDirectory.h" #include "TROOT.h" #include "TFile.h" #include "TH1.h" using namespace std::string_literals; using namespace ROOT::Experimental::Browsable; /** \class TDirectoryLevelIter \ingroup rbrowser Iterator over keys in TDirectory */ class TDirectoryLevelIter : public RLevelIter { TDirectory *fDir{nullptr}; ///<! current directory handle std::unique_ptr<TIterator> fIter; ///<! created iterator TKey *fKey{nullptr}; ///<! currently selected key std::string fCurrentName; ///<! current key name bool CreateIter() { if (!fDir) return false; fIter.reset(fDir->GetListOfKeys()->MakeIterator()); fKey = nullptr; return true; } bool NextDirEntry() { fCurrentName.clear(); if (!fIter) return false; fKey = dynamic_cast<TKey *>(fIter->Next()); if (!fKey) { fIter.reset(); return false; } fCurrentName = fKey->GetName(); fCurrentName.append(";"); fCurrentName.append(std::to_string(fKey->GetCycle())); return true; } public: explicit TDirectoryLevelIter(TDirectory *dir) : fDir(dir) { CreateIter(); } virtual ~TDirectoryLevelIter() = default; bool Reset() override { return CreateIter(); } bool Next() override { return NextDirEntry(); } // use default implementation for now // bool Find(const std::string &name) override { return FindDirEntry(name); } bool HasItem() const override { return fKey && !fCurrentName.empty(); } std::string GetName() const override { return fCurrentName; } int CanHaveChilds() const override { std::string clname = fKey->GetClassName(); return (clname.find("TDirectory") == 0) || (clname.find("TTree") == 0) || (clname.find("TNtuple") == 0) ? 1 : 0; } /** Create element for the browser */ std::unique_ptr<RItem> CreateItem() override { auto item = std::make_unique<TKeyItem>(GetName(), CanHaveChilds()); item->SetClassName(fKey->GetClassName()); item->SetIcon(RProvider::GetClassIcon(fKey->GetClassName())); return item; } /** Returns full information for current element */ std::shared_ptr<RElement> GetElement() override; }; // =============================================================================================================== /** \class TKeyElement \ingroup rbrowser Element representing TKey from TDirectory */ class TKeyElement : public RElement { TDirectory *fDir{nullptr}; TKey *fKey{nullptr}; public: TKeyElement(TDirectory *dir, TKey *key) : fDir(dir), fKey(key) {} virtual ~TKeyElement() = default; /** Name of TKeyElement, includes key cycle */ std::string GetName() const override { std::string name = fKey->GetName(); name.append(";"); name.append(std::to_string(fKey->GetCycle())); return name; } /** Title of TKeyElement (optional) */ std::string GetTitle() const override { return fKey->GetTitle(); } /** Create iterator for childs elements if any * Means we should try to browse inside. * Either it is directory or some complex object * */ std::unique_ptr<RLevelIter> GetChildsIter() override { std::string clname = fKey->GetClassName(); if (clname.find("TDirectory") == 0) { auto subdir = fDir->GetDirectory(GetName().c_str()); if (!subdir) return nullptr; return std::make_unique<TDirectoryLevelIter>(subdir); } auto obj = GetObject(); if (obj) { auto elem = RProvider::Browse(obj); if (elem) return elem->GetChildsIter(); } return nullptr; } /** Return object associated with TKey, if TDirectory has object of that name it will be returned */ std::unique_ptr<RHolder> GetObject() override { std::string clname = fKey->GetClassName(); auto obj_class = TClass::GetClass(clname.c_str()); if (!obj_class) return nullptr; if (obj_class->InheritsFrom(TObject::Class())) { TObject *tobj = fDir->FindObject(fKey->GetName()); if (!tobj) { tobj = fKey->ReadObj(); if (!tobj) return nullptr; } bool owned_by_dir = fDir->FindObject(tobj) == tobj; return std::make_unique<TObjectHolder>(tobj, !owned_by_dir); } void *obj = fKey->ReadObjectAny(obj_class); if (!obj) return nullptr; return std::make_unique<RAnyObjectHolder>(obj_class, obj, true); } }; // ============================================================================================== ///////////////////////////////////////////////////////////////////////////////// /// Return element for current TKey object in TDirectory std::shared_ptr<RElement> TDirectoryLevelIter::GetElement() { return std::make_shared<TKeyElement>(fDir, fKey); } // ============================================================================================== /** \class TDirectoryElement \ingroup rbrowser Element representing TDirectory */ class TDirectoryElement : public RElement { std::string fFileName; ///<! file name TDirectory *fDir{nullptr}; ///<! subdirectory (ifany) /////////////////////////////////////////////////////////////////// /// Get TDirectory. Checks if parent file is still there. If not, means it was closed outside ROOT TDirectory *GetDir() { if (fDir) { if (!gROOT->GetListOfFiles()->FindObject(fDir->GetFile())) fDir = nullptr; } else if (!fFileName.empty()) { fDir = TFile::Open(fFileName.c_str()); } return fDir; } public: TDirectoryElement(const std::string &fname, TDirectory *dir = nullptr) { fFileName = fname; fDir = dir; } virtual ~TDirectoryElement() = default; /** Name of TDirectoryElement */ std::string GetName() const override { if (fDir) return fDir->GetName(); if (!fFileName.empty()) { auto pos = fFileName.rfind("/"); return ((pos == std::string::npos) || (pos > fFileName.length() - 2)) ? fFileName : fFileName.substr(pos + 1); } return ""s; } /** Title of TDirectoryElement */ std::string GetTitle() const override { if (fDir) return fDir->GetTitle(); return "ROOT file "s + fFileName; } std::unique_ptr<RLevelIter> GetChildsIter() override { auto dir = GetDir(); return dir ? std::make_unique<TDirectoryLevelIter>(dir) : nullptr; } }; // ============================================================================================== /** \class RTFileProvider \ingroup rbrowser Provides access to ROOT files with extension "root" Other extensions can be registered */ class RTFileProvider : public RProvider { public: RTFileProvider() { RegisterFile("root", [] (const std::string &fullname) -> std::shared_ptr<RElement> { auto f = TFile::Open(fullname.c_str()); if (!f) return nullptr; return std::make_shared<TDirectoryElement>(fullname, f); }); RegisterBrowse(TFile::Class(), [](std::unique_ptr<RHolder> &object) -> std::shared_ptr<RElement> { return std::make_shared<TDirectoryElement>("", const_cast<TFile*>(object->Get<TFile>())); }); RegisterBrowse(TDirectory::Class(), [](std::unique_ptr<RHolder> &object) -> std::shared_ptr<RElement> { return std::make_shared<TDirectoryElement>("", const_cast<TDirectory*>(object->Get<TDirectory>())); }); } } newRTFileProvider ;