#include "TXMLFile.h"
#include "TROOT.h"
#include "TSystem.h"
#include "TList.h"
#include "TBrowser.h"
#include "TObjArray.h"
#include "TBufferXML.h"
#include "TKeyXML.h"
#include "TObjArray.h"
#include "TArrayC.h"
#include "TStreamerInfo.h"
#include "TStreamerElement.h"
#include "TProcessID.h"
#include "TError.h"
#include "TClass.h"
ClassImp(TXMLFile);
TXMLFile::TXMLFile() :
TFile(),
TXMLSetup(),
fDoc(0),
fStreamerInfoNode(0),
fXML(0),
fKeyCounter(0)
{
SetBit(kBinaryFile, kFALSE);
fIOVersion = TXMLFile::Class_Version();
}
TXMLFile::TXMLFile(const char* filename, Option_t* option, const char* title, Int_t compression) :
TFile(),
TXMLSetup(),
fDoc(0),
fStreamerInfoNode(0),
fXML(0),
fKeyCounter(0)
{
fXML = new TXMLEngine();
if (!gROOT)
::Fatal("TFile::TFile", "ROOT system not initialized");
if (filename && !strncmp(filename, "xml:", 4))
filename += 4;
gDirectory = 0;
SetName(filename);
SetTitle(title);
TDirectoryFile::Build(this, 0);
fD = -1;
fFile = this;
fFree = 0;
fVersion = gROOT->GetVersionInt();
fUnits = 4;
fOption = option;
SetCompressionSettings(compression);
fWritten = 0;
fSumBuffer = 0;
fSum2Buffer = 0;
fBytesRead = 0;
fBytesWrite = 0;
fClassIndex = 0;
fSeekInfo = 0;
fNbytesInfo = 0;
fProcessIDs = 0;
fNProcessIDs= 0;
fIOVersion = TXMLFile::Class_Version();
SetBit(kBinaryFile, kFALSE);
fOption = option;
fOption.ToUpper();
if (fOption == "NEW") fOption = "CREATE";
Bool_t create = (fOption == "CREATE") ? kTRUE : kFALSE;
Bool_t recreate = (fOption == "RECREATE") ? kTRUE : kFALSE;
Bool_t update = (fOption == "UPDATE") ? kTRUE : kFALSE;
Bool_t read = (fOption == "READ") ? kTRUE : kFALSE;
Bool_t xmlsetup = IsValidXmlSetup(option);
if (xmlsetup) recreate = kTRUE;
if (!create && !recreate && !update && !read) {
read = kTRUE;
fOption = "READ";
}
Bool_t devnull = kFALSE;
const char *fname = 0;
if (!filename || !strlen(filename)) {
Error("TXMLFile", "file name is not specified");
goto zombie;
}
if (!strcmp(filename, "/dev/null") &&
!gSystem->AccessPathName(filename, kWritePermission)) {
devnull = kTRUE;
create = kTRUE;
recreate = kFALSE;
update = kFALSE;
read = kFALSE;
fOption = "CREATE";
SetBit(TFile::kDevNull);
}
gROOT->cd();
fname = gSystem->ExpandPathName(filename);
if (fname) {
SetName(fname);
delete [] (char*)fname;
fname = GetName();
} else {
Error("TXMLFile", "error expanding path %s", filename);
goto zombie;
}
if (recreate) {
if (!gSystem->AccessPathName(fname, kFileExists))
gSystem->Unlink(fname);
recreate = kFALSE;
create = kTRUE;
fOption = "CREATE";
}
if (create && !devnull && !gSystem->AccessPathName(fname, kFileExists)) {
Error("TXMLFile", "file %s already exists", fname);
goto zombie;
}
if (update) {
if (gSystem->AccessPathName(fname, kFileExists)) {
update = kFALSE;
create = kTRUE;
}
if (update && gSystem->AccessPathName(fname, kWritePermission)) {
Error("TXMLFile", "no write permission, could not open file %s", fname);
goto zombie;
}
}
if (read) {
if (gSystem->AccessPathName(fname, kFileExists)) {
Error("TXMLFile", "file %s does not exist", fname);
goto zombie;
}
if (gSystem->AccessPathName(fname, kReadPermission)) {
Error("TXMLFile", "no read permission, could not open file %s", fname);
goto zombie;
}
}
fRealName = fname;
if (create || update)
SetWritable(kTRUE);
else
SetWritable(kFALSE);
if (create) {
if (xmlsetup)
ReadSetupFromStr(option);
else
ReadSetupFromStr(TXMLSetup::DefaultXmlSetup());
}
InitXmlFile(create);
return;
zombie:
MakeZombie();
gDirectory = gROOT;
}
void TXMLFile::InitXmlFile(Bool_t create)
{
Int_t len = gROOT->GetListOfStreamerInfo()->GetSize()+1;
if (len<5000) len = 5000;
fClassIndex = new TArrayC(len);
fClassIndex->Reset(0);
if (create) {
fDoc = fXML->NewDoc();
XMLNodePointer_t fRootNode = fXML->NewChild(0, 0, xmlio::Root, 0);
fXML->DocSetRootElement(fDoc, fRootNode);
} else {
ReadFromFile();
}
gROOT->GetListOfFiles()->Add(this);
cd();
fNProcessIDs = 0;
TKey* key = 0;
TIter iter(fKeys);
while ((key = (TKey*)iter())!=0) {
if (!strcmp(key->GetClassName(),"TProcessID")) fNProcessIDs++;
}
fProcessIDs = new TObjArray(fNProcessIDs+1);
}
void TXMLFile::Close(Option_t *option)
{
if (!IsOpen()) return;
TString opt = option;
if (opt.Length()>0)
opt.ToLower();
if (IsWritable()) SaveToFile();
fWritable = kFALSE;
if (fDoc) {
fXML->FreeDoc(fDoc);
fDoc = 0;
}
if (fClassIndex) {
delete fClassIndex;
fClassIndex = 0;
}
if (fStreamerInfoNode) {
fXML->FreeNode(fStreamerInfoNode);
fStreamerInfoNode = 0;
}
TDirectory *cursav = gDirectory;
cd();
if (cursav == this || cursav->GetFile() == this) {
cursav = 0;
}
TDirectoryFile::Close();
cd();
if (cursav)
cursav->cd();
else {
gFile = 0;
gDirectory = gROOT;
}
TList pidDeleted;
TIter next(fProcessIDs);
TProcessID *pid;
while ((pid = (TProcessID*)next())) {
if (!pid->DecrementCount()) {
if (pid != TProcessID::GetSessionProcessID()) pidDeleted.Add(pid);
} else if(opt.Contains("r")) {
pid->Clear();
}
}
pidDeleted.Delete();
gROOT->GetListOfFiles()->Remove(this);
}
TXMLFile::~TXMLFile()
{
Close();
if (fXML!=0) {
delete fXML;
fXML = 0;
}
}
void TXMLFile::operator=(const TXMLFile &)
{
}
Bool_t TXMLFile::IsOpen() const
{
return fDoc != 0;
}
Int_t TXMLFile::ReOpen(Option_t* mode)
{
cd();
TString opt = mode;
opt.ToUpper();
if (opt != "READ" && opt != "UPDATE") {
Error("ReOpen", "mode must be either READ or UPDATE, not %s", opt.Data());
return 1;
}
if (opt == fOption || (opt == "UPDATE" && fOption == "CREATE"))
return 1;
if (opt == "READ") {
if (IsOpen() && IsWritable())
SaveToFile();
fOption = opt;
SetWritable(kFALSE);
} else {
fOption = opt;
SetWritable(kTRUE);
}
return 0;
}
TKey* TXMLFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, Int_t)
{
return new TKeyXML(mother, ++fKeyCounter, obj, name);
}
TKey* TXMLFile::CreateKey(TDirectory* mother, const void* obj, const TClass* cl, const char* name, Int_t)
{
return new TKeyXML(mother, ++fKeyCounter, obj, cl, name);
}
void TXMLFile::ProduceFileNames(const char* filename, TString& fname, TString& dtdname)
{
fname = filename;
dtdname = filename;
Bool_t hasxmlext = kFALSE;
if (fname.Length()>4) {
TString last = fname(fname.Length()-4,4);
last.ToLower();
hasxmlext = (last==".xml");
}
if (hasxmlext) {
dtdname.Replace(dtdname.Length()-4,4,".dtd");
} else {
fname+=".xml";
dtdname+=".dtd";
}
}
void TXMLFile::SaveToFile()
{
if (fDoc==0) return;
if (gDebug>1)
Info("SaveToFile","File: %s",fRealName.Data());
XMLNodePointer_t fRootNode = fXML->DocGetRootElement(fDoc);
fXML->FreeAttr(fRootNode, xmlio::Setup);
fXML->NewAttr(fRootNode, 0, xmlio::Setup, GetSetupAsString());
fXML->FreeAttr(fRootNode, xmlio::Ref);
fXML->NewAttr(fRootNode, 0, xmlio::Ref, xmlio::Null);
if (GetIOVersion()>1) {
fXML->FreeAttr(fRootNode, xmlio::CreateTm);
fXML->NewAttr(fRootNode, 0, xmlio::CreateTm, fDatimeC.AsSQLString());
fXML->FreeAttr(fRootNode, xmlio::ModifyTm);
fXML->NewAttr(fRootNode, 0, xmlio::ModifyTm, fDatimeM.AsSQLString());
fXML->FreeAttr(fRootNode, xmlio::ObjectUUID);
fXML->NewAttr(fRootNode, 0, xmlio::ObjectUUID, fUUID.AsString());
fXML->FreeAttr(fRootNode, xmlio::Title);
if (strlen(GetTitle())>0)
fXML->NewAttr(fRootNode, 0, xmlio::Title, GetTitle());
fXML->FreeAttr(fRootNode, xmlio::IOVersion);
fXML->NewIntAttr(fRootNode, xmlio::IOVersion, GetIOVersion());
}
TString fname, dtdname;
ProduceFileNames(fRealName, fname, dtdname);
CombineNodesTree(this, fRootNode, kTRUE);
WriteStreamerInfo();
if (fStreamerInfoNode)
fXML->AddChild(fRootNode, fStreamerInfoNode);
Int_t layout = GetCompressionLevel()>5 ? 0 : 1;
fXML->SaveDoc(fDoc, fname, layout);
CombineNodesTree(this, fRootNode, kFALSE);
if (fStreamerInfoNode)
fXML->UnlinkNode(fStreamerInfoNode);
}
void TXMLFile::CombineNodesTree(TDirectory* dir, XMLNodePointer_t topnode, Bool_t dolink)
{
if (dir==0) return;
TIter iter(dir->GetListOfKeys());
TKeyXML* key = 0;
while ((key=(TKeyXML*)iter()) !=0) {
if (dolink)
fXML->AddChild(topnode, key->KeyNode());
else
fXML->UnlinkNode(key->KeyNode());
if (key->IsSubdir())
CombineNodesTree(FindKeyDir(dir, key->GetKeyId()), key->KeyNode(), dolink);
}
}
Bool_t TXMLFile::ReadFromFile()
{
fDoc = fXML->ParseFile(fRealName);
if (fDoc==0) return kFALSE;
XMLNodePointer_t fRootNode = fXML->DocGetRootElement(fDoc);
if ((fRootNode==0) || !fXML->ValidateVersion(fDoc)) {
fXML->FreeDoc(fDoc);
fDoc=0;
return kFALSE;
}
ReadSetupFromStr(fXML->GetAttr(fRootNode, xmlio::Setup));
if (fXML->HasAttr(fRootNode, xmlio::CreateTm)) {
TDatime tm(fXML->GetAttr(fRootNode, xmlio::CreateTm));
fDatimeC = tm;
}
if (fXML->HasAttr(fRootNode, xmlio::ModifyTm)) {
TDatime tm(fXML->GetAttr(fRootNode, xmlio::ModifyTm));
fDatimeM = tm;
}
if (fXML->HasAttr(fRootNode, xmlio::ObjectUUID)) {
TUUID id(fXML->GetAttr(fRootNode, xmlio::ObjectUUID));
fUUID = id;
}
if (fXML->HasAttr(fRootNode, xmlio::Title))
SetTitle(fXML->GetAttr(fRootNode, xmlio::Title));
if (fXML->HasAttr(fRootNode, xmlio::IOVersion))
fIOVersion = fXML->GetIntAttr(fRootNode, xmlio::IOVersion);
else
fIOVersion = 1;
fStreamerInfoNode = fXML->GetChild(fRootNode);
fXML->SkipEmpty(fStreamerInfoNode);
while (fStreamerInfoNode!=0) {
if (strcmp(xmlio::SInfos, fXML->GetNodeName(fStreamerInfoNode))==0) break;
fXML->ShiftToNext(fStreamerInfoNode);
}
fXML->UnlinkNode(fStreamerInfoNode);
if (fStreamerInfoNode!=0)
ReadStreamerInfo();
if (IsUseDtd())
if (!fXML->ValidateDocument(fDoc, gDebug>0)) {
fXML->FreeDoc(fDoc);
fDoc=0;
return kFALSE;
}
ReadKeysList(this, fRootNode);
fXML->CleanNode(fRootNode);
return kTRUE;
}
Int_t TXMLFile::ReadKeysList(TDirectory* dir, XMLNodePointer_t topnode)
{
if ((dir==0) || (topnode==0)) return 0;
Int_t nkeys = 0;
XMLNodePointer_t keynode = fXML->GetChild(topnode);
fXML->SkipEmpty(keynode);
while (keynode!=0) {
XMLNodePointer_t next = fXML->GetNext(keynode);
if (strcmp(xmlio::Xmlkey, fXML->GetNodeName(keynode))==0) {
fXML->UnlinkNode(keynode);
TKeyXML* key = new TKeyXML(dir, ++fKeyCounter, keynode);
dir->AppendKey(key);
if (gDebug>2)
Info("ReadKeysList","Add key %s from node %s",key->GetName(), fXML->GetNodeName(keynode));
nkeys++;
}
keynode = next;
fXML->SkipEmpty(keynode);
}
return nkeys;
}
void TXMLFile::WriteStreamerInfo()
{
if (fStreamerInfoNode) {
fXML->FreeNode(fStreamerInfoNode);
fStreamerInfoNode = 0;
}
if (!IsStoreStreamerInfos()) return;
TObjArray list;
TIter iter(gROOT->GetListOfStreamerInfo());
TStreamerInfo* info = 0;
while ((info = (TStreamerInfo*) iter()) !=0 ) {
Int_t uid = info->GetNumber();
if (fClassIndex->fArray[uid])
list.Add(info);
}
if (list.GetSize()==0) return;
fStreamerInfoNode = fXML->NewChild(0, 0, xmlio::SInfos);
for (int n=0;n<=list.GetLast();n++) {
info = (TStreamerInfo*) list.At(n);
XMLNodePointer_t infonode = fXML->NewChild(fStreamerInfoNode, 0, "TStreamerInfo");
fXML->NewAttr(infonode, 0, "name", info->GetName());
fXML->NewAttr(infonode, 0, "title", info->GetTitle());
fXML->NewIntAttr(infonode, "v", info->IsA()->GetClassVersion());
fXML->NewIntAttr(infonode, "classversion", info->GetClassVersion());
fXML->NewAttr(infonode, 0, "canoptimize", (info->TestBit(TStreamerInfo::kCannotOptimize) ? xmlio::False : xmlio::True));
fXML->NewIntAttr(infonode, "checksum", info->GetCheckSum());
TIter iter2(info->GetElements());
TStreamerElement* elem=0;
while ((elem= (TStreamerElement*) iter2()) != 0) {
StoreStreamerElement(infonode, elem);
}
}
}
TList* TXMLFile::GetStreamerInfoList()
{
if (fStreamerInfoNode==0) return 0;
TList* list = new TList();
XMLNodePointer_t sinfonode = fXML->GetChild(fStreamerInfoNode);
fXML->SkipEmpty(sinfonode);
while (sinfonode!=0) {
if (strcmp("TStreamerInfo",fXML->GetNodeName(sinfonode))==0) {
TString fname = fXML->GetAttr(sinfonode,"name");
TString ftitle = fXML->GetAttr(sinfonode,"title");
TStreamerInfo* info = new TStreamerInfo(TClass::GetClass(fname));
info->SetTitle(ftitle);
list->Add(info);
Int_t clversion = AtoI(fXML->GetAttr(sinfonode,"classversion"));
info->SetClassVersion(clversion);
Int_t checksum = AtoI(fXML->GetAttr(sinfonode,"checksum"));
info->SetCheckSum(checksum);
const char* canoptimize = fXML->GetAttr(sinfonode,"canoptimize");
if ((canoptimize==0) || (strcmp(canoptimize,xmlio::False)==0))
info->SetBit(TStreamerInfo::kCannotOptimize);
else
info->ResetBit(TStreamerInfo::kCannotOptimize);
XMLNodePointer_t node = fXML->GetChild(sinfonode);
fXML->SkipEmpty(node);
while (node!=0) {
ReadStreamerElement(node, info);
fXML->ShiftToNext(node);
}
}
fXML->ShiftToNext(sinfonode);
}
list->SetOwner();
return list;
}
void TXMLFile::StoreStreamerElement(XMLNodePointer_t infonode, TStreamerElement* elem)
{
TClass* cl = elem->IsA();
XMLNodePointer_t node = fXML->NewChild(infonode, 0, cl->GetName());
char sbuf[100], namebuf[100];
fXML->NewAttr(node,0,"name",elem->GetName());
if (strlen(elem->GetTitle())>0)
fXML->NewAttr(node,0,"title",elem->GetTitle());
fXML->NewIntAttr(node, "v", cl->GetClassVersion());
fXML->NewIntAttr(node, "type", elem->GetType());
if (strlen(elem->GetTypeName())>0)
fXML->NewAttr(node,0,"typename", elem->GetTypeName());
fXML->NewIntAttr(node, "size", elem->GetSize());
if (elem->GetArrayDim()>0) {
fXML->NewIntAttr(node, "numdim", elem->GetArrayDim());
for (int ndim=0;ndim<elem->GetArrayDim();ndim++) {
sprintf(namebuf, "dim%d", ndim);
fXML->NewIntAttr(node, namebuf, elem->GetMaxIndex(ndim));
}
}
if (cl == TStreamerBase::Class()) {
TStreamerBase* base = (TStreamerBase*) elem;
sprintf(sbuf, "%d", base->GetBaseVersion());
fXML->NewAttr(node,0, "baseversion", sbuf);
} else
if (cl == TStreamerBasicPointer::Class()) {
TStreamerBasicPointer* bptr = (TStreamerBasicPointer*) elem;
fXML->NewIntAttr(node, "countversion", bptr->GetCountVersion());
fXML->NewAttr(node, 0, "countname", bptr->GetCountName());
fXML->NewAttr(node, 0, "countclass", bptr->GetCountClass());
} else
if (cl == TStreamerLoop::Class()) {
TStreamerLoop* loop = (TStreamerLoop*) elem;
fXML->NewIntAttr(node, "countversion", loop->GetCountVersion());
fXML->NewAttr(node, 0, "countname", loop->GetCountName());
fXML->NewAttr(node, 0, "countclass", loop->GetCountClass());
} else
if ((cl == TStreamerSTL::Class()) || (cl == TStreamerSTLstring::Class())) {
TStreamerSTL* stl = (TStreamerSTL*) elem;
fXML->NewIntAttr(node, "STLtype", stl->GetSTLtype());
fXML->NewIntAttr(node, "Ctype", stl->GetCtype());
}
}
void TXMLFile::ReadStreamerElement(XMLNodePointer_t node, TStreamerInfo* info)
{
TClass* cl = TClass::GetClass(fXML->GetNodeName(node));
if ((cl==0) || !cl->InheritsFrom(TStreamerElement::Class())) return;
TStreamerElement* elem = (TStreamerElement*) cl->New();
int elem_type = fXML->GetIntAttr(node,"type");
elem->SetName(fXML->GetAttr(node,"name"));
elem->SetTitle(fXML->GetAttr(node,"title"));
elem->SetType(elem_type);
elem->SetTypeName(fXML->GetAttr(node,"typename"));
elem->SetSize(fXML->GetIntAttr(node,"size"));
if (cl == TStreamerBase::Class()) {
int basever = fXML->GetIntAttr(node,"baseversion");
((TStreamerBase*) elem)->SetBaseVersion(basever);
} else
if (cl == TStreamerBasicPointer::Class()) {
TString countname = fXML->GetAttr(node,"countname");
TString countclass = fXML->GetAttr(node,"countclass");
Int_t countversion = fXML->GetIntAttr(node, "countversion");
((TStreamerBasicPointer*)elem)->SetCountVersion(countversion);
((TStreamerBasicPointer*)elem)->SetCountName(countname);
((TStreamerBasicPointer*)elem)->SetCountClass(countclass);
} else
if (cl == TStreamerLoop::Class()) {
TString countname = fXML->GetAttr(node,"countname");
TString countclass = fXML->GetAttr(node,"countclass");
Int_t countversion = fXML->GetIntAttr(node,"countversion");
((TStreamerLoop*)elem)->SetCountVersion(countversion);
((TStreamerLoop*)elem)->SetCountName(countname);
((TStreamerLoop*)elem)->SetCountClass(countclass);
} else
if ((cl == TStreamerSTL::Class()) || (cl == TStreamerSTLstring::Class())) {
int fSTLtype = fXML->GetIntAttr(node,"STLtype");
int fCtype = fXML->GetIntAttr(node,"Ctype");
((TStreamerSTL*)elem)->SetSTLtype(fSTLtype);
((TStreamerSTL*)elem)->SetCtype(fCtype);
}
char namebuf[100];
if (fXML->HasAttr(node, "numdim")) {
int numdim = fXML->GetIntAttr(node,"numdim");
elem->SetArrayDim(numdim);
for (int ndim=0;ndim<numdim;ndim++) {
sprintf(namebuf, "dim%d", ndim);
int maxi = fXML->GetIntAttr(node, namebuf);
elem->SetMaxIndex(ndim, maxi);
}
}
elem->SetType(elem_type);
elem->SetNewType(elem_type);
info->GetElements()->Add(elem);
}
void TXMLFile::SetXmlLayout(EXMLLayout layout)
{
if (IsWritable() && (GetListOfKeys()->GetSize()==0))
TXMLSetup::SetXmlLayout(layout);
}
void TXMLFile::SetStoreStreamerInfos(Bool_t iConvert)
{
if (IsWritable() && (GetListOfKeys()->GetSize()==0))
TXMLSetup::SetStoreStreamerInfos(iConvert);
}
void TXMLFile::SetUsedDtd(Bool_t use)
{
if (IsWritable() && (GetListOfKeys()->GetSize()==0))
TXMLSetup::SetUsedDtd(use);
}
void TXMLFile::SetUseNamespaces(Bool_t iUseNamespaces)
{
if (IsWritable() && (GetListOfKeys()->GetSize()==0))
TXMLSetup::SetUseNamespaces(iUseNamespaces);
}
Bool_t TXMLFile::AddXmlComment(const char* comment)
{
if (!IsWritable() || (fXML==0)) return kFALSE;
return fXML->AddDocComment(fDoc, comment);
}
Bool_t TXMLFile::AddXmlStyleSheet(const char* href,
const char* type,
const char* title,
int alternate,
const char* media,
const char* charset)
{
if (!IsWritable() || (fXML==0)) return kFALSE;
return fXML->AddDocStyleSheet(fDoc, href,type,title,alternate,media,charset);
}
Bool_t TXMLFile::AddXmlLine(const char* line)
{
if (!IsWritable() || (fXML==0)) return kFALSE;
return fXML->AddDocRawLine(fDoc, line);
}
Long64_t TXMLFile::DirCreateEntry(TDirectory* dir)
{
TDirectory* mother = dir->GetMotherDir();
if (mother==0) mother = this;
TKeyXML* key = new TKeyXML(mother, ++fKeyCounter, dir, dir->GetName(), dir->GetTitle());
key->SetSubir();
return key->GetKeyId();
}
TKeyXML* TXMLFile::FindDirKey(TDirectory* dir)
{
TDirectory* motherdir = dir->GetMotherDir();
if (motherdir==0) motherdir = this;
TIter next(motherdir->GetListOfKeys());
TObject* obj = 0;
while ((obj = next())!=0) {
TKeyXML* key = dynamic_cast<TKeyXML*> (obj);
if (key!=0)
if (key->GetKeyId()==dir->GetSeekDir()) return key;
}
return 0;
}
TDirectory* TXMLFile::FindKeyDir(TDirectory* motherdir, Long64_t keyid)
{
if (motherdir==0) motherdir = this;
TIter next(motherdir->GetList());
TObject* obj = 0;
while ((obj = next())!=0) {
TDirectory* dir = dynamic_cast<TDirectory*> (obj);
if (dir!=0)
if (dir->GetSeekDir()==keyid) return dir;
}
return 0;
}
Int_t TXMLFile::DirReadKeys(TDirectory* dir)
{
TKeyXML* key = FindDirKey(dir);
if (key==0) return 0;
return ReadKeysList(dir, key->KeyNode());
}
void TXMLFile::DirWriteKeys(TDirectory*)
{
TIter next(GetListOfKeys());
TObject* obj = 0;
while ((obj = next())!=0) {
TKeyXML* key = dynamic_cast<TKeyXML*> (obj);
if (key!=0) key->UpdateAttributes();
}
}
void TXMLFile::DirWriteHeader(TDirectory* dir)
{
TKeyXML* key = FindDirKey(dir);
if (key!=0)
key->UpdateObject(dir);
}