#include <string>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <vector>
#include "TROOT.h"
#include "TSystem.h"
#include "TString.h"
#include "TObjString.h"
#include "TQObject.h"
#include "TSpline.h"
#include "TMatrix.h"
#include "TMath.h"
#include "TFile.h"
#include "TKey.h"
#include "TXMLEngine.h"
#include "TMVA/Configurable.h"
#include "TMVA/Config.h"
#include "TMVA/Tools.h"
ClassImp(TMVA::Configurable)
#ifdef _WIN32
#pragma warning ( disable : 4355 )
#endif
TMVA::Configurable::Configurable( const TString& theOption)
: fOptions ( theOption ),
fLooseOptionCheckingEnabled ( kTRUE ),
fLastDeclaredOption ( 0 ),
fConfigName ( "Configurable" ),
fConfigDescription ( "No description" ),
fReferenceFile ( "None" ),
fLogger ( new MsgLogger(this) )
{
fListOfOptions.SetOwner();
if (gTools().CheckForVerboseOption( theOption )) Log().SetMinType( kVERBOSE );
}
TMVA::Configurable::~Configurable()
{
delete fLogger;
}
void TMVA::Configurable::SplitOptions(const TString& theOpt, TList& loo) const
{
TString splitOpt(theOpt);
loo.SetOwner();
while (splitOpt.Length()>0) {
if (!splitOpt.Contains(':')) {
loo.Add(new TObjString(splitOpt));
splitOpt = "";
}
else {
TString toSave = splitOpt(0,splitOpt.First(':'));
loo.Add(new TObjString(toSave.Data()));
splitOpt = splitOpt(splitOpt.First(':')+1,splitOpt.Length());
}
}
}
void TMVA::Configurable::ResetSetFlag()
{
TListIter decOptIt(&fListOfOptions);
while (OptionBase* decOpt = (OptionBase*) decOptIt()) {
decOpt->fIsSet = kFALSE;
}
}
void TMVA::Configurable::ParseOptions()
{
Log() << kVERBOSE << "Parsing option string: " << Endl;
TString optionsWithoutTilde(fOptions);
optionsWithoutTilde.ReplaceAll(TString("~"),TString(""));
Log() << kVERBOSE << "... \"" << optionsWithoutTilde << "\"" << Endl;
TList loo;
fOptions = fOptions.Strip(TString::kLeading, ':');
SplitOptions(fOptions, loo);
fOptions = "";
std::map<TString, std::vector<std::pair<Int_t, TString> > > arrayTypeOptions;
TListIter decOptIt(&fListOfOptions);
TListIter setOptIt(&loo);
while (TObjString * os = (TObjString*) setOptIt()) {
TString s = os->GetString();
Bool_t preserveTilde = s.BeginsWith('~');
s = s.Strip(TString::kLeading, '~');
Bool_t paramParsed = kFALSE;
if (s.Contains('=')) {
TString optname = s(0,s.First('=')); optname.ToLower();
TString optval = s(s.First('=')+1,s.Length());
Int_t idx = -1;
OptionBase* decOpt = (OptionBase *)fListOfOptions.FindObject(optname);
if (decOpt==0 && optname.Contains('[')) {
TString st = optname(optname.First('[')+1,100);
st.Remove(st.First(']'));
std::stringstream str(st.Data());
str >> idx;
optname.Remove(optname.First('['));
decOpt = (OptionBase *)fListOfOptions.FindObject(optname);
}
TListIter optIt(&fListOfOptions);
if (decOpt!=0) {
if (decOpt->IsSet())
Log() << kWARNING << "Value for option " << decOpt->GetName()
<< " was previously set to " << decOpt->GetValue() << Endl;
if (!decOpt->HasPreDefinedVal() || (decOpt->HasPreDefinedVal() && decOpt->IsPreDefinedVal(optval)) ) {
if (decOpt->IsArrayOpt()) {
if (idx==-1) {
decOpt->SetValue(optval);
}
else {
if (!decOpt->SetValue(optval, idx))
Log() << kFATAL << "Index " << idx << " too large for option " << decOpt->TheName()
<< ", allowed range is [0," << decOpt->GetArraySize()-1 << "]" << Endl;
}
}
else {
if (idx!=-1)
Log() << kFATAL << "Option " << decOpt->TheName()
<< " is not an array, but you specified an index" << Endl;
decOpt->SetValue(optval);
}
paramParsed = kTRUE;
}
else Log() << kFATAL << "Option " << decOpt->TheName()
<< " does not have predefined value: \"" << optval << "\"" << Endl;
}
}
Bool_t preserveNotSign = kFALSE;
if (!paramParsed) {
Bool_t hasNotSign = kFALSE;
if (s.BeginsWith("!")) { s.Remove(0,1); preserveNotSign = hasNotSign = kTRUE; }
TString optname(s); optname.ToLower();
OptionBase* decOpt = 0;
Bool_t optionExists = kFALSE;
TListIter optIt(&fListOfOptions);
while ((decOpt = (OptionBase*)optIt()) !=0) {
TString predOptName(decOpt->GetName());
predOptName.ToLower();
if (predOptName == optname) optionExists = kTRUE;
if (dynamic_cast<Option<bool>*>(decOpt)==0) continue;
if (predOptName == optname) break;
}
if (decOpt != 0) {
decOpt->SetValue( hasNotSign ? "0" : "1" );
paramParsed = kTRUE;
}
else {
if (optionExists && hasNotSign) {
Log() << kFATAL << "Negating a non-boolean variable " << optname
<< ", please check the opions for method: " << GetName() << Endl;
}
}
}
if (!paramParsed && LooseOptionCheckingEnabled()) {
decOptIt.Reset();
while (OptionBase* decOpt = (OptionBase*) decOptIt()) {
if (decOpt->HasPreDefinedVal() && decOpt->IsPreDefinedVal(s) ) {
paramParsed = decOpt->SetValue(s);
break;
}
}
}
if (fOptions!="") fOptions += ":";
if (paramParsed || preserveTilde) fOptions += '~';
if (preserveNotSign) fOptions += '!';
fOptions += s;
}
PrintOptions();
if (gConfig().WriteOptionsReference()) WriteOptionsReferenceToFile();
}
void TMVA::Configurable::CheckForUnusedOptions() const
{
TString theOpt(fOptions);
theOpt = theOpt.Strip(TString::kLeading, ':');
TList loo;
SplitOptions(theOpt, loo);
TListIter setOptIt(&loo);
TString unusedOptions("");
while (TObjString * os = (TObjString*) setOptIt()) {
TString s = os->GetString();
if (!s.BeginsWith('~')) {
if (unusedOptions != "") unusedOptions += ':';
unusedOptions += s;
}
}
if (unusedOptions != "") {
Log() << kFATAL
<< "The following options were specified, but could not be interpreted: \'"
<< unusedOptions << "\', please check!" << Endl;
}
}
void TMVA::Configurable::PrintOptions() const
{
Log() << kVERBOSE << "The following options are set:" << Endl;
TListIter optIt( &fListOfOptions );
Log() << kVERBOSE << "- By User:" << Endl;
Bool_t found = kFALSE;
while (OptionBase* opt = (OptionBase *) optIt()) {
if (opt->IsSet()) { Log() << kVERBOSE << " "; opt->Print(Log()); Log() << Endl; found = kTRUE; }
}
if (!found) Log() << kVERBOSE << " <none>" << Endl;
optIt.Reset();
Log() << kVERBOSE << "- Default:" << Endl;
found = kFALSE;
while (OptionBase* opt = (OptionBase *) optIt()) {
if (!opt->IsSet()) { Log() << kVERBOSE << " "; opt->Print(Log()); Log() << Endl; found = kTRUE; }
}
if (!found) Log() << kVERBOSE << " <none>" << Endl;
}
void TMVA::Configurable::WriteOptionsToStream( ostream& o, const TString& prefix ) const
{
TListIter optIt( &fListOfOptions );
o << prefix << "# Set by User:" << std::endl;
while (OptionBase * opt = (OptionBase *) optIt())
if (opt->IsSet()) { o << prefix; opt->Print(o); o << std::endl; }
optIt.Reset();
o << prefix << "# Default:" << std::endl;
while (OptionBase * opt = (OptionBase *) optIt())
if (!opt->IsSet()) { o << prefix; opt->Print(o); o << std::endl; }
o << prefix << "##" << std::endl;
}
void TMVA::Configurable::AddOptionsXMLTo( void* parent ) const
{
if (!parent) return;
void* opts = gTools().xmlengine().NewChild(parent, 0, "Options");
TListIter optIt( &fListOfOptions );
while (OptionBase * opt = (OptionBase *) optIt()) {
void* optnode = 0;
if (opt->IsArrayOpt()) {
std::stringstream s("");
s.precision( 16 );
for(Int_t i=0; i<opt->GetArraySize(); i++) {
if(i>0) s << " ";
s << std::scientific << opt->GetValue(i);
}
optnode = gTools().xmlengine().NewChild(opts,0,"Option",s.str().c_str());
}
else {
optnode = gTools().xmlengine().NewChild(opts,0,"Option", opt->GetValue());
}
gTools().AddAttr(optnode, "name", opt->TheName());
if (opt->IsArrayOpt()) {
gTools().AddAttr(optnode, "size", opt->GetArraySize());
}
gTools().AddAttr(optnode, "modified", (opt->IsSet()?"Yes":"No") );
}
}
void TMVA::Configurable::ReadOptionsFromXML( void* node )
{
void* opt = gTools().xmlengine().GetChild(node);
TString optName, optValue;
fOptions="";
while (opt != 0) {
if (fOptions.Length()!=0) fOptions += ":";
gTools().ReadAttr(opt, "name", optName);
optValue = TString( gTools().xmlengine().GetNodeContent(opt) );
std::stringstream s("");
s.precision( 16 );
if (gTools().xmlengine().HasAttr(opt, "size")) {
UInt_t size;
gTools().ReadAttr(opt, "size", size);
std::vector<TString> values = gTools().SplitString(optValue, ' ');
for(UInt_t i=0; i<size; i++) {
if(i!=0) s << ":";
s << std::scientific << optName << "[" << i << "]=" << values[i];
}
}
else {
s << std::scientific << optName << "=" << optValue;
}
fOptions += s.str().c_str();
opt = gTools().xmlengine().GetNext(opt);
}
}
void TMVA::Configurable::WriteOptionsReferenceToFile()
{
TString dir = gConfig().GetIONames().fOptionsReferenceFileDir;
gSystem->MakeDirectory( dir );
fReferenceFile = dir + "/" + GetConfigName() + "_optionsRef.txt";
std::ofstream o( fReferenceFile );
if (!o.good()) {
Log() << kFATAL << "<WriteOptionsToInfoFile> Unable to open output file: " << fReferenceFile << Endl;
}
TListIter optIt( &fListOfOptions );
o << "# List of options:" << std::endl;
o << "# Configurable: " << GetConfigName() << std::endl;
o << "# Description: " << GetConfigDescription() << std::endl;
while (OptionBase * opt = (OptionBase *) optIt()) {
opt->Print( o, 1 );
o << std::endl << "# ------------------------------------------------" << std::endl;
}
o.close();
Log() << kVERBOSE << "Wrote options reference file: \"" << fReferenceFile << "\"" << Endl;
}
void TMVA::Configurable::ReadOptionsFromStream(istream& istr)
{
ResetSetFlag();
fOptions = "";
char buf[512];
istr.getline(buf,512);
TString stropt, strval;
while (istr.good() && !istr.eof() && !(buf[0]=='#' && buf[1]=='#')) {
char *p = buf;
while (*p==' ' || *p=='\t') p++;
if (*p=='#' || *p=='\0') {
istr.getline(buf,512);
continue;
}
std::stringstream sstr(buf);
sstr >> stropt >> strval;
stropt.ReplaceAll(':','=');
strval.ReplaceAll("\"","");
if (fOptions.Length()!=0) fOptions += ":";
fOptions += stropt;
fOptions += strval;
istr.getline(buf,512);
}
}