/*****************************************************************************
 * Project: RooFit                                                           *
 * Package: RooFitCore                                                       *
 * @(#)root/roofitcore:$Id: RooWorkspace.cxx 37380 2010-12-07 21:26:55Z wouter $
 * Authors:                                                                  *
 *   WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu       *
 *                                                                           *
 * Copyright (c) 2000-2005, Regents of the University of California          *
 *                          and Stanford University. All rights reserved.    *
 *                                                                           *
 * Redistribution and use in source and binary forms,                        *
 * with or without modification, are permitted according to the terms        *
 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)             *
 *****************************************************************************/

//////////////////////////////////////////////////////////////////////////////
//
// BEGIN_HTML
// The RooWorkspace is a persistable container for RooFit projects. A workspace
// can contain and own variables, p.d.f.s, functions and datasets. All objects
// that live in the workspace are owned by the workspace. The import() method
// enforces consistency of objects upon insertion into the workspace (e.g. no
// duplicate object with the same name are allowed) and makes sure all objects
// in the workspace are connected to each other. Easy accessor methods like
// pdf(), var() and data() allow to refer to the contents of the workspace by
// object name. The entire RooWorkspace can be saved into a ROOT TFile and organises
// the consistent streaming of its contents without duplication.
// <p>
// If a RooWorkspace contains custom classes, i.e. classes not in the 
// ROOT distribution, portability of workspaces can be enhanced by
// storing the source code of those classes in the workspace as well.
// This process is also organized by the workspace through the
// importClassCode() method.
// END_HTML
//

#include "RooFit.h"
#include "RooWorkspace.h"
#include "RooAbsPdf.h"
#include "RooRealVar.h"
#include "RooCategory.h"
#include "RooAbsData.h"
#include "RooCmdConfig.h"
#include "RooMsgService.h"
#include "RooConstVar.h"
#include "RooResolutionModel.h"
#include "TInterpreter.h"
#include "TClassTable.h"
#include "TBaseClass.h"
#include "TSystem.h"
#include "TRegexp.h"
#include "RooFactoryWSTool.h"
#include "RooAbsStudy.h"
#include "RooTObjWrap.h"
#include "RooAbsOptTestStatistic.h"
#include "TROOT.h"
#include "TFile.h"
#include "TH1.h"
#include "Api.h"
#include <map>
#include <string>
#include <list>
#include <set>

using namespace std ;


#if ROOT_VERSION_CODE <= ROOT_VERSION(5,19,02)
#include "Api.h"
#endif


#include "TClass.h"
#include "Riostream.h"
#include <string.h>
#include <assert.h>

ClassImp(RooWorkspace)
;

//_____________________________________________________________________________
ClassImp(RooWorkspace::CodeRepo)
;

//_____________________________________________________________________________
ClassImp(RooWorkspace::WSDir)
;

list<string> RooWorkspace::_classDeclDirList ;
list<string> RooWorkspace::_classImplDirList ;
string RooWorkspace::_classFileExportDir = ".wscode.%s.%s" ;
Bool_t RooWorkspace::_autoClass = kFALSE ;


//_____________________________________________________________________________
void RooWorkspace::addClassDeclImportDir(const char* dir) 
{
  // Add 'dir' to search path for class declaration (header) files, when
  // attempting to import class code with importClassClode()

  _classDeclDirList.push_back(dir) ;
}


//_____________________________________________________________________________
void RooWorkspace::addClassImplImportDir(const char* dir) 
{
  // Add 'dir' to search path for class implementation (.cxx) files, when
  // attempting to import class code with importClassClode()

  _classImplDirList.push_back(dir) ;
}


//_____________________________________________________________________________
void RooWorkspace::setClassFileExportDir(const char* dir) 
{
  // Specify the name of the directory in which embedded source
  // code is unpacked and compiled. The specified string may contain
  // one '%s' token which will be substituted by the workspace name

  if (dir) {
    _classFileExportDir = dir ;
  } else {
    _classFileExportDir = ".wscode.%s.%s" ;
  }
}


//_____________________________________________________________________________
void RooWorkspace::autoImportClassCode(Bool_t flag) 
{
  // If flag is true, source code of classes not the the ROOT distribution
  // is automatically imported if on object of such a class is imported
  // in the workspace
  _autoClass = flag ; 
}



//_____________________________________________________________________________
RooWorkspace::RooWorkspace() : _classes(this), _dir(0), _factory(0), _doExport(kFALSE), _openTrans(kFALSE)
{
  // Default constructor
}



//_____________________________________________________________________________
RooWorkspace::RooWorkspace(const char* name, const char* title) : 
  TNamed(name,title?title:name), _classes(this), _dir(0), _factory(0), _doExport(kFALSE), _openTrans(kFALSE)
{
  // Construct empty workspace with given name and title
}


RooWorkspace::RooWorkspace(const char* name, Bool_t doCINTExport)  : 
  TNamed(name,name), _classes(this), _dir(0), _factory(0), _doExport(kFALSE), _openTrans(kFALSE)
{
  // Construct empty workspace with given name and option to export reference to all workspace contents to a CINT namespace with the same name
  if (doCINTExport) {
    exportToCint(name) ;
  }
}


//_____________________________________________________________________________
RooWorkspace::RooWorkspace(const RooWorkspace& other) : 
  TNamed(other), _uuid(other._uuid), _classes(this), _dir(0), _factory(0), _doExport(kFALSE), _openTrans(kFALSE)
{
  // Workspace copy constructor

  // Copy owned nodes
  other._allOwnedNodes.snapshot(_allOwnedNodes,kTRUE) ;

  // Copy datasets
  TIterator* iter = other._dataList.MakeIterator() ;
  TObject* data2 ;
  while((data2=iter->Next())) {
    _dataList.Add(data2->Clone()) ;
  }
  delete iter ;

  // Copy snapshots
  TIterator* iter2 = other._snapshots.MakeIterator() ;
  RooArgSet* snap ;
  while((snap=(RooArgSet*)iter2->Next())) {
    RooArgSet* snapClone = (RooArgSet*) snap->snapshot() ;
    snapClone->setName(snap->GetName()) ;
    _snapshots.Add(snapClone) ;
  }
  delete iter2 ;

  // Copy named sets
  for (map<string,RooArgSet>::const_iterator iter3 = other._namedSets.begin() ; iter3 != other._namedSets.end() ; ++iter3) {
    // Make RooArgSet with equivalent content of this workspace
    RooArgSet* tmp = (RooArgSet*) _allOwnedNodes.selectCommon(iter3->second) ;
    _namedSets[iter3->first].add(*tmp) ;
    delete tmp ;
  }

  // Copy generic objects
  TIterator* iter4 = other._genObjects.MakeIterator() ;
  TObject* gobj ;
  while((gobj=iter4->Next())) {
    _genObjects.Add(gobj->Clone()) ;
  }
  delete iter4 ;
  
}



//_____________________________________________________________________________
RooWorkspace::~RooWorkspace() 
{
  // Workspace destructor

  // Delete references to variables that were declared in CINT 
  if (_doExport) {
    unExport() ;
  }

  // Delete contents
  _dataList.Delete() ;
  if (_dir) {
    delete _dir ;
  }
  _snapshots.Delete() ;

  // WVE named sets too?

  _genObjects.Delete() ;
}


//_____________________________________________________________________________
Bool_t RooWorkspace::import(const char* fileSpec, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3) 
{
  // Import a RooAbsArg or RooAbsData set from a workspace in a file. Filespec should be constructed as "filename:wspacename:objectname"
  // The arguments will be passed on to the relevant RooAbsArg& or RooAbsData& import call

  // Parse file/workspace/objectname specification
  char buf[1024] ;
  strlcpy(buf,fileSpec,1024) ;
  char* filename = strtok(buf,":") ;
  char* wsname = strtok(0,":") ;
  char* objname = strtok(0,":") ;

  // Check that parsing was successful
  if (!filename||!wsname||!objname) {
    coutE(InputArguments) << "RooWorkspace(" << GetName() << ") ERROR in file specification, expecting for 'filename:wsname:objname'" << endl ;
    return kTRUE ;
  }

  // Check that file can be opened
  TFile* f = TFile::Open(filename) ;
  if (f==0) {
    coutE(InputArguments) << "RooWorkspace(" << GetName() << ") ERROR opening file " << filename << endl ;
    return 0 ;
  }

  // That that file contains workspace
  RooWorkspace* w = dynamic_cast<RooWorkspace*>(f->Get(wsname)) ;
  if (w==0) {
    coutE(InputArguments) << "RooWorkspace(" << GetName() << ") ERROR: No object named " << wsname << " in file " << filename 
			  << " or object is not a RooWorkspace" << endl ;
    return 0 ;
  }

  // Check that workspace contains object and forward to appropriate import method
  RooAbsArg* warg = w->arg(objname) ;
  if (warg) {
    Bool_t ret = import(*warg,arg1,arg2,arg3) ;
    delete f ;
    return ret ;    
  }
  RooAbsData* wdata = w->data(objname) ;
  if (wdata) {
    Bool_t ret = import(*wdata,arg1,arg2,arg3) ;
    delete f ;
    return ret ;    
  }

  coutE(InputArguments) << "RooWorkspace(" << GetName() << ") ERROR: No RooAbsArg or RooAbsData object named " << objname 
			<< " in workspace " << wsname << " in file " << filename << endl ;
  return kTRUE ;  
}


