TFileMerger.cxx

Go to the documentation of this file.
00001 // @(#)root/proofplayer:$Id: TFileMerger.cxx 34286 2010-07-01 20:38:57Z rdm $
00002 // Author: Andreas Peters + Fons Rademakers + Rene Brun  26/5/2005
00003 
00004 /*************************************************************************
00005  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers.               *
00006  * All rights reserved.                                                  *
00007  *                                                                       *
00008  * For the licensing terms see $ROOTSYS/LICENSE.                         *
00009  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
00010  *************************************************************************/
00011 
00012 //////////////////////////////////////////////////////////////////////////
00013 //                                                                      //
00014 // TFileMerger                                                          //
00015 //                                                                      //
00016 // This class provides file copy and merging services.                  //
00017 //                                                                      //
00018 // It can be used to copy files (not only ROOT files), using TFile or   //
00019 // any of its remote file access plugins. It is therefore usefull in    //
00020 // a Grid environment where the files might be accessable via Castor,   //
00021 // rfio, dcap, etc.                                                     //
00022 // The merging interface allows files containing histograms and trees   //
00023 // to be merged, like the standalone hadd program.                      //
00024 //                                                                      //
00025 //////////////////////////////////////////////////////////////////////////
00026 
00027 #include "TFileMerger.h"
00028 #include "TUrl.h"
00029 #include "TFile.h"
00030 #include "TUUID.h"
00031 #include "TSystem.h"
00032 #include "TH1.h"
00033 #include "THStack.h"
00034 #include "TChain.h"
00035 #include "TKey.h"
00036 #include "THashList.h"
00037 #include "TObjString.h"
00038 #include "TClass.h"
00039 #include "TMethodCall.h"
00040 #include "Riostream.h"
00041 
00042 
00043 ClassImp(TFileMerger)
00044 
00045 //______________________________________________________________________________
00046 TFileMerger::TFileMerger(Bool_t isLocal, Bool_t histoOneGo)
00047             : fOutputFile(0), fFastMethod(kTRUE), fNoTrees(kFALSE),
00048               fLocal(isLocal), fHistoOneGo(histoOneGo)
00049 {
00050    // Create file merger object.
00051 
00052    fFileList = new TList;
00053    fFileList->SetOwner(kTRUE);
00054 
00055    fMergeList = new TList;
00056    fMergeList->SetOwner(kTRUE);
00057 }
00058 
00059 //______________________________________________________________________________
00060 TFileMerger::~TFileMerger()
00061 {
00062    // Cleanup.
00063 
00064    SafeDelete(fFileList);
00065    SafeDelete(fMergeList);
00066    SafeDelete(fOutputFile);
00067 }
00068 
00069 //______________________________________________________________________________
00070 void TFileMerger::Reset()
00071 {
00072    // Reset merger file list.
00073 
00074    fFileList->Clear();
00075    fMergeList->Clear();
00076 }
00077 
00078 //______________________________________________________________________________
00079 Bool_t TFileMerger::AddFile(const char *url, Bool_t cpProgress)
00080 {
00081    // Add file to file merger.
00082 
00083    TFile *newfile = 0;
00084    TString localcopy;
00085 
00086    if (fLocal) {
00087       TUUID uuid;
00088       localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
00089       if (!TFile::Cp(url, localcopy, cpProgress)) {
00090          Error("AddFile", "cannot get a local copy of file %s", url);
00091          return kFALSE;
00092       }
00093       newfile = TFile::Open(localcopy, "READ");
00094    } else {
00095       newfile = TFile::Open(url, "READ");
00096    }
00097 
00098    if (!newfile) {
00099       if (fLocal)
00100          Error("AddFile", "cannot open local copy %s of URL %s",
00101                           localcopy.Data(), url);
00102       else
00103          Error("AddFile", "cannot open file %s", url);
00104       return kFALSE;
00105    } else {
00106       fFileList->Add(newfile);
00107 
00108       if (!fMergeList)
00109          fMergeList = new TList;
00110       TObjString *urlObj = new TObjString(url);
00111       fMergeList->Add(urlObj);
00112 
00113       return  kTRUE;
00114    }
00115 }
00116 
00117 //______________________________________________________________________________
00118 Bool_t TFileMerger::OutputFile(const char *outputfile)
00119 {
00120    // Open merger output file.
00121 
00122    SafeDelete(fOutputFile);
00123 
00124    fOutputFilename = outputfile;
00125 
00126    if (!(fOutputFile = TFile::Open(outputfile, "RECREATE"))) {
00127       Error("OutputFile", "cannot open the MERGER output file %s", fOutputFilename.Data());
00128       return kFALSE;
00129    }
00130    return kTRUE;
00131 }
00132 
00133 //______________________________________________________________________________
00134 void TFileMerger::PrintFiles(Option_t *options)
00135 {
00136    // Print list of files being merged.
00137 
00138    fFileList->Print(options);
00139 }
00140 
00141 //______________________________________________________________________________
00142 Bool_t TFileMerger::Merge(Bool_t)
00143 {
00144    // Merge the files. If no output file was specified it will write into
00145    // the file "FileMerger.root" in the working directory. Returns true
00146    // on success, false in case of error.
00147 
00148    if (!fOutputFile) {
00149       TString outf(fOutputFilename);
00150       if (outf.IsNull()) {
00151          outf.Form("file:%s/FileMerger.root", gSystem->TempDirectory());
00152          Info("Merge", "will merge the results to the file %s\n"
00153                        "since you didn't specify a merge filename",
00154                        TUrl(outf).GetFile());
00155       }
00156       if (!OutputFile(outf.Data())) {
00157          return kFALSE;
00158       }
00159    }
00160 
00161    Bool_t result = MergeRecursive(fOutputFile, fFileList);
00162    if (!result) {
00163       Error("Merge", "error during merge of your ROOT files");
00164    } else {
00165       // But Close is required so the file is complete.
00166       fOutputFile->Close();
00167    }
00168 
00169    // Cleanup
00170    SafeDelete(fOutputFile);
00171 
00172    // Remove local copies if there are any
00173    TIter next(fFileList);
00174    TFile *file;
00175    while ((file = (TFile*) next())) {
00176       // close the files
00177       file->Close();
00178       // remove the temporary files
00179       if(fLocal) {
00180          TString p(file->GetPath());
00181          p = p(0, p.Index(':',0));
00182          gSystem->Unlink(p);
00183       }
00184    }
00185    return result;
00186 }
00187 
00188 //______________________________________________________________________________
00189 Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist)
00190 {
00191    // Merge all objects in a directory
00192    // NB. This function is a copy of the hadd function MergeROOTFile
00193 
00194    // Get the dir name
00195    TString path(target->GetPath());
00196    path.Remove(0, path.Last(':') + 2);
00197 
00198    //gain time, do not add the objects in the list in memory
00199    Bool_t addDirStat = TH1::AddDirectoryStatus();
00200    TH1::AddDirectory(kFALSE);
00201 
00202    TDirectory *first_source = (TDirectory*)sourcelist->First();
00203 
00204    Int_t nguess = sourcelist->GetSize()+1000;
00205    THashList allNames(nguess);
00206    ((THashList*)target->GetList())->Rehash(nguess);
00207    ((THashList*)target->GetListOfKeys())->Rehash(nguess);
00208 
00209    while (first_source) {
00210       TDirectory *current_sourcedir = first_source->GetDirectory(path);
00211       if (!current_sourcedir) {
00212          first_source = (TDirectory*)sourcelist->After(first_source);
00213          continue;
00214       }
00215 
00216       // loop over all keys in this directory
00217       TChain *globChain = 0;
00218       TIter nextkey( current_sourcedir->GetListOfKeys() );
00219       TKey *key, *oldkey=0;
00220 
00221       while ( (key = (TKey*)nextkey())) {
00222          if (current_sourcedir == target) break;
00223          //keep only the highest cycle number for each key
00224          if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
00225          if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
00226          if (allNames.FindObject(key->GetName())) continue;
00227          TClass *cl = TClass::GetClass(key->GetClassName());
00228          if (!cl || !cl->InheritsFrom(TObject::Class())) {
00229             Info("MergeRecursive", "cannot merge object type, name: %s title: %s",
00230                                    key->GetName(), key->GetTitle());
00231             continue;
00232          }
00233          allNames.Add(new TObjString(key->GetName()));
00234 
00235          // read object from first source file
00236          current_sourcedir->cd();
00237          TObject *obj = key->ReadObj();
00238          if (!obj) {
00239             Info("MergeRecursive", "could not read object for key {%s, %s}",
00240                                    key->GetName(), key->GetTitle());
00241             continue;
00242          }
00243 
00244          if (obj->IsA()->InheritsFrom(TH1::Class())) {
00245             // descendant of TH1 -> merge it
00246 
00247             TH1 *h1 = (TH1*)obj;
00248             TList listH;
00249 
00250             // loop over all source files and add the content of the
00251             // correspondant histogram to the one pointed to by "h1"
00252             TFile *nextsource = (TFile*)sourcelist->After( first_source );
00253             while ( nextsource ) {
00254                // make sure we are at the correct directory level by cd'ing to path
00255                TDirectory *ndir = nextsource->GetDirectory(path);
00256                if (ndir) {
00257                   ndir->cd();
00258                   TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
00259                   if (key2) {
00260                      TObject *hobj = key2->ReadObj();
00261                      hobj->ResetBit(kMustCleanup);
00262                      listH.Add(hobj);
00263                      // Run the merging now, if required
00264                      if (!fHistoOneGo) {
00265                         h1->Merge(&listH);
00266                         listH.Delete();
00267                      }
00268                   }
00269                }
00270                nextsource = (TFile*)sourcelist->After( nextsource );
00271             }
00272             // Merge the list, if still to be done
00273             if (fHistoOneGo) {
00274                h1->Merge(&listH);
00275                listH.Delete();
00276             }
00277          } else if ( obj->IsA()->InheritsFrom( TTree::Class() ) ) {
00278 
00279             // loop over all source files create a chain of Trees "globChain"
00280             if (!fNoTrees) {
00281                TString obj_name;
00282                if (path.Length()) {
00283                   obj_name = path + "/" + obj->GetName();
00284                } else {
00285                   obj_name = obj->GetName();
00286                }
00287                globChain = new TChain(obj_name);
00288                globChain->Add(first_source->GetName());
00289                TFile *nextsource = (TFile*)sourcelist->After( first_source );
00290                while ( nextsource ) {
00291                   //do not add to the list a file that does not contain this Tree
00292                   TFile *curf = TFile::Open(nextsource->GetName());
00293                   if (curf) {
00294                      Bool_t mustAdd = kFALSE;
00295                      if (curf->FindKey(obj_name)) {
00296                         mustAdd = kTRUE;
00297                      } else {
00298                         //we could be more clever here. No need to import the object
00299                         //we are missing a function in TDirectory
00300                         TObject *aobj = curf->Get(obj_name);
00301                         if (aobj) { mustAdd = kTRUE; delete aobj;}
00302                      }
00303                      if (mustAdd) {
00304                         globChain->Add(nextsource->GetName());
00305                      }
00306                   }
00307                   delete curf;
00308                   nextsource = (TFile*)sourcelist->After( nextsource );
00309                }
00310             }
00311          } else if ( obj->IsA()->InheritsFrom( TDirectory::Class() ) ) {
00312             // it's a subdirectory
00313 
00314             //cout << "Found subdirectory " << obj->GetName() << endl;
00315             // create a new subdir of same name and title in the target file
00316             target->cd();
00317             TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
00318 
00319             // newdir is now the starting point of another round of merging
00320             // newdir still knows its depth within the target file via
00321             // GetPath(), so we can still figure out where we are in the recursion
00322             MergeRecursive( newdir, sourcelist);
00323 
00324          } else if (obj->InheritsFrom(TObject::Class()) &&
00325                     obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
00326             // Object implements Merge(TCollection*)
00327 
00328             TList listH;
00329             TString listHargs;
00330             listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH);
00331 
00332             // Loop over all source files and merge same-name object
00333             TFile *nextsource = (TFile*)sourcelist->After( first_source );
00334             while (nextsource) {
00335                // make sure we are at the correct directory level by cd'ing to path
00336                TDirectory *ndir = nextsource->GetDirectory(path);
00337                if (ndir) {
00338                   ndir->cd();
00339                   TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
00340                   if (key2) {
00341                      TObject *hobj = key2->ReadObj();
00342                      // Set ownership for collections
00343                      if (hobj->InheritsFrom(TCollection::Class())) {
00344                         ((TCollection*)hobj)->SetOwner();
00345                      }
00346                      hobj->ResetBit(kMustCleanup);
00347                      listH.Add(hobj);
00348                      Int_t error = 0;
00349                      obj->Execute("Merge", listHargs.Data(), &error);
00350                      if (error) {
00351                         Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
00352                                                  obj->GetName(), nextsource->GetName());
00353                      }
00354                      listH.Delete();
00355                   }
00356                }
00357                nextsource = (TFile*)sourcelist->After( nextsource );
00358             }
00359          } else if ( obj->IsA()->InheritsFrom( THStack::Class() ) ) {
00360             THStack *hstack1 = (THStack*) obj;
00361             TList* l = new TList();
00362 
00363             // loop over all source files and merge the histos of the
00364             // corresponding THStacks with the one pointed to by "hstack1"
00365             TFile *nextsource = (TFile*)sourcelist->After( first_source );
00366             while ( nextsource ) {
00367                // make sure we are at the correct directory level by cd'ing to path
00368                TDirectory *ndir = nextsource->GetDirectory(path);
00369                if (ndir) {
00370                   ndir->cd();
00371                   TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(hstack1->GetName());
00372                   if (key2) {
00373                      THStack *hstack2 = (THStack*) key2->ReadObj();
00374                      l->Add(hstack2->GetHists()->Clone());
00375                      delete hstack2;
00376                   }
00377                }
00378 
00379                nextsource = (TFile*)sourcelist->After( nextsource );
00380             }
00381             hstack1->GetHists()->Merge(l);
00382             l->Delete();
00383          } else {
00384             // Object is of no type that we can merge
00385             Warning("MergeRecursive", "cannot merge object type (n:'%s', t:'%s') - "
00386                                       "Merge(TCollection *) not implemented",
00387                                       obj->GetName(), obj->GetTitle());
00388 
00389             // Loop over all source files and write similar objects directly to the output file
00390             TFile *nextsource = (TFile*)sourcelist->After( first_source );
00391             while (nextsource) {
00392                // make sure we are at the correct directory level by cd'ing to path
00393                TDirectory *ndir = nextsource->GetDirectory(path);
00394                if (ndir) {
00395                   ndir->cd();
00396                   TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
00397                   if (key2) {
00398                      TObject *nobj = key2->ReadObj();
00399                      nobj->ResetBit(kMustCleanup);
00400                      if (target->WriteTObject(nobj, key2->GetName(), "SingleKey") <= 0) {
00401                         Warning("MergeRecursive", "problems copying object (n:'%s', t:'%s') to output file ",
00402                                                   obj->GetName(), obj->GetTitle());
00403                      }
00404                      delete nobj;
00405                   }
00406                }
00407                nextsource = (TFile*)sourcelist->After( nextsource );
00408             }
00409          }
00410 
00411          // now write the merged histogram (which is "in" obj) to the target file
00412          // note that this will just store obj in the current directory level,
00413          // which is not persistent until the complete directory itself is stored
00414          // by "target->Write()" below
00415          target->cd();
00416 
00417          //!!if the object is a tree, it is stored in globChain...
00418          if(obj->IsA()->InheritsFrom( TDirectory::Class() )) {
00419             //printf("cas d'une directory\n");
00420          } else if(obj->IsA()->InheritsFrom( TTree::Class() )) {
00421             if (!fNoTrees) {
00422                if (globChain) {
00423                   globChain->ls();
00424                   if (fFastMethod) globChain->Merge(target->GetFile(),0,"keep fast");
00425                   else             globChain->Merge(target->GetFile(),0,"keep");
00426                   delete globChain;
00427                }
00428             }
00429          } else if (obj->IsA()->InheritsFrom( TCollection::Class() )) {
00430             obj->Write( key->GetName(), TObject::kSingleKey );
00431             ((TCollection*)obj)->SetOwner();
00432          } else {
00433             obj->Write( key->GetName() );
00434          }
00435          if (obj->IsA()->InheritsFrom(TCollection::Class())) ((TCollection*)obj)->Delete();
00436          oldkey = key;
00437          delete obj;
00438       } // while ( ( TKey *key = (TKey*)nextkey() ) )
00439       first_source = (TDirectory*)sourcelist->After(first_source);
00440    }
00441    // save modifications to target file
00442    target->SaveSelf(kTRUE);
00443    TH1::AddDirectory(addDirStat);
00444    return kTRUE;
00445 }

Generated on Tue Jul 5 14:52:17 2011 for ROOT_528-00b_version by  doxygen 1.5.1