#include "QFramework/TQObservable.h"
#include "TTreeFormula.h"
#include "QFramework/TQStringUtils.h"
#include "QFramework/TQSample.h"
#include "QFramework/TQToken.h"
#include "QFramework/TQUtils.h"
#include "TTree.h"
#include "QFramework/TQIterator.h"
#include "QFramework/TQFilterObservable.h"
#include "QFramework/TQLibrary.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
TQObservable::Manager& TQObservable::getManager(){
return TQObservable::manager;
}
TQObservable::Manager::Manager(){
this->activeSet = new TFolder("default","default observable set");
this->sets->Add(this->activeSet);
}
bool TQObservable::Manager::setActiveSet(const TString& name){
TFolder * set = dynamic_cast<TFolder*>(this->sets->FindObject(name));
if(set) {
this->activeSet = set;
return true;
} else {
return false;
}
}
void TQObservable::Manager::cloneActiveSet(const TString& newName){
this->createEmptySet(newName);
TFolder* newset = dynamic_cast<TFolder*>(this->sets->FindObject(newName));
TQObservableIterator itr(this->activeSet->GetListOfFolders());
while(itr.hasNext()){
TQObservable* obs = itr.readNext();
if(!obs) continue;
newset->Add(obs->getClone());
}
}
void TQObservable::Manager::clearSet(const TString& name){
TFolder* set = dynamic_cast<TFolder*>(this->sets->FindObject(name));
TQObservableIterator itr(set->GetListOfFolders());
while(itr.hasNext()){
TQObservable* obs = itr.readNext();
if(obs) delete obs;
}
this->sets->Remove(set);
delete set;
}
void TQObservable::Manager::createEmptySet(const TString& name){
this->sets->Add(new TFolder(name,name));
}
TCollection* TQObservable::Manager::listOfSets(){
return this->sets->GetListOfFolders();
}
TFolder* TQObservable::Manager::getActiveSet(){
return this->activeSet;
}
TQObservable::Manager TQObservable::manager = TQObservable::Manager();
bool TQObservable::gAllowErrorMessages = false;
ClassImp(TQObservable)
void TQObservable::clearAll(){
TQIterator itr(getManager().listOfSets());
while(itr.hasNext()){
TFolder* set = dynamic_cast<TFolder*>(itr.readNext());
if(!set) continue;
TQObservableIterator obsitr(set->GetListOfFolders());
while(obsitr.hasNext()){
TQObservable* obs = obsitr.readNext();
if(obs){
set->Remove(obs);
delete obs;
}
}
}
}
TQObservable::TQObservable() :
TNamed("TQObservable","TQObservable")
{
DEBUGclass("default constructor called");
}
TQObservable::TQObservable(const TString& expression) :
TQObservable()
{
DEBUGclass("constructor called with expression '%s'",expression.Data());
this->SetName(TQObservable::makeObservableName(expression));
}
void TQObservable::Manager::registerFactory(const TQObservable::FactoryBase* factory, bool putFirst){
if(putFirst){
this->observableFactories.insert(this->observableFactories.begin(),factory);
} else {
this->observableFactories.push_back(factory);
}
}
void TQObservable::Manager::clearFactories(){
this->observableFactories.clear();
}
#include <typeinfo>
void TQObservable::Manager::printFactories(){
for(size_t i=0; i<this->observableFactories.size(); ++i){
std::cout << this->observableFactories[i]->className() << std::endl;
}
}
#include "QFramework/TQMultiObservable.h"
#include "QFramework/TQMVAObservable.h"
#include "QFramework/TQConstObservable.h"
#include "QFramework/TQTreeFormulaObservable.h"
#include "QFramework/TQHistogramObservable.h"
#include "QFramework/TQVectorAuxObservable.h"
bool TQObservableFactory::setupDefault(){
TQObservable::manager.clearFactories();
TQObservable::manager.registerFactory(TQConstObservable::getFactory(),false);
TQObservable::manager.registerFactory(TQHistogramObservable<TH3>::getFactory(),false);
TQObservable::manager.registerFactory(TQHistogramObservable<TH2>::getFactory(),false);
TQObservable::manager.registerFactory(TQHistogramObservable<TH1>::getFactory(),false);
TQObservable::manager.registerFactory(TQFilterObservable::getFactory(),false);
TQObservable::manager.registerFactory(TQVectorAuxObservable::getFactory(),false);
TQObservable::manager.registerFactory(TQMultiObservable::getFactory(),false);
TQObservable::manager.registerFactory(TQMVAObservable::getFactory(),false);
TQObservable::manager.registerFactory(TQTreeFormulaObservable::getFactory(),false);
return true;
}
namespace {
const bool _TQObservableFactory__setupDefault = TQObservableFactory::setupDefault();
}
TQObservable* TQObservable::Manager::createObservable(TString expression, TQTaggable* tags){
expression = TQObservable::unreplaceBools(TQObservable::compileExpression(expression,tags,true));
TQStringUtils::removeLeadingBlanks(expression);
TQStringUtils::removeTrailingBlanks(expression);
for(size_t i=0; i<this->observableFactories.size(); ++i){
TQObservable* obs = this->observableFactories[i]->tryCreateInstance(expression);
if(obs){
TString obsname(TQObservable::makeObservableName(expression));
TQObservable* other = (TQObservable*)(this->activeSet->FindObject(obsname));
if(other){
ERRORclass("These are the tags used when attempting to create a new observable instance:");
tags->printTags();
ERRORclass("These are the already existing observables:");
TQObservable::printObservables();
ERRORclass("An observable was created via its factory from expression '%s'. However, there is already another observable with the same name in the current set of observables. The list of already existing observables has been printed above. The duplicate name is '%s'. if this happens with the latest version of QFramework pease report it to qframework-users@cern.ch and try to provide a test case to reproduce the issue!", expression.Data(), obsname.Data());
throw std::runtime_error(TString::Format("Internal Error: cannot add observable with name '%s' (generated from expression '%s') to set '%s' -- name is already in use, observable has expression '%s'!",obsname.Data(),expression.Data(),this->activeSet->GetName(),other->getExpression().Data()).Data());
} else {
DEBUG(TString::Format("adding observable '%s' to set '%s'",obsname.Data(),this->activeSet->GetName()).Data());
obs->SetName(obsname);
this->activeSet->Add(obs);
}
return obs;
}
}
return NULL;
}
bool TQObservable::addObservable(TQObservable* obs){
if(!obs) return false;
if(dynamic_cast<TQObservable*>(TQObservable::manager.activeSet->FindObject(obs->GetName()))){
WARNclass("Failed to add observable '%s': an observable with this name already present",obs->GetName());
return false;
}
TQObservable::manager.activeSet->Add(obs);
obs->fIsManaged = true;
return true;
}
bool TQObservable::addObservable(TQObservable* obs, const TString& name){
if(!obs) return false;
if(dynamic_cast<TQObservable*>(TQObservable::manager.activeSet->FindObject(name))){
WARNclass("Failed to add observable '%s': an observable with this name already present",name.Data());
return false;
}
obs->SetName(name);
TQObservable::manager.activeSet->Add(obs);
obs->fIsManaged = true;
return true;
}
void TQObservable::printObservables(const TString& filter){
int width = TQLibrary::getConsoleWidth();
TQIterator itr(TQObservable::manager.listOfSets());
while(itr.hasNext()){
TFolder* f = dynamic_cast<TFolder*>(itr.readNext());
TQObservableIterator obsitr(f->GetListOfFolders());
std::cout << TQStringUtils::makeBoldWhite(TQStringUtils::fixedWidth(f->GetName(),.2*width,"l")) << " " << TQStringUtils::makeBoldWhite(TQStringUtils::fixedWidth("name",.3*width,"l")) << " " << TQStringUtils::makeBoldWhite("expression") << std::endl;
std::cout << TQStringUtils::makeBoldWhite(TQStringUtils::repeat("=",width)) << std::endl;
while(obsitr.hasNext()){
TQObservable* obs = obsitr.readNext();
if(!obs) continue;
if(!TQStringUtils::matches(obs->GetName(),filter)) continue;
std::cout << TQStringUtils::makeBoldBlue(TQStringUtils::fixedWidth(obs->IsA()->GetName(),.2*width,"l"));
std::cout << " ";
std::cout << TQStringUtils::makeBoldWhite(TQStringUtils::fixedWidth(obs->GetName(),.3*width,"l"));
std::cout << " ";
std::cout << TQStringUtils::fixedWidth(obs->isInitialized() ? TQStringUtils::makeBoldRed(obs->getActiveExpression()) : obs->getExpression(),.5*width,"l.");
std::cout << std::endl;
}
}
}
bool TQObservable::matchExpressions(const TString& ex1, const TString& ex2, bool requirePrefix){
DEBUGclass("trying to match expressions '%s' and '%s'. Prefix matching: %s",ex1.Data(),ex2.Data(),requirePrefix?"required":"ignored");
if(ex1.Length() == ex2.Length()) return TQStringUtils::equal(ex1,ex2);
if (requirePrefix) {
DEBUGclass("prefix is required but lengths don't match. Expressions do not match.");
return false;
}
if(ex1.Length() > ex2.Length()){
int first = ex1.First(":");
DEBUGclass("comparing: '%s' vs '%s'",ex1(first+1,ex1.Length()-first-1).Data(),ex2.Data());
return (first < .5*ex1.Length()) && TQStringUtils::isValidIdentifier(ex1(0,first),TQStringUtils::alphanum) && TQStringUtils::equal(ex1(first+1,ex1.Length()-first-1),ex2);
}
if(ex1.Length() < ex2.Length()){
int first = ex2.First(":");
DEBUGclass("comparing: '%s' vs '%s'",ex2(first+1,ex2.Length()-first-1).Data(),ex1.Data());
return (first < .5*ex2.Length()) && TQStringUtils::isValidIdentifier(ex2(0,first),TQStringUtils::alphanum) && TQStringUtils::equal(ex2(first+1,ex2.Length()-first-1),ex1);
}
DEBUGclass("finished matching, no attempt succeeded");
return false;
}
TQObservable* TQObservable::getObservable(const TString& origexpr, TQTaggable* tags){
DEBUGclass("trying to obtain observable with origexpr '%s'",origexpr.Data());
TString incarnation = TQObservable::unreplaceBools(TQObservable::compileExpression(origexpr,tags,true));
if(incarnation.Contains("$")){
tags->printTags();
ERRORclass("there seem to be unresolved aliases/tags in expression '%s', available aliases are listed above.",incarnation.Data());
return NULL;
}
TString expression(origexpr);
expression.ReplaceAll("\\","");
TString obsname(TQObservable::makeObservableName(expression));
TString incname(TQObservable::makeObservableName(incarnation));
DEBUGclass("attempting to retrieve observable '%s' (raw name)/'%s' (compiled name) with expression '%s'",obsname.Data(),incname.Data(),incarnation.Data());
if(obsname.IsNull()) {ERRORclass("Failed to retrieve observable: Observable name is empty! Original expression was '%s'",expression.Data()); return NULL;}
TQObservableIterator itr(TQObservable::manager.activeSet->GetListOfFolders());
while(itr.hasNext()){
TQObservable* obs = itr.readNext();
if(!obs) continue;
DEBUGclass("next object is: '%s' ",obs->GetName());
if(TQObservable::matchExpressions(obs->GetName(),incname, obs->isPrefixRequired() )){
DEBUGclass("found perfect match '%s' for '%s'",obs->GetName(),incname.Data());
return obs;
}
DEBUGclass("comparing '%s' vs. '%s' with expression=%d, initialized=%d",obs->getActiveExpression().Data(),incarnation.Data(),obs->hasExpression(),obs->isInitialized());
if(obs->hasExpression()){
if(obs->isInitialized() && TQObservable::matchExpressions(TQObservable::unreplaceBools(obs->getActiveExpression()),incarnation,obs->isPrefixRequired() )){
DEBUGclass("found pre-initialized match '%s' for '%s'",obs->getActiveExpression().Data(),incarnation.Data());
return obs;
}
if(!obs->isInitialized() && TQObservable::matchExpressions(TQObservable::unreplaceBools(obs->getExpression()),expression,obs->isPrefixRequired() )){
DEBUGclass("found free match '%s' for '%s'",obs->getExpression().Data(),expression.Data());
return obs;
}
}
}
itr.reset();
TQObservable* match = NULL;
size_t rating = -1;
while(itr.hasNext()){
TQObservable* obs = itr.readNext();
if(!obs) continue;
DEBUGclass("comparing observable's compiled expression to incarnation: '%s' vs. '%s'",TQObservable::unreplaceBools(obs->getCompiledExpression(tags)).Data(),incarnation.Data());
if(TQObservable::matchExpressions(TQObservable::unreplaceBools(obs->getCompiledExpression(tags)),incarnation,obs->isPrefixRequired())){
size_t obsrating = TQStringUtils::countText(obs->getExpression(),"$");
DEBUGclass("found general match '%s' for '%s' with rating %d (expression is '%s')",obs->GetName(),incname.Data(),(int)(obsrating),obs->getExpression().Data());
if(!match || obsrating < rating){
match = obs;
rating = obsrating;
}
}
}
if(match){
if(match->isInitialized()){
if(!(match->hasFactory())){
TQObservable* clone = match->getClone();
if(!clone){
throw std::runtime_error(TString::Format("tried to retrieve observable '%s' with expression '%s' in incarnation '%s' and found a near match '%s' that can't be cloned...",obsname.Data(),expression.Data(),incarnation.Data(),match->getActiveExpression().Data()).Data());
}
clone->SetName(incname);
clone->fIsManaged = true;
clone->finalize();
TQObservable::manager.activeSet->Add(clone);
DEBUGclass("cloned '%s' for '%s'",match->GetName(),clone->GetName());
return clone;
}
DEBUGclass("existing observable '%s' is initialized but factory exists",match->GetName());
} else {
DEBUGclass("returning unused match with name '%s' and compiled expression '%s'",match->GetName(),match->getCompiledExpression(NULL).Data());
return match;
}
}
DEBUGclass("no match found for '%s' in set '%s' -- creating new observable from expression '%s' (incarnation is '%s')",obsname.Data(),TQObservable::manager.activeSet->GetName(),expression.Data(),incarnation.Data());
TQObservable* retObs = TQObservable::manager.createObservable(origexpr, tags);
if (!retObs) {
tags->printTags();
ERRORclass("Failed to get observable from expression '%s'. Tags/aliases used have been printed above.",origexpr.Data());
ERRORclass("The following steps have been attempted:");
ERRORclass("1) Trying to find a) existing observable with name matching '%s' or",incname.Data());
ERRORclass(" b) already existing and initialized observable with activeExpression matching '%s' or",incarnation.Data());
ERRORclass(" c) already existing and UNinitialized observable expression matching '%s' or",expression.Data());
ERRORclass("2) Trying to find existing observable instances which would yield a compiledExpression matching '%s' if initialized with the provided tags (tags see above). either such an observable was not found or the best match has a factory (in which case we do not clone an existing instance but ask the factory to create a new instance.",incarnation.Data());
ERRORclass("3) No factory managed to produce a working observable instance");
}
return retObs;
}
bool TQObservable::hasFactory() const {
return false;
}
TString TQObservable::makeObservableName(const TString& name){
TString tmpname(name);
tmpname.ReplaceAll("!","NOT");
tmpname.ReplaceAll("&&","AND");
tmpname.ReplaceAll("||","OR");
tmpname.ReplaceAll("[","_");
tmpname.ReplaceAll("]","_");
tmpname.ReplaceAll("(","_");
tmpname.ReplaceAll(")","_");
tmpname.ReplaceAll("/",":");
return TQStringUtils::makeValidIdentifier(tmpname,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._*+-><=:/");
}
bool TQObservable::initialize(TQSample * sample){
if(this->isInitialized()) return true;
if (fSample){
if(fSample == sample) return true;
else throw std::runtime_error(TString::Format("observable '%s' already initialized to sample '%s'",this->GetName(),fSample->getPath().Data()).Data());
}
if(!sample) return false;
fSample = sample;
if(!this->initializeSelf()){
this->fIsInitialized = false;
this->fSample = NULL;
throw std::runtime_error(TString::Format("unable to initialize observable '%s'",this->GetName()).Data());
}
this->fIsInitialized = true;
return true;
}
bool TQObservable::isInitialized() const {
return this->fIsInitialized;
}
bool TQObservable::finalize() {
DEBUGclass("finalizing '%s'",this->GetName());
if(!this->isInitialized()){
return true;
}
if (!this->fSample){
ERRORclass("no sample assigned to this observable: '%s'",this->GetName());
return false;
}
this->fIsInitialized = !(this->finalizeSelf());
if(this->fIsInitialized){
ERRORclass("unable to finalize observable '%s'",this->GetName());
}
this->fSample = 0;
return (!this->fIsInitialized);
}
TQObservable::~TQObservable() {
if(this->fIsManaged){
TQObservable::manager.activeSet->Remove(this);
}
}
void TQObservable::print() const {
std::cout << TQStringUtils::makeBoldYellow(this->getExpression()) << std::endl;
}
TString TQObservable::getCompiledExpression(TQTaggable* tags) const {
return this->compileExpression(getExpression(),tags);
}
TString TQObservable::replaceBools(TString expression){
expression = TQStringUtils::replaceEnclosed(expression,"true", "(1==1)"," /*+-?!|&\n\t ()[]{}=:");
expression = TQStringUtils::replaceEnclosed(expression,"false","(1==0)"," /*+-?!|&\n\t ()[]{}=:");
return expression;
}
TString TQObservable::unreplaceBools(TString expression){
expression.ReplaceAll("(1==1)","true");
expression.ReplaceAll("(1==0)","false");
return expression;
}
TString TQObservable::compileExpression(const TString& input, TQTaggable* tags, bool replaceBools) {
if(input.IsNull()) return input;
TString expression = tags ? tags->replaceInTextRecursive(input,"~",false) : input;
expression.ReplaceAll("'","\"");
if(replaceBools){
expression = TQObservable::replaceBools(expression);
}
TString retval("");
while (!expression.IsNull()) {
TQStringUtils::readUpTo(expression,retval,"{");
if(expression.Length() < 1) return TQStringUtils::minimize(retval);
size_t endpos = TQStringUtils::findParenthesisMatch(expression,0,"{","}");
if(endpos > (size_t)expression.Length()){
ERRORclass("parenthesis mismatch in expression '%s'",expression.Data());
return "";
}
size_t posIf = TQStringUtils::find(expression,"?");
if (posIf < (size_t)expression.Length()){
TString ifExpr(expression(1, posIf-1));
if(TQStringUtils::hasUnquotedStrings(ifExpr)){
retval.Append("(");
TString subExpr(expression(1,endpos-1));
retval.Append(compileExpression(subExpr,NULL,replaceBools));
retval.Append(")");
} else {
TFormula formula("if", ifExpr);
TString thenExpr, elseExpr;
size_t posElse = posIf;
while(posElse == posIf || expression[posElse-1] == '\\'){
posElse = TQStringUtils::findFree(expression,":","{}()[]",posElse+1);
}
if (posElse < (size_t)expression.Length()) {
thenExpr = expression(posIf+1, posElse-posIf-1);
elseExpr = expression(posElse+1, endpos-posElse-1);
} else {
thenExpr = expression(posIf+1,endpos-posIf-1);
elseExpr = "";
}
if (formula.Eval(0.)){
retval.Append(compileExpression(thenExpr,NULL,replaceBools));
} else {
retval.Append(compileExpression(elseExpr,NULL,replaceBools));
}
}
} else {
retval.Append("(");
TString subExpr(expression(1,endpos-1));
retval.Append(compileExpression(subExpr,NULL,replaceBools));
retval.Append(")");
}
expression.Remove(0,endpos+1);
TQStringUtils::removeLeadingBlanks(expression);
}
if(retval.Index("$") != kNPOS){
size_t start = retval.Index("$");
size_t stop;
if(retval[start+1] == '('){
stop = TQStringUtils::findParenthesisMatch(retval,start+1,"(",")")+1;
} else {
stop = TQStringUtils::findFirstNotOf(retval,TQStringUtils::defaultIDchars,start+1);
}
TString var(retval(start,stop-start));
TString err = TString::Format("unresolved variable '%s' in expression '%s'!",var.Data(),retval.Data());
if(tags){
ERRORclass(err+" Available tags are listed above.");
tags->printTags("l");
}
throw std::runtime_error(err.Data());
}
retval.ReplaceAll("\\","");
return TQStringUtils::minimize(retval);
}
TString TQObservable::getName() const {
return this->fName;
}
void TQObservable::setName(const TString& newName) {
this->fName = newName;
}
const TString& TQObservable::getNameConst() const {
return this->fName;
}
bool TQObservable::isSetup() const {
return true;
}
void TQObservable::allowErrorMessages(bool val){
TQObservable::gAllowErrorMessages = val;
}
const TString& TQObservable::getExpression() const {
return this->fName;
}
bool TQObservable::hasExpression() const {
DEBUGclass("doesn't know expressions");
return false;
}
TQObservable* TQObservable::getClone() const {
TQObservable* obs = NULL;
if(this->hasExpression()){
obs = this->tryCreateInstanceVirtual(this->getExpression());
}
if(!obs){
obs = (TQObservable*)(this->Clone());
}
if(obs->isInitialized()) obs->finalize();
return obs;
}
void TQObservable::setExpression(const TString&){
ERRORclass("this function is not implemented. did you forget to implement 'virtual void YOURCLASS::setExpression(const TString)'?");
}
TQObservable* TQObservable::tryCreateInstanceVirtual (const TString&) const {
return NULL;
}
TString TQObservable::getActiveExpression() const {
return this->getExpression();
}
double TQObservable::getValueAt(int index) const {
if (index!=0) {throw std::runtime_error("Attempt to retrieve value from illegal index in observable."); return -999.;} else return this->getValue();
}