From 0ec8f0b4ba05728b5c992bf8ec1731d5202fcc7c Mon Sep 17 00:00:00 2001 From: randomdan Date: Tue, 12 Feb 2013 00:36:09 +0000 Subject: Basic eventing system Component registration by script and name Fully comparable variable types Tidy up around tests IsDistinct and IsUniq row filters --- project2/common/if.cpp | 71 ----------------------------- project2/common/if.h | 9 ---- project2/common/rowProcessor.cpp | 2 + project2/common/rowProcessor.h | 3 +- project2/common/scripts.h | 3 ++ project2/common/sourceObject.cpp | 30 ++++++++++++- project2/common/sourceObject.h | 15 +++++++ project2/common/test.cpp | 5 +++ project2/common/test.h | 1 + project2/common/tests/compoundTest.cpp | 79 +++++++++++++++++++++++++++++++++ project2/common/tests/compoundTest.h | 16 +++++++ project2/common/tests/isdistinct.cpp | 40 +++++++++++++++++ project2/common/tests/isuniq.cpp | 48 ++++++++++++++++++++ project2/common/variables-modlookup.cpp | 1 + project2/common/variables.cpp | 73 +++++++++++++++++++++++++----- project2/common/variables.h | 14 +++++- 16 files changed, 315 insertions(+), 95 deletions(-) create mode 100644 project2/common/tests/compoundTest.cpp create mode 100644 project2/common/tests/compoundTest.h create mode 100644 project2/common/tests/isdistinct.cpp create mode 100644 project2/common/tests/isuniq.cpp diff --git a/project2/common/if.cpp b/project2/common/if.cpp index b9db1db..c7f790e 100644 --- a/project2/common/if.cpp +++ b/project2/common/if.cpp @@ -9,13 +9,6 @@ DECLARE_LOADER("if", If); StaticMessageException(NoTestsToPerform, "No tests to perform"); -CompoundTest::CompoundTest(ScriptNodePtr s) : - SourceObject(s), - Test(s) -{ - s->script->loader.addLoadTarget(s, Storer::into(&tests)); -} - If::If(ScriptNodePtr e) : SourceObject(e), IHaveSubTasks(e), @@ -55,67 +48,3 @@ If::execute() const } } - -class All : public CompoundTest { - public: - All(ScriptNodePtr s) : - SourceObject(s), - CompoundTest(s) { - } - bool passes() const { - if (tests.empty()) { - throw NoTestsToPerform(); - } - return (std::find_if(tests.begin(), tests.end(), !boost::bind(&Test::passes, _1)) == tests.end()); - } -}; -DECLARE_LOADER("all", All); - -class Any : public CompoundTest { - public: - Any(ScriptNodePtr s) : - SourceObject(s), - CompoundTest(s) { - } - bool passes() const { - if (tests.empty()) { - throw NoTestsToPerform(); - } - return (std::find_if(tests.begin(), tests.end(), boost::bind(&Test::passes, _1)) != tests.end()); - } -}; -DECLARE_LOADER("any", Any); - -class None : public CompoundTest { - public: - None(ScriptNodePtr s) : - SourceObject(s), - CompoundTest(s) { - } - bool passes() const { - if (tests.empty()) { - throw NoTestsToPerform(); - } - return (std::find_if(tests.begin(), tests.end(), boost::bind(&Test::passes, _1)) == tests.end()); - } -}; -DECLARE_LOADER("none", None); - -class Not : public Test { - public: - Not(ScriptNodePtr s) : - SourceObject(s), - Test(s) - { - s->script->loader.addLoadTarget(s, Storer::into(&test)); - } - bool passes() const { - if (!test) { - throw NoTestsToPerform(); - } - return !test->passes(); - } - private: - TestPtr test; -}; -DECLARE_LOADER("not", Not); diff --git a/project2/common/if.h b/project2/common/if.h index bcf83cd..2496dc4 100644 --- a/project2/common/if.h +++ b/project2/common/if.h @@ -5,15 +5,6 @@ #include "view.h" #include "test.h" -class CompoundTest : public Test { - public: - CompoundTest(ScriptNodePtr); - - protected: - typedef ANONORDEREDSTORAGEOF(Test) Tests; - Tests tests; -}; - /// Project2 component to conditionally execute its children class If : public IHaveSubTasks, public View { public: diff --git a/project2/common/rowProcessor.cpp b/project2/common/rowProcessor.cpp index 541ff11..f83dfa7 100644 --- a/project2/common/rowProcessor.cpp +++ b/project2/common/rowProcessor.cpp @@ -7,6 +7,7 @@ #include RowProcessor::RowProcessor(ScriptNodePtr p) : + SourceObject(p), IHaveParameters(p), recordSource(p->value("source").as()), filter(p->value("filter", "").as()), @@ -42,6 +43,7 @@ RowProcessor::execute() const } } Logger()->messagebf(LOG_DEBUG, "Executing from source '%s'", source->name); + ScopeObject onComplete(boost::bind(&SourceObject::send, this, Complete)); if (IRSE) { try { source->execute(filter, this); diff --git a/project2/common/rowProcessor.h b/project2/common/rowProcessor.h index cbbf6c3..a7e8544 100644 --- a/project2/common/rowProcessor.h +++ b/project2/common/rowProcessor.h @@ -12,8 +12,9 @@ class Presenter; /// Base class for Project2 components that work with row sets -class RowProcessor : public IHaveParameters { +class RowProcessor : public IHaveParameters, public virtual SourceObject { public: + enum EventIDs { Complete }; RowProcessor(ScriptNodePtr); void loadComplete(const CommonObjects *); diff --git a/project2/common/scripts.h b/project2/common/scripts.h index 0f1a1a6..ce2cf0e 100644 --- a/project2/common/scripts.h +++ b/project2/common/scripts.h @@ -55,6 +55,9 @@ class ScriptReader : public virtual IntrusivePtrBase { virtual bool isCurrent() const = 0; virtual time_t modifiedTime() const = 0; mutable LoaderBase loader; + + friend class SourceObject; + mutable std::map namedComponents; }; /// Base class to implement script reader modules diff --git a/project2/common/sourceObject.cpp b/project2/common/sourceObject.cpp index ef909b9..6f5bcc5 100644 --- a/project2/common/sourceObject.cpp +++ b/project2/common/sourceObject.cpp @@ -1,18 +1,24 @@ #include #include "sourceObject.h" +#include "exceptions.h" +#include "safeMapFind.h" unsigned int SourceObject::loadOrder = 1; +SimpleMessageException(ComponentNotFound); SourceObject::SourceObject(ScriptNodePtr p) : name(p ? p->value("name", "anon").as() : "anon"), - order(loadOrder++) + order(loadOrder++), + script(p->script.get()) { LoaderBase::loadedObjects.insert(this); + script->namedComponents[name] = this; } SourceObject::SourceObject(const std::string & n) : name(n), - order(loadOrder++) + order(loadOrder++), + script(NULL) { LoaderBase::loadedObjects.insert(this); } @@ -27,3 +33,23 @@ SourceObject::loadComplete(const CommonObjects *) { } +void +SourceObject::send(int eventID) const +{ + for (Events::const_iterator i = events.lower_bound(eventID); i != events.upper_bound(eventID); i++) { + i->second(); + } +} + +void +SourceObject::registerFor(int eventID, const Event & event) const +{ + events.insert(Events::value_type(eventID, event)); +} + +SourceObject * +SourceObject::findComponent(const std::string & name) const +{ + return safeMapLookup(script->namedComponents, name); +} + diff --git a/project2/common/sourceObject.h b/project2/common/sourceObject.h index f8963ea..8529100 100644 --- a/project2/common/sourceObject.h +++ b/project2/common/sourceObject.h @@ -3,6 +3,7 @@ #include #include +#include #include "intrusivePtrBase.h" #include "scriptLoader.h" #include "scripts_fwd.h" @@ -15,16 +16,30 @@ class CommonObjects; /// Base class for all Project2 components that can be placed in a Project2 script class SourceObject : public virtual IntrusivePtrBase { public: + typedef int EventID; + typedef boost::function Event; + typedef std::multimap Events; + SourceObject(ScriptNodePtr p); SourceObject(const std::string & name); virtual ~SourceObject() = 0; virtual void loadComplete(const CommonObjects *); + SourceObject * findComponent(const std::string & name) const; + + void registerFor(EventID eventID, const Event & event) const; + void send(EventID eventID) const; + const std::string name; const unsigned int order; + + private: static unsigned int loadOrder; + + mutable Events events; + const ScriptReader * const script; }; #endif diff --git a/project2/common/test.cpp b/project2/common/test.cpp index f4066af..427848a 100644 --- a/project2/common/test.cpp +++ b/project2/common/test.cpp @@ -5,3 +5,8 @@ Test::Test(ScriptNodePtr s) : { } +void +Test::reset() const +{ +} + diff --git a/project2/common/test.h b/project2/common/test.h index aa2709c..4f87c10 100644 --- a/project2/common/test.h +++ b/project2/common/test.h @@ -8,6 +8,7 @@ class Test : public virtual SourceObject { public: Test(ScriptNodePtr); virtual bool passes() const = 0; + virtual void reset() const; }; typedef boost::intrusive_ptr TestPtr; diff --git a/project2/common/tests/compoundTest.cpp b/project2/common/tests/compoundTest.cpp new file mode 100644 index 0000000..c3babf7 --- /dev/null +++ b/project2/common/tests/compoundTest.cpp @@ -0,0 +1,79 @@ +#include +#include "compoundTest.h" +#include "../scriptLoader.h" +#include +#include +#include + +StaticMessageException(NoTestsToPerform, "No tests to perform"); + +CompoundTest::CompoundTest(ScriptNodePtr s) : + SourceObject(s), + Test(s) +{ + s->script->loader.addLoadTarget(s, Storer::into(&tests)); +} + +class All : public CompoundTest { + public: + All(ScriptNodePtr s) : + SourceObject(s), + CompoundTest(s) { + } + bool passes() const { + if (tests.empty()) { + throw NoTestsToPerform(); + } + return (std::find_if(tests.begin(), tests.end(), !boost::bind(&Test::passes, _1)) == tests.end()); + } +}; +DECLARE_LOADER("all", All); + +class Any : public CompoundTest { + public: + Any(ScriptNodePtr s) : + SourceObject(s), + CompoundTest(s) { + } + bool passes() const { + if (tests.empty()) { + throw NoTestsToPerform(); + } + return (std::find_if(tests.begin(), tests.end(), boost::bind(&Test::passes, _1)) != tests.end()); + } +}; +DECLARE_LOADER("any", Any); + +class None : public CompoundTest { + public: + None(ScriptNodePtr s) : + SourceObject(s), + CompoundTest(s) { + } + bool passes() const { + if (tests.empty()) { + throw NoTestsToPerform(); + } + return (std::find_if(tests.begin(), tests.end(), boost::bind(&Test::passes, _1)) == tests.end()); + } +}; +DECLARE_LOADER("none", None); + +class Not : public Test { + public: + Not(ScriptNodePtr s) : + SourceObject(s), + Test(s) + { + s->script->loader.addLoadTarget(s, Storer::into(&test)); + } + bool passes() const { + if (!test) { + throw NoTestsToPerform(); + } + return !test->passes(); + } + private: + TestPtr test; +}; +DECLARE_LOADER("not", Not); diff --git a/project2/common/tests/compoundTest.h b/project2/common/tests/compoundTest.h new file mode 100644 index 0000000..38aadea --- /dev/null +++ b/project2/common/tests/compoundTest.h @@ -0,0 +1,16 @@ +#ifndef IF_H +#define IF_H + +#include "../test.h" + +class CompoundTest : public Test { + public: + CompoundTest(ScriptNodePtr); + + protected: + typedef ANONORDEREDSTORAGEOF(Test) Tests; + Tests tests; +}; + +#endif + diff --git a/project2/common/tests/isdistinct.cpp b/project2/common/tests/isdistinct.cpp new file mode 100644 index 0000000..b754d2d --- /dev/null +++ b/project2/common/tests/isdistinct.cpp @@ -0,0 +1,40 @@ +#include +#include "../test.h" +#include "../scriptLoader.h" +#include "../iHaveParameters.h" +#include "../rowProcessor.h" + +class IsDistinct : public Test, IHaveParameters { + public: + IsDistinct(ScriptNodePtr s) : + SourceObject(s), + Test(s), + IHaveParameters(s), + scope(s, "scope") + { + } + + void loadComplete(const CommonObjects *) + { + findComponent(scope())->registerFor(RowProcessor::Complete, boost::bind(&IsDistinct::reset, this)); + } + + bool passes() const { + Vars row; + BOOST_FOREACH(const Parameters::value_type & p, parameters) { + row.push_back(p.second()); + } + return previous.insert(row).second; + } + + void reset() const { + previous.clear(); + } + + private: + Variable scope; + typedef std::vector Vars; + typedef std::set Rows; + mutable Rows previous; +}; +DECLARE_LOADER("isdistinct", IsDistinct); diff --git a/project2/common/tests/isuniq.cpp b/project2/common/tests/isuniq.cpp new file mode 100644 index 0000000..6966985 --- /dev/null +++ b/project2/common/tests/isuniq.cpp @@ -0,0 +1,48 @@ +#include +#include "../test.h" +#include "../scriptLoader.h" +#include "../iHaveParameters.h" +#include "../rowProcessor.h" + +class IsUniq : public Test, IHaveParameters { + public: + IsUniq(ScriptNodePtr s) : + SourceObject(s), + Test(s), + IHaveParameters(s), + scope(s, "scope") + { + } + + void loadComplete(const CommonObjects *) + { + findComponent(scope())->registerFor(RowProcessor::Complete, boost::bind(&IsUniq::reset, this)); + } + + bool passes() const { + if (previous.size() > 0) { + Vars row; + BOOST_FOREACH(const Parameters::value_type & p, parameters) { + row.push_back(p.second()); + } + std::swap(row, previous); + return row != previous; + } + else { + BOOST_FOREACH(const Parameters::value_type & p, parameters) { + previous.push_back(p.second()); + } + return true; + } + } + + void reset() const { + previous.clear(); + } + + private: + Variable scope; + typedef std::vector Vars; + mutable Vars previous; +}; +DECLARE_LOADER("isuniq", IsUniq); diff --git a/project2/common/variables-modlookup.cpp b/project2/common/variables-modlookup.cpp index 64ff93d..67d2924 100644 --- a/project2/common/variables-modlookup.cpp +++ b/project2/common/variables-modlookup.cpp @@ -31,6 +31,7 @@ class VariableLookup : public VariableImplDyn, public RowProcessor { } }; VariableLookup(ScriptNodePtr e) : + SourceObject(e), VariableImplDyn(e), RowProcessor(e), name(e->value("name").as()) diff --git a/project2/common/variables.cpp b/project2/common/variables.cpp index 8515e44..4b42aa3 100644 --- a/project2/common/variables.cpp +++ b/project2/common/variables.cpp @@ -18,12 +18,6 @@ SimpleMessageException(UnknownVariableType); -bool -Null::operator<(const Null &) const -{ - return false; -} - Boolean::Boolean(bool v) : value(v) { } @@ -159,13 +153,13 @@ VariableType::VariableType(const VariableType & vt) : { } -template +template class compi : public boost::static_visitor { public: compi(const S & s) : _s(s) { } bool operator()(const S & t) const { - return t < _s; + return Op()(t, _s); } template bool operator()(const T &) const @@ -176,13 +170,15 @@ class compi : public boost::static_visitor { private: const S & _s; }; + +template