// ---------------------------------------------------------------------- // // HepHistoFileManager.cc - implementation the wrapper for the // Histoscope package // // HepHistoFileManager is a C++ wrapper class that controls the // initialization and creation of histograms and ntuples and allows // them to be written to a file in the Histo format. // // This class is derived from the HepFileManager, and is not an abstract // class. It should be instantiated before any histograms or ntuples - a // pointer to the manager is passed to every histogram and ntuple upon // creation. // // The code presumes that Fortran is callable from C by the (mostly) // standard BSD convention: all lowercase symbol names with an // underscore ("_") appended. // // write() writes the managed Histo objects to a file, as does the // destructor. // dumpAllData() and clearAllData() initiate calls of dumpData() or // clearData() to all managed objects. // // // History // 20-Aug-1998 Philippe Canal Initial draft // 25-Fev-1999 Philippe Canal Add read-only files protection // 19-Mar-2001 Fischler hepFileFormat // // ---------------------------------------------------------------------- #include "ZMutility/ZMenvironment.h" #include "ZMutility/iostream" #include #include #include #include USING( std::string ) #include USING( std::deque ) #ifndef HEPTRACE_H #include "HepTuple/HepTrace.h" #endif #ifndef HEPHISTOFILEMANAGER_H #include "HepTuple/HepHistoFileManager.h" #endif #ifndef HEPHISTOHIST1D_H #include "HepTuple/HepHistoHist1D.h" #endif #ifndef HEPHISTOHIST2D_H #include "HepTuple/HepHistoHist2D.h" #endif #ifndef HEPHISTOHISTPROF_H #include "HepTuple/HepHistoHistProf.h" #endif #ifndef HEPHISTOTUPLE_H #include "HepTuple/HepHistoNtuple.h" #endif #ifndef HEPDIR_H #include "HepTuple/HepDir.h" #endif #ifndef HEPOBJ_H #include "HepTuple/HepObj.h" #endif // for sprintf, remove, mkstemp, rename and close #include #include #include #include #include #include #ifdef WIN32 #include "io.h" #endif #ifdef __KCC extern "C" { int mkstemp( char* ); } #endif #include "histoscope.h" // this is defined in nirvana/histo_util/histo.h // but is not available in the 'release' package #define HS_MAX_CATEGORY_LENGTH 255 #define HS_MAX_TITLE_LENGTH 80 #define HS_MAX_NAME_LENGTH 80 // define in nirvana/histo_util/HistoClient.h extern int InitializedAndActive; ZM_BEGIN_NAMESPACE( zmht ) /* namespace zmht { */ #define NOTYET(fctnName) \ ZMthrow(ZMxHepNotYet( \ "Warning: this manager class does not yet fully support the " \ #fctnName " call; some results may be anomalous.",ZMexWARNING)); bool HepHistoFileManager::init_done = false; // HepHisto initialized? // Histo manual p.166 says 8, but internally uses this number: static const size_t ROOTDIRNAMELENGTH = HS_MAX_CATEGORY_LENGTH; // get this file's simple name static string HistoBaseName( const string & fileName ) { return string( fileName, fileName.rfind( '/' ) + 1 #ifdef WIN32 , string::npos #endif ); } // ensure that a path name is enforcing length requirements on root string validHistoPath( const string & pathName ) { string s = pathName; size_t i; if ( HepDir::isAbsPath( s ) ) { if ( s == "//" ) s = "\\"; // enforce Histo req's on root directory name i = s.find( '/' ); if ( i >= ROOTDIRNAMELENGTH+2 ) // is root name part too long? s.replace( ROOTDIRNAMELENGTH+2, i-1, string("") ); // yes, truncate that part } return s; } // enforcing Histo's length requirement const string HistoRootDir( const string & S ) { string rootDir = HistoBaseName( S ); if ( rootDir.length() > ROOTDIRNAMELENGTH ) rootDir.replace( ROOTDIRNAMELENGTH, rootDir.length(), string("") ); return rootDir; } void HepHistoFileManager::init( // constructor body (shared by both constructors) const string& fName, // desired file name HepFileManager::mode req_mode, // desired opening mode const string& rootDirName // desired name of root directory ) { // one-time class initialization if ( ! init_done && ! InitializedAndActive ) { hs_initialize("HepHistoFileManager"); init_done = true; } // check status of this file: if exists, then open it & read its contents; // if non-existent, create it afresh: filemode mode = checkFile ( fName ); bool fileExists = ( mode != HEP_ABSENT ); if ( ((req_mode & HEP_READONLY) !=0) && (mode == HEP_ALL) ) { mode = HEP_READ; } // Added 26 March 2001 JMM if( fileExists && ( (req_mode & HEP_REMOVE) != 0 ) ) { int rc = remove( fName.c_str() ); if( rc != 0 ) { char tmp[20]; sprintf(tmp,"%d",errno); ZMthrow(ZMxHepCantDeleteFile( string("In HepHistoFileManager: Cannot delete file ") +string( "\"" ) +fName +string( "\"\n") +string(" RC = ") +string( tmp ))); } fileExists = false; mode = checkFile ( fName ); } // Added 28 March 2001 JMM if( fileExists && ( (req_mode & HEP_RENAME) != 0 ) ) { string tempName = fName + string("XXXXXX"); char * temp = new char[1024]; strcpy( temp, tempName.c_str() ); int fd = mkstemp( temp ); if( fd == -1 ) ZMthrow(ZMxHepCantGenerateName( string("In HepHistoFileManager: Cannot generate new file name "))); int rc = rename( fName.c_str(), temp ); if( rc != 0 ) { char tmp[20]; sprintf(tmp,"%d",errno); ZMthrow(ZMxHepCantRenameFile( string("In HepHistoFileManager: Cannot rename file ") +string( "\"" ) +fName +string( "\"\n") +string(" RC = ") +string( tmp ))); } delete [] temp; close( fd ); fileExists = false; mode = checkFile ( fName ); } if ( (mode != HEP_ABSENT) && (mode != HEP_ALL) ) { if ( mode == HEP_READ ) { ZMthrow(ZMxHepNotYet("HepHistoFileManager can not open a read-only file yet" )); } else { ZMthrow(ZMxHepCantOpenFile(string("in HepHistoFileManager: can't ") +string( "open" ) +string( "\"" ) +fName +string( "\"\n") )); } setReadOnly( true ); return; } HEP_DEBUG( " Opening \"" << fName << "\" with root \"" << rootDir() << "\"" ); int rc = hs_connect_to_file( (char*)fName.c_str(), (char*)( rootDir().c_str() ), ""); if ( rc == 0 ) { // oops, got a problem! char tmp[20]; sprintf(tmp,"%d",rc); ZMthrow(ZMxHepCantOpenFile(string("in HepHistoFileManager: can't ") +string( fileExists ? "open" : "create" ) +string( " \"" ) +fName +string( "\"\n") +string(" status = ") +string( tmp ))); } cd( "//"+rootDir() ); // position ourselves to root directory (both of 'em!) if ( fileExists ) retrieveExistingItems(); // create file's structure in memory } HepHistoFileManager::HepHistoFileManager(// constructor const string& fName, // desired file name const string& rootDirName // desired name of root directory ) : HepFileManager( fName, HistoRootDir(rootDirName.length()!=0? rootDirName:fName) ) { HEP_DEBUG( "HepHistoFileManager::constructor( \"" << fName << "\" )" ); init ( fName, HEP_UPDATE, rootDirName ); } // HepHistoFileManager::HepHistoFileManager() HepHistoFileManager::HepHistoFileManager(// constructor const string& fName, // desired file name HepFileManager::mode req_mode, // desired opening mode const string& rootDirName // desired name of root directory ) : HepFileManager( fName, HistoRootDir(rootDirName.length()!=0? rootDirName:fName) ) { HEP_DEBUG( "HepHistoFileManager::constructor( \"" << fName << "\" )" ); init ( fName, req_mode, rootDirName ); } // HepHistoFileManager::HepHistoFileManager() HepHistoFileManager::~HepHistoFileManager() { // destructor HEP_DEBUG( "HepHistoFileManager::destructor()" ); write(); int status = hs_disconnect_from_file((char*)(rootDir().c_str())); status &= hs_close_file((char*)fileName().c_str()); if ( ! status ) { ZMthrow(ZMxHepBadIO(string( "HepHistoFileManager destructor: unable to properly close ")+fileName())); } } // HepHistoFileManager::~HepHistoFileManager() void HepHistoFileManager::cd( const string & path ) { // change dir HEP_DEBUG( "HepHistoFileManager::cd( \"" << path << "\" )" ); // If not path is given, we go the rootdir. if ( path.length() == 0 ) { cd( absStart + rootDir() ); } else { HepFileManager::cd( validHistoPath(path) ); // record the current site } //HEP_DEBUG( " now in \"" << HistoPwd() << "\" == \"" << pwd() << "\"" ); } // HepHistoFileManager::cd() void HepHistoFileManager::mkdir( const string & path ) { // create a dir HEP_DEBUG( "HepHistoFileManager::mkdir( \"" << path << "\" )" ); if ( isReadOnly() ) { ZMthrow(ZMxHepCantWriteFile( fileName() + " is read only. Can not make directory " + path + "." )); return; } // Histoscope does not have a real notion of directories. // so we only create them 'internally' (if no objects in created // in the directory it will be lost when the file is reloaded. string newDir = validHistoPath( path ); if (newDir.length() == 0 ) return; HepFileManager::mkdir(newDir); } // HepHistoFileManager::mkdir() string HepHistoFileManager::ls( // list a dir const string & path , const string & chopt ) const { string p = validHistoPath(path); return HepFileManager::ls(p,chopt); } void HepHistoFileManager::rmdir( const string & path, bool recursive ) { // delete a dir HEP_DEBUG( "HepHistoFileManager::rmdir( \"" << path << "\" )" ); if ( isReadOnly() ) { ZMthrow(ZMxHepCantWriteFile( fileName() + " is read only. Can not remove directory " + path + "." )); return; } if ( ! recursive ) { // We only remove the directory if it is empty string res = ls(path); // This always contains as a first line the name of the current directory res = res.substr(res.find("\n")+1,res.length()); if ( res.length() != 0 ) { // the directory is not empty. let's fails: ZMthrow(ZMxHepImproperUse("Try to remove a non empty directory. use rmdir(path,true) if this was intended\n")); return; } } string goner; if ( HepDir::isRelPath(path) ) { goner = category(pwd()+dirSep+path); } else { goner = category(path); } int done = hs_eliminate_category((char*)(category(goner).c_str())); // remove the object in the HepFileManager even in case of error // because we do not know how much was removed! HepFileManager::rmdir( path, recursive ); if ( ! done ) { ZMthrow(ZMxHepBadIO(string("in HepHistoFilemanager failed to remove dir ") +goner)); return; } HEP_DEBUG( " removed \"" << goner << "\"" ); } // HepHistoFileManager::rmdir() void HepHistoFileManager::rm( int id ) { // delete a histogram or ntuple HEP_DEBUG( "HepHistoFileManager::rm( " << id << " )" ); if ( isReadOnly() ) { ZMthrow(ZMxHepCantWriteFile( fileName() + " is read only. Can not remove item." )); return; } // we mapped Histo's uids to HepFileManager's ids. int histo_id = hs_id( id, (char*)(category(pwd()).c_str()) ); bool done = false; if ( histo_id != 0 ) { done = hs_eliminate(histo_id); } // remove the object in the HepFileManager even in case of error // because we do not know how much was removed! HepFileManager::rm( id ); if ( ! done ) { char tmp[20]; sprintf(tmp,"%d",id); ZMthrow(ZMxHepBadIO(string("in HepHistoFileManager failed to remove item ") +tmp)); return; } } void HepHistoFileManager::rm( const string & title ) { // delete a histogram or ntuple HEP_DEBUG( "HepHistoFileManager::rm( \"" << title << "\" )" ); if ( isReadOnly() ) { ZMthrow(ZMxHepCantWriteFile( fileName() + " is read only. Can not remove item " + title + "." )); return; } int id = hs_id_from_title((char*)title.c_str(), (char*)category(pwd()).c_str()); int uid = hs_uid(id); bool done = hs_eliminate(id); // remove the object in the HepFileManager even in case of error // because we do not know how much was removed! HepFileManager::rm( uid ); if ( ! done ) { ZMthrow(ZMxHepBadIO(string("in HepHistoFileManager failed to remove item ") +title)); return; } } void HepHistoFileManager::writeDirectory() { // write the managed objects to the file HEP_DEBUG( "HepHistoFileManager::writeDirectory()" ); hs_update_on_file((char*) category(pwd()).c_str() ); const string path(pwd()); const string sub_path(path+dirSep); int len = sub_path.length(); for ( HepObjList::const_iterator i = objList_.begin() ; i != objList_.end() ; ++i ) { HepObj * o = *i; if ( o->dir() == path || sub_path == (o->dir()).substr(0,len) ) HepFileManager::writeOne( o ); } } // HepHistoFileManager::writeDirectory() int HepHistoFileManager::write() { // write the managed objects to the file HEP_DEBUG( "HepHistoFileManager::write()" ); int result = hs_update_on_file( (char*) rootDir().c_str() ); for ( HepObjList::const_iterator i = objList_.begin() ; i != objList_.end() ; ++i ) { HepObj * o = *i; HepFileManager::writeOne( o ); } return result; } // HepHistoFileManager::write() void HepHistoFileManager::writeOne( HepObj * o ) { HEP_DEBUG( "HepHistoFileManager::writeOne( " << id << " )" ); int histo_id = hs_id( o->id(), (char*)(category(o->dir()).c_str()) ); hs_update_item_on_file( histo_id ); HepFileManager::writeOne( o ); } // HepHistoFileManager::writeOne() #ifdef LATER void HepHistoFileManager::dumpAllData() { // call dumpData() for all listed tuples HepAListIterator i( ntuples_ ); HepHistoTuple *p; while ( p = i() ) p->dumpData(); } // HepHistoFileManager::dumpAllData() void HepHistoFileManager::clearAllData() { // call clearData() for all listed tuples HepAListIterator i( ntuples_ ); HepHistoTuple *p; while ( p = i() ) p->clearData(); } // HepHistoFileManager::clearAllData() #endif // LATER // Create HepHisto-specific histograms: -------------------------------- // Using title as a key, find the plot with the given title. If it is // not found, create a new plot with the given title, and return that. // The HepHisto ID of the plot is optionally specified by the user. HepHist1D & HepHistoFileManager::hist1D( const string& title , const int nBins, const float low, const float high , const int uid ) { if ( ! checkTitle( title ) ) { ZMthrow(ZMxHepCantMakeHist( string("'")+ title + "' is an invalid object title." )); return deadHist1D(); } if ( findObj( title ) == objList_.end() && findObj( uid ) == objList_.end() ) { // Histo uid is the HepFileManager id string name = title; HepHistoHist1D * h = new HepHistoHist1D( this, title, nBins, low, high, uid); isInUse( h, true ); isDirty( h, true ); isCreated( h, true ); return *h; } else { ZMthrow(ZMxHepCantMakeHist( "Can not create a hist1D with an existing name or id")); return deadHist1D(); } } HepHist2D & HepHistoFileManager::hist2D( const string& title , const int nBinsX, const float lowX, const float highX , const int nBinsY, const float lowY, const float highY , const int uid ) { if ( ! checkTitle( title ) ) { ZMthrow(ZMxHepCantMakeHist( string("'")+ title + "' is an invalid object title." )); return deadHist2D(); } if ( findObj( title ) == objList_.end() && findObj( uid ) == objList_.end() ) { HepHistoHist2D * h = new HepHistoHist2D( this, title, nBinsX, lowX, highX, nBinsY, lowY, highY, uid ); isInUse( h, true ); isDirty( h, true ); isCreated( h, true ); return *h; } else { ZMthrow(ZMxHepCantMakeHist( "Can not create a hist2D with an existing name or id")); return deadHist2D(); } } HepHistProf & HepHistoFileManager::histProf( const string& title , const int nBinsX, const float lowX, const float highX , const float lowY, const float highY , const string& chopt , const int id ) { NOTYET("HepHistoFileManager contructor"); return deadHistProf(); #ifdef HISTOPROFILE_IMPLEMENTED if ( ! checkTitle( title ) ) { ZMthrow(ZMxHepCantMakeHist( string("'")+ title + "' is an invalid object title." )); return deadHistProf(); } if ( findObj( title ) == objList_.end() && findObj( id ) == objList_.end() ) { HepHistoHistProf * h = NULL;// new HepHistoHistProf( this, title, // nBinsX, lowX, highX, // lowY, highY, chopt, id ); isInUse( h, true ); isDirty( h, true ); // HBPROF( ID, ((char*)title), nBinsX, lowX, highX, // lowY, highY, ((char*)chopt) ); isCreated( h, true ); return *h; } else { ZMthrow(ZMxHepCantMakeHist( "Can not create a histProf with an existing name or id")); return deadHistProf(); } #endif } HepHistProf & HepHistoFileManager::histProf( const string& title , const int nBinsX, const float lowX, const float highX , const float lowY, const float highY , const int id ) { NOTYET("HepHistProf contructor"); return deadHistProf(); #ifdef HISTOPROFILE_IMPLEMENTED if ( ! checkTitle( title ) ) { ZMthrow(ZMxHepCantMakeHist( string("'")+ title + "' is an invalid object title." )); return deadHistProf(); } if ( findObj( title ) == objList_.end() && findObj( id ) == objList_.end() ) { // HepHistoHistProf * h = new HepHistoHistProf( this, title, // nBinsX, lowX, highX, // lowY, highY, " ", id ); // isInUse( h, true ); // isDirty( h, true ); // make the Fortran object //HBPROF( ID, ((char*)title), nBinsX, lowX, highX, // lowY, highY, chopt ); // isCreated( h, true ); // return *h; } else { ZMthrow(ZMxHepCantMakeHist( "Can not create a histProf with an existing name or id")); return deadHistProf(); } #endif } // attach to existing histograms: ------------------------------------ HepHist1D & HepHistoFileManager::retrieveHist1D( // 1-D const string& title, const int hid ) { HepHist1D & h = HepFileManager::retrieveHist1D( title, hid ); isInUse( &h, true ); return h; } HepHist2D & HepHistoFileManager::retrieveHist2D( // 2-D const string& title, const int hid ) { HepHist2D & h = HepFileManager::retrieveHist2D( title, hid ); isInUse( &h, true ); return h; } HepHistProf & HepHistoFileManager::retrieveHistProf( // profile plot const string& title, const int hid ) { HepHistProf & h = HepFileManager::retrieveHistProf( title, hid ); isInUse( &h, true ); return h; } // Create HepNtuple HepNtuple & HepHistoFileManager::ntuple( // ntuple const string& title, const int id ) { HEP_DEBUG( "HepHistoFileManager::ntuple( \"" << title << "\", " << id << " )" ); if ( ! checkTitle( title ) ) { ZMthrow(ZMxHepCantMakeNtuple( string("'")+ title + "' is an invalid object title." )); return deadNtuple(); } if ( findObj( title ) == objList_.end() && findObj( id ) == objList_.end() ) { HepNtuple * t = new HepHistoNtuple( this, title, id ); isInUse( t, true ); isDirty( t, true ); return *t; } else { ZMthrow(ZMxHepCantMakeNtuple( "Can not create an Ntuple with an existing name or id")); return deadNtuple(); } } HepNtuple & HepHistoFileManager::retrieveNtuple( // Ntuple const string& title, const int hid ) { HepNtuple & h = HepFileManager::retrieveNtuple( title, hid ); isInUse( &h, true ); return h; } // user is finished with the indicated object void HepHistoFileManager::release( HepObj & o ) { HEP_DEBUG( "HepHistoFileManager::release()" ); HepFileManager::release( o ); } // traverse the directories in the file to create C++ objects for existing // histograms & ntuples: void HepHistoFileManager::retrieveExistingItems() { HEP_DEBUG( "HepHistoFileManager::retrieveExistingItems()" ); int num = hs_num_items(); int *ids = new int[num]; string directory; hs_list_items(" ",(char*) (rootDir()+"/...").c_str(), ids, num, 1); // We disable ZMxHepUnknownHepObj thrown by getsubdir for this // piece of code: ZMexHandler oldHandler = ZMxHepUnknownHepObj::getHandler(); ZMexLogger oldLogger = ZMxHepUnknownHepObj::getLogger(); ZMxHepUnknownHepObj::setHandler ( ZMexIgnoreAlways() ); ZMxHepUnknownHepObj::setLogger ( ZMexLogNever() ) ; for(int i=0; irestoreNtuple() ) { ZMthrow(ZMxHepCantMakeNtuple(string("Failed to restore Ntuple with title: ")+ title)); // Reset exception handler; ZMxHepUnknownHepObj::setHandler ( oldHandler ); ZMxHepUnknownHepObj::setLogger ( oldLogger ); } break; case HS_INDICATOR: case HS_CONTROL: case HS_TRIGGER: case HS_NONE: case HS_NFIT: case HS_GROUP: case HS_CONFIG_STRING: case HS_CWNBLOCK: case HS_CWNCOLUMN: case HS_COLUMNDATA: // totally ignore those extra types ... // but what about name/uid clash ? // they are handled at creation time by checking the existence ? break; default: char temp[16]; sprintf(temp,"%d",type); ZMthrow(ZMxHepUnknownType( string("retrieveExistingItems: unknown item kind (") +string(temp)+string(")") +string(" in \"")+fileName()+string("\")")) ); // Reset exception handler; ZMxHepUnknownHepObj::setLogger ( oldLogger ); ZMxHepUnknownHepObj::setHandler ( oldHandler ); } // end of switch } // end of for each items cd(""); // We disabled ZMxHepUnknownHepObj thrown by getsubdir for this // piece of code. Now we reset it. ZMxHepUnknownHepObj::setHandler ( oldHandler ); ZMxHepUnknownHepObj::setLogger ( oldLogger ); } // HepHistoFileManager::retrieveExistingItems() HepTupleFileFormat HepHistoFileManager::hepFileFormat() const { return HEPTUPLE_HISTOSCOPE; } bool HepHistoFileManager::switchFile( const string& newFileName, const bool resetHists ) { // We don't do file switching for the Histo manager but we can at least // write out the old file and exit cleanly. write(); ZMthrow(ZMxHepUnsupported(string("File switching is not supported for this manager"))); return false; } bool HepHistoFileManager::switchFile( const string& newFileName, const bool resetHists, HepFileSwitch& userHandler ) { // We don't do file switching for the Histo manager but we can at least // write out the old file and exit cleanly. write(); ZMthrow(ZMxHepUnsupported(string("File switching is not supported for this manager"))); return false; } // ************************************ // // ******* Dead Object creators ******* // // ************************************ // HepNtuple& HepHistoFileManager::deadNtuple() const { return HepHistoFileManager::deadHistoNtuple(); } HepHist1D& HepHistoFileManager::deadHist1D() const { return HepHistoFileManager::deadHistoHist1D(); } HepHist2D& HepHistoFileManager::deadHist2D() const { return HepHistoFileManager::deadHistoHist2D(); } HepHistProf& HepHistoFileManager::deadHistProf() const { return HepHistoFileManager::deadHistoHistProf(); } HepHistoNtuple& HepHistoFileManager::deadHistoNtuple() { static HepHistoNtuple deadHisto; return deadHisto; } HepHistoHist1D& HepHistoFileManager::deadHistoHist1D() { static HepHistoHist1D deadHisto; return deadHisto; } HepHistoHist2D& HepHistoFileManager::deadHistoHist2D() { static HepHistoHist2D deadHisto; return deadHisto; } HepHistoHistProf& HepHistoFileManager::deadHistoHistProf() { static HepHistoHistProf deadHisto; return deadHisto; } // transform an HepTuple path into an Histoscope category string HepHistoFileManager::category(string path) { if ( HepDir::isRelPath(path) ) { return path; } else { // we just need to remove the fist '/' return path.substr(2 #ifdef WIN32 , string::npos #endif ); } } ZM_END_NAMESPACE( zmht ) /* } // namespace zmht */