//_____________________________________________________________________________
Bool_t RooWorkspace::import(const RooArgSet& args, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3) 
{
  // Import multiple RooAbsArg objects into workspace. For details on arguments see documentation
  // of import() method for single RooAbsArg

  TIterator* iter = args.createIterator() ;
  RooAbsArg* oneArg ;
  Bool_t ret(kFALSE) ;
  while((oneArg=(RooAbsArg*)iter->Next())) {
    ret |= import(*oneArg,arg1,arg2,arg3) ;
  }
  return ret ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::import(const RooAbsArg& inArg, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3) 
{
  //  Import a RooAbsArg object, e.g. function, p.d.f or variable into the workspace. This import function clones the input argument and will
  //  own the clone. If a composite object is offered for import, e.g. a p.d.f with parameters and observables, the
  //  complete tree of objects is imported. If any of the _variables_ of a composite object (parameters/observables) are already 
  //  in the workspace the imported p.d.f. is connected to the already existing variables. If any of the _function_ objects (p.d.f, formulas) 
  //  to be imported already exists in the workspace an error message is printed and the import of the entire tree of objects is cancelled. 
  //  Several optional arguments can be provided to modify the import procedure.
  //
  //  Accepted arguments
  //  -------------------------------
  //  RenameConflictNodes(const char* suffix) -- Add suffix to branch node name if name conflicts with existing node in workspace
  //  RenameAllNodes(const char* suffix) -- Add suffix to all branch node names including top level node
  //  RenameAllVariables(const char* suffix) -- Add suffix to all variables names
  //  RenameAllVariablesExcept(const char* suffix, const char* exceptionList) -- Add suffix to all variables names, except ones listed
  //  RenameVariable(const char* inputName, const char* outputName) -- Rename variable as specified upon import.
  //  RecycleConflictNodes() -- If any of the function objects to be imported already exist in the name space, connect the
  //                            imported expression to the already existing nodes. WARNING: use with care! If function definitions
  //                            do not match, this alters the definition of your function upon import
  //  Silence() -- Do not issue any info message
  //
  //  The RenameConflictNodes, RenameNodes and RecycleConflictNodes arguments are mutually exclusive. The RenameVariable argument can be repeated
  //  as often as necessary to rename multiple variables. Alternatively, a single RenameVariable argument can be given with
  //  two comma separated lists.

  RooLinkedList args ;
  args.Add((TObject*)&arg1) ;
  args.Add((TObject*)&arg2) ;
  args.Add((TObject*)&arg3) ;

  // Select the pdf-specific commands 
  RooCmdConfig pc(Form("RooWorkspace::import(%s)",GetName())) ;

  pc.defineString("conflictSuffix","RenameConflictNodes",0) ;
  pc.defineString("allSuffix","RenameAllNodes",0) ;
  pc.defineString("allVarsSuffix","RenameAllVariables",0) ;
  pc.defineString("allVarsExcept","RenameAllVariables",1) ;
  pc.defineString("varChangeIn","RenameVar",0,"",kTRUE) ;
  pc.defineString("varChangeOut","RenameVar",1,"",kTRUE) ;
  pc.defineString("factoryTag","FactoryTag",0) ;
  pc.defineInt("useExistingNodes","RecycleConflictNodes",0,0) ;
  pc.defineInt("silence","Silence",0,0) ;
  pc.defineMutex("RenameConflictNodes","RenameAllNodes") ;
  pc.defineMutex("RenameConflictNodes","RecycleConflictNodes") ;
  pc.defineMutex("RenameAllNodes","RecycleConflictNodes") ;
  pc.defineMutex("RenameVariable","RenameAllVariables") ;

  // Process and check varargs 
  pc.process(args) ;
  if (!pc.ok(kTRUE)) {
    return kTRUE ;
  }

  // Decode renaming logic into suffix string and boolean for conflictOnly mode
  const char* suffixC = pc.getString("conflictSuffix") ;
  const char* suffixA = pc.getString("allSuffix") ;
  const char* suffixV = pc.getString("allVarsSuffix") ;
  const char* exceptVars = pc.getString("allVarsExcept") ;
  const char* varChangeIn = pc.getString("varChangeIn") ;
  const char* varChangeOut = pc.getString("varChangeOut") ;
  Int_t useExistingNodes = pc.getInt("useExistingNodes") ;
  Int_t silence = pc.getInt("silence") ;

  // Turn zero length strings into null pointers 
  if (suffixC && strlen(suffixC)==0) suffixC = 0 ;
  if (suffixA && strlen(suffixA)==0) suffixA = 0 ;

  Bool_t conflictOnly = suffixA ? kFALSE : kTRUE ;
  const char* suffix = suffixA ? suffixA : suffixC ;

  // Process any change in variable names 
  map<string,string> varMap ;
  if (strlen(varChangeIn)>0) {
    
    // Parse comma separated lists into map<string,string>
    char tmp[1024] ;
    strlcpy(tmp,varChangeIn,1024) ;
    list<string> tmpIn,tmpOut ;
    char* ptr = strtok(tmp,",") ;
    while (ptr) {
      tmpIn.push_back(ptr) ;
      ptr = strtok(0,",") ;
    }
    strlcpy(tmp,varChangeOut,1024) ;
    ptr = strtok(tmp,",") ;
    while (ptr) {
      tmpOut.push_back(ptr) ;
      ptr = strtok(0,",") ;
    }    
    list<string>::iterator iin = tmpIn.begin() ;
    list<string>::iterator iout = tmpOut.begin() ;
    for (;iin!=tmpIn.end() ; ++iin,++iout) {
      varMap[*iin]=*iout ;
    }       
  }

  // Process RenameAllVariables argument if specified  
  // First convert exception list if provided
  std::set<string> exceptVarNames ;
  char tmp[1024] ;
  if (exceptVars && strlen(exceptVars)) {
    strlcpy(tmp,exceptVars,1024) ;
    char* ptr = strtok(tmp,",") ;
    while(ptr) {
      exceptVarNames.insert(ptr) ;
      ptr = strtok(0,",") ;
    }
  }

  if (suffixV != 0 && strlen(suffixV)>0) {
    RooArgSet* vars = inArg.getVariables() ;
    TIterator* iter = vars->createIterator() ;
    RooAbsArg* v ;
    while((v=(RooAbsArg*)iter->Next())) {
      if (exceptVarNames.find(v->GetName())==exceptVarNames.end()) {
	varMap[v->GetName()] = Form("%s_%s",v->GetName(),suffixV) ;
      }
    }
    delete iter ;
    delete vars ;
  }
  
  // Scan for overlaps with current contents
  RooAbsArg* wsarg = _allOwnedNodes.find(inArg.GetName()) ;

  // Check for factory specification match
  const char* tagIn = inArg.getStringAttribute("factory_tag") ;
  const char* tagWs = wsarg ? wsarg->getStringAttribute("factory_tag") : 0 ;
  Bool_t factoryMatch = (tagIn && tagWs && !strcmp(tagIn,tagWs)) ;
  if (factoryMatch) {
    ((RooAbsArg&)inArg).setAttribute("RooWorkspace::Recycle") ;
  }

  if (!suffix && wsarg && !useExistingNodes && !(inArg.isFundamental() && varMap[inArg.GetName()]!="")) {
    if (!factoryMatch) {
      if (wsarg!=&inArg) {
	coutE(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") ERROR importing object named " << inArg.GetName() 
			      << ": another instance with same name already in the workspace and no conflict resolution protocol specified" << endl ;
	return kTRUE ;    
      } else {
	if (!silence) {
	  coutI(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") Object " << inArg.GetName() << " is already in workspace!" << endl ;
	}
	return kTRUE ;    
      }
    } else {
      coutI(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") Recycling existing object " << inArg.GetName() << " created with identical factory specification" << endl ;
    }
  }

  // Make list of conflicting nodes
  RooArgSet conflictNodes ;
  RooArgSet branchSet ;
  inArg.branchNodeServerList(&branchSet) ;
  TIterator* iter = branchSet.createIterator() ;
  RooAbsArg* branch ;
  while ((branch=(RooAbsArg*)iter->Next())) {
    RooAbsArg* wsbranch = _allOwnedNodes.find(branch->GetName()) ;
    if (wsbranch && wsbranch!=branch && !branch->getAttribute("RooWorkspace::Recycle") && !useExistingNodes) {
      conflictNodes.add(*branch) ;
    }
  }
  delete iter ;
  
  // Terminate here if there are conflicts and no resolution protocol
  if (conflictNodes.getSize()>0 && !suffix && !useExistingNodes) {
      coutE(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") ERROR object named " << inArg.GetName() << ": component(s) " 
	   << conflictNodes << " already in the workspace and no conflict resolution protocol specified" << endl ;      
      return kTRUE ;
  }
    
  // Now create a working copy of the incoming object tree
  RooArgSet* cloneSet = (RooArgSet*) RooArgSet(inArg).snapshot(kTRUE) ;
  RooAbsArg* cloneTop = cloneSet->find(inArg.GetName()) ;

  // Mark all nodes for renaming if we are not in conflictOnly mode
  if (!conflictOnly) {
    conflictNodes.removeAll() ;
    conflictNodes.add(branchSet) ;
  }

  // Mark nodes that are to be renamed with special attribute
  TIterator* citer = conflictNodes.createIterator() ;
  string topName2 = cloneTop->GetName() ;
  RooAbsArg* cnode ;
  while ((cnode=(RooAbsArg*)citer->Next())) {
    RooAbsArg* cnode2 = cloneSet->find(cnode->GetName()) ;
    string origName = cnode2->GetName() ;
    cnode2->SetName(Form("%s_%s",cnode2->GetName(),suffix)) ;
    cnode2->SetTitle(Form("%s (%s)",cnode2->GetTitle(),suffix)) ;
    string tag = Form("ORIGNAME:%s",origName.c_str()) ;
    cnode2->setAttribute(tag.c_str()) ;

    // Save name of new top level node for later use
    if (cnode2==cloneTop) {
      topName2 = cnode2->GetName() ;      
    }

    if (!silence) {
      coutI(ObjectHandling) << "RooWorkspace::import(" << GetName() 
			    << ") Resolving name conflict in workspace by changing name of imported node  " 
			    << origName << " to " << cnode2->GetName() << endl ;
    }
  }  
  delete citer ;

  // Process any change in variable names 
  if (strlen(varChangeIn)>0 || (suffixV && strlen(suffixV)>0)) {
    
    // Process all changes in variable names
    TIterator* cliter = cloneSet->createIterator() ;
    while ((cnode=(RooAbsArg*)cliter->Next())) {
      
      if (varMap.find(cnode->GetName())!=varMap.end()) { 	
	string origName = cnode->GetName() ;
	cnode->SetName(varMap[cnode->GetName()].c_str()) ;
	string tag = Form("ORIGNAME:%s",origName.c_str()) ;
	cnode->setAttribute(tag.c_str()) ;
	if (!silence) {
	  coutI(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") Changing name of variable " 
				<< origName << " to " << cnode->GetName() << " on request" << endl ;
	}

	if (cnode==cloneTop) {
	  topName2 = cnode->GetName() ;
	}

      }    
    }
    delete cliter ;
  }
  
  // Now clone again with renaming effective
  RooArgSet* cloneSet2 = (RooArgSet*) RooArgSet(*cloneTop).snapshot(kTRUE) ;
  RooAbsArg* cloneTop2 = cloneSet2->find(topName2.c_str()) ;

  // Make final check list of conflicting nodes
  RooArgSet conflictNodes2 ;
  RooArgSet branchSet2 ;
  inArg.branchNodeServerList(&branchSet) ;
  TIterator* iter2 = branchSet2.createIterator() ;
  RooAbsArg* branch2 ;
  while ((branch2=(RooAbsArg*)iter2->Next())) {
    if (_allOwnedNodes.find(branch2->GetName())) {
      conflictNodes2.add(*branch2) ;
    }
  }
  delete iter2 ;

  // Terminate here if there are conflicts and no resolution protocol
  if (conflictNodes2.getSize()) {
    coutE(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") ERROR object named " << inArg.GetName() << ": component(s) " 
			  << conflictNodes2 << " cause naming conflict after conflict resolution protocol was executed" << endl ;      
    return kTRUE ;
  }
    
  // Print a message for each imported node
  iter = cloneSet2->createIterator() ;
  
  // Perform any auxiliary imports at this point
  RooAbsArg* node ;
  while((node=(RooAbsArg*)iter->Next())) {
    if (node->importWorkspaceHook(*this)) {
      coutE(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") ERROR object named " << node->GetName() 
			    << " has an error in importing in one or more of its auxiliary objects, aborting" << endl ;
      return kTRUE ;
    }
  }
  iter->Reset() ;

  RooArgSet recycledNodes ;
  RooArgSet nodesToBeDeleted ;
  while((node=(RooAbsArg*)iter->Next())) {

    if (_autoClass) {
      if (!_classes.autoImportClass(node->IsA())) {
	coutW(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") WARNING: problems import class code of object " 
			      << node->IsA()->GetName() << "::" << node->GetName() << ", reading of workspace will require external definition of class" << endl ;
      }
    }

    // Point expensiveObjectCache to copy in this workspace
    RooExpensiveObjectCache& oldCache = node->expensiveObjectCache() ;
    node->setExpensiveObjectCache(_eocache) ;    
    _eocache.importCacheObjects(oldCache,node->GetName(),kTRUE) ;

    // Check if node is already in workspace (can only happen for variables or identical instances, unless RecycleConflictNodes is specified)
    RooAbsArg* wsnode = _allOwnedNodes.find(node->GetName()) ;

    if (wsnode) {
      // Do not import node, add not to list of nodes that require reconnection
      if (!silence && useExistingNodes) {
	coutI(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") using existing copy of " << node->IsA()->GetName() 
			      << "::" << node->GetName() << " for import of " << cloneTop2->IsA()->GetName() << "::" 
			      << cloneTop2->GetName() << endl ;      
      }
      recycledNodes.add(*_allOwnedNodes.find(node->GetName())) ;

      // Delete clone of incoming node
      nodesToBeDeleted.addOwned(*node) ;
      
    } else {
      // Import node
      if (!silence) {
	coutI(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") importing " << node->IsA()->GetName() << "::" 
			      << node->GetName() << endl ;
      }
      _allOwnedNodes.addOwned(*node) ;
      if (_openTrans) {
	_sandboxNodes.add(*node) ;
      } else {
	if (_dir && node->IsA() != RooConstVar::Class()) {	
	  _dir->InternalAppend(node) ;
	}
	if (_doExport && node->IsA() != RooConstVar::Class()) {
	  exportObj(node) ;
	}
      }
    }
  }

  // Release working copy
  delete cloneSet ;

  // Reconnect any nodes that need to be
  if (recycledNodes.getSize()>0) {
    iter->Reset() ;
    while((node=(RooAbsArg*)iter->Next())) {
      node->redirectServers(recycledNodes) ;
    }
  }
  delete iter ;

  cloneSet2->releaseOwnership() ;
  delete cloneSet2 ;  

  return kFALSE ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::import(RooAbsData& inData, const RooCmdArg& arg1, const RooCmdArg& arg2, const RooCmdArg& arg3) 
{
  //  Import a dataset (RooDataSet or RooDataHist) into the work space. The workspace will contain a copy of the data
  //  The dataset and its variables can be renamed upon insertion with the options below
  //
  //  Accepted arguments
  //  -------------------------------
  //  Rename(const char* suffix) -- Rename dataset upon insertion
  //  RenameVariable(const char* inputName, const char* outputName) -- Change names of observables in dataset upon insertion

  coutI(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") importing dataset " << inData.GetName() << endl ;

  RooLinkedList args ;
  args.Add((TObject*)&arg1) ;
  args.Add((TObject*)&arg2) ;
  args.Add((TObject*)&arg3) ;

  // Select the pdf-specific commands 
  RooCmdConfig pc(Form("RooWorkspace::import(%s)",GetName())) ;

  pc.defineString("dsetName","Rename",0,"") ;
  pc.defineString("varChangeIn","RenameVar",0,"",kTRUE) ;
  pc.defineString("varChangeOut","RenameVar",1,"",kTRUE) ;

  // Process and check varargs 
  pc.process(args) ;
  if (!pc.ok(kTRUE)) {
    return kTRUE ;
  }

  // Decode renaming logic into suffix string and boolean for conflictOnly mode
  const char* dsetName = pc.getString("dsetName") ;
  const char* varChangeIn = pc.getString("varChangeIn") ;
  const char* varChangeOut = pc.getString("varChangeOut") ;

  // Transform emtpy string into null pointer
  if (dsetName && strlen(dsetName)==0) {
    dsetName=0 ;
  }

  // Check that no dataset with target name already exists
  if (dsetName && _dataList.FindObject(dsetName)) {
    coutE(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") ERROR dataset with name " << dsetName << " already exists in workspace, import aborted" << endl ;
    return kTRUE ;
  }
  if (!dsetName && _dataList.FindObject(inData.GetName())) {
    coutE(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") ERROR dataset with name " << inData.GetName() << " already exists in workspace, import aborted" << endl ;
    return kTRUE ;
  }

  // Rename dataset if required
  RooAbsData* clone ;
  if (dsetName) {
    coutI(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") changing name of dataset from  " << inData.GetName() << " to " << dsetName << endl ;
    clone = (RooAbsData*) inData.Clone(dsetName) ;
  } else {
    clone = (RooAbsData*) inData.Clone(inData.GetName()) ;
  }


  // Process any change in variable names 
  if (strlen(varChangeIn)>0) {
    
    // Parse comma separated lists of variable name changes
    char tmp[1024] ;
    strlcpy(tmp,varChangeIn,1024) ;
    list<string> tmpIn,tmpOut ;
    char* ptr = strtok(tmp,",") ;
    while (ptr) {
      tmpIn.push_back(ptr) ;
      ptr = strtok(0,",") ;
    }
    strlcpy(tmp,varChangeOut,1024) ;
    ptr = strtok(tmp,",") ;
    while (ptr) {
      tmpOut.push_back(ptr) ;
      ptr = strtok(0,",") ;
    }    
    list<string>::iterator iin = tmpIn.begin() ;
    list<string>::iterator iout = tmpOut.begin() ;

    for (; iin!=tmpIn.end() ; ++iin,++iout) {
      coutI(ObjectHandling) << "RooWorkSpace::import(" << GetName() << ") changing name of dataset observable " << *iin << " to " << *iout << endl ;
      clone->changeObservableName(iin->c_str(),iout->c_str()) ;
    }
  }

  // Now import the dataset observables
  TIterator* iter = clone->get()->createIterator() ;
  RooAbsArg* carg ;
  while((carg=(RooAbsArg*)iter->Next())) {
    if (!arg(carg->GetName())) {
      import(*carg) ;
    }
  }
  delete iter ;
    
  _dataList.Add(clone) ;
  if (_dir) {	
    _dir->InternalAppend(clone) ;
  }
  if (_doExport) {
    exportObj(clone) ;
  }
  return kFALSE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::defineSet(const char* name, const RooArgSet& aset, Bool_t importMissing) 
{
  // Define a named RooArgSet with given constituents. If importMissing is true, any constituents
  // of aset that are not in the workspace will be imported, otherwise an error is returned
  // for missing components

  // Check if set was previously defined, if so print warning
  map<string,RooArgSet>::iterator i = _namedSets.find(name) ;
  if (i!=_namedSets.end()) {
    coutW(InputArguments) << "RooWorkspace::defineSet(" << GetName() << ") WARNING redefining previously defined named set " << name << endl ;
  }

  RooArgSet wsargs ;

  // Check all constituents of provided set
  TIterator* iter = aset.createIterator() ;
  RooAbsArg* sarg ;
  while((sarg=(RooAbsArg*)iter->Next())) {
    // If missing, either import or report error
    if (!arg(sarg->GetName())) {
      if (importMissing) {
	import(*sarg) ;
      } else {
	coutE(InputArguments) << "RooWorkspace::defineSet(" << GetName() << ") ERROR set constituent " << sarg->GetName() 
			      << " is not in workspace and importMissing option is disabled" << endl ;
	return kTRUE ;
      }
    }
    wsargs.add(*arg(sarg->GetName())) ;    
  }
  delete iter ;

  // Install named set
  _namedSets[name].removeAll() ;
  _namedSets[name].add(wsargs) ;
   
  return kFALSE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::defineSet(const char* name, const char* contentList) 
{
  // Define a named set in the work space through a comma separated list of
  // names of objects already in the workspace

  // Check if set was previously defined, if so print warning
  map<string,RooArgSet>::iterator i = _namedSets.find(name) ;
  if (i!=_namedSets.end()) {
    coutW(InputArguments) << "RooWorkspace::defineSet(" << GetName() << ") WARNING redefining previously defined named set " << name << endl ;
  }

  RooArgSet wsargs ;

  // Check all constituents of provided set
  char buf[1024] ;
  strlcpy(buf,contentList,1024) ;
  char* token = strtok(buf,",") ;
  while(token) {
    // If missing, either import or report error
    if (!arg(token)) {
      coutE(InputArguments) << "RooWorkspace::defineSet(" << GetName() << ") ERROR proposed set constituent " << token
			    << " is not in workspace" << endl ;
      return kTRUE ;
    }
    wsargs.add(*arg(token)) ;    
    token = strtok(0,",") ;
  }

  // Install named set
  _namedSets[name].removeAll() ;
  _namedSets[name].add(wsargs) ;
   
  return kFALSE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::extendSet(const char* name, const char* newContents) 
{
  // Define a named set in the work space through a comma separated list of
  // names of objects already in the workspace

  RooArgSet wsargs ;

  // Check all constituents of provided set
  char buf[1024] ;
  strlcpy(buf,newContents,1024) ;
  char* token = strtok(buf,",") ;
  while(token) {
    // If missing, either import or report error
    if (!arg(token)) {
      coutE(InputArguments) << "RooWorkspace::defineSet(" << GetName() << ") ERROR proposed set constituent " << token
			    << " is not in workspace" << endl ;
      return kTRUE ;
    }
    wsargs.add(*arg(token)) ;    
    token = strtok(0,",") ;
  }

  // Extend named set
  _namedSets[name].add(wsargs,kTRUE) ;
   
  return kFALSE ;
}



//_____________________________________________________________________________
const RooArgSet* RooWorkspace::set(const char* name) 
{
  // Return pointer to previously defined named set with given nmame
  // If no such set is found a null pointer is returned

  map<string,RooArgSet>::iterator i = _namedSets.find(name) ;
  return (i!=_namedSets.end()) ? &(i->second) : 0 ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::startTransaction() 
{
  // Open an import transaction operations. Returns kTRUE if successful, kFALSE
  // if there is already an ongoing transaction

  // Check that there was no ongoing transaction
  if (_openTrans) {
    return kFALSE ;
  } 

  // Open transaction
  _openTrans = kTRUE ;
  return kTRUE ;
}

Bool_t RooWorkspace::cancelTransaction() 
{
  // Cancel an ongoing import transaction. All objects imported since startTransaction()
  // will be removed and the transaction will be terminated. Return kTRUE if cancel operation
  // succeeds, return kFALSE if there was no open transaction
  
  // Check that there is an ongoing transaction
  if (!_openTrans) {
    return kFALSE ;
  }

  // Delete all objects in the sandbox
  TIterator* iter = _sandboxNodes.createIterator() ;
  RooAbsArg* tmpArg ;
  while((tmpArg=(RooAbsArg*)iter->Next())) {
    _allOwnedNodes.remove(*tmpArg) ;
  }
  delete iter ;
  _sandboxNodes.removeAll() ;
  
  // Mark transaction as finished
  _openTrans = kFALSE ;

  return kTRUE ;
}

Bool_t RooWorkspace::commitTransaction() 
{
  // Commit an ongoing import transaction. Returns kTRUE if commit succeeded,
  // return kFALSE if there was no ongoing transaction

  // Check that there is an ongoing transaction
  if (!_openTrans) {
    return kFALSE ;
  }

  // Publish sandbox nodes in directory and/or CINT if requested 
  TIterator* iter = _sandboxNodes.createIterator() ;
  RooAbsArg* sarg ;
  while((sarg=(RooAbsArg*)iter->Next())) {
    if (_dir && sarg->IsA() != RooConstVar::Class()) {	
      _dir->InternalAppend(sarg) ;
    }
    if (_doExport && sarg->IsA() != RooConstVar::Class()) {
      exportObj(sarg) ;
    }    
  }
  delete iter ;

  // Remove all commited objects from the sandbox
  _sandboxNodes.removeAll() ;

  // Mark transaction as finished
  _openTrans = kFALSE ;

  return kTRUE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::importClassCode(TClass* theClass, Bool_t doReplace) 
{
  return _classes.autoImportClass(theClass,doReplace) ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::importClassCode(const char* pat, Bool_t doReplace)  
{
  // Inport code of all classes in the workspace that have a class name
  // that matches pattern 'pat' and which are not found to be part of
  // the standard ROOT distribution. If doReplace is true any existing
  // class code saved in the workspace is replaced

  Bool_t ret(kTRUE) ;

  TRegexp re(pat,kTRUE) ;
  TIterator* iter = componentIterator() ;
  RooAbsArg* carg ;
  while((carg=(RooAbsArg*)iter->Next())) {
    TString className = carg->IsA()->GetName() ;
    if (className.Index(re)>=0 && !_classes.autoImportClass(carg->IsA(),doReplace)) {
      coutW(ObjectHandling) << "RooWorkspace::import(" << GetName() << ") WARNING: problems import class code of object " 
			    << carg->IsA()->GetName() << "::" << carg->GetName() << ", reading of workspace will require external definition of class" << endl ;
      ret = kFALSE ;
    }
  }  
  delete iter ;
  return ret ;
}





//_____________________________________________________________________________
Bool_t RooWorkspace::saveSnapshot(const char* name, const char* paramNames) 
{
  // Save snapshot of values and attributes (including "Constant") of parameters 'params'
  // If importValues is FALSE, the present values from the object in the workspace are
  // saved. If importValues is TRUE, the values of the objects passed in the 'params'
  // argument are saved

  return saveSnapshot(name,argSet(paramNames),kFALSE) ;
}





//_____________________________________________________________________________
Bool_t RooWorkspace::saveSnapshot(const char* name, const RooArgSet& params, Bool_t importValues) 
{
  // Save snapshot of values and attributes (including "Constant") of parameters 'params'
  // If importValues is FALSE, the present values from the object in the workspace are
  // saved. If importValues is TRUE, the values of the objects passed in the 'params'
  // argument are saved

  RooArgSet* actualParams = (RooArgSet*) _allOwnedNodes.selectCommon(params) ;
  RooArgSet* snapshot = (RooArgSet*) actualParams->snapshot() ;
  delete actualParams ;

  snapshot->setName(name) ;

  if (importValues) {
    *snapshot = params ;
  }

  RooArgSet* oldSnap = (RooArgSet*) _snapshots.FindObject(name) ;
  if (oldSnap) {
    coutI(ObjectHandling) << "RooWorkspace::saveSnaphot(" << GetName() << ") replacing previous snapshot with name " << name << endl ;
    _snapshots.Remove(oldSnap) ;
    delete oldSnap ;
  }

  _snapshots.Add(snapshot) ;

  return kTRUE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::loadSnapshot(const char* name) 
{
  // Load the values and attributes of the parameters in the snapshot saved with
  // the given name

  RooArgSet* snap = (RooArgSet*) _snapshots.find(name) ;
  if (!snap) {
    coutE(ObjectHandling) << "RooWorkspace::loadSnapshot(" << GetName() << ") no snapshot with name " << name << " is available" << endl ;
    return kFALSE ;
  }

  RooArgSet* actualParams = (RooArgSet*) _allOwnedNodes.selectCommon(*snap) ;
  *actualParams = *snap ;
  delete actualParams ;

  return kTRUE ;
}



// //_____________________________________________________________________________
// RooAbsPdf* RooWorkspace::joinPdf(const char* jointPdfName, const char* indexName, const char* inputMapping) 
// {
//   // Join given list of p.d.f.s into a simultaneous p.d.f with given name. If the named index category
//   // does not exist, it is created. 
//   //
//   //  Example : joinPdf("simPdf","expIndex","A=pdfA,B=pdfB") ;
//   //
//   //            will return a RooSimultaneous named 'simPdf' with index category 'expIndex' with
//   //            state names A and B. Pdf 'pdfA' will be associated with state A and pdf 'pdfB'
//   //            will be associated with state B
//   //
//   return 0 ;
// }

// //_____________________________________________________________________________
// RooAbsData* RooWorkspace::joinData(const char* jointDataName, const char* indexName, const char* inputMapping) 
// {
//   // Join given list of dataset into a joint dataset for use with a simultaneous pdf
//   // (as e.g. created by joingPdf"
//   //
//   //  Example : joinData("simData","expIndex","A=dataA,B=dataB") ;
//   //
//   //            will return a RooDataSet named 'simData' that consist of the entries of both
//   //            dataA and dataB. An extra category column 'expIndex' is added that labels
//   //            each entry with state 'A' and 'B' to indicate the originating dataset
//   return 0 ;
// }


//_____________________________________________________________________________
RooAbsPdf* RooWorkspace::pdf(const char* name) const
{ 
  // Retrieve p.d.f (RooAbsPdf) with given name. A null pointer is returned if not found

  return dynamic_cast<RooAbsPdf*>(_allOwnedNodes.find(name)) ; 
}


//_____________________________________________________________________________
RooAbsReal* RooWorkspace::function(const char* name) const 
{ 
  // Retrieve function (RooAbsReal) with given name. Note that all RooAbsPdfs are also RooAbsReals. A null pointer is returned if not found.

  return dynamic_cast<RooAbsReal*>(_allOwnedNodes.find(name)) ; 
}


//_____________________________________________________________________________
RooRealVar* RooWorkspace::var(const char* name) const
{ 
  // Retrieve real-valued variable (RooRealVar) with given name. A null pointer is returned if not found

  return dynamic_cast<RooRealVar*>(_allOwnedNodes.find(name)) ; 
}


//_____________________________________________________________________________
RooCategory* RooWorkspace::cat(const char* name) const
{ 
  // Retrieve discrete variable (RooCategory) with given name. A null pointer is returned if not found

  return dynamic_cast<RooCategory*>(_allOwnedNodes.find(name)) ; 
}


//_____________________________________________________________________________
RooAbsCategory* RooWorkspace::catfunc(const char* name) const
{
  // Retrieve discrete function (RooAbsCategory) with given name. A null pointer is returned if not found
  return dynamic_cast<RooAbsCategory*>(_allOwnedNodes.find(name)) ; 
}



//_____________________________________________________________________________
RooAbsArg* RooWorkspace::arg(const char* name) const
{
  // Return RooAbsArg with given name. A null pointer is returned if none is found.
  return _allOwnedNodes.find(name) ;
}



//_____________________________________________________________________________
RooArgSet RooWorkspace::argSet(const char* nameList) const
{
  // Return set of RooAbsArgs matching to given list of names
  RooArgSet ret ;

  char tmp[1024] ;
  strlcpy(tmp,nameList,1024) ;
  char* token = strtok(tmp,",") ;
  while(token) {
    RooAbsArg* oneArg = arg(token) ;
    if (oneArg) {
      ret.add(*oneArg) ;
    } else {
      coutE(InputArguments) << " RooWorkspace::argSet(" << GetName() << ") no RooAbsArg named " << token << " in workspace" << endl ;
    }
    token = strtok(0,",") ; 
  }
  return ret ;
}



//_____________________________________________________________________________
RooAbsArg* RooWorkspace::fundArg(const char* name) const
{
  // Return fundamental (i.e. non-derived) RooAbsArg with given name. Fundamental types
  // are e.g. RooRealVar, RooCategory. A null pointer is returned if none is found.
  RooAbsArg* tmp = arg(name) ;
  if (!tmp) {
    return 0 ;
  }
  return tmp->isFundamental() ? tmp : 0 ;
}



//_____________________________________________________________________________
RooAbsData* RooWorkspace::data(const char* name) const
{
  // Retrieve dataset (binned or unbinned) with given name. A null pointer is returned if not found

  return (RooAbsData*)_dataList.FindObject(name) ;
}




//_____________________________________________________________________________
RooArgSet RooWorkspace::allVars() const
{
  // Return set with all variable objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {
    if (parg->IsA()->InheritsFrom(RooRealVar::Class())) {
      ret.add(*parg) ;
    }
  }
  delete iter ;

  return ret ;
}


//_____________________________________________________________________________
RooArgSet RooWorkspace::allCats() const
{
  // Return set with all category objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {
    if (parg->IsA()->InheritsFrom(RooCategory::Class())) {
      ret.add(*parg) ;
    }
  }
  delete iter ;

  return ret ;
}



//_____________________________________________________________________________
RooArgSet RooWorkspace::allFunctions() const
{
  // Return set with all function objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {
    if (parg->IsA()->InheritsFrom(RooAbsReal::Class()) && 
	!parg->IsA()->InheritsFrom(RooAbsPdf::Class()) && 
	!parg->IsA()->InheritsFrom(RooConstVar::Class()) && 
	!parg->IsA()->InheritsFrom(RooRealVar::Class())) {
      ret.add(*parg) ;
    }
  }

  return ret ;
}


//_____________________________________________________________________________
RooArgSet RooWorkspace::allCatFunctions() const
{
  // Return set with all category function objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {  
    if (parg->IsA()->InheritsFrom(RooAbsCategory::Class()) && 
	!parg->IsA()->InheritsFrom(RooCategory::Class())) {
      ret.add(*parg) ;
    }
  }
  return ret ;
}



//_____________________________________________________________________________
RooArgSet RooWorkspace::allResolutionModels() const
{
  // Return set with all resolution model objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {  
    if (parg->IsA()->InheritsFrom(RooResolutionModel::Class())) {
      if (!((RooResolutionModel*)parg)->isConvolved()) {
	ret.add(*parg) ;
      }
    }
  }
  return ret ;
}


//_____________________________________________________________________________
RooArgSet RooWorkspace::allPdfs() const
{
  // Return set with all probability density function objects
  RooArgSet ret ;

  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  RooAbsArg* parg ;
  while((parg=(RooAbsArg*)iter->Next())) {  
    if (parg->IsA()->InheritsFrom(RooAbsPdf::Class()) &&
	!parg->IsA()->InheritsFrom(RooResolutionModel::Class())) {
      ret.add(*parg) ;
    }
  }
  return ret ;
}



//_____________________________________________________________________________
list<RooAbsData*> RooWorkspace::allData() const 
{
  // Return list of all dataset in the workspace

  list<RooAbsData*> ret ;
  TIterator* iter = _dataList.MakeIterator() ;
  RooAbsData* dat ;
  while((dat=(RooAbsData*)iter->Next())) {
    ret.push_back(dat) ;
  }
  delete iter ;
  return ret ;
}



//_____________________________________________________________________________
list<TObject*> RooWorkspace::allGenericObjects() const 
{
  // Return list of all generic objects in the workspace

  list<TObject*> ret ;
  TIterator* iter = _genObjects.MakeIterator() ;
  TObject* gobj ;
  while((gobj=(RooAbsData*)iter->Next())) {    

    // If found object is wrapper, return payload
    if (gobj->IsA()==RooTObjWrap::Class()) {
      ret.push_back(((RooTObjWrap*)gobj)->obj()) ;
    } else {
      ret.push_back(gobj) ;
    }
  }
  delete iter ;
  return ret ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::CodeRepo::autoImportClass(TClass* tc, Bool_t doReplace) 
{
  // Import code of class 'tc' into the repository. If code is already in repository it is only imported
  // again if doReplace is false. The names and location of the source files is determined from the information
  // in TClass. If no location is found in the TClass information, the files are searched in the workspace
  // search path, defined by addClassDeclImportDir() and addClassImplImportDir() for declaration and implementation
  // files respectively. If files cannot be found, abort with error status, otherwise update the internal
  // class-to-file map and import the contents of the files, if they are not imported yet.


  oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo(" << _wspace->GetName() << ") request to import code of class " << tc->GetName() << endl ;

  // *** PHASE 1 *** Check if file needs to be imported, or is in ROOT distribution, and check if it can be persisted

  // Check if we already have the class (i.e. it is in the classToFile map)
  if (!doReplace && _c2fmap.find(tc->GetName())!=_c2fmap.end()) {
    oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo(" << _wspace->GetName() << ") code of class " << tc->GetName() << " already imported, skipping" << endl ;
    return kTRUE ;
  }

  // Retrieve file names through ROOT TClass interface
  string implfile = tc->GetImplFileName() ;
  string declfile = tc->GetDeclFileName() ;

  // Check that file names are not empty
  if (implfile.empty() || declfile.empty()) {
    oocoutE(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo(" << _wspace->GetName() << ") ERROR: cannot retrieve code file names for class " 
				   << tc->GetName() << " through ROOT TClass interface, unable to import code" << endl ;
    return kFALSE ;
  }

  // Check if header filename is found in ROOT distribution, if so, do not import class
  TString rootsys = gSystem->Getenv("ROOTSYS") ;
  if (TString(implfile.c_str()).Index(rootsys)>=0) {
    oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo(" << _wspace->GetName() << ") code of class " << tc->GetName() << " is in ROOT distribution, skipping " << endl ;
    return kTRUE ;
  }
  const char* implpath=0 ;

  // Require that class meets technical criteria to be persistable (i.e it has a default ctor)
  // (We also need a default ctor of abstract classes, but cannot check that through is interface
  //  as TClass::HasDefaultCtor only returns true for callable default ctors)
#if ROOT_VERSION_CODE <= ROOT_VERSION(5,19,02)
  if (!(tc->GetClassInfo()->Property()&G__BIT_ISABSTRACT) && !tc->HasDefaultConstructor()) {
#else
  if (!(gCint->ClassInfo_Property(tc->GetClassInfo())&G__BIT_ISABSTRACT) && !tc->HasDefaultConstructor()) {
#endif
    oocoutW(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() << ") WARNING cannot import class " 
				    << tc->GetName() << " : it cannot be persisted because it doesn't have a default constructor. Please fix " << endl ;
    return kFALSE ;      
  }


  // *** PHASE 2 *** Check if declaration and implementation files can be located 

  char* declpath = 0 ;

  // Check if header file can be found in specified location
  // If not, scan through list of 'class declaration' paths in RooWorkspace
  if (gSystem->AccessPathName(declfile.c_str())) {

    // Check list of additional declaration paths
    list<string>::iterator diter = RooWorkspace::_classDeclDirList.begin() ;

    while(diter!= RooWorkspace::_classDeclDirList.end()) {
      
      declpath = gSystem->ConcatFileName(diter->c_str(),declfile.c_str()) ;      
      if (!gSystem->AccessPathName(declpath)) {
	// found declaration file
	break ;
      }
      // cleanup and continue ;
      delete[] declpath ;
      declpath=0 ;

      ++diter ;
    }
    
    // Header file cannot be found anywhere, warn user and abort operation
    if (!declpath) {
      oocoutW(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() << ") WARNING Cannot access code of class " 
				      << tc->GetName() << " because header file " << declfile << " is not found in current directory nor in $ROOTSYS" ;
      if (_classDeclDirList.size()>0) {
	ooccoutW(_wspace,ObjectHandling) << ", nor in the search path " ;
	diter = RooWorkspace::_classDeclDirList.begin() ;

	while(diter!= RooWorkspace::_classDeclDirList.end()) {

	  if (diter!=RooWorkspace::_classDeclDirList.begin()) {
	    ooccoutW(_wspace,ObjectHandling) << "," ;
	  }
	  ooccoutW(_wspace,ObjectHandling) << diter->c_str() ;
	  ++diter ;
	}
      }
      ooccoutW(_wspace,ObjectHandling) << ". To fix this problem add the required directory to the search "
				       << "path using RooWorkspace::addClassDeclDir(const char* dir)" << endl ;
      
      return kFALSE ;
    }
  }

  
  // Check if implementation file can be found in specified location
  // If not, scan through list of 'class implementation' paths in RooWorkspace
  if (gSystem->AccessPathName(implfile.c_str())) {

    // Check list of additional declaration paths
    list<string>::iterator iiter = RooWorkspace::_classImplDirList.begin() ;

    while(iiter!= RooWorkspace::_classImplDirList.end()) {
      
      implpath = gSystem->ConcatFileName(iiter->c_str(),implfile.c_str()) ;      
      if (!gSystem->AccessPathName(implpath)) {
	// found implementation file
	break ;
      }
      // cleanup and continue ;
      delete[] implpath ;
      implpath=0 ;

      ++iiter ;
    }
     
    // Implementation file cannot be found anywhere, warn user and abort operation
    if (!implpath) {
      oocoutW(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() << ") WARNING Cannot access code of class " 
				      << tc->GetName() << " because implementation file " << implfile << " is not found in current directory nor in $ROOTSYS" ;
      if (_classDeclDirList.size()>0) {
	ooccoutW(_wspace,ObjectHandling) << ", nor in the search path " ;
	iiter = RooWorkspace::_classImplDirList.begin() ;

	while(iiter!= RooWorkspace::_classImplDirList.end()) {

	  if (iiter!=RooWorkspace::_classImplDirList.begin()) {
	    ooccoutW(_wspace,ObjectHandling) << "," ;
	  }
	  ooccoutW(_wspace,ObjectHandling) << iiter->c_str() ;
	  ++iiter ;
	}
      }
      ooccoutW(_wspace,ObjectHandling) << ". To fix this problem add the required directory to the search "
				       << "path using RooWorkspace::addClassImplDir(const char* dir)" << endl ;    
      return kFALSE ;
    }
  }
  
  char buf[1024] ;

  // *** Phase 3 *** Prepare to import code from files into STL string buffer
  //
  // Code storage is organized in two linked maps
  //
  // _fmap contains stl strings with code, indexed on declaration file name
  //
  // _c2fmap contains list of declaration file names and list of base classes
  //                  and is indexed on class name
  //
  // Phase 3 is skipped if fmap already contains an entry with given filebasename

  string declfilename = declpath?gSystem->BaseName(declpath):gSystem->BaseName(declfile.c_str()) ;

  // Split in base and extension
  int dotpos2 = strrchr(declfilename.c_str(),'.') - declfilename.c_str() ;
  string declfilebase = declfilename.substr(0,dotpos2) ;
  string declfileext = declfilename.substr(dotpos2+1) ;

  // If file has not beed stored yet, enter stl strings with implementation and declaration in file map
  if (_fmap.find(declfilebase) == _fmap.end()) {

    // Open declaration file
    fstream fdecl(declpath?declpath:declfile.c_str()) ;
    
    // Abort import if declaration file cannot be opened
    if (!fdecl) {
      oocoutE(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() 
				      << ") ERROR opening declaration file " <<  declfile << endl ;
      return kFALSE ;      
    }
    
    oocoutI(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() 
				    << ") importing code of class " << tc->GetName() 
				    << " from " << (implpath?implpath:implfile.c_str()) 
				    << " and " << (declpath?declpath:declfile.c_str()) << endl ;
    
    
    // Read entire file into an stl string
    string decl ;
    while(fdecl.getline(buf,1023)) {
      decl += buf ;
      decl += '\n' ;
    }
    
    // Open implementation file
    fstream fimpl(implpath?implpath:implfile.c_str()) ;
    
    // Abort import if implementation file cannot be opened
    if (!fimpl) {
      oocoutE(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() 
				      << ") ERROR opening implementation file " <<  implfile << endl ;
      return kFALSE ;      
    }
    
    
    // Import entire implentation file into stl string
    string impl ;
    while(fimpl.getline(buf,1023)) {
      // Process #include statements here
      
      // Look for include state of self
      Bool_t foundSelfInclude=kFALSE ;
      
      // Look for include of declaration file corresponding to this implementation file
      if (strstr(buf,"#include")) {
	// Process #include statements here
	char tmp[1024] ;
	strlcpy(tmp,buf,1024) ;
	strtok(tmp," <\"") ;
	char* incfile = strtok(0," <\"") ;
	
	if (strstr(incfile,declfilename.c_str())) {
	  foundSelfInclude=kTRUE ;
	}
      } 
      
      // Explicitly rewrite include of own declaration file to string
      // any directory prefixes, copy all other lines verbatim in stl string
      if (foundSelfInclude) {
	// If include of self is found, substitute original include 
	// which may have directory structure with a plain include
	impl += Form("#include \"%s.%s\"\n",declfilebase.c_str(),declfileext.c_str()) ;
      } else {
	impl += buf ;
	impl += '\n' ;
      }
    }
    
        
    // Create entry in file map
    _fmap[declfilebase]._hfile = decl ;
    _fmap[declfilebase]._cxxfile = impl ;   
    _fmap[declfilebase]._hext = declfileext ;

  } else {

    // Inform that existing file entry is being recycled because it already contained class code
    oocoutI(_wspace,ObjectHandling) << "RooWorkspace::autoImportClass(" << _wspace->GetName() 
				    << ") code of class " << tc->GetName() 
				    << " was already imported from " << (implpath?implpath:implfile.c_str()) 
				    << " and " << (declpath?declpath:declfile.c_str()) << endl ;
    
  }


  // *** PHASE 4 *** Import stl strings with code into workspace 
  //
  // If multiple classes are declared in a single code unit, there will be
  // multiple _c2fmap entries all pointing to the same _fmap entry.  
  
  // Make list of all immediate base classes of this class
  TString baseNameList ;
  TList* bl = tc->GetListOfBases() ;
  TIterator* iter = bl->MakeIterator() ;
  TBaseClass* base ;
  list<TClass*> bases ;
  while((base=(TBaseClass*)iter->Next())) {
    if (baseNameList.Length()>0) {
      baseNameList += "," ;
    }
    baseNameList += base->GetClassPointer()->GetName() ;
    bases.push_back(base->GetClassPointer()) ;
  }
  
  // Map class name to above _fmap entries, along with list of base classes
  // in _c2fmap
  _c2fmap[tc->GetName()]._baseName = baseNameList ;
  _c2fmap[tc->GetName()]._fileBase = declfilebase ;
  
  // Recursive store all base classes.
  list<TClass*>::iterator biter = bases.begin() ;
  while(biter!=bases.end()) {
    autoImportClass(*biter,doReplace) ;
    ++biter ;
  }

  // Cleanup 
  if (implpath) {
    delete[] implpath ;
  }
  if (declpath) {
    delete[] declpath ;
  }

  return kTRUE ;
}


//_____________________________________________________________________________
Bool_t RooWorkspace::makeDir() 
{
  // Create transient TDirectory representation of this workspace. This directory
  // will appear as a subdirectory of the directory that contains the workspace
  // and will have the name of the workspace suffixed with "Dir". The TDirectory
  // interface is read-only. Any attempt to insert objects into the workspace
  // directory representation will result in an error message. Note that some
  // ROOT object like TH1 automatically insert themselves into the current directory
  // when constructed. This will give error messages when done in a workspace
  // directory.

  if (_dir) return kTRUE ;

  TString title= Form("TDirectory representation of RooWorkspace %s",GetName()) ;
  _dir = new WSDir(GetName(),title.Data(),this) ;
  
  TIterator* iter = componentIterator() ;
  RooAbsArg* darg ;
  while((darg=(RooAbsArg*)iter->Next())) {
    if (darg->IsA() != RooConstVar::Class()) {
      _dir->InternalAppend(darg) ;
    }
  }
  
  return kTRUE ;
}
 
 

//_____________________________________________________________________________
Bool_t RooWorkspace::import(TObject& object, Bool_t replaceExisting) 
{
  // Import a clone of a generic TObject into workspace generic object container. Imported
  // object can be retrieved by name through the obj() method. The object is cloned upon
  // importation and the input argument does not need to live beyond the import call
  // 
  // Returns kTRUE if an error has occurred.
  
  // First check if object with given name already exists
  TObject* oldObj = _genObjects.FindObject(object.GetName()) ;
  if (oldObj && !replaceExisting) {
    coutE(InputArguments) << "RooWorkspace::import(" << GetName() << ") generic object with name " 
			  << object.GetName() << " is already in workspace and replaceExisting flag is set to false" << endl ;
    return kTRUE ;
  }  
  TH1::AddDirectory(kFALSE) ;
  if (oldObj) {
    _genObjects.Replace(oldObj,object.Clone()) ;
    delete oldObj ;
  } else {
    _genObjects.Add(object.Clone()) ;
  }
  TH1::AddDirectory(kTRUE) ;
  return kFALSE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::import(TObject& object, const char* aliasName, Bool_t replaceExisting) 
{
  // Import a clone of a generic TObject into workspace generic object container. 
  // The imported object will be stored under the given alias name rather than its
  // own name. Imported object can be retrieved its alias name through the obj() method. 
  // The object is cloned upon importation and the input argument does not need to live beyond the import call
  // This method is mostly useful for importing objects that do not have a settable name such as TMatrix
  // 
  // Returns kTRUE if an error has occurred.
  
  // First check if object with given name already exists
  TObject* oldObj = _genObjects.FindObject(object.GetName()) ;
  if (oldObj && !replaceExisting) {
    coutE(InputArguments) << "RooWorkspace::import(" << GetName() << ") generic object with name " 
			  << object.GetName() << " is already in workspace and replaceExisting flag is set to false" << endl ;
    return kTRUE ;
  }  
  
  TH1::AddDirectory(kFALSE) ;
  RooTObjWrap* wrapper = new RooTObjWrap(object.Clone()) ;
  TH1::AddDirectory(kTRUE) ;
  wrapper->setOwning(kTRUE) ;
  wrapper->SetName(aliasName) ;
  wrapper->SetTitle(aliasName) ;
    
  if (oldObj) {
    _genObjects.Replace(oldObj,wrapper) ;
    delete oldObj ;
  } else {
    _genObjects.Add(wrapper) ;
  }
  return kFALSE ;
}




//_____________________________________________________________________________
Bool_t RooWorkspace::addStudy(RooAbsStudy& study) 
{
  // Insert RooStudyManager module
  RooAbsStudy* clone = (RooAbsStudy*) study.Clone() ;
  _studyMods.Add(clone) ;
  return kFALSE ;
}




//_____________________________________________________________________________
void RooWorkspace::clearStudies() 
{
  // Remove all RooStudyManager modules
  _studyMods.Delete() ;
}




//_____________________________________________________________________________
TObject* RooWorkspace::obj(const char* name) const
{
  // Return any type of object (RooAbsArg, RooAbsData or generic object) with given name)

  // Try RooAbsArg first
  TObject* ret = arg(name) ;
  if (ret) return ret ;

  // Then try RooAbsData
  ret = data(name) ;
  if (ret) return ret ;

  // Finally try generic object store
  return genobj(name) ;
}



//_____________________________________________________________________________
TObject* RooWorkspace::genobj(const char* name)  const
{
  // Return generic object with given name

  // Find object by name
  TObject* gobj = _genObjects.FindObject(name) ;

  // Exit here if not found
  if (!gobj) return 0 ;

  // If found object is wrapper, return payload
  if (gobj->IsA()==RooTObjWrap::Class()) return ((RooTObjWrap*)gobj)->obj() ;

  return gobj ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::cd(const char* path) 
{
  makeDir() ;
  return _dir->cd(path) ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::writeToFile(const char* fileName, Bool_t recreate) 
{
  // Save this current workspace into given file
  TFile f(fileName,recreate?"RECREATE":"UPDATE") ;
  Write() ;
  return kFALSE ;
}
 
 

//_____________________________________________________________________________
RooFactoryWSTool& RooWorkspace::factory()
{
  // Return instance to factory tool 

  if (_factory) {
    return *_factory ;  
  }
  cxcoutD(ObjectHandling) << "INFO: Creating RooFactoryWSTool associated with this workspace" << endl ;
  _factory = new RooFactoryWSTool(*this) ;
  return *_factory  ;
}




//_____________________________________________________________________________
RooAbsArg* RooWorkspace::factory(const char* expr)
{
  // Short-hand function for factory()->process(expr) ;
  return factory().process(expr) ;
}




//_____________________________________________________________________________
void RooWorkspace::Print(Option_t* opts) const 
{
  // Print contents of the workspace 

  Bool_t treeMode(kFALSE) ;
  if (TString(opts).Contains("t")) {
    treeMode=kTRUE ;
  }

  cout << endl << "RooWorkspace(" << GetName() << ") " << GetTitle() << " contents" << endl << endl  ;
  
  RooAbsArg* parg ;

  RooArgSet pdfSet ;
  RooArgSet funcSet ;
  RooArgSet varSet ;
  RooArgSet catfuncSet ;
  RooArgSet convResoSet ;
  RooArgSet resoSet ;


  // Split list of components in pdfs, functions and variables
  TIterator* iter = _allOwnedNodes.createIterator() ;
  while((parg=(RooAbsArg*)iter->Next())) {

    //---------------

    if (treeMode) {
      
      // In tree mode, only add nodes with no clients to the print lists

      if (parg->IsA()->InheritsFrom(RooAbsPdf::Class())) {
	if (!parg->hasClients()) {
	  pdfSet.add(*parg) ;
	}
      }
      
      if (parg->IsA()->InheritsFrom(RooAbsReal::Class()) && 
	  !parg->IsA()->InheritsFrom(RooAbsPdf::Class()) && 
	  !parg->IsA()->InheritsFrom(RooConstVar::Class()) && 
	  !parg->IsA()->InheritsFrom(RooRealVar::Class())) {
	if (!parg->hasClients()) {
	  funcSet.add(*parg) ;
	}
      }
      
      
      if (parg->IsA()->InheritsFrom(RooAbsCategory::Class()) && 
	  !parg->IsA()->InheritsFrom(RooCategory::Class())) {
	if (!parg->hasClients()) {	
	  catfuncSet.add(*parg) ;
	}
      }

    } else {

      if (parg->IsA()->InheritsFrom(RooResolutionModel::Class())) {
	if (((RooResolutionModel*)parg)->isConvolved()) {
	  convResoSet.add(*parg) ;
	} else {
	  resoSet.add(*parg) ;
	}
      }
      
      if (parg->IsA()->InheritsFrom(RooAbsPdf::Class()) &&
	  !parg->IsA()->InheritsFrom(RooResolutionModel::Class())) {
	pdfSet.add(*parg) ;
      }
      
      if (parg->IsA()->InheritsFrom(RooAbsReal::Class()) && 
	  !parg->IsA()->InheritsFrom(RooAbsPdf::Class()) && 
	  !parg->IsA()->InheritsFrom(RooConstVar::Class()) && 
	  !parg->IsA()->InheritsFrom(RooRealVar::Class())) {
	funcSet.add(*parg) ;
      }
      
      if (parg->IsA()->InheritsFrom(RooAbsCategory::Class()) && 
	  !parg->IsA()->InheritsFrom(RooCategory::Class())) {
	catfuncSet.add(*parg) ;
      }
    }

    if (parg->IsA()->InheritsFrom(RooRealVar::Class())) {
      varSet.add(*parg) ;
    }

    if (parg->IsA()->InheritsFrom(RooCategory::Class())) {
      varSet.add(*parg) ;
    }

  }
  delete iter ;


  RooFit::MsgLevel oldLevel = RooMsgService::instance().globalKillBelow() ;
  RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING) ;

  if (varSet.getSize()>0) {
    varSet.sort() ;
    cout << "variables" << endl ;
    cout << "---------" << endl ;
    cout << varSet << endl ;
    cout << endl ;
  }

  if (pdfSet.getSize()>0) {
    cout << "p.d.f.s" << endl ;
    cout << "-------" << endl ;
    pdfSet.sort() ;
    iter = pdfSet.createIterator() ;
    while((parg=(RooAbsArg*)iter->Next())) {
      if (treeMode) {
	parg->printComponentTree() ;
      } else {
	parg->Print() ;
      }
    }
    delete iter ;
    cout << endl ;
  }

  if (!treeMode) {
    if (resoSet.getSize()>0) {
      cout << "analytical resolution models" << endl ;
      cout << "----------------------------" << endl ;
      resoSet.sort() ;
      iter = resoSet.createIterator() ;
      while((parg=(RooAbsArg*)iter->Next())) {
	parg->Print() ;
      }
      delete iter ;
      //     iter = convResoSet.createIterator() ;
      //     while((parg=(RooAbsArg*)iter->Next())) {
      //       parg->Print() ;
      //     }
      //     delete iter ;
      cout << endl ;
    }
  }

  if (funcSet.getSize()>0) {
    cout << "functions" << endl ;
    cout << "--------" << endl ;
    funcSet.sort() ;
    iter = funcSet.createIterator() ;
    while((parg=(RooAbsArg*)iter->Next())) {
      if (treeMode) {
	parg->printComponentTree() ;
      } else {
	parg->Print() ;
      }
    }
    delete iter ;
    cout << endl ;
  }

  if (catfuncSet.getSize()>0) {
    cout << "category functions" << endl ;
    cout << "------------------" << endl ;
    catfuncSet.sort() ;
    iter = catfuncSet.createIterator() ;
    while((parg=(RooAbsArg*)iter->Next())) {
      if (treeMode) {
	parg->printComponentTree() ;
      } else {
	parg->Print() ;
      }
    }
    delete iter ;
    cout << endl ;
  }

  if (_dataList.GetSize()>0) {
    cout << "datasets" << endl ;
    cout << "--------" << endl ;
    iter = _dataList.MakeIterator() ;
    RooAbsData* data2 ;
    while((data2=(RooAbsData*)iter->Next())) {
      cout << data2->IsA()->GetName() << "::" << data2->GetName() << *data2->get() << endl ;
    }
    delete iter ;
    cout << endl ;
  }

  if (_snapshots.GetSize()>0) {
    cout << "parameter snapshots" << endl ;
    cout << "-------------------" << endl ;
    iter = _snapshots.MakeIterator() ;
    RooArgSet* snap ;
    while((snap=(RooArgSet*)iter->Next())) {
      cout << snap->GetName() << " = (" ;
      TIterator* aiter = snap->createIterator() ;
      RooAbsArg* a ;
      Bool_t first(kTRUE) ;
      while((a=(RooAbsArg*)aiter->Next())) {
	if (first) { first=kFALSE ; } else { cout << "," ; }
	cout << a->GetName() << "=" ; 
	a->printValue(cout) ;
	if (a->isConstant()) {
	  cout << "[C]" ;
	}
      }
      cout << ")" << endl ;
      delete aiter ;
    }
    delete iter ;
    cout << endl ;
  }


  if (_namedSets.size()>0) {
    cout << "named sets" << endl ;
    cout << "----------" << endl ;
    for (map<string,RooArgSet>::const_iterator it = _namedSets.begin() ; it != _namedSets.end() ; it++) {
      cout << it->first << ":" << it->second << endl ;
    }
    
    cout << endl ;
  }

 
  if (_genObjects.GetSize()>0) {
    cout << "generic objects" << endl ;
    cout << "---------------" << endl ;
    iter = _genObjects.MakeIterator() ;
    TObject* gobj ;
    while((gobj=(TObject*)iter->Next())) {
      if (gobj->IsA()==RooTObjWrap::Class()) {
	cout << ((RooTObjWrap*)gobj)->obj()->IsA()->GetName() << "::" << gobj->GetName() << endl ;
      } else {
	cout << gobj->IsA()->GetName() << "::" << gobj->GetName() << endl ;
      }
    }
    delete iter ;
    cout << endl ;
    
  }

  if (_studyMods.GetSize()>0) {
    cout << "study modules" << endl ;
    cout << "-------------" << endl ;
    iter = _studyMods.MakeIterator() ;
    TObject* smobj ;
    while((smobj=(TObject*)iter->Next())) {
      cout << smobj->IsA()->GetName() << "::" << smobj->GetName() << endl ;
    }
    delete iter ;
    cout << endl ;
    
  }

  if (_classes.listOfClassNames().size()>0) {
    cout << "embedded class code" << endl ;
    cout << "-------------------" << endl ;    
    cout << _classes.listOfClassNames() << endl ;
    cout << endl ;
  }

  if (_eocache.size()>0) {
    cout << "embedded precalculated expensive components" << endl ;
    cout << "-------------------------------------------" << endl ;
    _eocache.print() ;
  }

  RooMsgService::instance().setGlobalKillBelow(oldLevel) ;

  return ;
}


//_____________________________________________________________________________
void RooWorkspace::CodeRepo::Streamer(TBuffer &R__b)
{
  // Custom streamer for the workspace. Stream contents of workspace
  // and code repository. When reading, read code repository first
  // and compile missing classes before proceeding with streaming
  // of workspace contents to avoid errors.

  typedef ::RooWorkspace::CodeRepo thisClass;

   // Stream an object of class RooWorkspace::CodeRepo.
   if (R__b.IsReading()) {

     UInt_t R__s, R__c;
     R__b.ReadVersion(&R__s, &R__c); 

     // Stream contents of ClassFiles map
     Int_t count(0) ;
     R__b >> count ;
     while(count--) {
       TString name ;
       name.Streamer(R__b) ;       
       _fmap[name]._hext.Streamer(R__b) ;
       _fmap[name]._hfile.Streamer(R__b) ;
       _fmap[name]._cxxfile.Streamer(R__b) ;    
     }     
 
     // Stream contents of ClassRelInfo map
     count=0 ;
     R__b >> count ;
     while(count--) {
       TString name,bname,fbase ;
       name.Streamer(R__b) ;       
       _c2fmap[name]._baseName.Streamer(R__b) ;
       _c2fmap[name]._fileBase.Streamer(R__b) ;
     }     
     R__b.CheckByteCount(R__s, R__c, thisClass::IsA());

     // Instantiate any classes that are not defined in current session
     _compiledOK = !compileClasses() ;

   } else {
     
     UInt_t R__c;
     R__c = R__b.WriteVersion(thisClass::IsA(), kTRUE);
     
     // Stream contents of ClassFiles map
     UInt_t count = _fmap.size() ;
     R__b << count ;
     map<TString,ClassFiles>::iterator iter = _fmap.begin() ;
     while(iter!=_fmap.end()) {       
       TString key_copy(iter->first) ;
       key_copy.Streamer(R__b) ;
       iter->second._hext.Streamer(R__b) ;
       iter->second._hfile.Streamer(R__b);
       iter->second._cxxfile.Streamer(R__b);

       ++iter ;
     }
     
     // Stream contents of ClassRelInfo map
     count = _c2fmap.size() ;
     R__b << count ;
     map<TString,ClassRelInfo>::iterator iter2 = _c2fmap.begin() ;
     while(iter2!=_c2fmap.end()) {
       TString key_copy(iter2->first) ;
       key_copy.Streamer(R__b) ;
       iter2->second._baseName.Streamer(R__b) ;
       iter2->second._fileBase.Streamer(R__b);
       ++iter2 ;
     }

     R__b.SetByteCount(R__c, kTRUE);
     
   }
}


//______________________________________________________________________________
void RooWorkspace::Streamer(TBuffer &R__b)
{
  // Stream an object of class RooWorkspace. This is a standard ROOT streamer for the
  // I/O part. This custom function exists to detach all external client links
  // from the payload prior to writing the payload so that these client links
  // are not persisted. (Client links occur if external function objects use 
  // objects contained in the workspace as input)
  // After the actual writing, these client links are restored.

   if (R__b.IsReading()) {

      R__b.ReadClassBuffer(RooWorkspace::Class(),this);

      // Make expensive object cache of all objects point to intermal copy.
      // Somehow this doesn't work OK automatically
      TIterator* iter = _allOwnedNodes.createIterator() ;
      RooAbsArg* node ;
      while((node=(RooAbsArg*)iter->Next())) {
	node->setExpensiveObjectCache(_eocache) ;
	if (node->IsA()->InheritsFrom(RooAbsOptTestStatistic::Class())) {
	  RooAbsOptTestStatistic* tmp = (RooAbsOptTestStatistic*) node ;
	  if (tmp->isSealed() && tmp->sealNotice() && strlen(tmp->sealNotice())>0) {
	    cout << "RooWorkspace::Streamer(" << GetName() << ") " << node->IsA()->GetName() << "::" << node->GetName() << " : " << tmp->sealNotice() << endl ;
	  }
	}
      }
      delete iter ;


   } else {


     // Make lists of external clients of WS objects, and remove those links temporarily

     map<RooAbsArg*,list<RooAbsArg*> > extClients, extValueClients, extShapeClients ;

     TIterator* iter = _allOwnedNodes.createIterator() ;
     RooAbsArg* tmparg ;
     while((tmparg=(RooAbsArg*)iter->Next())) {

       // Loop over client list of this arg
       TIterator* clientIter = tmparg->_clientList.MakeIterator() ;
       RooAbsArg* client ;
       while((client=(RooAbsArg*)clientIter->Next())) {
	 if (!_allOwnedNodes.containsInstance(*client)) {
	   while(tmparg->_clientList.refCount(client)>0) {
	     tmparg->_clientList.Remove(client) ;
	     extClients[tmparg].push_back(client) ;
	   }
	 }
       }
       delete clientIter ;

       // Loop over value client list of this arg
       TIterator* vclientIter = tmparg->_clientListValue.MakeIterator() ;
       RooAbsArg* vclient ;
       while((vclient=(RooAbsArg*)vclientIter->Next())) {
	 if (!_allOwnedNodes.containsInstance(*vclient)) {
	   cxcoutD(ObjectHandling) << "RooWorkspace::Streamer(" << GetName() << ") element " << tmparg->GetName() 
				   << " has external value client link to " << vclient << " (" << vclient->GetName() << ") with ref count " << tmparg->_clientListValue.refCount(vclient) << endl ;
	   while(tmparg->_clientListValue.refCount(vclient)>0) {
	     tmparg->_clientListValue.Remove(vclient) ;
	     extValueClients[tmparg].push_back(vclient) ;
	   }
	 }
       }
       delete vclientIter ;

       // Loop over shape client list of this arg
       TIterator* sclientIter = tmparg->_clientListShape.MakeIterator() ;
       RooAbsArg* sclient ;
       while((sclient=(RooAbsArg*)sclientIter->Next())) {
	 if (!_allOwnedNodes.containsInstance(*sclient)) {
	   cxcoutD(ObjectHandling) << "RooWorkspace::Streamer(" << GetName() << ") element " << tmparg->GetName() 
				 << " has external shape client link to " << sclient << " (" << sclient->GetName() << ") with ref count " << tmparg->_clientListShape.refCount(sclient) << endl ;
	   while(tmparg->_clientListShape.refCount(sclient)>0) {
	     tmparg->_clientListShape.Remove(sclient) ;
	     extShapeClients[tmparg].push_back(sclient) ;
	   }
	 }
       }
       delete sclientIter ;

     }
     delete iter ;

     R__b.WriteClassBuffer(RooWorkspace::Class(),this);

     // Reinstate clients here

     
     for (map<RooAbsArg*,list<RooAbsArg*> >::iterator iterx = extClients.begin() ; iterx!=extClients.end() ; iterx++) {
       for (list<RooAbsArg*>::iterator citer = iterx->second.begin() ; citer!=iterx->second.end() ; citer++) {
	 iterx->first->_clientList.Add(*citer) ;
       }
     }

     for (map<RooAbsArg*,list<RooAbsArg*> >::iterator iterx = extValueClients.begin() ; iterx!=extValueClients.end() ; iterx++) {
       for (list<RooAbsArg*>::iterator citer = iterx->second.begin() ; citer!=iterx->second.end() ; citer++) {
	 iterx->first->_clientListValue.Add(*citer) ;
       }
     }

     for (map<RooAbsArg*,list<RooAbsArg*> >::iterator iterx = extShapeClients.begin() ; iterx!=extShapeClients.end() ; iterx++) {
       for (list<RooAbsArg*>::iterator citer = iterx->second.begin() ; citer!=iterx->second.end() ; citer++) {
	 iterx->first->_clientListShape.Add(*citer) ;
       }
     }

   }
}




//_____________________________________________________________________________
std::string RooWorkspace::CodeRepo::listOfClassNames() const 
{
  // Return STL string with last of class names contained in the code repository

  string ret ;
  map<TString,ClassRelInfo>::const_iterator iter = _c2fmap.begin() ;
  while(iter!=_c2fmap.end()) {
    if (ret.size()>0) {
      ret += ", " ;
    }
    ret += iter->first ;    
    ++iter ;
  }  
  
  return ret ;
}



//_____________________________________________________________________________
Bool_t RooWorkspace::CodeRepo::compileClasses() 
{
  // For all classes in the workspace for which no class definition is
  // found in the ROOT class table extract source code stored in code
  // repository into temporary directory set by
  // setClassFileExportDir(), compile classes and link them with
  // current ROOT session. If a compilation error occurs print
  // instructions for user how to fix errors and recover workspace and
  // abort import procedure.

  Bool_t haveDir=kFALSE ;

  // Retrieve name of directory in which to export code files
  string dirName = Form(_classFileExportDir.c_str(),_wspace->uuid().AsString(),_wspace->GetName()) ;

  // Process all class entries in repository
  map<TString,ClassRelInfo>::iterator iter = _c2fmap.begin() ;
  while(iter!=_c2fmap.end()) {

    oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() now processing class " << iter->first.Data() << endl ;

    // If class is already known, don't load
    if (gClassTable->GetDict(iter->first.Data())) {
      oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() Embedded class " 
				      << iter->first << " already in ROOT class table, skipping" << endl ;
      ++iter ;
      continue ;
    }

    // Check that export directory exists
    if (!haveDir) {

      // If not, make local directory to extract files 
      if (!gSystem->AccessPathName(dirName.c_str())) {
	oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() reusing code export directory " << dirName.c_str() 
					<< " to extract coded embedded in workspace" << endl ;
      } else {
	if (gSystem->MakeDirectory(dirName.c_str())==0) { 
	  oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() creating code export directory " << dirName.c_str() 
					  << " to extract coded embedded in workspace" << endl ;
	} else {
	  oocoutE(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() ERROR creating code export directory " << dirName.c_str() 
					  << " to extract coded embedded in workspace" << endl ;
	  return kFALSE ;
	}
      }
      haveDir=kTRUE ;
    }

    // Navigate from class to file
    ClassFiles& cfinfo = _fmap[iter->second._fileBase] ;

    oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() now processing file with base " << iter->second._fileBase << endl ;
    
    // If file is already processed, skip to next class
    if (cfinfo._extracted) {
      oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() file with base name " << iter->second._fileBase 
					 << " has already been extracted, skipping to next class" << endl ;
      continue ;
    }

    // Check if identical declaration file (header) is already written
    Bool_t needDeclWrite=kTRUE ;
    string fdname = Form("%s/%s.%s",dirName.c_str(),iter->second._fileBase.Data(),cfinfo._hext.Data()) ;
    ifstream ifdecl(fdname.c_str()) ;
    if (ifdecl) {
      TString contents ;
      char buf[1024] ;
      while(ifdecl.getline(buf,1024)) {
	contents += buf ;
	contents += "\n" ;
      }      
      UInt_t crcFile = RooAbsArg::crc32(contents.Data()) ;
      UInt_t crcWS   = RooAbsArg::crc32(cfinfo._hfile.Data()) ;
      needDeclWrite = (crcFile!=crcWS) ;
    }

    // Write declaration file if required 
    if (needDeclWrite) {
      oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() Extracting declaration code of class " << iter->first << ", file " << fdname << endl ;
      ofstream fdecl(fdname.c_str()) ;
      if (!fdecl) {
	oocoutE(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() ERROR opening file" 
					<< fdname << " for writing" << endl ;
	return kFALSE ;
      }
      fdecl << cfinfo._hfile ;
      fdecl.close() ;
    }

    // Check if identical implementation file is already written
    Bool_t needImplWrite=kTRUE ;
    string finame = Form("%s/%s.cxx",dirName.c_str(),iter->second._fileBase.Data()) ;
    ifstream ifimpl(finame.c_str()) ;
    if (ifimpl) {
      TString contents ;
      char buf[1024] ;
      while(ifimpl.getline(buf,1024)) {
	contents += buf ;
	contents += "\n" ;
      }      
      UInt_t crcFile = RooAbsArg::crc32(contents.Data()) ;
      UInt_t crcWS   = RooAbsArg::crc32(cfinfo._cxxfile.Data()) ;
      needImplWrite = (crcFile!=crcWS) ;
    }

    // Write implementation file if required
    if (needImplWrite) {
      oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() Extracting implementation code of class " << iter->first << ", file " << finame << endl ;
      ofstream fimpl(finame.c_str()) ;
      if (!fimpl) {
	oocoutE(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() ERROR opening file" 
					<< finame << " for writing" << endl ;
	return kFALSE ;
      }
      fimpl << cfinfo._cxxfile ;
      fimpl.close() ;
    }

    // Mark this file as extracted
    cfinfo._extracted = kTRUE ;
    oocxcoutD(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() marking code unit  " << iter->second._fileBase << " as extracted" << endl ;

    // Compile class
    oocoutI(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() Compiling code unit " << iter->second._fileBase.Data() << " to define class " << iter->first << endl ;
    Bool_t ok = gSystem->CompileMacro(finame.c_str(),"k") ;
    
    if (!ok) {
      oocoutE(_wspace,ObjectHandling) << "RooWorkspace::CodeRepo::compileClasses() ERROR compiling class " << iter->first.Data() << ", to fix this you can do the following: " << endl 
				      << "  1) Fix extracted source code files in directory " << dirName.c_str() << "/" << endl 
				      << "  2) In clean ROOT session compiled fixed classes by hand using '.x " << dirName.c_str() << "/ClassName.cxx+'" << endl
				      << "  3) Reopen file with RooWorkspace with broken source code in UPDATE mode. Access RooWorkspace to force loading of class" << endl
				      << "     Broken instances in workspace will _not_ be compiled, instead precompiled fixed instances will be used." << endl
				      << "  4) Reimport fixed code in workspace using 'RooWorkspace::importClassCode(\"*\",kTRUE)' method, Write() updated workspace to file and close file" << endl
				      << "  5) Reopen file in clean ROOT session to confirm that problems are fixed" << endl ;
	return kFALSE ;
    }
    
    ++iter ;
  }

  return kTRUE ;
}



//_____________________________________________________________________________
void RooWorkspace::WSDir::InternalAppend(TObject* obj) 
{
  // Internal access to TDirectory append method

#if ROOT_VERSION_CODE <= ROOT_VERSION(5,19,02)
  TDirectory::Append(obj) ;
#else
  TDirectory::Append(obj,kFALSE) ;
#endif

}


//_____________________________________________________________________________
#if ROOT_VERSION_CODE <= ROOT_VERSION(5,19,02)
void RooWorkspace::WSDir::Add(TObject* obj) 
#else
void RooWorkspace::WSDir::Add(TObject* obj,Bool_t) 
#endif
{
  // Overload TDirectory interface method to prohibit insertion of objects in read-only directory workspace representation
  if (dynamic_cast<RooAbsArg*>(obj) || dynamic_cast<RooAbsData*>(obj)) {
    coutE(ObjectHandling) << "RooWorkspace::WSDir::Add(" << GetName() << ") ERROR: Directory is read-only representation of a RooWorkspace, use RooWorkspace::import() to add objects" << endl ;
  } else {
    InternalAppend(obj) ;
  }
} 


//_____________________________________________________________________________
#if ROOT_VERSION_CODE <= ROOT_VERSION(5,19,02)
void RooWorkspace::WSDir::Append(TObject* obj) 
#else
void RooWorkspace::WSDir::Append(TObject* obj,Bool_t) 
#endif
{
  // Overload TDirectory interface method to prohibit insertion of objects in read-only directory workspace representation
  if (dynamic_cast<RooAbsArg*>(obj) || dynamic_cast<RooAbsData*>(obj)) {
    coutE(ObjectHandling) << "RooWorkspace::WSDir::Add(" << GetName() << ") ERROR: Directory is read-only representation of a RooWorkspace, use RooWorkspace::import() to add objects" << endl ;
  } else {
    InternalAppend(obj) ;
  }
}



//_____________________________________________________________________________
void RooWorkspace::exportToCint(const char* nsname) 
{
  // Activate export of workspace symbols to CINT in a namespace with given name. If no name
  // is given the namespace will have the same name as the workspace

  // If export is already active, do nothing
  if (_doExport) {
    coutE(ObjectHandling) << "RooWorkspace::exportToCint(" << GetName() << ") WARNING: repeated calls to exportToCint() have no effect" << endl ;
    return ;
  }


  // Set flag so that future import to workspace are automatically exported to CINT
  _doExport = kTRUE ;

  // If no name is provided choose name of workspace
  if (!nsname) nsname = GetName() ;
  _exportNSName = nsname ;

  coutI(ObjectHandling) << "RooWorkspace::exportToCint(" << GetName() 
			<< ") INFO: references to all objects in this workspace will be created in CINT in 'namespace " << _exportNSName << "'" << endl ;

  // Export present contents of workspace to CINT
  TIterator* iter = _allOwnedNodes.createIterator() ;
  TObject* wobj ;
  while((wobj=iter->Next())) {
    exportObj(wobj) ;
  }  
  delete iter ;
  iter = _dataList.MakeIterator() ;
  while((wobj=iter->Next())) {
    exportObj(wobj) ;
  }  
  delete iter ;
}


//_____________________________________________________________________________
void RooWorkspace::exportObj(TObject* wobj)
{
  // Export reference to given workspace object to CINT

  // Do nothing if export flag is not set
  if (!_doExport) return ;

  // Do not export RooConstVars
  if (wobj->IsA() == RooConstVar::Class()) {
    return ;
  }


  // Determine if object name is a valid C++ identifier name

  // Do not export objects that have names that are not valid C++ identifiers
  if (!isValidCPPID(wobj->GetName())) {
    cxcoutD(ObjectHandling) << "RooWorkspace::exportObj(" << GetName() << ") INFO: Workspace object name " << wobj->GetName() << " is not a valid C++ identifier and is not exported to CINT" << endl ;
    return ;
  }

  // Declare correctly typed reference to object in CINT in the namespace associated with this workspace
  string cintExpr = Form("namespace %s { %s& %s = *(%s *)%p ; }",_exportNSName.c_str(),wobj->IsA()->GetName(),wobj->GetName(),wobj->IsA()->GetName(),wobj) ;
  gROOT->ProcessLine(cintExpr.c_str()) ;  
}



//_____________________________________________________________________________
Bool_t RooWorkspace::isValidCPPID(const char* name)   
{
  // Return true if given name is a valid C++ identifier name

  string oname(name) ;
  if (isdigit(oname[0])) {
    return kFALSE ;
  } else {
    for (UInt_t i=0 ; i<oname.size() ; i++) {
      char c = oname[i] ;
      if (!isalnum(c) && (c!='_')) {
	return kFALSE ;
      }    
    }
  }
  return kTRUE ;
}

//_____________________________________________________________________________
void RooWorkspace::unExport()
{
  // Delete exported reference in CINT namespace 
  char buf[1024] ;
  TIterator* iter = _allOwnedNodes.createIterator() ;
  TObject* wobj ;
  while((wobj=iter->Next())) {
    if (isValidCPPID(wobj->GetName())) {
      strlcpy(buf,Form("%s::%s",_exportNSName.c_str(),wobj->GetName()),1024) ;
      G__deletevariable(buf) ;
    }
  }
  delete iter ;
}

