#include "TBasket.h"
#include "TBranch.h"
#include "TBranchClones.h"
#include "TBranchElement.h"
#include "TStreamerInfo.h"
#include "TBranchRef.h"
#include "TError.h"
#include "TProcessID.h"
#include "TMath.h"
#include "TTree.h"
#include "TTreeCloner.h"
#include "TFile.h"
#include "TLeafB.h"
#include "TLeafI.h"
#include "TLeafL.h"
#include "TLeafS.h"
#include "TLeafO.h"
#include "TLeafC.h"
#include <algorithm>
Bool_t TTreeCloner::CompareSeek::operator()(UInt_t i1, UInt_t i2)
{
if (fObject->fBasketSeek[i1] == fObject->fBasketSeek[i2]) {
if (fObject->fBasketEntry[i1] == fObject->fBasketEntry[i2]) {
return i1 < i2;
}
return fObject->fBasketEntry[i1] < fObject->fBasketEntry[i2];
}
return fObject->fBasketSeek[i1] < fObject->fBasketSeek[i2];
}
Bool_t TTreeCloner::CompareEntry::operator()(UInt_t i1, UInt_t i2)
{
if (fObject->fBasketEntry[i1] == fObject->fBasketEntry[i2]) {
return i1 < i2;
}
return fObject->fBasketEntry[i1] < fObject->fBasketEntry[i2];
}
TTreeCloner::TTreeCloner(TTree *from, TTree *to, Option_t *method, UInt_t options) :
fWarningMsg(),
fIsValid(kTRUE),
fNeedConversion(kFALSE),
fOptions(options),
fFromTree(from),
fToTree(to),
fMethod(method),
fFromBranches( from ? from->GetListOfLeaves()->GetEntries()+1 : 0),
fToBranches( to ? to->GetListOfLeaves()->GetEntries()+1 : 0),
fMaxBaskets(CollectBranches()),
fBasketBranchNum(new UInt_t[fMaxBaskets]),
fBasketNum(new UInt_t[fMaxBaskets]),
fBasketSeek(new Long64_t[fMaxBaskets]),
fBasketEntry(new Long64_t[fMaxBaskets]),
fBasketIndex(new UInt_t[fMaxBaskets]),
fPidOffset(0),
fCloneMethod(TTreeCloner::kDefault),
fToStartEntries(0)
{
TString opt(method);
opt.ToLower();
if (opt.Contains("sortbasketsbybranch")) {
fCloneMethod = TTreeCloner::kSortBasketsByBranch;
} else if (opt.Contains("sortbasketsbyentry")) {
fCloneMethod = TTreeCloner::kSortBasketsByEntry;
} else {
fCloneMethod = TTreeCloner::kSortBasketsByOffset;
}
if (fToTree) fToStartEntries = fToTree->GetEntries();
if (fToTree == 0) {
fWarningMsg.Form("An output TTree is required (cloning %s).",
from->GetName());
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
} else if (fToTree->GetDirectory() == 0) {
fWarningMsg.Form("The output TTree (%s) must be associated with a directory.",
fToTree->GetName());
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
} else if (fToTree->GetCurrentFile() == 0) {
fWarningMsg.Form("The output TTree (%s) must be associated with a directory (%s) that is in a file.",
fToTree->GetName(),fToTree->GetDirectory()->GetName());
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
} else if (! fToTree->GetDirectory()->IsWritable()) {
if (fToTree->GetDirectory()==fToTree->GetCurrentFile()) {
fWarningMsg.Form("The output TTree (%s) must be associated with a writable file (%s).",
fToTree->GetName(),fToTree->GetCurrentFile()->GetName());
} else {
fWarningMsg.Form("The output TTree (%s) must be associated with a writable directory (%s in %s).",
fToTree->GetName(),fToTree->GetDirectory()->GetName(),fToTree->GetCurrentFile()->GetName());
}
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
}
}
Bool_t TTreeCloner::Exec()
{
if (!IsValid()) {
return kFALSE;
}
ImportClusterRanges();
CopyStreamerInfos();
CopyProcessIds();
CloseOutWriteBaskets();
CollectBaskets();
SortBaskets();
WriteBaskets();
CopyMemoryBaskets();
return kTRUE;
}
TTreeCloner::~TTreeCloner()
{
delete [] fBasketBranchNum;
delete [] fBasketNum;
delete [] fBasketSeek;
delete [] fBasketEntry;
delete [] fBasketIndex;
}
void TTreeCloner::CloseOutWriteBaskets()
{
for(Int_t i=0; i<fToBranches.GetEntries(); ++i) {
TBranch *to = (TBranch*)fToBranches.UncheckedAt(i);
to->FlushOneBasket(to->GetWriteBasket());
}
}
UInt_t TTreeCloner::CollectBranches(TBranch *from, TBranch *to) {
UInt_t numBaskets = 0;
if (from->InheritsFrom(TBranchClones::Class())) {
TBranchClones *fromclones = (TBranchClones*) from;
TBranchClones *toclones = (TBranchClones*) to;
numBaskets += CollectBranches(fromclones->fBranchCount, toclones->fBranchCount);
} else if (from->InheritsFrom(TBranchElement::Class())) {
Int_t nb = from->GetListOfLeaves()->GetEntries();
Int_t fnb = to->GetListOfLeaves()->GetEntries();
if (nb != fnb && (nb == 0 || fnb == 0)) {
fWarningMsg.Form("The export branch and the import branch do not have the same split level. (The branch name is %s.)",
from->GetName());
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fNeedConversion = kTRUE;
fIsValid = kFALSE;
return 0;
}
if (((TBranchElement*) from)->GetStreamerType() != ((TBranchElement*) to)->GetStreamerType()) {
fWarningMsg.Form("The export branch and the import branch do not have the same streamer type. (The branch name is %s.)",
from->GetName());
if (!(fOptions & kNoWarnings)) {
Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
return 0;
}
TBranchElement *fromelem = (TBranchElement*) from;
TBranchElement *toelem = (TBranchElement*) to;
if (fromelem->fMaximum > toelem->fMaximum) toelem->fMaximum = fromelem->fMaximum;
} else {
Int_t nb = from->GetListOfLeaves()->GetEntries();
Int_t fnb = to->GetListOfLeaves()->GetEntries();
if (nb != fnb) {
fWarningMsg.Form("The export branch and the import branch (%s) do not have the same number of leaves (%d vs %d)",
from->GetName(), fnb, nb);
if (!(fOptions & kNoWarnings)) {
Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
return 0;
}
for (Int_t i=0;i<nb;i++) {
TLeaf *fromleaf_gen = (TLeaf*)from->GetListOfLeaves()->At(i);
TLeaf *toleaf_gen = (TLeaf*)to->GetListOfLeaves()->At(i);
if (toleaf_gen->IsA() != fromleaf_gen->IsA() ) {
fWarningMsg.Form("The export leaf and the import leaf (%s.%s) do not have the data type (%s vs %s)",
from->GetName(),fromleaf_gen->GetName(),fromleaf_gen->GetTypeName(),toleaf_gen->GetTypeName());
if (! (fOptions & kNoWarnings) ) {
Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
fNeedConversion = kTRUE;
return 0;
}
if (fromleaf_gen->IsA()==TLeafI::Class()) {
TLeafI *fromleaf = (TLeafI*)fromleaf_gen;
TLeafI *toleaf = (TLeafI*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
} else if (fromleaf_gen->IsA()==TLeafL::Class()) {
TLeafL *fromleaf = (TLeafL*)fromleaf_gen;
TLeafL *toleaf = (TLeafL*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
} else if (fromleaf_gen->IsA()==TLeafB::Class()) {
TLeafB *fromleaf = (TLeafB*)fromleaf_gen;
TLeafB *toleaf = (TLeafB*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
} else if (fromleaf_gen->IsA()==TLeafS::Class()) {
TLeafS *fromleaf = (TLeafS*)fromleaf_gen;
TLeafS *toleaf = (TLeafS*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
} else if (fromleaf_gen->IsA()==TLeafO::Class()) {
TLeafO *fromleaf = (TLeafO*)fromleaf_gen;
TLeafO *toleaf = (TLeafO*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
} else if (fromleaf_gen->IsA()==TLeafC::Class()) {
TLeafC *fromleaf = (TLeafC*)fromleaf_gen;
TLeafC *toleaf = (TLeafC*)toleaf_gen;
if (fromleaf->GetMaximum() > toleaf->GetMaximum())
toleaf->SetMaximum( fromleaf->GetMaximum() );
if (fromleaf->GetMinimum() < toleaf->GetMinimum())
toleaf->SetMinimum( fromleaf->GetMinimum() );
if (fromleaf->GetLenStatic() > toleaf->GetLenStatic())
toleaf->SetLen(fromleaf->GetLenStatic());
}
}
}
fFromBranches.AddLast(from);
if (!from->TestBit(TBranch::kDoNotUseBufferMap)) {
to->ResetBit(TBranch::kDoNotUseBufferMap);
}
fToBranches.AddLast(to);
numBaskets += from->GetWriteBasket();
numBaskets += CollectBranches(from->GetListOfBranches(),to->GetListOfBranches());
return numBaskets;
}
UInt_t TTreeCloner::CollectBranches(TObjArray *from, TObjArray *to)
{
Int_t fnb = from->GetEntries();
Int_t tnb = to->GetEntries();
if (!fnb || !tnb) {
return 0;
}
UInt_t numBasket = 0;
Int_t fi = 0;
Int_t ti = 0;
while (ti < tnb) {
TBranch* fb = (TBranch*) from->UncheckedAt(fi);
TBranch* tb = (TBranch*) to->UncheckedAt(ti);
Int_t firstfi = fi;
while (strcmp(fb->GetName(), tb->GetName())) {
++fi;
if (fi >= fnb) {
fi = 0;
}
if (fi==firstfi) {
fb = 0;
break;
}
fb = (TBranch*) from->UncheckedAt(fi);
}
if (fb) {
numBasket += CollectBranches(fb, tb);
++fi;
if (fi >= fnb) {
fi = 0;
}
} else {
if (tb->GetMother()==tb) {
if (!(fOptions & kIgnoreMissingTopLevel)) {
fWarningMsg.Form("One of the export top level branches (%s) is not present in the import TTree.",
tb->GetName());
if (!(fOptions & kNoWarnings)) {
Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
}
} else {
fWarningMsg.Form("One of the export sub-branches (%s) is not present in the import TTree.",
tb->GetName());
if (!(fOptions & kNoWarnings)) {
Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
}
fIsValid = kFALSE;
}
}
++ti;
}
return numBasket;
}
UInt_t TTreeCloner::CollectBranches()
{
if (!fFromTree || !fToTree) {
return 0;
}
UInt_t numBasket = CollectBranches(fFromTree->GetListOfBranches(),
fToTree->GetListOfBranches());
if (fFromTree->GetBranchRef()) {
fToTree->BranchRef();
numBasket += CollectBranches(fFromTree->GetBranchRef(),fToTree->GetBranchRef());
}
return numBasket;
}
void TTreeCloner::CollectBaskets()
{
UInt_t len = fFromBranches.GetEntries();
for(UInt_t i=0,bi=0; i<len; ++i) {
TBranch *from = (TBranch*)fFromBranches.UncheckedAt(i);
for(Int_t b=0; b<from->GetWriteBasket(); ++b,++bi) {
fBasketBranchNum[bi] = i;
fBasketNum[bi] = b;
fBasketSeek[bi] = from->GetBasketSeek(b);
fBasketEntry[bi] = from->GetBasketEntry()[b];
fBasketIndex[bi] = bi;
}
}
}
void TTreeCloner::CopyStreamerInfos()
{
TFile *fromFile = fFromTree->GetDirectory()->GetFile();
TFile *toFile = fToTree->GetDirectory()->GetFile();
TList *l = fromFile->GetStreamerInfoList();
TIter next(l);
TStreamerInfo *oldInfo;
while ( (oldInfo = (TStreamerInfo*)next()) ) {
if (oldInfo->IsA() != TStreamerInfo::Class()) {
continue;
}
TStreamerInfo *curInfo = 0;
TClass *cl = TClass::GetClass(oldInfo->GetName());
if ((cl->IsLoaded() && (cl->GetNew()!=0 || cl->HasDefaultConstructor()))
|| !cl->IsLoaded()) {
curInfo = (TStreamerInfo*)cl->GetStreamerInfo(oldInfo->GetClassVersion());
if (oldInfo->GetClassVersion()==1) {
TStreamerInfo *matchInfo = (TStreamerInfo*)cl->FindStreamerInfo(oldInfo->GetCheckSum());
if (matchInfo) {
curInfo = matchInfo;
}
}
curInfo->ForceWriteInfo(toFile);
} else {
oldInfo->ForceWriteInfo(toFile);
}
}
delete l;
}
void TTreeCloner::CopyMemoryBaskets()
{
TBasket *basket = 0;
for(Int_t i=0; i<fToBranches.GetEntries(); ++i) {
TBranch *from = (TBranch*)fFromBranches.UncheckedAt( i );
TBranch *to = (TBranch*)fToBranches.UncheckedAt( i );
basket = from->GetListOfBaskets()->GetEntries() ? from->GetBasket(from->GetWriteBasket()) : 0;
if (basket) {
basket = (TBasket*)basket->Clone();
basket->SetBranch(to);
to->AddBasket(*basket, kFALSE, fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()]);
} else {
to->AddLastBasket( fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()] );
}
if (from->GetEntries()!=0 && from->GetWriteBasket()==0 && (basket==0 || basket->GetNevBuf()==0)) {
to->SetEntries(to->GetEntries()+from->GetEntries());
}
}
}
void TTreeCloner::CopyProcessIds()
{
TFile *fromfile = fFromTree->GetDirectory()->GetFile();
TFile *tofile = fToTree->GetDirectory()->GetFile();
fPidOffset = tofile->GetNProcessIDs();
TIter next(fromfile->GetListOfKeys());
TKey *key;
TDirectory::TContext cur(gDirectory,fromfile);
while ((key = (TKey*)next())) {
if (!strcmp(key->GetClassName(),"TProcessID")) {
TProcessID *pid = (TProcessID*)key->ReadObjectAny(0);
if (!pid) continue;
UShort_t out = 0;
TObjArray *pids = tofile->GetListOfProcessIDs();
Int_t npids = tofile->GetNProcessIDs();
Bool_t wasIn = kFALSE;
for (Int_t i=0;i<npids;i++) {
if (pids->At(i) == pid) {out = (UShort_t)i; wasIn = kTRUE; break;}
}
if (!wasIn) {
TDirectory *dirsav = gDirectory;
tofile->cd();
tofile->SetBit(TFile::kHasReferences);
pids->AddAtAndExpand(pid,npids);
pid->IncrementCount();
char name[32];
snprintf(name,32,"ProcessID%d",npids);
pid->Write(name);
tofile->IncrementProcessIDs();
if (gDebug > 0) {
Info("WriteProcessID", "name=%s, file=%s", name, tofile->GetName());
}
if (dirsav) dirsav->cd();
out = (UShort_t)npids;
}
if (out<fPidOffset) {
Error("CopyProcessIDs","Copied %s from %s might already exist!\n",
pid->GetName(),fromfile->GetName());
}
}
}
}
void TTreeCloner::ImportClusterRanges()
{
fToTree->SetEntries(fToTree->GetEntries() - fFromTree->GetTree()->GetEntries());
fToTree->ImportClusterRanges( fFromTree->GetTree() );
fToTree->SetEntries(fToTree->GetEntries() + fFromTree->GetTree()->GetEntries());
}
void TTreeCloner::SortBaskets()
{
switch (fCloneMethod) {
case kSortBasketsByBranch:
break;
case kSortBasketsByEntry: {
for(UInt_t i = 0; i < fMaxBaskets; ++i) { fBasketIndex[i] = i; }
std::sort(fBasketIndex, fBasketIndex+fMaxBaskets, CompareEntry( this) );
break;
}
case kSortBasketsByOffset:
default: {
for(UInt_t i = 0; i < fMaxBaskets; ++i) { fBasketIndex[i] = i; }
std::sort(fBasketIndex, fBasketIndex+fMaxBaskets, CompareSeek( this) );
break;
}
}
}
void TTreeCloner::WriteBaskets()
{
TBasket *basket = new TBasket();
for(UInt_t j=0; j<fMaxBaskets; ++j) {
TBranch *from = (TBranch*)fFromBranches.UncheckedAt( fBasketBranchNum[ fBasketIndex[j] ] );
TBranch *to = (TBranch*)fToBranches.UncheckedAt( fBasketBranchNum[ fBasketIndex[j] ] );
TFile *tofile = to->GetFile(0);
TFile *fromfile = from->GetFile(0);
Int_t index = fBasketNum[ fBasketIndex[j] ];
Long64_t pos = from->GetBasketSeek(index);
if (pos!=0) {
if (from->GetBasketBytes()[index] == 0) {
from->GetBasketBytes()[index] = basket->ReadBasketBytes(pos, fromfile);
}
Int_t len = from->GetBasketBytes()[index];
basket->LoadBasketBuffers(pos,len,fromfile,fFromTree);
basket->IncrementPidOffset(fPidOffset);
basket->CopyTo(tofile);
to->AddBasket(*basket,kTRUE,fToStartEntries + from->GetBasketEntry()[index]);
} else {
TBasket *frombasket = from->GetBasket( index );
if (frombasket && frombasket->GetNevBuf()>0) {
TBasket *tobasket = (TBasket*)frombasket->Clone();
tobasket->SetBranch(to);
to->AddBasket(*tobasket, kFALSE, fToStartEntries+from->GetBasketEntry()[index]);
to->FlushOneBasket(to->GetWriteBasket());
}
}
}
delete basket;
}