From 113678ff9324ad45836b354de4e58c473017232d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 15 Feb 2008 02:56:25 +0100 Subject: [PATCH 01/24] some refacturing of the (preliminary) struct asset impl... --- doc/devel/.gitignore | 2 + src/proc/asset/procpatt.cpp | 56 ++++++++++------------------ src/proc/asset/procpatt.hpp | 6 +-- src/proc/asset/struct.cpp | 25 +++++++++++++ src/proc/asset/struct.hpp | 3 ++ src/proc/asset/structfactoryimpl.hpp | 4 +- 6 files changed, 55 insertions(+), 41 deletions(-) create mode 100644 doc/devel/.gitignore diff --git a/doc/devel/.gitignore b/doc/devel/.gitignore new file mode 100644 index 000000000..96886436c --- /dev/null +++ b/doc/devel/.gitignore @@ -0,0 +1,2 @@ +,doxylog +html/* diff --git a/src/proc/asset/procpatt.cpp b/src/proc/asset/procpatt.cpp index ec9ecb781..f7ce8895d 100644 --- a/src/proc/asset/procpatt.cpp +++ b/src/proc/asset/procpatt.cpp @@ -32,46 +32,26 @@ using util::isnil; namespace asset { - namespace // ProcPattern implementation details - { - /** @internal derive a sensible asset ident tuple when creating - * a processing pattern asset based on a query - * @todo define the actual naming scheme of struct assets - */ - const Asset::Ident - createPatternIdent (const string& properties) - { - string name ("pattern-" + properties); // TODO something sensible here; append number, sanitize etc. - TODO ("Implement ProcPatt name scheme!!"); - Category category (STRUCT,"patterns"); - return Asset::Ident (name, category ); - } - } - /** */ - ProcPatt::ProcPatt (const string& properties) - : Struct (createPatternIdent (properties)), - propDescriptor_ (properties) + /** new processing pattern with empty instruction list. + * @todo preliminary implementation, storing the capabilities + * in the asset name field. We can do better, when + * it's clear what capabilities we need + */ + ProcPatt::ProcPatt (const Asset::Ident& idi) + : Struct (idi), + instructions_() + { } + + + /** @internal used for creating a clone copy */ + ProcPatt::ProcPatt (const Asset::Ident& idi, const InstructionSequence& instru) + : Struct (idi), + instructions_(instru) { TODO ("verify building instructions, maybe preprocess..."); } - - /** @internal used for creating a clone */ - ProcPatt::ProcPatt (const string& props, const InstructionSequence& instructs) - : Struct (createPatternIdent (props)), - propDescriptor_ (props), - instructions_ (instructs) - { } - - /** query the currently defined properties of this - processing pattern for a stream-ID predicate */ - const string& - ProcPatt::queryStreamID() const - { - TODO ("really implement querying the properties"); - return propDescriptor_; /////////////////////////////TODO grober Unfug - } /** create a new ProcPatt asset as a literal copy @@ -84,7 +64,11 @@ namespace asset ProcPatt::newCopy (string newID) const { TODO ("implement the Pattern-ID within the propDescriptor!"); - ProcPatt* pP = new ProcPatt (this->propDescriptor_, this->instructions_); + TODO ("implement a consitent struct asset naming scheme at one central location!!!!!"); + const Asset::Ident newIdi ( this->ident.name+".X" + , this->ident.category + ); + ProcPatt* pP = new ProcPatt (newIdi, this->instructions_); return AssetManager::instance().wrap (*pP); } diff --git a/src/proc/asset/procpatt.hpp b/src/proc/asset/procpatt.hpp index 5cd22e1e1..593c57a0f 100644 --- a/src/proc/asset/procpatt.hpp +++ b/src/proc/asset/procpatt.hpp @@ -52,17 +52,15 @@ namespace asset */ class ProcPatt : public Struct { - string propDescriptor_; InstructionSequence instructions_; - ProcPatt (const string& props, const InstructionSequence& instructs); + ProcPatt (const Asset::Ident&, const InstructionSequence&); protected: - explicit ProcPatt (const string& propDescriptor); + explicit ProcPatt (const Asset::Ident& idi); friend class StructFactoryImpl; public: - const string& queryStreamID() const; shared_ptr newCopy (string newID) const; ProcPatt& attach (Symbol where, PProc& node); diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp index 729da826a..47586202a 100644 --- a/src/proc/asset/struct.cpp +++ b/src/proc/asset/struct.cpp @@ -34,6 +34,12 @@ #include "common/util.hpp" #include "nobugcfg.h" +#include + +using boost::regex; +using boost::smatch; +using boost::regex_search; + using mobject::Session; using cinelerra::query::normalizeID; @@ -47,6 +53,25 @@ namespace asset /****** NOTE: not really implemented yet. What follows is partially a hack to build simple tests *******/ + namespace // Implementation details + { + regex streamID_pattern("stream\\(\\s*(\\w+)\\s*\\)"); + } + + + + /** query the currently defined properties of this + structural asset for a stream-ID predicate */ + const string + Struct::queryStreamID() const + { + smatch match; + + if (regex_search (this->ident.name, match, streamID_pattern)) + return string (match[1]); + else + return ""; + } diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index e9cb9de7d..4beb5479e 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -84,6 +84,9 @@ namespace asset return static_cast& > (Asset::getID()); } + const string queryStreamID() const; + + protected: Struct (const Asset::Ident& idi) : Asset(idi) {} friend class StructFactory; diff --git a/src/proc/asset/structfactoryimpl.hpp b/src/proc/asset/structfactoryimpl.hpp index 506baea83..a1086a9fa 100644 --- a/src/proc/asset/structfactoryimpl.hpp +++ b/src/proc/asset/structfactoryimpl.hpp @@ -84,7 +84,9 @@ namespace asset const Asset::Ident createIdent (const Query& query) { - string name (Traits::namePrefix + query); // TODO something sensible here; append number, sanitize etc. + static int i=0; + static format namePattern ("%s%d-%s"); // TODO finally just use the capability string as name?? + string name = str(namePattern % Traits::namePrefix % (++i) % query); TODO ("struct asset naming scheme??"); Category cat (STRUCT, Traits::catFolder); return Asset::Ident (name, cat ); From 34b14a226ea9f40afea8c9007a23be32d393e9e6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 16 Feb 2008 02:47:01 +0100 Subject: [PATCH 02/24] defer creation of the empty default sesison, avoids running complex code in static initialisation --- src/proc/mobject/session.hpp | 4 ++ src/proc/mobject/session/sessionimpl.hpp | 2 +- src/proc/mobject/session/sessmanagerimpl.cpp | 49 ++++++++++++++++---- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/proc/mobject/session.hpp b/src/proc/mobject/session.hpp index 149dc6c82..d8b89d16c 100644 --- a/src/proc/mobject/session.hpp +++ b/src/proc/mobject/session.hpp @@ -141,6 +141,10 @@ namespace mobject virtual ~SessManager() {}; }; + + CINELERRA_ERROR_DECLARE (CREATE_SESSION); ///< unable to create basic session. + + } // namespace mobject::session } // namespace mobject diff --git a/src/proc/mobject/session/sessionimpl.hpp b/src/proc/mobject/session/sessionimpl.hpp index d9dc1f078..38448ac41 100644 --- a/src/proc/mobject/session/sessionimpl.hpp +++ b/src/proc/mobject/session/sessionimpl.hpp @@ -98,7 +98,7 @@ namespace mobject virtual void reset () ; virtual void load () ; virtual void save () ; - virtual Session* operator-> () throw() { return pImpl_.get(); } + virtual SessionImpl* operator-> () throw() ; }; diff --git a/src/proc/mobject/session/sessmanagerimpl.cpp b/src/proc/mobject/session/sessmanagerimpl.cpp index ca2797151..ad94c9949 100644 --- a/src/proc/mobject/session/sessmanagerimpl.cpp +++ b/src/proc/mobject/session/sessmanagerimpl.cpp @@ -39,6 +39,7 @@ #include "proc/mobject/session.hpp" #include "proc/mobject/session/sessionimpl.hpp" #include "proc/mobject/session/defsmanager.hpp" +#include "common/error.hpp" using boost::scoped_ptr; @@ -48,17 +49,49 @@ namespace mobject { namespace session { + + CINELERRA_ERROR_DEFINE (CREATE_SESSION, "unable to create basic session"); + + /** Access to the "current session", which actually is + * an SessionImpl instance. This session object is created + * either by loading an existing session, or on demand by + * this accessor function here (when no session was loaded + * or created) + * @note any exceptions arising while building the basic + * session object(s) will halt the system. + */ + SessionImpl* + SessManagerImpl::operator-> () throw() + { + if (!pImpl_) + try + { // create empty default configured session + this->reset(); + } + catch (...) + { + ERROR (oper, "Unrecoverable Failure while creating the empty default session."); + throw cinelerra::error::Fatal ( "Failure while creating the basic session object. Sysstem halted." + , CINELERRA_ERROR_CREATE_SESSION ); + } - /** Besides creating the single system-wide Session manger instance, - * creates an empty default Session as well. - * @note any exceptions arising in the course of this will halt - * the system (and this behaviour is desirable). + + return pImpl_.get(); + } + + + + /** Initially (at static init time), only the single system-wide + * Session manger instance is created. It can be used to load an + * existing session; otherwise an empty default Session an a + * Defaults manager (Config Query system) is created at first + * \link #operator-> access \endlink to the sesion object. */ SessManagerImpl::SessManagerImpl () throw() - : pDefs_ (new DefsManager), - pImpl_ (new SessionImpl (*pDefs_)) - { - } + : pDefs_ (0), + pImpl_ (0) + { } + /** @note no transactional behaviour. * may succeed partial. From ea0416881e9c891eff4fe3142fd62cbc64c2bee3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 18 Feb 2008 04:16:53 +0100 Subject: [PATCH 03/24] WIP towards a asset::Struct naming scheme --- src/common/query.cpp | 37 +++++++++++ src/common/query.hpp | 9 ++- src/common/query/mockconfigrules.cpp | 18 +++++- src/proc/asset/pipe.cpp | 35 +++++------ src/proc/asset/pipe.hpp | 4 +- src/proc/asset/struct.cpp | 37 +++++------ src/proc/asset/struct.hpp | 1 + src/proc/asset/structfactoryimpl.hpp | 61 ++++++++++++------- tests/components/proc/asset/basicpipetest.cpp | 11 ++-- 9 files changed, 143 insertions(+), 70 deletions(-) diff --git a/src/common/query.cpp b/src/common/query.cpp index bb3e6ae86..152086bc3 100644 --- a/src/common/query.cpp +++ b/src/common/query.cpp @@ -26,10 +26,18 @@ #include "nobugcfg.h" #include +#include +#include +using std::map; +using boost::regex; +using boost::smatch; +using boost::regex_search; using boost::algorithm::is_upper; using boost::algorithm::is_alpha; +using util::contains; + namespace cinelerra { @@ -48,6 +56,35 @@ namespace cinelerra if (is_upper() (first)) id[0] = std::tolower (first); } + + + + + namespace // Implementation details + { + map regexTable; + } + + /** (preliminary) helper: instead of really parsing and evaluating the terms, + * just do a regular expression match to extract the literal argument + * behind the given predicate symbol. e.g calling + * queryID ("stream", "id(abc), stream(mpeg)") + * yields "mpeg" + */ + const string + extractID (Symbol sym, const string& termString) + { + + if (!contains (regexTable, sym)) + regexTable[sym] = regex (string(sym)+="\\(\\s*([\\w_\\.\\-]+)\\s*\\)"); + + smatch match; + if (regex_search (termString, match, regexTable[sym])) + return string (match[1]); + else + return ""; + + } } // namespace query diff --git a/src/common/query.hpp b/src/common/query.hpp index b5575f0da..9bdb3a389 100644 --- a/src/common/query.hpp +++ b/src/common/query.hpp @@ -28,11 +28,13 @@ #include #include +#include namespace cinelerra { using std::string; + using boost::format; /* ==== comon definitions for rule based queries ==== */ @@ -47,7 +49,9 @@ namespace cinelerra class Query : public std::string { public: - Query (const string& predicate="") : string(predicate) {} + explicit Query (const string& predicate="") : string(predicate) {} + explicit Query (format& pattern) : string(str(pattern)) {} + const string asKey() const { @@ -65,6 +69,9 @@ namespace cinelerra */ void normalizeID (string& id); + const string extractID (Symbol, const string& termString); + + } // namespace query diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp index 52f7f93fd..54ba5aaf1 100644 --- a/src/common/query/mockconfigrules.cpp +++ b/src/common/query/mockconfigrules.cpp @@ -67,6 +67,14 @@ namespace cinelerra Ptr obj = Struct::create (query); return AnyPair(query.asKey(), obj); } + + /** shortcut for simply accessing a table entry */ + template + any& + item (PTAB& table, const string& query) + { + return (*table)[Query(query).asKey()]; + } } @@ -82,8 +90,16 @@ namespace cinelerra INFO (config, "creating mock answers for some config queries..."); isInit_ = true; // allow re-entrance + typedef const ProcPatt cPP; + // for baiscpipetest.cpp --------- - answer_->insert (entry_Struct ("stream(teststream)")); + answer_->insert (entry_Struct ("stream(video)")); + answer_->insert (entry_Struct ("stream(teststream)")); + item (answer_, "stream(default)") = item (answer_,"stream(video)"); //TODO killme + + answer_->insert (entry_Struct ("pipe(master), stream(video)")); + item (answer_, "pipe(default)") = item(answer_,"pipe(master), stream(video)"); //TODO killme + TODO ("remove the default entries!!! DefaultsManager should find them automatically"); } diff --git a/src/proc/asset/pipe.cpp b/src/proc/asset/pipe.cpp index e15a09254..78899e844 100644 --- a/src/proc/asset/pipe.cpp +++ b/src/proc/asset/pipe.cpp @@ -22,35 +22,30 @@ #include "proc/asset/pipe.hpp" +#include "common/util.hpp" + +using util::isnil; namespace asset { - namespace // Pipe Asset implementation details - { - /** @internal derive a sensible asset ident tuple when creating - * a pipe asset based on a query - * @todo define the actual naming scheme of struct assets - */ - const Asset::Ident - createPipeIdent (PProcPatt& wiring, string& id, wstring& shortD, wstring& longD) - { - string name ("pipe-" + id); // TODO something sensible here; append number, sanitize etc. - TODO ("Implement pipe name scheme!!"); - Category category (STRUCT,"pipes"); - return Asset::Ident (name, category ); - } - } /** */ - Pipe::Pipe (PProcPatt& wiring, string pipeID, wstring shortDesc, wstring longDesc) - : Struct (createPipeIdent (wiring,pipeID,shortDesc,longDesc)), + Pipe::Pipe ( const Asset::Ident& idi + , PProcPatt& wiring + , const string& pipeID + , wstring shortName + , wstring longName + ) + : Struct (idi), pipeID_ (pipeID), wiringTemplate(wiring), - shortDesc (shortDesc), - longDesc (longDesc) + shortDesc (shortName), + longDesc (longName) { - + REQUIRE (!isnil (pipeID)); + if (isnil (shortDesc)) + shortDesc = wstring (pipeID.begin(), pipeID.end()); } diff --git a/src/proc/asset/pipe.hpp b/src/proc/asset/pipe.hpp index 0761efe65..ea354361d 100644 --- a/src/proc/asset/pipe.hpp +++ b/src/proc/asset/pipe.hpp @@ -52,7 +52,7 @@ namespace asset */ class Pipe : public Struct { - string pipeID_; + const string pipeID_; PProcPatt wiringTemplate; public: @@ -66,7 +66,7 @@ namespace asset protected: - Pipe (PProcPatt& wiring, string pipeID="", wstring shortDesc =wstring(), wstring longDesc =wstring()) ; + Pipe (const Asset::Ident&, PProcPatt& wiring, const string& pipeID, wstring shortName =wstring(), wstring longName =wstring()) ; friend class StructFactory; friend class StructFactoryImpl; diff --git a/src/proc/asset/struct.cpp b/src/proc/asset/struct.cpp index 47586202a..bcaff3118 100644 --- a/src/proc/asset/struct.cpp +++ b/src/proc/asset/struct.cpp @@ -26,7 +26,6 @@ #include "proc/asset/procpatt.hpp" #include "proc/asset/track.hpp" #include "proc/asset/pipe.hpp" -#include "proc/mobject/session.hpp" #include "common/configrules.hpp" #include "proc/asset/structfactoryimpl.hpp" @@ -34,18 +33,16 @@ #include "common/util.hpp" #include "nobugcfg.h" -#include +#include -using boost::regex; -using boost::smatch; -using boost::regex_search; +using boost::format; -using mobject::Session; +using cinelerra::Symbol; using cinelerra::query::normalizeID; - -using cinelerra::ConfigRules; using cinelerra::query::QueryHandler; +using cinelerra::ConfigRules; +using util::contains; namespace asset @@ -53,11 +50,6 @@ namespace asset /****** NOTE: not really implemented yet. What follows is partially a hack to build simple tests *******/ - namespace // Implementation details - { - regex streamID_pattern("stream\\(\\s*(\\w+)\\s*\\)"); - } - /** query the currently defined properties of this @@ -65,12 +57,15 @@ namespace asset const string Struct::queryStreamID() const { - smatch match; - - if (regex_search (this->ident.name, match, streamID_pattern)) - return string (match[1]); - else - return ""; + return cinelerra::query::extractID ("stream", this->ident.name); + } + + /** query the currently defined properties of this + structural asset for a stream-ID predicate */ + const string + Struct::queryPipeID() const + { + return cinelerra::query::extractID ("pipe", this->ident.name); } @@ -126,8 +121,8 @@ namespace asset { normalizeID (pipeID); normalizeID (streamID); - PProcPatt processingPattern = Session::current->defaults (Query("stream("+streamID+")")); - Pipe* pP = new Pipe (processingPattern, pipeID); + static format descriptor("pipe(%s), stream(%s)."); + Pipe* pP = impl_->fabricate (Query (descriptor % pipeID % streamID)); return AssetManager::instance().wrap (*pP); } diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp index 4beb5479e..ed6642690 100644 --- a/src/proc/asset/struct.hpp +++ b/src/proc/asset/struct.hpp @@ -85,6 +85,7 @@ namespace asset } const string queryStreamID() const; + const string queryPipeID() const; protected: diff --git a/src/proc/asset/structfactoryimpl.hpp b/src/proc/asset/structfactoryimpl.hpp index a1086a9fa..3d78920d3 100644 --- a/src/proc/asset/structfactoryimpl.hpp +++ b/src/proc/asset/structfactoryimpl.hpp @@ -34,16 +34,22 @@ #define ASSET_STRUCTFACTORYIMPL_H +#include "proc/mobject/session.hpp" #include "common/configrules.hpp" #include "common/error.hpp" +#include "common/util.hpp" #include using boost::format; +using mobject::Session; + +using util::isnil; +using util::contains; using asset::Query; using cinelerra::query::CINELERRA_ERROR_CAPABILITY_QUERY; - +using cinelerra::query::extractID; namespace asset { @@ -54,16 +60,20 @@ namespace asset { static Symbol namePrefix; static Symbol catFolder; + static Symbol idSymbol; }; - template<> Symbol Traits::namePrefix = "track-"; + template<> Symbol Traits::namePrefix = "track"; template<> Symbol Traits::catFolder = "tracks"; + template<> Symbol Traits::idSymbol = "track"; - template<> Symbol Traits::namePrefix = "pipe-"; + template<> Symbol Traits::namePrefix = "pipe"; template<> Symbol Traits::catFolder = "pipes"; + template<> Symbol Traits::idSymbol = "pipe"; - template<> Symbol Traits::namePrefix = "patt-"; + template<> Symbol Traits::namePrefix = "patt"; template<> Symbol Traits::catFolder = "build-templates"; + template<> Symbol Traits::idSymbol = "procPatt"; @@ -78,30 +88,32 @@ namespace asset /** @internal derive a sensible asset ident tuple when creating * structural asset instances based on a capability query - * @todo define the actual naming scheme of struct assets */ template const Asset::Ident createIdent (const Query& query) { - static int i=0; - static format namePattern ("%s%d-%s"); // TODO finally just use the capability string as name?? - string name = str(namePattern % Traits::namePrefix % (++i) % query); - TODO ("struct asset naming scheme??"); + string name (query); + string nameID = extractID (Traits::idSymbol, query); + if (isnil (nameID)) + { + // no name-ID contained in the query... + // so we'll create a new one + static int i=0; + static format namePattern ("%s.%d"); + static format predPattern ("%s(%s), "); + nameID = str(namePattern % Traits::namePrefix % (++i) ); + name.insert(0, + str(predPattern % Traits::idSymbol % nameID )); + } + ENSURE (!isnil (name)); + ENSURE (!isnil (nameID)); + ENSURE (contains (name, nameID)); + Category cat (STRUCT, Traits::catFolder); return Asset::Ident (name, cat ); } - typedef std::pair PipeIDs; - - PipeIDs - createPipeIdent (const Query& query) - { - string name (Traits::namePrefix + query); // TODO get some more sensible dummy values - TODO ("pipe naming scheme??"); - TODO ("actually extract the pipe stream type from the query..."); - return PipeIDs (name, "data"); // dummy stream type - } @@ -153,8 +165,15 @@ namespace asset Pipe* StructFactoryImpl::fabricate (const Query& caps) { - PipeIDs ids (createPipeIdent (caps)); - return recursive_create_ (ids.first, ids.second).get(); + const Asset::Ident idi (createIdent (caps)); + string pipeID = extractID ("pipe", idi.name); + string streamID = extractID ("stream", caps); + if (isnil (streamID)) streamID = "default"; + PProcPatt processingPattern = Session::current->defaults (Query("stream("+streamID+")")); + return new Pipe( idi + , processingPattern + , pipeID + ); } diff --git a/tests/components/proc/asset/basicpipetest.cpp b/tests/components/proc/asset/basicpipetest.cpp index 315ba83a7..62644b95a 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -65,11 +65,12 @@ namespace asset { virtual void run(Arg arg) { - string pipeID = isnil(arg)? "blackHole" : arg[1]; + string pipeID = isnil(arg)? "Black Hole" : arg[1]; string streamID = 2>arg.size()? "teststream" : arg[2] ; createExplicit (pipeID,streamID); create_or_ref (pipeID); + create_using_default (); dependProcPatt (pipeID); } @@ -80,6 +81,7 @@ namespace asset { string pID_sane (pID); normalizeID (pID_sane); + ASSERT (pID_sane != pID); PPipe thePipe = asset::Struct::create (pID,sID); @@ -118,6 +120,7 @@ namespace asset ASSERT (c1 == c2); PPipe pipe3 = Pipe::query ("pipe("+pID2+")"); +//////////////////////////////////////////////////////////////TODO: er macht eine Neue, anstatt die Bestehende zu finden ASSERT (pipe3 == pipe2); } @@ -130,7 +133,7 @@ namespace asset ASSERT (pipe1 == Session::current->defaults (Query())); ASSERT (pipe1->ident.category.hasKind(VIDEO)); ASSERT (pipe1->getProcPatt()); - PProcPatt popa = Session::current->defaults (Query("pipe()")); + PProcPatt popa = Session::current->defaults (Query("pipe(default)")); ASSERT (popa == pipe1->getProcPatt()); // several variants to query for "the default pipe" @@ -138,14 +141,14 @@ namespace asset ASSERT (pipe2 == pipe1); pipe2 = asset::Struct::create (Query ()); ASSERT (pipe2 == pipe1); - pipe2 = asset::Struct::create (Query ("pipe()")); + pipe2 = asset::Struct::create (Query ("pipe(default)")); ASSERT (pipe2 == pipe1); string sID = popa->queryStreamID(); // sort of a "default stream type" PPipe pipe3 = Pipe::query ("stream("+sID+")"); ASSERT (pipe3); ASSERT (pipe3->getProcPatt()->queryStreamID() == sID); - ASSERT (pipe3->getProcPatt() == Session::current->defaults (Query("stream("+sID+")"))); + ASSERT (pipe3->getProcPatt() == Session::current->defaults (Query("stream("+sID+")"))); } From 4bbf3aa53f90f0b7795b27e8961183162ca63e66 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 20 Feb 2008 04:05:37 +0100 Subject: [PATCH 04/24] test-driven-brainstorming: how to handle query-for-default? --- tests/51asset.tests | 2 +- tests/53session.tests | 4 + .../proc/mobject/session/defsmanagertest.cpp | 165 ++++++++++++++++++ wiki/renderengine.html | 96 ++++++---- 4 files changed, 232 insertions(+), 35 deletions(-) create mode 100644 tests/components/proc/mobject/session/defsmanagertest.cpp diff --git a/tests/51asset.tests b/tests/51asset.tests index 6d8195ceb..5e259e95e 100644 --- a/tests/51asset.tests +++ b/tests/51asset.tests @@ -34,7 +34,7 @@ return: 0 END -PLANNED "BasicPort_test" BasicPort_test < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "common/test/run.hpp" +#include "common/util.hpp" + +#include "proc/asset.hpp" +#include "proc/asset/pipe.hpp" +#include "common/query.hpp" +#include "proc/assetmanager.hpp" +#include "proc/mobject/session.hpp" + +#include +#include + +using boost::format; +using util::isnil; +using std::string; + + +namespace asset + { + namespace test + { + using mobject::Session; + using cinelerra::Query; + using cinelerra::query::normalizeID; + + + + /*********************************************************************** + * @test basic behaviour of the defaults manager. + *
  1. retrieving a "default" object repeatedly
  2. + *
  3. retrieving a more constrained "default" object
  4. + *
  5. failure registers a new "default"
  6. + *
+ * Using pipe assets as an example. The defaults manager shouldn't + * interfere with memory management (it holds weak refs). + */ + class DefsManager_test : public Test + { + virtual void run(Arg arg) + { + string pipeID = isnil(arg)? "Black Hole" : arg[1]; + string streamID = 2>arg.size()? "teststream" : arg[2] ; + + normalizeID (pipeID); + normalizeID (streamID); + + retrieveSimpleDefault (pipeID); + retrieveConstrainedDefault (pipeID, streamID); + pipeID = failureCreatesNewDefault(); + verifyRemoval (pipeID); + } + + + + + void retrieveSimpleDefault(string pID) + { + PPipe pipe1 = Pipe::query (""); // "the default pipe" + PPipe pipe2; + + // several variants to query for "the default pipe" + pipe2 = Pipe::query (""); + ASSERT (pipe2 == pipe1); + pipe2 = Pipe::query ("default(X)"); + ASSERT (pipe2 == pipe1); + pipe2 = Session::current->defaults(Query ()); + ASSERT (pipe2 == pipe1); + pipe2 = asset::Struct::create (Query ()); + ASSERT (pipe2 == pipe1); + pipe2 = asset::Struct::create (Query ("default(X)")); + ASSERT (pipe2 == pipe1); + } + + + void retrieveConstrainedDefault(string pID, string sID) + { + PPipe pipe1 = Pipe::query (""); // "the default pipe" + ASSERT (sID != pipe1->getProcPatt()->queryStreamID(), + "stream-ID \"%s\" not suitable for test, because " + "the default-pipe \"%s\" happens to have the same " + "stream-ID. We need it to be different", + sID.c_str(), pID.c_str() + ); + + string query_for_sID ("stream("+sID+")"); + PPipe pipe2 = Pipe::query (query_for_sID); + ASSERT (sID == pipe2->getProcPatt()->queryStreamID()); + ASSERT (pipe2 != pipe1); + ASSERT (pipe2 == Pipe::query (query_for_sID)); // reproducible + } + + + string failureCreatesNewDefault () + { + PPipe pipe1 = Session::current->defaults(Query ()); // "the default pipe" + + string new_pID (str (format ("dummy_%s_%i") + % pipe1->getPipeID() + % std::rand() + )); // make random new pipeID + string query_for_new ("pipe("+new_pID+")"); + PPipe pipe2 = Session::current->defaults(Query (query_for_new)); + + TODO ("a way to do only a query, without creating. Use this to check it is really added as default"); + + ASSERT (pipe1 != pipe2); + ASSERT (pipe2 == Pipe::query (query_for_new)); + return new_pID; + } + + + void verifyRemoval(string pID) + { + string query_for_pID ("pipe("+pID+")"); + size_t hash; + { + PPipe pipe1 = Pipe::query (query_for_pID); + hash = pipe1->getID(); + } + // now AssetManager should have the only ref + ID assetID (hash); + + AssetManager& aMang (AssetManager::instance()); + ASSERT ( aMang.known (assetID)); + aMang.remove (assetID); + ASSERT (!aMang.known (assetID)); + + TODO ("assure the bare default-query now fails..."); + PPipe pipe2 = Session::current->defaults(Query (query_for_pID)); + TODO ("assure the bare default-query now succeeds..."); + } + }; + + + /** Register this test class... */ + LAUNCHER (DefsManager_test, "function session"); + + + + } // namespace test + +} // namespace asset diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 5bc243c00..35ed4e339 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -797,7 +797,7 @@ Error: #f88 &rarr; [[Configuration Rules system|ConfigRules]] -
+
Many features can be implemented by specifically configuring and wiring some unspecific components. Rather than tie the client code in need of some given feature to these configuration internals, in Cinelerra-3 the client can //query // for some kind of object providing the //needed capabilities. // Right from start (summer 2007), Ichthyo had the intention to implement such a feature using sort of a ''declarative database'', e.g. by embedding a Prolog system. By adding rules to the basic session configuration, users should be able to customize the semi-automatic part of Cinelerra's behaviour to great extent.
 
 [[Configuration Queries|ConfigQuery]] are used at various places, when creating and adding new objects, as well when building or optimizing the render engine node network.
@@ -808,7 +808,7 @@ Error: #f88
!anatomy of a Configuration Query The query is given as a number of logic predicates, which are required to be true. Syntactically, it is a string in prolog syntax, e.g. {{{stream(mpeg)}}}, where "stream" is the //predicate, // meaning here "the stream type is...?" and "mpeg" is a //term // denoting an actual property, object, thing, number etc &mdash; the actual kind of stream in the given example. Multible comma separated predicates are combined with logical "and". Terms may be //variable // at start, which is denoted syntactically by starting them with a uppercase letter. But any variable term need to be //bound // to some known value while computing the solution to the query, otherwise the query fails. A failed query is treated as a local failure, which may cause some operation being aborted or just some other possibility being chosen. -Queries are represented by instantiations of the {{{Query<TYPE>}}} template, because their actual meaning is "retrieve or create an object of TYPE, configured such that...!". At the C++ side, this ensures type safety and fosters programming against interfaces, while being implemented rule-wise by silently prepending the query with the predicate "{{{object(tYPE)}}}" +Queries are represented by instantiations of the {{{Query<TYPE>}}} template, because their actual meaning is "retrieve or create an object of TYPE, configured such that...!". At the C++ side, this ensures type safety and fosters programming against interfaces, while being implemented rule-wise by silently prepending the query with the predicate "{{{object(TYPE)}}}" !executing a Configuration Query Actually posing such an configuration query, for example to the [[Defaults Manager in the Sessison|DefaultsManagement]], may trigger several actions: First it is checked against internal object registries (depending on the target object type), which may cause the delivery of an already existing object (as reference, clone, or smart pointer). Otherwise, the system tries to figure out an viable configuration for a newly created object instance, possibly by issuing recursive queries. In the most general case this may silently impose additional decisions onto the //execution context // of the query &mdash; by default the session. @@ -818,6 +818,9 @@ At start and for debugging/testing, there is an ''dummy'' implementation using a &rarr; [[considerations for a Prolog based implementation|QueryImplProlog]] &rarr; see {{{src/common/query/mockconfigrules.cpp}}} for the table with the hard wired (mock) answers + +{{red{WARN}}} there is an interference with the (planned) Undo function: a totally correct implementation of "Undo" would need to remember and restore the internal state of the query system (similar to backtracking). But, more generally, such an correct implementation is not achievable, because we are never able to capture and control the whole state of a real world system doing such advanced things like video and sound processing. Seemingly we have to accept that after undoing an action, there is no guarantee we can re-do it the same way as it was first time. +
@@ -839,10 +842,36 @@ This is an very important external Interface, because it links together all thre
[[ProcLayer and Engine]]
 
-
-
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. The Queries are implemented by [[configuration rules|ConfigRules]]
+
+
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (stores a weak ref in the ~DefsManager). Alongside with each object successfully queried via "default", the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
+
+!Skeleton
+# ''search'': using the predicate {{{default(X)}}} enumerates existing objects of suitable type
+#* candidates are delivered starting with the least constrained default
+#* the argument is unified
+#** if the rest of the query succeeds we've found our //default object// and are happy.
+#** otherwise, if all enumerated solutions are exhausted without success, we enter
+# ''default creation'': try to get an object fulfilling the conditions and remember this situation
+#* we issue an ConfigQuery with the query terms //minus// the {{{default(X)}}} predicate
+#* it depends on the circumstances how this query is handled. Typically the query resolution first searches existing objects and then creates a new instance to match the required capabilities. Usually, this process succeeds, but there can be configurations leading to failure.
+#** failing the ~ConfigQuery is considered an (non-critical) exception (throws), as defaults queries are supposed to succeed
+#** otherwise, the newly created object is remembered (tagged) as new default, together with the degree of constriction
+
+!!!Implementation details
+Taken precisely, the "degree of constriction" yields only a partial ordering &mdash; but as the "default"-predicate is sort of a existential quantification anyways, its sole purpose is to avoid polluting the session with unnecessary default objects, and we don't need to care for absolute precision. A suitable approximation is to count the number of predicates terms in the query and use a pqueue (separate for each Type) to store weak refs to the the objects tagged as "default"
+{{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; it seems reasonable we can ignore this issue.
+
+!!!Problems with the (preliminary) mock implementation
+As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see {{{MockConfigQuery}}}). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, because the system isn't capable to do any real resolution &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.
 
+
+
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. The Queries are implemented by [[configuration rules|ConfigRules]]
+
+!!!!what is denoted by {{{default}}}?
+{{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.
+&rarr; [[implementing defaults|DefaultsImplementation]]
+
Along the way of working out various [[implementation details|ImplementationDetails]], decisions need to be made on how to understand the different facilities and entities and how to tackle some of the problems. This page is mainly a collection of keywords, summaries and links to further the discussion. And the various decisions should allways be read as proposals to solve some problem at hand...
 
@@ -2226,6 +2255,33 @@ DAMAGE.
 <html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
 ***/
+
+
Pipes play an central role within the Proc Layer, because for everything placed and handled within the EDL, the final goal is to get it transformed into data which can be retrieved at some pipe's exit port. Pipes are special facilities, rather like inventory, separate and not treated like all the other objects.
+We don't distinguish between "input" and "output" ports &mdash; rather, pipes are thought to be ''hooks for making connections to''. By following this line of thought, each pipe has an input side and an output side and is in itself something like a ''Bus'' or ''processing chain''. Other processing entities like effects and transitions can be placed (attached) at the pipe, resulting them to be appended to form this chain. Likewise, we can place [[wiring requests|WiringRequest]] to the pipe, meaning we want it connected so to send it's output to another destination pipe. The [[Builder]] may generate further wiring requests to fulfil the placement of other entities.
+Thus //Pipes are the basic building blocks// of the whole render network. We distinguish ''global available'' Pipes, which are like the sum groups of a mixing console, and the ''lokal pipe'' or [[source port|ClipSourcePort]] of the individual clips, which exist only within the duration of the corresponding clip. The design //limits the possible kinds of pipes // to these two types &mdash; thus we can build local processing chains at clips and global processing chains at the global pipes of the session and that's all we can do. (because of the flexibility which comes with the concept of [[placements|Placement]], this is no real limitation)
+
+The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe points|ProbePoint]] placed like effects and connected to some pipe), and likewise, when starting a ''render'', we get the opportunity to specify the pipes to pull the data from. Pulling data from some pipe is the (only) way to activate the render nodes network reachable from this pipe.
+
+&rarr; [[Handling of Tracks|TrackHandling]]
+&rarr; [[Handling of Pipes|PipeHandling]]
+
+
+
+
+
!Identification
+Pipes are distinct objects and can be identified by their asset ~IDs. Besides, as for all [[structural assets|StructAsset]] there are extended query capabilities, including a symbolic pipe-id and a media (stream) type id. Any pipe can accept and deliver exactly one media stream kind (which may be inherently structured though, e.g. spatial sound systems or stereoscopic video)
+
+!creating pipes
+Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes, and further pipes can be created by using an new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]].
+
+!removal
+Deleting a Pipe is an advanced operation, because it includes finding and "detaching" all references, otherwise the pipe will leap back into existence immediately. Thus, global pipe entries in the Session and pipe references in [[locating pins|LocatingPin]] within any placement have to be removed, while clips using a given source port will be disabled. {{red{todo: implementation deferred}}}
+
+!using Pipes
+there is not much you can do directly with a pipe asset. It is an point of reference, after all. Any connection to some pipe is only temporarily done by a placement in some part of the timeline, so it isn't stored with the pipe. You can edit the (user visible) description an you can globally disable a pipe asset. The pipe's ID and media stream type of course are fixed, because any connection and referral (via the asset ID) is based on them. Later on, we should provide a {{{rewire(oldPipe, newPipe)}}} to search any ref to the {{{oldPipe}}} and try to rewrite it to use the {{{newPipe}}}, possibly with a new media stream type.
+Pipes are integrated with the [[management of defaults|DefaultsManagement]]. For example, any pipe uses implicitly some [[processing pattern|ProcPatt]] &mdash; it may default to the empty pattern. This feature enables to apply some standard wiring to the pipes (e.g a fader for audio, similar to the classic mixing consoles). This //is // a global property of the pipe, but &mdash; contrary to the stream type &mdash; this pattern may be switched
+
+
A Placement represents a //relation:// it is always linked to a //Subject// (this being a [[Media Object|MObject]]) and has the meaning to //place// this Subject in some manner, either relatively to other Media Objects, or by some Constraint or simply absolute at (time,track). The latter case is especially important and represented by a special [[Sub-Interface|ExplicitPlacement]]
 
@@ -2351,34 +2407,6 @@ We need a way of addressing existing [[pipes|Pipe]]. Besides, as the Pipes and T
 //Note, we have yet to specify how exactly the building and rendering will work together with the backend. There are several possibilities how to structure the Playlist//
 
-
-
-Pipes play an central role within the Proc Layer, because for everything placed and handled within the EDL, the final goal is to get it transformed into data which can be retrieved at some pipe's exit port. Pipes are special facilities, rather like inventory, separate and not treated like all the other objects.
-We don't distinguish between "input" and "output" ports &mdash; rather, pipes are thought to be ''hooks for making connections to''. By following this line of thought, each pipe has an input side and an output side and is in itself something like a ''Bus'' or ''processing chain''. Other processing entities like effects and transitions can be placed (attached) at the pipe, resulting them to be appended to form this chain. Likewise, we can place [[wiring requests|WiringRequest]] to the pipe, meaning we want it connected so to send it's output to another destination pipe. The [[Builder]] may generate further wiring requests to fulfil the placement of other entities.
-Thus //Pipes are the basic building blocks// of the whole render network. We distinguish ''global available'' Pipes, which are like the sum groups of a mixing console, and the ''lokal pipe'' or [[source port|ClipSourcePort]] of the individual clips, which exist only within the duration of the corresponding clip. The design //limits the possible kinds of pipes // to these two types &mdash; thus we can build local processing chains at clips and global processing chains at the global pipes of the session and that's all we can do. (because of the flexibility which comes with the concept of [[placements|Placement]], this is no real limitation)
-
-The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe points|ProbePoint]] placed like effects and connected to some pipe), and likewise, when starting a ''render'', we get the opportunity to specify the pipes to pull the data from. Pulling data from some pipe is the (only) way to activate the render nodes network reachable from this pipe.
-
-&rarr; [[Handling of Tracks|TrackHandling]]
-&rarr; [[Handling of Pipes|PipeHandling]]
-
-
-
-
-
!Identification
-Pipes are distinct objects and can be identified by their asset ~IDs. Besides, as for all [[structural assets|StructAsset]] there are extended query capabilities, including a symbolic pipe-id and a media (stream) type id. Any pipe can accept and deliver exactly one media stream kind (which may be inherently structured though, e.g. spatial sound systems or stereoscopic video)
-
-!creating pipes
-Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes, and further pipes can be created by using an new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]].
-
-!removal
-Deleting a Pipe is an advanced operation, because it includes finding and "detaching" all references, otherwise the pipe will leap back into existence immediately. Thus, global pipe entries in the Session and pipe references in [[locating pins|LocatingPin]] within any placement have to be removed, while clips using a given source port will be disabled. {{red{todo: implementation deferred}}}
-
-!using Pipes
-there is not much you can do directly with a pipe asset. It is an point of reference, after all. Any connection to some pipe is only temporarily done by a placement in some part of the timeline, so it isn't stored with the pipe. You can edit the (user visible) description an you can globally disable a pipe asset. The pipe's ID and media stream type of course are fixed, because any connection and referral (via the asset ID) is based on them. Later on, we should provide a {{{rewire(oldPipe, newPipe)}}} to search any ref to the {{{oldPipe}}} and try to rewrite it to use the {{{newPipe}}}, possibly with a new media stream type.
-Pipes are integrated with the [[management of defaults|DefaultsManagement]]. For example, any pipe uses implicitly some [[processing pattern|ProcPatt]] &mdash; it may default to the empty pattern. This feature enables to apply some standard wiring to the pipes (e.g a fader for audio, similar to the classic mixing consoles). This //is // a global property of the pipe, but &mdash; contrary to the stream type &mdash; this pattern may be switched
-
-
Open issues, Things to be worked out, Problems still to be solved... 
 
@@ -2484,9 +2512,9 @@ Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming s
 
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
-
+
//obviously, getting this one to work requires quite a lot of technical details to be planned and implemented.// This said...
-The intention is to get much more readable ("declarative") and changeable configuration as by programming it directly within the implementation of some object.
+The intention is to get much more readable ("declarative") and changeable configuration as by programming the decision logic literately within the implementation of some object.
 
 !Draft
 As an example, specifying how a Track can be configured for connecting automatically to some "mpeg" bus (=pipe)

From 919a57ed9a2717348d9c5fe08980d4ab09a9bb03 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 22 Feb 2008 04:58:37 +0100
Subject: [PATCH 05/24] doc comment fix

---
 src/common/configrules.hpp                   | 2 +-
 src/common/query/mockconfigrules.hpp         | 2 +-
 src/common/singletonsubclass.hpp             | 2 +-
 src/proc/asset/db.hpp                        | 2 +-
 src/proc/mobject/explicitplacement.hpp       | 2 +-
 tests/components/common/typelistutiltest.cpp | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp
index a247eb30d..872d34724 100644
--- a/src/common/configrules.hpp
+++ b/src/common/configrules.hpp
@@ -26,7 +26,7 @@
  ** By using the Query template, you can pose a query in prolog syntax and get some
  ** existing or newly created object fulfilling the requested predicates. The actual 
  ** implementation is hidden behind the #instance (Singleton factory). As of 1/2008, 
- ** it is \i planned to use an embedded YAP Prolog system at some point in the future,
+ ** it is \e planned to use an embedded YAP Prolog system at some point in the future,
  ** for now we use a \link MockConfigRules mock implementation \endlink employing a
  ** preconfigured Map.
  **
diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp
index 7b6ce6374..51f4a4cb7 100644
--- a/src/common/query/mockconfigrules.hpp
+++ b/src/common/query/mockconfigrules.hpp
@@ -96,7 +96,7 @@ namespace cinelerra
     
     /** 
      * building block defining how to do 
-     * the mock implementation for \i one type.
+     * the mock implementation for \e one type.
      * We simply access a table holding pre-created objects.
      */
     template
diff --git a/src/common/singletonsubclass.hpp b/src/common/singletonsubclass.hpp
index e779b820e..269351185 100644
--- a/src/common/singletonsubclass.hpp
+++ b/src/common/singletonsubclass.hpp
@@ -81,7 +81,7 @@ namespace cinelerra
               My_scoped_ptr() : scoped_ptr (get()? get() : 0) {}   ///< bypass if already configured
             };
             
-          /** we configure this link \i later, when the singleton factory
+          /** we configure this link \e later, when the singleton factory
            *  is actually created, to point at the desired implementation subclass.
            */
           static My_scoped_ptr link;
diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp
index 8de442102..ee6ea753d 100644
--- a/src/proc/asset/db.hpp
+++ b/src/proc/asset/db.hpp
@@ -110,7 +110,7 @@ namespace asset
        *  As the destructor of DB needs to call clear(), this
        *  could result in segfaults. This doesn't seem to be
        *  a problem, though, because we register and process
-       *  \i all assets and the net effect is just breaking
+       *  \e all assets and the net effect is just breaking
        *  any cyclic dependencies) 
        */ 
       void
diff --git a/src/proc/mobject/explicitplacement.hpp b/src/proc/mobject/explicitplacement.hpp
index 9cf0917aa..5f70047b9 100644
--- a/src/proc/mobject/explicitplacement.hpp
+++ b/src/proc/mobject/explicitplacement.hpp
@@ -53,7 +53,7 @@ namespace mobject
       typedef std::pair SolutionData;  //TODO (ichthyo consideres better passing of solution by subclass)
 
       /** no need to resolve any further, as this ExplicitPlacement
-       *  already \i is the result of a resolve()-call.
+       *  already \e is the result of a resolve()-call.
        */
       virtual
       ExplicitPlacement resolve ()  const 
diff --git a/tests/components/common/typelistutiltest.cpp b/tests/components/common/typelistutiltest.cpp
index 842a1c5ec..327242e8e 100644
--- a/tests/components/common/typelistutiltest.cpp
+++ b/tests/components/common/typelistutiltest.cpp
@@ -26,7 +26,7 @@
  **
  ** the following test composes both an interface and the corresponding implementation
  ** by instantiating "building block" templates over a collection of types. The resulting
- ** class ends up inheriting a \i virtual function instantiated for each of the types 
+ ** class ends up inheriting a \e virtual function instantiated for each of the types 
  ** in the list. (remember: normally the number and signature of all virtual functions
  ** need to be absolutely fixed in the class definition)
  **

From 4af2d479956d27affc8ff74d63c464273f344fb0 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 29 Feb 2008 04:27:24 +0100
Subject: [PATCH 06/24] test-driven-brainstorming: DefsManager interface
 building blocks

---
 src/proc/mobject/session/defsmanager.cpp      |  30 ++++
 src/proc/mobject/session/defsmanager.hpp      |  37 ++++
 .../mobject/session/defsmanagerimpltest.cpp   | 163 ++++++++++++++++++
 .../proc/mobject/session/defsmanagertest.cpp  |  37 ++--
 wiki/renderengine.html                        |   6 +-
 5 files changed, 258 insertions(+), 15 deletions(-)
 create mode 100644 tests/components/proc/mobject/session/defsmanagerimpltest.cpp

diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp
index bfb505efb..52b78437b 100644
--- a/src/proc/mobject/session/defsmanager.cpp
+++ b/src/proc/mobject/session/defsmanager.cpp
@@ -44,6 +44,7 @@ namespace mobject
   {
   namespace session
     {
+    using std::tr1::shared_ptr;
     
     /** initialize the most basic internal defaults. */
     DefsManager::DefsManager ()  throw()
@@ -51,11 +52,40 @@ namespace mobject
       TODO ("setup basic defaults of the session");
     }
 
+    template
+    shared_ptr 
+    DefsManager::search  (const Query& capabilities)
+    {
+      UNIMPLEMENTED ("search for default registered object, dont create");
+    }
+    
+    template
+    shared_ptr 
+    DefsManager::create  (const Query& capabilities)
+    {
+      UNIMPLEMENTED ("retrieve object and register as default");
+    }
+    
+    template
+    bool 
+    DefsManager::define  (shared_ptr& defaultObj, const Query& capabilities)
+    {
+      UNIMPLEMENTED ("just do the defaults registration");
+    }
+
+    template
+    bool 
+    DefsManager::forget  (shared_ptr& defaultObj)
+    {
+      UNIMPLEMENTED ("purge defaults registration");
+    }
+
     
     template
     shared_ptr 
     DefsManager::operator() (const Query& capabilities)
     {
+      TODO ("move this code to create()");
       QueryHandler& typeHandler = ConfigRules::instance();  
       shared_ptr res = typeHandler.resolve (capabilities);
       
diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp
index b48411058..16c6a7bfc 100644
--- a/src/proc/mobject/session/defsmanager.hpp
+++ b/src/proc/mobject/session/defsmanager.hpp
@@ -56,9 +56,46 @@ namespace mobject
         friend class SessManagerImpl;
         
       public:
+        /** common access point: retrieve the default object fulfilling
+         *  some given conditions. May silently trigger object creation.
+         *  @throw error::Config in case no solution is possible, which
+         *         is considered \e misconfiguration. 
+         */
         template
         shared_ptr operator() (const cinelerra::Query&);
         
+        /** search through the registered defaults, never create anything.
+         *  @return object fulfilling the query, \c empty ptr if not found. 
+         */
+        template
+        shared_ptr search  (const cinelerra::Query&);
+        
+        /** retrieve an object fulfilling the query and register it as default.
+         *  The resolution is delegated to the ConfigQuery system (which may cause
+         *  creation of new object instances) 
+         *  @return object fulfilling the query, \c empty ptr if no solution.
+         */ 
+        template
+        shared_ptr create  (const cinelerra::Query&);
+        
+        /** register the given object as default, after ensuring it fulfills the
+         *  query. The latter may cause some properties of the object to be set,
+         *  trigger creation of additional objects, and may fail altogether.
+         *  @return true if query was successfull and object is registered as default
+         *  @note only a weak ref to the object is stored
+         */ 
+        template
+        bool define  (shared_ptr&, const cinelerra::Query&);
+        
+        /** remove the defaults registration of the given object, if there was such
+         *  @return false if nothing has been changed because the object wasn't registered
+         */
+        template
+        bool forget  (shared_ptr&);
+        
+        
+// Q: can we have something along the line of...?
+//
 //        template
 //          < class TAR,                   ///< the target to query for 
 //            template  class SMP  ///<  smart pointer class to wrap the result
diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
new file mode 100644
index 000000000..920da9467
--- /dev/null
+++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
@@ -0,0 +1,163 @@
+/*
+  DefsManagerImpl(Test)  -  checking implementation details of the defaults manager
+ 
+  Copyright (C)         CinelerraCV
+    2007,               Hermann Vosseler 
+ 
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+* *****************************************************/
+
+
+#include "common/test/run.hpp"
+#include "common/util.hpp"
+
+#include "proc/asset.hpp"
+#include "proc/asset/pipe.hpp"
+#include "common/configrules.hpp"
+#include "proc/assetmanager.hpp"
+#include "proc/mobject/session.hpp"
+
+#include 
+#include 
+
+using boost::format;
+using util::isnil;
+using std::string;
+
+
+namespace asset
+  {
+  namespace test
+    {
+    using mobject::Session;
+    using cinelerra::Symbol;
+    using cinelerra::Query;
+    using cinelerra::query::normalizeID;
+    
+    using cinelerra::ConfigRules;
+    using cinelerra::query::QueryHandler;
+    
+    
+    /** shortcut: query without creating new instances */
+    bool 
+    find (Query& q) 
+    { 
+      return Session::current->defaults.search (q); 
+    }
+    
+    /** shortcut: query for given Pipe-ID */
+    bool 
+    find (const string& pID) 
+    { 
+      return Session::current->defaults.search (Query ("pipe("+pID+")")); 
+    }
+    
+    
+    format pattern ("dummy_%s_%i");
+    
+    /** create a random new ID */
+    string 
+    newID (Symbol prefix)
+    {
+      return str (pattern % prefix % std::rand());
+    }
+    
+    
+    
+    
+    /************************************************************************
+     * @test verify some general assumptions regarding implementation details
+     *       of the the defaults manager.
+     * @see  DefsManager_test for the "big picture"
+     */
+    class DefsManagerImpl_test : public Test
+      {
+        virtual void run(Arg arg) 
+          {
+            define_and_search();
+            string pipeID = create();
+            forget(pipeID);
+          }
+        
+        
+        
+        
+        void define_and_search ()
+          {
+            string sID = newID ("stream");
+            
+             // create Pipes explicitly 
+            //  (without utilizing default queries)
+            PPipe pipe1 = Struct::create (newID("pipe"), newID("stream")); 
+            PPipe pipe2 = Struct::create (newID("pipe"), sID            );
+            
+            ASSERT (pipe1 != pipe2);
+            ASSERT (sID == pipe2->getProcPatt()->queryStreamID());
+            
+            ASSERT (!find (pipe1->getPipeID()), "accidental clash of random test-IDs");
+            ASSERT (!find (pipe2->getPipeID()), "accidental clash of random test-IDs");
+            
+            // now declare that these objects should be considered "default"
+            ASSERT (Session::current->defaults.define (pipe1, Query (""))); // unrestricted default
+            ASSERT (Session::current->defaults.define (pipe1, Query ("stream("+sID+")")));
+            
+            ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default");
+            ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default");
+          }
+        
+        
+        const string& create ()
+          { 
+            string sID = newID ("stream");
+            Query query_for_streamID ("stream("+sID+")");
+            
+            // issue a ConfigQuery directly, without involving the DefaultsManager
+            QueryHandler& typeHandler = ConfigRules::instance();  
+            PPipe pipe1 = typeHandler.resolve (query_for_streamID);
+            ASSERT (pipe1);
+            
+            ASSERT (!find (pipe1->getPipeID()));
+            PPipe pipe2 = Session::current->defaults.create (query_for_streamID);
+            ASSERT (pipe2);
+            ASSERT (pipe2 == pipe1);
+            ASSERT ( find (pipe1->getPipeID())); // now declared as "default Pipe" for this stream-ID
+            
+            return pipe1->getPipeID();
+          }
+        
+        
+        void forget(string pID)
+          { 
+            PPipe pipe = Pipe::query ("pipe("+pID+")");
+            REQUIRE (find (pipe->getPipeID()), "need an object registered as default");
+            long cnt = pipe.use_count();
+            
+            // now de-register the pipe as "default Pipe"
+            ASSERT (Session::current->defaults.forget (pipe));
+            ASSERT (!find (pipe->getPipeID()));
+            ASSERT (cnt == pipe.use_count());   // indicates that DefaultsManager only holds a weak ref.
+          }
+      };
+    
+    
+    /** Register this test class... */
+    LAUNCHER (DefsManagerImpl_test, "function session");
+    
+    
+    
+  } // namespace test
+
+} // namespace asset
diff --git a/tests/components/proc/mobject/session/defsmanagertest.cpp b/tests/components/proc/mobject/session/defsmanagertest.cpp
index c43f00583..16c09336a 100644
--- a/tests/components/proc/mobject/session/defsmanagertest.cpp
+++ b/tests/components/proc/mobject/session/defsmanagertest.cpp
@@ -47,6 +47,17 @@ namespace asset
     using cinelerra::query::normalizeID;
     
     
+    /** shortcut: run just a query
+     *  without creating new instances
+     */
+    bool 
+    find (Query& q) 
+    { 
+      return Session::current->defaults.search (q); 
+    }
+    
+    
+    
     
     /***********************************************************************
      * @test basic behaviour of the defaults manager.
@@ -76,7 +87,7 @@ namespace asset
         
         
         
-        void retrieveSimpleDefault(string pID)
+        void retrieveSimpleDefault (string pID)
           { 
             PPipe pipe1 = Pipe::query (""); // "the default pipe"
             PPipe pipe2;
@@ -95,7 +106,7 @@ namespace asset
           }
         
         
-        void retrieveConstrainedDefault(string pID, string sID)
+        void retrieveConstrainedDefault (string pID, string sID)
           { 
             PPipe pipe1 = Pipe::query (""); // "the default pipe"
             ASSERT (sID != pipe1->getProcPatt()->queryStreamID(),
@@ -121,23 +132,24 @@ namespace asset
                                  % pipe1->getPipeID()
                                  % std::rand()
                                 ));     // make random new pipeID  
-            string query_for_new ("pipe("+new_pID+")");
-            PPipe pipe2 = Session::current->defaults(Query (query_for_new));
+            Query query_for_new ("pipe("+new_pID+")");
             
-            TODO ("a way to do only a query, without creating. Use this to check it is really added as default");
+            ASSERT (!find (query_for_new));                             // check it doesn't exist 
+            PPipe pipe2 = Session::current->defaults (query_for_new);   // triggers creation
+            ASSERT ( find (query_for_new));                             // check it exists now
             
             ASSERT (pipe1 != pipe2);
-            ASSERT (pipe2 == Pipe::query (query_for_new));
+            ASSERT (pipe2 == Session::current->defaults (query_for_new));
             return new_pID;
           }
         
         
-        void verifyRemoval(string pID)
+        void verifyRemoval (string pID)
           { 
-            string query_for_pID ("pipe("+pID+")");
+            Query query_for_pID ("pipe("+pID+")");
             size_t hash;
               {
-                PPipe pipe1 = Pipe::query (query_for_pID);
+                PPipe pipe1 = Session::current->defaults (query_for_pID);
                 hash = pipe1->getID();
               }
              // now AssetManager should have the only ref
@@ -148,9 +160,10 @@ namespace asset
             aMang.remove (assetID);
             ASSERT (!aMang.known (assetID));
             
-            TODO ("assure the bare default-query now fails...");
-            PPipe pipe2 = Session::current->defaults(Query (query_for_pID));
-            TODO ("assure the bare default-query now succeeds...");
+            
+            ASSERT (!find(query_for_pID));                              // bare default-query should fail...
+            PPipe pipe2 = Session::current->defaults (query_for_pID);   // triggers re-creation
+            ASSERT ( find(query_for_pID));                              // should succeed again
           }
       };
     
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 35ed4e339..0f3427f05 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -842,8 +842,8 @@ This is an very important external Interface, because it links together all thre
 
[[ProcLayer and Engine]]
 
-
-
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (stores a weak ref in the ~DefsManager). Alongside with each object successfully queried via "default", the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
+
+
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory track regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (storing a weak ref in the ~DefsManager). Alongside with each object successfully queried via "default", the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
 
 !Skeleton
 # ''search'': using the predicate {{{default(X)}}} enumerates existing objects of suitable type
@@ -859,7 +859,7 @@ This is an very important external Interface, because it links together all thre
 
 !!!Implementation details
 Taken precisely, the "degree of constriction" yields only a partial ordering &mdash; but as the "default"-predicate is sort of a existential quantification anyways, its sole purpose is to avoid polluting the session with unnecessary default objects, and we don't need to care for absolute precision. A suitable approximation is to count the number of predicates terms in the query and use a pqueue (separate for each Type) to store weak refs to the the objects tagged as "default"
-{{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; it seems reasonable we can ignore this issue.
+{{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; just ignoring this issue seems reasonable.
 
 !!!Problems with the (preliminary) mock implementation
 As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see {{{MockConfigQuery}}}). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, because the system isn't capable to do any real resolution &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.

From a3d91286c87a2400b0d977a9df32e5c651ad6cdf Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Fri, 29 Feb 2008 18:58:29 +0100
Subject: [PATCH 07/24] WIP defaults manager implementation

---
 src/common/configrules.hpp                    | 24 +++---
 src/common/query/mockconfigrules.hpp          | 14 ++--
 src/proc/mobject/session/defsmanager.cpp      | 79 ++++++++++++++-----
 src/proc/mobject/session/defsmanager.hpp      |  4 +-
 src/proc/mobject/session/defsregistry.cpp     | 48 +++++++++++
 src/proc/mobject/session/defsregistry.hpp     | 68 ++++++++++++++++
 .../mobject/session/defsmanagerimpltest.cpp   |  6 --
 wiki/renderengine.html                        | 10 +--
 8 files changed, 206 insertions(+), 47 deletions(-)
 create mode 100644 src/proc/mobject/session/defsregistry.cpp
 create mode 100644 src/proc/mobject/session/defsregistry.hpp

diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp
index 872d34724..4dcbef8c9 100644
--- a/src/common/configrules.hpp
+++ b/src/common/configrules.hpp
@@ -50,7 +50,7 @@
 #include "common/typelistutil.hpp"
 #include "common/singletonsubclass.hpp"
 
-//TODO: is it sensible to bring in the types explicitly here? (it's not necessary, but may be convienient...)
+//TODO: is it sensible to bring in the types explicitly here? (it's not necessary, but may be convenient...)
 #include "proc/mobject/session/track.hpp"
 #include "proc/asset/procpatt.hpp"
 #include "proc/asset/pipe.hpp"
@@ -73,11 +73,11 @@ namespace cinelerra
     {
     // The intention is to support the following style of Prolog code
     //
-    // retrieve(O, Cap) :- find(T), capabilities(Cap).
-    // retrieve(O, Cap) :- make(T), capabilities(Cap).
-    // capabilities(Q) :- call(Q).
-    //
-    // stream(T, mpeg) :- type(T, track), type(P, pipe), retrieve(P, stream(P,mpeg)), place_to(P, T).
+    //  resolve(O, Cap) :- find(O), capabilities(Cap).
+    //  resolve(O, Cap) :- make(O), capabilities(Cap).
+    //  capabilities(Q) :- call(Q).
+    //  
+    //  stream(T, mpeg) :- type(T, track), type(P, pipe), resolve(P, stream(P,mpeg)), place_to(P, T).
     //
     // The type guard is inserted auomatically, while the predicate implementations for
     // find/1, make/1, stream/2, and place_to/2 are to be provided by the target types.
@@ -111,6 +111,8 @@ namespace cinelerra
      * type of object. Registering  such a TypeHandler should create
      * the necessary handler functions to be installed into 
      * the Prolog system.
+     * @todo it can't be done exactly this way, but I leave it in
+     *       as a reminder for later, to show the intention
      */  
     template
     class TypeHandler
@@ -128,7 +130,8 @@ namespace cinelerra
      * the "frontside" interface: the Proc-Layer code can
      * use this QueryHandler to retrieve instances of the
      * type TY fulfilling the given Query. To start with,
-     * we use a mock implementation-
+     * we use a mock implementation. 
+     * (this code works and is already used 2/2008)
      * @see cinelerra::query::LookupPreconfigured
      * @see cinelerra::query::MockTable
      */
@@ -140,9 +143,12 @@ namespace cinelerra
       public:
         /** try to find or create an object of type TY 
          *  fulfilling the given query.
-         *  @return empty shared-ptr if not found,
+         *  @param solution object fulfilling the query. Will be bound or
+         *         unified (in case it's already bound) with the first solution.
+         *  @query any goals to be fulfilled by the solution.
+         *  @return false if resolution failed. In this case, solution ptr is empty.
          */
-        virtual shared_ptr resolve (const Query& q) = 0;
+        virtual bool resolve (shared_ptr solution, const Query& q) = 0;
       };
 
     // TODO: the Idea is to provide specialisations for the concrete types
diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp
index 51f4a4cb7..7466f7050 100644
--- a/src/common/query/mockconfigrules.hpp
+++ b/src/common/query/mockconfigrules.hpp
@@ -105,14 +105,18 @@ namespace cinelerra
         typedef typename WrapReturn::Wrapper Ret;
         
       public:
-        virtual Ret 
-        resolve (const Query& q)
+        /** (dummy) implementation of the QueryHandler interface */
+        virtual bool 
+        resolve (Ret& solution, const Query& q)
           {
             const any& entry = fetch_from_table_for (q.asKey());
             if (!isnil (entry))
-              return any_cast (entry);
-            else
-              return Ret(); // default-constructed empty smart ptr
+              if (! solution
+                 ||(solution && *solution == *candidate)    // simulates a real unification
+                 )
+                return solution = candidate;
+            
+            return solution = Ret();    // fail: return default-constructed empty smart ptr
           }
       };
     
diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp
index 52b78437b..1a0107ff5 100644
--- a/src/proc/mobject/session/defsmanager.cpp
+++ b/src/proc/mobject/session/defsmanager.cpp
@@ -22,8 +22,7 @@
 
 
 #include "proc/mobject/session/defsmanager.hpp"
-#include "proc/asset/procpatt.hpp"
-#include "proc/asset/pipe.hpp"
+#include "proc/mobject/session/defsregistry.hpp"
 #include "common/configrules.hpp"
 #include "common/error.hpp"
 
@@ -32,52 +31,70 @@
 using boost::format;
 
 using asset::Query;
-using asset::Pipe;
-using asset::ProcPatt;
-using asset::PProcPatt;
-
 using cinelerra::ConfigRules;
 using cinelerra::query::QueryHandler;
 using cinelerra::query::CINELERRA_ERROR_CAPABILITY_QUERY;
 
+
 namespace mobject
   {
   namespace session
     {
     using std::tr1::shared_ptr;
     
+    
+    namespace // Implementation details
+      {
+      DefsRegistry& defaultsRegistry; //////////////////TODO
+    }
+    
     /** initialize the most basic internal defaults. */
     DefsManager::DefsManager ()  throw()
     {
       TODO ("setup basic defaults of the session");
     }
-
+    
+    
     template
-    shared_ptr 
+    shared_ptr
     DefsManager::search  (const Query& capabilities)
     {
-      UNIMPLEMENTED ("search for default registered object, dont create");
+      defaultsRegistry.get (capabilities);
     }
     
+    
     template
     shared_ptr 
     DefsManager::create  (const Query& capabilities)
     {
-      UNIMPLEMENTED ("retrieve object and register as default");
+      shared_ptr res;
+      QueryHandler& typeHandler = ConfigRules::instance();  
+      typeHandler.resolve (res, capabilities);
+      if (res)
+        defaultsRegistry.put (res, capabilities);
+      return res;
     }
     
+    
     template
     bool 
-    DefsManager::define  (shared_ptr& defaultObj, const Query& capabilities)
+    DefsManager::define  (const shared_ptr& defaultObj, const Query& capabilities)
     {
-      UNIMPLEMENTED ("just do the defaults registration");
+      shared_ptr candidate (defaultObj);
+      QueryHandler& typeHandler = ConfigRules::instance();  
+      typeHandler.resolve (candidate, capabilities);
+      if (!candidate)
+        return false;
+      else
+        return defaultsRegistry.put (candidate, capabilities);
     }
-
+    
+    
     template
     bool 
-    DefsManager::forget  (shared_ptr& defaultObj)
+    DefsManager::forget  (const shared_ptr& defaultObj)
     {
-      UNIMPLEMENTED ("purge defaults registration");
+      defaultsRegistry.forget (defaultObj);
     }
 
     
@@ -85,9 +102,11 @@ namespace mobject
     shared_ptr 
     DefsManager::operator() (const Query& capabilities)
     {
-      TODO ("move this code to create()");
-      QueryHandler& typeHandler = ConfigRules::instance();  
-      shared_ptr res = typeHandler.resolve (capabilities);
+      shared_ptr res (search (capabilities));
+      if (res) 
+        return res;
+      else
+        res = create (capabilities); // not yet known as default, create new
       
       if (!res)
         throw cinelerra::error::Config ( str(format("The following Query could not be resolved: %s.") 
@@ -97,14 +116,34 @@ namespace mobject
         return res;
     }
 
+  } // namespace mobject::session
+
+} // namespace mobject
     
     
    /***************************************************************/
    /* explicit template instantiations for querying various Types */
    /***************************************************************/
 
-    template shared_ptr DefsManager::operator ()(const Query&); 
-    template PProcPatt        DefsManager::operator ()(const Query&); 
+#include "proc/asset/procpatt.hpp"
+#include "proc/asset/pipe.hpp"
+
+namespace mobject
+  {
+  namespace session
+    {
+
+    using asset::Pipe;
+    using asset::PPipe;
+    using asset::ProcPatt;
+    using asset::PProcPatt;
+    
+    
+    template PPipe     DefsManager::operator() (const Query&); 
+    template PProcPatt DefsManager::operator() (const Query&); 
+    
+    template bool DefsManager::define (const PPipe&, const Query&);
+    template bool DefsManager::forget (const PPipe&);
 
   } // namespace mobject::session
 
diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp
index 16c6a7bfc..3b01f6e6c 100644
--- a/src/proc/mobject/session/defsmanager.hpp
+++ b/src/proc/mobject/session/defsmanager.hpp
@@ -85,13 +85,13 @@ namespace mobject
          *  @note only a weak ref to the object is stored
          */ 
         template
-        bool define  (shared_ptr&, const cinelerra::Query&);
+        bool define  (const shared_ptr&, const cinelerra::Query&);
         
         /** remove the defaults registration of the given object, if there was such
          *  @return false if nothing has been changed because the object wasn't registered
          */
         template
-        bool forget  (shared_ptr&);
+        bool forget  (const shared_ptr&);
         
         
 // Q: can we have something along the line of...?
diff --git a/src/proc/mobject/session/defsregistry.cpp b/src/proc/mobject/session/defsregistry.cpp
new file mode 100644
index 000000000..c1d370bde
--- /dev/null
+++ b/src/proc/mobject/session/defsregistry.cpp
@@ -0,0 +1,48 @@
+/*
+  DefsManager  -  implementation of the default object store
+ 
+  Copyright (C)         CinelerraCV
+    2008,               Hermann Vosseler 
+ 
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+* *****************************************************/
+
+
+#include "proc/mobject/session/defsregistry.hpp"
+#include "common/error.hpp"
+
+//#include 
+
+//using boost::format;
+
+
+
+namespace mobject
+  {
+  namespace session
+    {
+    
+    /** @todo */
+    DefsRegistry::DefsRegistry ()  throw()
+    {
+      UNIMPLEMENTED ("initialize store for default objects");
+    }
+    
+
+  } // namespace mobject::session
+
+} // namespace mobject
+
diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp
new file mode 100644
index 000000000..33b57b634
--- /dev/null
+++ b/src/proc/mobject/session/defsregistry.hpp
@@ -0,0 +1,68 @@
+/*
+  DEFSREGISTRY.hpp  -  implementation of the default object store
+ 
+  Copyright (C)         CinelerraCV
+    2008,               Hermann Vosseler 
+ 
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+*/
+
+
+#ifndef MOBJECT_SESSION_DEFSREGISTRY_H
+#define MOBJECT_SESSION_DEFSREGISTRY_H
+
+
+#include "common/singleton.hpp"
+
+#include 
+
+
+
+namespace mobject
+  {
+  namespace session
+    {
+    using std::tr1::shared_ptr;
+
+
+    /**
+     * Organize a collection of preconfigured default objects.
+     * For various kinds of objects we can tweek the default parametrisation
+     * as part of the general session configuration. A ref to an instance of
+     * this class is accessible through the current session and can be used
+     * to fill in parts of the configuration of new objects, if the user 
+     * code didn't give more specific parameters. Necessary sub-objects 
+     * will be created on demand, and any default configuration, once
+     * found, will be remembered and stored with the current session.
+     */
+    class DefsRegistry
+      {
+      protected:
+        
+        DefsRegistry ();
+        friend class cinelerra::singleton::StaticCreate;
+        
+      public:
+        
+      };
+
+      
+
+  } // namespace mobject::session
+
+} // namespace mobject
+
+#endif
diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
index 920da9467..dc0cc22a4 100644
--- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
+++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
@@ -51,12 +51,6 @@ namespace asset
     using cinelerra::query::QueryHandler;
     
     
-    /** shortcut: query without creating new instances */
-    bool 
-    find (Query& q) 
-    { 
-      return Session::current->defaults.search (q); 
-    }
     
     /** shortcut: query for given Pipe-ID */
     bool 
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 0f3427f05..f1de96a22 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -2512,21 +2512,21 @@ Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming s
 
a given Render Engine configuration is a list of Processors. Each Processor in turn contains a Graph of ProcNode.s to do the acutal data processing. In order to cary out any calculations, the Processor needs to be called with a StateProxy containing the state information for this RenderProcess
 
-
+
//obviously, getting this one to work requires quite a lot of technical details to be planned and implemented.// This said...
 The intention is to get much more readable ("declarative") and changeable configuration as by programming the decision logic literately within the implementation of some object.
 
 !Draft
 As an example, specifying how a Track can be configured for connecting automatically to some "mpeg" bus (=pipe)
 {{{
-retrieve(O, Cap) :- find(O), capabilities(Cap).
-retrieve(O, Cap) :- make(O), capabilities(Cap).
+resolve(O, Cap) :- find(O), capabilities(Cap).
+resolve(O, Cap) :- make(O), capabilities(Cap).
 capabilities(Q) :- call(Q).
 
-stream(T, mpeg) :- type(T, track), type(P, pipe), retrieve(P, stream(P,mpeg)), place_to(P, T).
+stream(T, mpeg) :- type(T, track), type(P, pipe), resolve(P, stream(P,mpeg)), place_to(P, T).
 }}}
 
-Then, running the goal {{{:-retrieve(T, stream(T,mpeg)).}}} would search a Track object, try to retrieve a pipe object with stream-type=mpeg and associate the track with this pipe. This relies on a predicate "stream(P,mpeg)" implemented (natively) for the pipe object. So, "Cap" is the query issued from calling code &mdash; here {{{stream(T,mpeg)}}}, the type guard {{{type(T, track)}}} will probably be handled or inserted automatically, while the predicate implementations for find/1, make/1, stream/2, and place_to/2 are to be provided by the target types.
+Then, running the goal {{{:-resolve(T, stream(T,mpeg)).}}} would search a Track object, try to retrieve a pipe object with stream-type=mpeg and associate the track with this pipe. This relies on a predicate "stream(P,mpeg)" implemented (natively) for the pipe object. So, "Cap" is the query issued from calling code &mdash; here {{{stream(T,mpeg)}}}, the type guard {{{type(T, track)}}} will probably be handled or inserted automatically, while the predicate implementations for find/1, make/1, stream/2, and place_to/2 are to be provided by the target types.
 * __The supporting system__ had to combine several code snippets into one rule system to be used for running queries, with some global base rules, rules injected by each individual participating object kind and finally user provided rules added by the current session. The actual query is bound to "Cap" (and consequently run as a goal by {{{call(Q)}}}). The implementation needs to provide a symbol table associating variable terms (like "T" or "P") to C/C++ object types, enabling the participating object kinds to register their specific predicate implementations. This is crucial, because there can be no general scheme of object-provided predicates (for each object kind different predicates make sense, e.g. [[pipes|PipeHandling]] have other possibilities than [[wiring requests|WiringRequest]]). Basically, a query issues a Prolog goal, which in turn evaluates domain specific predicates provided by the participating objects and thus calls back into C/C++ code. The supporting system maintains the internal connection (via the "type" predicate) such that from Prolog viewpoint it looks as if we were binding Variables directly to object instances. (there are some nasty technical details because of the backtracking nature of Prolog evaluations which need to be hidden away)
 * Any __participating object kind__ needs a way to declare domain specific predicates, thus triggering the registration of the necessary hooks within the supporting system. Moreover, it should be able to inject further prolog code (as shown in the example above with the {{{strem(T, mpeg)}}} predicate. For each of these new domain specific predicates, there needs to be a functor which can be invoked when the C implementation of the predicate is called from Prolog (in some cases even later, when the final solution is "executed", e.g. a new instance has been created and now some properties need to be set).
 

From 6596699f6ec0a20be9b66acf5a7456437d7c816b Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 31 Mar 2008 03:21:28 +0200
Subject: [PATCH 08/24] WIP code for handling registration of defaults objects.
 Missing some TODOs and test coverage

---
 src/common/appconfig.cpp                      |   7 -
 src/common/configrules.hpp                    |   2 +-
 src/common/multithread.hpp                    |   1 +
 src/common/query.cpp                          |  23 +-
 src/common/query.hpp                          |   5 +
 src/common/query/mockconfigrules.hpp          |  12 +-
 src/common/util.hpp                           |  33 +++
 src/proc/asset/struct.hpp                     |   6 +-
 src/proc/mobject/session/defsmanager.cpp      |  29 +-
 src/proc/mobject/session/defsmanager.hpp      |  10 +-
 src/proc/mobject/session/defsregistry.cpp     |  48 ----
 src/proc/mobject/session/defsregistry.hpp     | 257 +++++++++++++++++-
 src/proc/mobject/session/session.cpp          |   1 +
 src/proc/mobject/session/sessmanagerimpl.cpp  |   1 +
 .../mobject/session/defsmanagerimpltest.cpp   |   3 +-
 wiki/renderengine.html                        |   4 +-
 16 files changed, 350 insertions(+), 92 deletions(-)
 delete mode 100644 src/proc/mobject/session/defsregistry.cpp

diff --git a/src/common/appconfig.cpp b/src/common/appconfig.cpp
index 612376022..17a245cdf 100644
--- a/src/common/appconfig.cpp
+++ b/src/common/appconfig.cpp
@@ -37,13 +37,6 @@ using util::isnil;
 namespace lumiera
   {
   
-  /** This internal pointer to the single instance is deliberately
-   *  not initialized (i.e. rely on implicit initialisation to 0),
-   *  because when static init reaches this definition, the
-   *  Appconfig::instance() probably already has been called
-   *  by another compilation unit. This is ugliy, but preferable
-   *  to beeing dependant on inclusion order of headers. */
-//  scoped_ptr Appconfig::theApp_;
 
 #ifndef LUMIERA_VERSION
 #define LUMIERA_VERSION 3++devel
diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp
index a7f0fe37a..890a36a86 100644
--- a/src/common/configrules.hpp
+++ b/src/common/configrules.hpp
@@ -148,7 +148,7 @@ namespace lumiera
          *  @query any goals to be fulfilled by the solution.
          *  @return false if resolution failed. In this case, solution ptr is empty.
          */
-        virtual bool resolve (shared_ptr solution, const Query& q) = 0;
+        virtual bool resolve (shared_ptr& solution, const Query& q) = 0;
       };
 
     // TODO: the Idea is to provide specialisations for the concrete types
diff --git a/src/common/multithread.hpp b/src/common/multithread.hpp
index 1dbbac519..0e936964c 100644
--- a/src/common/multithread.hpp
+++ b/src/common/multithread.hpp
@@ -27,6 +27,7 @@
 #define LUMIERA_MULTITHREAD_H
 
 #include "nobugcfg.h"
+#include "common/util.hpp"
 
 
 namespace lumiera
diff --git a/src/common/query.cpp b/src/common/query.cpp
index 266c628e5..09c642f1c 100644
--- a/src/common/query.cpp
+++ b/src/common/query.cpp
@@ -33,6 +33,7 @@ using std::map;
 using boost::regex;
 using boost::smatch;
 using boost::regex_search;
+using boost::sregex_iterator;
 using boost::algorithm::is_upper;
 using boost::algorithm::is_alpha;
 
@@ -63,6 +64,9 @@ namespace lumiera
     namespace // Implementation details
       {
       map regexTable;
+      
+      Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\)"; 
+      regex findPredicate (string("(\\w+)")+matchArgument);
     }
     
     /** (preliminary) helper: instead of really parsing and evaluating the terms,
@@ -76,7 +80,7 @@ namespace lumiera
     {
       
       if (!contains (regexTable, sym))
-        regexTable[sym] = regex (string(sym)+="\\(\\s*([\\w_\\.\\-]+)\\s*\\)"); 
+        regexTable[sym] = regex (string(sym)+=matchArgument); 
       
       smatch match;
       if (regex_search (termString, match, regexTable[sym]))
@@ -86,6 +90,23 @@ namespace lumiera
     
   } 
   
+    
+    /** @note this is a very hackish preliminary implementation. 
+     *  The regex used will flounder when applied to nested terms. 
+     *  We need a real parser for predicate logic terms (which we
+     *  probably get for free when we embed a prolog system)...
+     */
+    uint 
+    countPraed (const string& q)
+    {
+      uint cnt (0);
+      sregex_iterator end;
+      for (sregex_iterator i (q.begin(),q.end(), findPredicate); 
+           i != end; ++i)
+        ++cnt;
+      return cnt;
+    }
+    
   } // namespace query
     
   
diff --git a/src/common/query.hpp b/src/common/query.hpp
index 591c45c74..b7d22498b 100644
--- a/src/common/query.hpp
+++ b/src/common/query.hpp
@@ -71,6 +71,11 @@ namespace lumiera
     
     const string extractID (Symbol, const string& termString);
 
+    /** count the top-level predicates in the query string.
+     *  usable for ordering queries, as more predicates usually
+     *  mean more conditions, i.e. more constriction
+     */
+    uint countPraed (const string&);
     
   
   } // namespace query
diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp
index 909f7ee50..5a1531cc9 100644
--- a/src/common/query/mockconfigrules.hpp
+++ b/src/common/query/mockconfigrules.hpp
@@ -111,10 +111,14 @@ namespace lumiera
           {
             const any& entry = fetch_from_table_for (q.asKey());
             if (!isnil (entry))
-              if (! solution
-                 ||(solution && *solution == *candidate)    // simulates a real unification
-                 )
-                return solution = candidate;
+              {
+                const Ret& candidate (any_cast (entry));
+                if (! solution
+//                   ||(solution && *solution == *candidate)    // simulates a real unification
+//////////////TODO: not only Assets (i.e. define comparison Operators on Assets!)
+                   )
+                  return solution = candidate;
+              }
             
             return solution = Ret();    // fail: return default-constructed empty smart ptr
           }
diff --git a/src/common/util.hpp b/src/common/util.hpp
index a4c3c4b54..d56a45432 100644
--- a/src/common/util.hpp
+++ b/src/common/util.hpp
@@ -124,6 +124,39 @@ namespace util
   }
   
   
+  /** remove all elements fulfilling a given predicate
+   *  from a (sorted) set.
+   *  @return true if anything has been removed. */
+  template
+  bool remove_if (SET& set, PRD test)
+    {
+      typedef typename SET::iterator Itor;
+      bool found = false;
+      Itor   end = set.end();
+      Itor begin = set.begin();
+      Itor   pos = begin;
+      while (pos!=end)
+        {
+          if (!test (*pos)) ++pos;
+          else
+            {
+              found = true;
+              if (pos==begin) 
+                {
+                  set.erase(pos);
+                  pos = begin = set.begin();
+                }
+              else
+                {
+                  set.erase(pos--);
+                  ++pos;
+                }
+              end = set.end();
+        }   }
+      return found;
+    }
+  
+  
   /** shortcut for operating on all elements of a container.
    *  Isn't this already defined somewhere? It's so obvious..
    */
diff --git a/src/proc/asset/struct.hpp b/src/proc/asset/struct.hpp
index 25a14ee25..df6c5f1f6 100644
--- a/src/proc/asset/struct.hpp
+++ b/src/proc/asset/struct.hpp
@@ -45,13 +45,13 @@
 #include 
 #include 
 
-using std::string;
-using std::wstring;
-using boost::scoped_ptr;
 
 
 namespace asset
   {
+  using std::string;
+  using std::wstring;
+  using boost::scoped_ptr;
   using lumiera::Query;
   
   class Struct;
diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp
index 9188df748..4c5cd9f82 100644
--- a/src/proc/mobject/session/defsmanager.cpp
+++ b/src/proc/mobject/session/defsmanager.cpp
@@ -30,7 +30,6 @@
 
 using boost::format;
 
-using asset::Query;
 using lumiera::ConfigRules;
 using lumiera::query::QueryHandler;
 using lumiera::query::LUMIERA_ERROR_CAPABILITY_QUERY;
@@ -43,13 +42,10 @@ namespace mobject
     using std::tr1::shared_ptr;
     
     
-    namespace // Implementation details
-      {
-      DefsRegistry& defaultsRegistry; //////////////////TODO
-    }
     
     /** initialize the most basic internal defaults. */
     DefsManager::DefsManager ()  throw()
+      : defsRegistry(new DefsRegistry)
     {
       TODO ("setup basic defaults of the session");
     }
@@ -59,7 +55,17 @@ namespace mobject
     shared_ptr
     DefsManager::search  (const Query& capabilities)
     {
-      defaultsRegistry.get (capabilities);
+      shared_ptr res;
+      QueryHandler& typeHandler = ConfigRules::instance();  
+      for (DefsRegistry::Iter i = defsRegistry->candidates(capabilities); 
+           i.hasNext(); ++i )
+        {
+          shared_ptr res (*i);
+          typeHandler.resolve (res, capabilities);
+          if (res)
+            return res;
+        }
+      return res; // "no solution found"
     }
     
     
@@ -71,7 +77,7 @@ namespace mobject
       QueryHandler& typeHandler = ConfigRules::instance();  
       typeHandler.resolve (res, capabilities);
       if (res)
-        defaultsRegistry.put (res, capabilities);
+        defsRegistry->put (res, capabilities);
       return res;
     }
     
@@ -86,7 +92,7 @@ namespace mobject
       if (!candidate)
         return false;
       else
-        return defaultsRegistry.put (candidate, capabilities);
+        return defsRegistry->put (candidate, capabilities);
     }
     
     
@@ -94,7 +100,7 @@ namespace mobject
     bool 
     DefsManager::forget  (const shared_ptr& defaultObj)
     {
-      defaultsRegistry.forget (defaultObj);
+      return defsRegistry->forget (defaultObj);
     }
 
     
@@ -119,8 +125,9 @@ namespace mobject
   } // namespace mobject::session
 
 } // namespace mobject
-    
-    
+
+
+
    /***************************************************************/
    /* explicit template instantiations for querying various Types */
    /***************************************************************/
diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp
index da98fe6c1..753b452d2 100644
--- a/src/proc/mobject/session/defsmanager.hpp
+++ b/src/proc/mobject/session/defsmanager.hpp
@@ -27,6 +27,8 @@
 
 #include "common/query.hpp"
 
+#include 
+#include 
 #include 
 
 
@@ -36,6 +38,9 @@ namespace mobject
   namespace session
     {
     using std::tr1::shared_ptr;
+    using boost::scoped_ptr;
+    
+    class DefsRegistry;
 
 
     /**
@@ -48,8 +53,10 @@ namespace mobject
      * will be created on demand, and any default configuration, once
      * found, will be remembered and stored with the current session.
      */
-    class DefsManager
+    class DefsManager : private boost::noncopyable
       {
+        scoped_ptr defsRegistry;
+        
       protected:
         
         DefsManager ()  throw();
@@ -64,6 +71,7 @@ namespace mobject
         template
         shared_ptr operator() (const lumiera::Query&);
         
+        
         /** search through the registered defaults, never create anything.
          *  @return object fulfilling the query, \c empty ptr if not found. 
          */
diff --git a/src/proc/mobject/session/defsregistry.cpp b/src/proc/mobject/session/defsregistry.cpp
deleted file mode 100644
index 4eea8df6c..000000000
--- a/src/proc/mobject/session/defsregistry.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-  DefsManager  -  implementation of the default object store
- 
-  Copyright (C)         Lumiera.org
-    2008,               Hermann Vosseler 
- 
-  This program is free software; you can redistribute it and/or
-  modify it under the terms of the GNU General Public License as
-  published by the Free Software Foundation; either version 2 of the
-  License, or (at your option) any later version.
- 
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
- 
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- 
-* *****************************************************/
-
-
-#include "proc/mobject/session/defsregistry.hpp"
-#include "common/error.hpp"
-
-//#include 
-
-//using boost::format;
-
-
-
-namespace mobject
-  {
-  namespace session
-    {
-    
-    /** @todo */
-    DefsRegistry::DefsRegistry ()
-    {
-      UNIMPLEMENTED ("initialize store for default objects");
-    }
-    
-
-  } // namespace mobject::session
-
-} // namespace mobject
-
diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp
index e6f102738..d52867f42 100644
--- a/src/proc/mobject/session/defsregistry.hpp
+++ b/src/proc/mobject/session/defsregistry.hpp
@@ -25,10 +25,14 @@
 #define MOBJECT_SESSION_DEFSREGISTRY_H
 
 
-#include "common/singleton.hpp"
+#include "common/multithread.hpp"
+#include "common/query.hpp"
+#include "common/util.hpp"
 
+#include 
+#include 
 #include 
-
+#include 
 
 
 namespace mobject
@@ -36,27 +40,254 @@ namespace mobject
   namespace session
     {
     using std::tr1::shared_ptr;
+    using std::tr1::weak_ptr;
+    using lumiera::Thread;
+    using lumiera::Query;
+    
+    
+    namespace // Implementation details
+      {
+      struct TableEntry 
+        {
+          virtual ~TableEntry() {};
+        };
+      
+      /** we maintain an independent defaults registry
+       *  for every participating kind of objects */
+      typedef std::vector > Table;
+      
+      uint maxSlots (0); ///< number of different registered Types
+      
+      
+      /** holding a single "default object" entry */
+      template
+      struct Record
+        {
+          uint degree;
+          Query query;
+          weak_ptr objRef;
+          
+          Record (const Query& q, const shared_ptr& obj)
+            : degree (lumiera::query::countPraed (q)),
+              query (q),
+              objRef (obj)
+            { }
 
+          
+          struct Search  ///< Functor searching for a specific object
+            {
+              Search (const shared_ptr& obj)
+                : obj_(obj) { }
+
+              const shared_ptr& obj_;
+              
+              bool 
+              operator() (const Record& rec)
+              {
+                shared_ptr storedObj (rec.objRef.lock());
+                return storedObj && pAsset(storedObj)==pAsset(obj_);               //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!)
+              }
+            };
+          
+          struct OrderRelation
+          {
+            inline bool
+            operator() (Record one, Record two) ///< @note doesn't touch the objRef
+              {
+                return (  one.degree < two.degree
+                       ||(one.degree == two.degree && one.query < two.query)
+                       );
+              }
+          };
+        };
+        
+      /** every new kind of object (Type) creates a new
+       *  slot in the main Table holding all registered 
+       *  default objects. Each slot actually holds a
+       *  separate tree (set) of registry entries
+       */
+      template
+      struct Slot : public TableEntry
+        {
+          typedef typename Record::OrderRelation Ordering;
+          typedef std::set, Ordering> Registry;
+          
+          Registry registry;
+          static size_t index; ///< where to find this Slot in every Table
+          
+          static Registry&
+          access (Table& table)
+            {
+              if ( !index 
+                 || index > table.size() 
+                 ||!table[index-1])
+                createSlot (table);
+              
+              ASSERT (0 < index && index<=table.size() && table[index-1]);
+              Slot* item = static_cast (table[index-1].get());
+              return item->registry;
+            }
+          
+        private:
+          static void
+          createSlot (Table& table)
+            {
+              Thread::Lock guard   SIDEEFFECT;
+              if (!index)
+                index = ++maxSlots;
+              if (index > table.size())
+                table.resize (index);
+              table[index-1].reset(new Slot);   
+            }
+        };
+        
+        
+      // static vars to organize one Table Slot per type....
+      template
+      size_t Slot::index (0);
+      
+    }
+    
 
     /**
-     * Organize a collection of preconfigured default objects.
-     * For various kinds of objects we can tweek the default parametrisation
-     * as part of the general session configuration. A ref to an instance of
-     * this class is accessible through the current session and can be used
-     * to fill in parts of the configuration of new objects, if the user 
-     * code didn't give more specific parameters. Necessary sub-objects 
-     * will be created on demand, and any default configuration, once
-     * found, will be remembered and stored with the current session.
+     * @internal Helper for organizing preconfigured default objects.
+     * Maintaines a collection of objects known or encountered as "default"
+     * for a given type. This collection is ordered by "degree of constriction",
+     * which is implemented by counting the number of predicates in the query
+     * used to define or identify each object.
+     * Accessing an object identified by a query causes the query to be resolved
+     * (executed in prolog), which may result in some additional properties of
+     * the object being bound or specified.
+     * @todo as of 3/2008 the real query implementation is missing, and the
+     * exact behaviour has to be defined.
      */
-    class DefsRegistry
+    class DefsRegistry : private boost::noncopyable
       {
+        Table table_;
+        
       protected:
         
-        DefsRegistry ();
-        friend class lumiera::singleton::StaticCreate;
+        DefsRegistry () {};
+        friend class DefsManager;
         
       public:
+        /** used for enumerating solutions */
+        template
+        class Iter          
+          {
+            friend class DefsRegistry;
+            typedef typename Slot::Registry::iterator II;
+            
+            II p,i,e;
+            shared_ptr next, ptr;
+            
+            Iter (II from, II to) ///< just ennumerates the given range 
+              : p(from), i(from), e(to)
+              {
+                if (i!=e) ++i;  // p is next to be tested, i always one ahead
+                ptr = findNext();
+              }
+            
+            Iter (II match, II from, II to) ///< returns direct match first, then ennumerates 
+              : p(match), i(from), e(to),
+                next(), ptr(findNext())
+              { }
+            
+            shared_ptr findNext ()  throw()
+              {
+                while (!next)
+                  {
+                    if (p==e) break;
+                    next = p->objRef.lock();
+                    p = i++;
+                  }
+                return next;
+              }
+            
+          
+          public:
+            shared_ptr operator* () { return ptr; }
+            bool hasNext ()              { return next || findNext(); }
+            Iter& operator++ ()           
+              { 
+                ptr=findNext();
+                next.reset();
+                return *this; 
+              }
+          };
         
+        /** find a sequence of "default" objects possibliy matching the query.
+         *  If there was a registration for some object of the given kind with
+         *  the \em same query, this one will be first in the sequence. Besides,
+         *  the sequence will yield all still existing registered "default" objects
+         *  of this kind, ordered ascending by "degree of constriction", i.e. starting
+         *  with the object registered togehter with the shortest query.
+         *  @return a forward input iterator yielding this sequence
+         *  @note none of the queries will be evaluated (we're just counting predicates)
+         */
+        template
+        Iter candidates (const Query& query)
+          {
+            shared_ptr dummy;
+            Record entry (query, dummy);
+            typedef typename Slot::Registry Registry;
+            Registry& registry = Slot::access(table_);
+            
+            // try to get a possible direct match (same query)
+            typename Registry::iterator pos = registry.find (entry);
+            typename Registry::iterator end = registry.end();
+            
+            if (pos==end)
+              return Iter (registry.begin(), end);        // just ennumerate contents
+            else
+              return Iter (pos, registry.begin(), end); // start with direct match
+          }
+        
+        
+        /** register the object as being "default" when searching something
+         *  similar as designated by the given query. Only a weak ref is stored.
+         *  @param obj Will be rebound, if another object is already stored.
+         *  @return true if object has actually been stored, false if another
+         *          object is registered for exactly the same query. In this
+         *          case, also the param obj shared-ptr is rebound!
+         */
+        template
+        bool put (shared_ptr& obj, const Query& query)
+          {
+            Record entry (query, obj);
+            typedef typename Slot::Registry Registry;
+            typedef typename Registry::iterator RIter;
+            
+            Registry& registry = Slot::access(table_);
+            RIter pos = registry.lower_bound (entry);
+            if (  pos!=registry.end()
+               && pos->query == query)
+              {
+                shared_ptr storedObj (pos->objRef.lock());
+                if (storedObj)
+                  return (pAsset(storedObj) == pAsset(obj));               //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!)
+                else
+                  // use the opportunity and purge the expired entry
+                  registry.erase(pos);
+              }
+            // no existing entry....
+            registry.insert(pos, entry);
+            return true;
+          }
+        
+        
+        /** if this object is registered as "default" in some way, drop the registration.
+         *  @return false if the object wasn't registered at all.
+         */
+        template
+        bool forget (const shared_ptr& obj)
+          {
+            typedef typename Slot::Registry Registry;
+            typedef typename Record::Search SearchFunc;
+            
+            Registry& registry = Slot::access(table_);
+            return util::remove_if(registry, SearchFunc (obj));
+          }
       };
 
       
diff --git a/src/proc/mobject/session/session.cpp b/src/proc/mobject/session/session.cpp
index 3544ed4a0..5c4bb8592 100644
--- a/src/proc/mobject/session/session.cpp
+++ b/src/proc/mobject/session/session.cpp
@@ -34,6 +34,7 @@
 #include "proc/mobject/session.hpp"
 #include "proc/mobject/session/sessionimpl.hpp"
 #include "proc/mobject/session/defsmanager.hpp"
+#include "proc/mobject/session/defsregistry.hpp"
 
 #include "common/singleton.hpp"
 
diff --git a/src/proc/mobject/session/sessmanagerimpl.cpp b/src/proc/mobject/session/sessmanagerimpl.cpp
index 6b5749463..8195e4fbe 100644
--- a/src/proc/mobject/session/sessmanagerimpl.cpp
+++ b/src/proc/mobject/session/sessmanagerimpl.cpp
@@ -39,6 +39,7 @@
 #include "proc/mobject/session.hpp"
 #include "proc/mobject/session/sessionimpl.hpp"
 #include "proc/mobject/session/defsmanager.hpp"
+#include "proc/mobject/session/defsregistry.hpp"
 #include "common/error.hpp"
 
 using boost::scoped_ptr;
diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
index a4c18f2d9..fff9e6966 100644
--- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
+++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
@@ -120,7 +120,8 @@ namespace asset
             
             // issue a ConfigQuery directly, without involving the DefaultsManager
             QueryHandler& typeHandler = ConfigRules::instance();  
-            PPipe pipe1 = typeHandler.resolve (query_for_streamID);
+            PPipe pipe1;
+            typeHandler.resolve (pipe1, query_for_streamID);
             ASSERT (pipe1);
             
             ASSERT (!find (pipe1->getPipeID()));
diff --git a/wiki/renderengine.html b/wiki/renderengine.html
index 80a57a86b..5f5dbcd9d 100644
--- a/wiki/renderengine.html
+++ b/wiki/renderengine.html
@@ -865,8 +865,8 @@ Taken precisely, the "degree of constriction" yields only a partial or
 As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see {{{MockConfigQuery}}}). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, because the system isn't capable to do any real resolution &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.
 
-
-
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. The Queries are implemented by [[configuration rules|ConfigRules]]
+
+
For several components and properties there is an implicit default value or configuration; it is stored alongside with the session. The intention is that defaults never create an error, instead, they are to be extended silently on demand. Objects configured according to this defaults can be retrieved at the [[Session]] interface by a set of overloaded functions {{{Session::current->default(Query<TYPE> ("query string"))}}}, where the //query string // defines a capability query similar to what is employed for pipes, stream types, codecs etc. This query mechanism is implemented by [[configuration rules|ConfigRules]]
 
 !!!!what is denoted by {{{default}}}?
 {{{default(Obj)}}} is a predicate expressing that the object {{{Obj}}} can be considered the default setup under the given conditions. Using the //default// can be considered as a shortcut for actually finding a exact and unique solution. The latter would require to specify all sorts of detailed properties up to the point where only one single object can satisfy all conditions. On the other hand, leaving some properties unspecified would yield a set of solutions (and the user code issuing the query had to provide means for selecting one soltution from this set). Just falling back on the //default// means that the user code actually doesn't care for any additional properties (as long as the properties he //does// care for are satisfied). Nothing is said specifically on //how//&nbsp; this default gets configured; actually there can be rules //somewhere,// and, additionally, anything encountered once while asking for a default can be re-used as default under similar circumstances.

From e9a364f7adb516ec906a4238bf558e2acef6de73 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Tue, 1 Apr 2008 06:57:00 +0200
Subject: [PATCH 09/24] WIP test covering implementation of the default object
 registry (doesn't yet compile)

---
 tests/components/common/typelistutiltest.cpp  |   4 +-
 .../mobject/session/defsmanagerimpltest.cpp   |   8 +-
 .../proc/mobject/session/defsmanagertest.cpp  |   2 +-
 .../mobject/session/defsregistryimpltest.cpp  | 308 ++++++++++++++++++
 wiki/renderengine.html                        |   4 +-
 5 files changed, 319 insertions(+), 7 deletions(-)
 create mode 100644 tests/components/proc/mobject/session/defsregistryimpltest.cpp

diff --git a/tests/components/common/typelistutiltest.cpp b/tests/components/common/typelistutiltest.cpp
index d0e2f683f..73cab939e 100644
--- a/tests/components/common/typelistutiltest.cpp
+++ b/tests/components/common/typelistutiltest.cpp
@@ -56,8 +56,8 @@ namespace lumiera
       
       /** template for generating lots of different test types */
       template
-      struct Block    
-        { 
+      struct Block
+        {
           static string name;
           string talk()   { return name+"::eat(..)"; }  
         };
diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
index fff9e6966..ff94f8a85 100644
--- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
+++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp
@@ -106,10 +106,14 @@ namespace asset
             
             // now declare that these objects should be considered "default"
             ASSERT (Session::current->defaults.define (pipe1, Query (""))); // unrestricted default
-            ASSERT (Session::current->defaults.define (pipe1, Query ("stream("+sID+")")));
+            ASSERT (Session::current->defaults.define (pipe2, Query ("stream("+sID+")")));
             
             ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default");
             ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default");
+            
+            ASSERT (sID != pipe1->getProcPatt()->queryStreamID(), "accidental clash");
+            ASSERT (!Session::current->defaults.define (pipe1, Query ("stream("+sID+")")));
+                    // can't be registered with this query, due to failure caused by wrong stream-ID
           }
         
         
@@ -143,7 +147,7 @@ namespace asset
             // now de-register the pipe as "default Pipe"
             ASSERT (Session::current->defaults.forget (pipe));
             ASSERT (!find (pipe->getPipeID()));
-            ASSERT (cnt == pipe.use_count());   // indicates that DefaultsManager only holds a weak ref.
+            ASSERT (cnt == pipe.use_count());   // indicates DefaultsManager holding only a weak ref.
           }
       };
     
diff --git a/tests/components/proc/mobject/session/defsmanagertest.cpp b/tests/components/proc/mobject/session/defsmanagertest.cpp
index b1c768836..1e66a0553 100644
--- a/tests/components/proc/mobject/session/defsmanagertest.cpp
+++ b/tests/components/proc/mobject/session/defsmanagertest.cpp
@@ -152,7 +152,7 @@ namespace asset
                 PPipe pipe1 = Session::current->defaults (query_for_pID);
                 hash = pipe1->getID();
               }
-             // now AssetManager should have the only ref
+             // now AssetManager should hold the only ref
             ID assetID (hash);
               
             AssetManager& aMang (AssetManager::instance());
diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
new file mode 100644
index 000000000..49c2fb343
--- /dev/null
+++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
@@ -0,0 +1,308 @@
+/*
+  DefsRegistryImpl(Test)  -  verifying correct behaviour of the defaults registry
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+* *****************************************************/
+
+
+#include "common/test/run.hpp"
+#include "common/util.hpp"
+
+#include "proc/mobject/session/defsregistry.hpp"
+#include "common/factory.hpp"
+#include "common/query.hpp"
+
+#include 
+#include 
+#include 
+#include 
+
+using lumiera::Query;
+
+using std::tr1::shared_ptr;
+using boost::scoped_ptr;
+using boost::format;
+using util::isnil;
+using std::string;
+using std::rand;
+using std::map;
+
+
+namespace mobject
+  {
+  namespace session
+    {
+    namespace test
+      {
+      
+      const uint MAX_DEGREE_RAND = 9;
+      
+      format typePatt ("Dummy<%2i>");
+      format instancePatt ("obj_%s_%i");
+      format predicatePatt ("%s_%2i( %s )");
+      
+      const string garbage ("asdfghjklqwertzuiop");
+      
+      
+      /** create a random new ID */
+      string 
+      newID (Symbol prefix)
+      {
+        return str (instancePatt % prefix % rand());
+      }
+      
+      
+      /** template for generating some different test types */
+      template
+      struct Dummy
+        {
+          static string name;
+          string instanceID;
+          
+          Dummy () : instanceID (newID (name)) {}
+        };
+        
+      template
+      string Dummy::name = str (typePatt % I);
+      
+      
+      
+      /** fabricating (random) query strings */
+      string
+      q_str (int degree=0)
+      {
+        string fake;
+        if (!degree) 
+          degree = 1 + rand() % MAX_DEGREE_RAND;
+        while (0 < --degree)
+          fake += garbage_term() + ", ";
+        fake += garbage_term() + ".";
+        return fake;
+      }
+      
+      string
+      garbage_term ()
+      {
+        return (predicatePatt 
+               % char ('a'+ rand() % 26)
+               % rand() % 100
+               % garbage.substr(rand() % 17 , 3)
+               );
+      }
+      
+      
+      /************************************************************************
+       * @test build an registry table (just for this test) configured for
+       *       some artificial test Types. Register some entries and verify
+       *       the intended behaviour of the storage structure.
+       * @see  DefsManagerImpl_test for checking the implementation details
+       *       in the actual context used in Lumiera.
+       */
+      class DefsRegistryImpl_test : public Test
+        {
+          scoped_ptr reg_;
+          
+          typedef shared_ptr > O;
+          typedef shared_ptr > P;
+          
+          typedef Query > Q13;
+          typedef Query > Q23;
+
+          // fabricating Objects wrapped into smart-ptrs
+          lumiera::factory::RefcountPtr oFac;
+          lumiera::factory::RefcountPtr

pFac; + + O o1, o2, o3; + Q13 q1, q2, q3, q4, q5; + map ps; + + DefsRegistryImpl_test () + : o1 (oFac()), o2 (oFac()), o3 (oFac()), + q1 (q_str (1)), + q2 (q_str (2)), + q3 (q_str (3)), + q4 (q_str (4)), + q5 (q_str (5)) + { } + + + virtual void run(Arg arg) + { + this->reg_.reset (new DefsRegistry); + + fill_table (); + check_query (); + check_remove (); + } + + + + + void fill_table () + { + // at start the registry is indeed empty + // thus a query doesnt yield any results.... + ASSERT ( ! *(reg_->candidates(Q13 ("something"))) ); + + reg_->put (o1, q5); + reg_->put (o2, q4); + reg_->put (o3, q3); + reg_->put (o3, q2); + reg_->put (o2, q1); + reg_->put (o1, Q13); // the empty query + + ps.clear(); + for (int i=0; i<100; ++i) + { + P px (pFac()); + Q23 qx (q_str()); + ps[qx] = px; + reg_->put (px, qx); + px.instanceID = qx; + } + } + + + void check_query () + { + DefsRegistry::Iter > i; + i = reg_->candidates(Q13 ("irrelevant query")); + ASSERT ( i.hasNext()); + ASSERT ( *i++ == o1); // ordered according to the degree of the queries + ASSERT ( *i++ == o2); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o2); + ASSERT ( *i == o1); + ASSERT (!i.hasNext()); + + i = reg_->candidates(q3); + ASSERT ( *i++ == o3); // found by direct match + ASSERT ( *i++ == o1); // followed by the ordered ennumeration + ASSERT ( *i++ == o2); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o2); + ASSERT ( *i++ == o1); + ASSERT (!i.hasNext()); + + i = reg_->candidates(Q13); + ASSERT ( *i++ == o1); // found by direct match to the empty query + ASSERT ( *i++ == o1); + ASSERT ( *i++ == o2); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o2); + ASSERT ( *i++ == o1); + ASSERT (!i.hasNext()); + + uint d=0; + uint d_prev=0; + for (i = reg_->candidates(Q23 ("some crap")); + i.hasNext(); ++i ) + { + ASSERT ( *i ); + Q23 qx = (*i)->instanceID; + ASSERT ( ps[qx] == (*i)); + d = lumiera::query::countPraed (qx); + ASSERT ( d_prev <= d ); + d_prev = d; + } + ASSERT (!i.hasNext()); + + // calling with an arbitrary (registered) query + // yields the corresponding object at start of the ennumeration + i = reg_->candidates(ps.begin()->first); + ASSERT ( *i == ps.begin()->second); + + } + + + void check_remove () + { + reg_->forget (o2); + + DefsRegistry::Iter > i; + i = reg_->candidates(q4); + ASSERT ( i.hasNext()); + ASSERT ( *i++ == o1); // ordered according to the degree of the queries + // but the o2 entries are missing + ASSERT ( *i++ == o3); + ASSERT ( *i++ == o3); + // missing + ASSERT ( *i == o1); + ASSERT (!i.hasNext()); + + o3.reset(); // killing the only reference.... + // expires the weak ref in the registry + + i = reg_->candidates(Q13 ("something")); + ASSERT ( i.hasNext()); + ASSERT ( *i++ == o1); // ordered according to the degree of the queries + // but now also the o3 entries are missing... + ASSERT ( *i == o1); + ASSERT (!i.hasNext()); + + ASSERT ( reg_->put (o1, q5)); // trying to register the same object at the same place + // doesn't change anything (but counts as "success") + i = reg_->candidates(q5); + ASSERT ( *i++ == o1); // direct match + ASSERT ( *i++ == o1); + ASSERT ( *i++ == o1); + ASSERT (!i.hasNext()); + + ASSERT ( reg_->put (o2, q5)); // trying to (re)register o2 with a existing query + // counts as failure (nothing chages) + i = reg_->candidates(q5); + ASSERT ( *i++ == o1); // direct match + ASSERT ( *i++ == o1); + ASSERT ( *i++ == o1); + ASSERT (!i.hasNext()); + + ASSERT ( reg_->put (o2, q2)); // trying to (re)register o2 with another query succeeds + i = reg_->candidates(q2); + ASSERT ( *i++ == o2); // direct match + ASSERT ( *i++ == o1); + ASSERT ( *i++ == o2); // inserted here in the dataset + ASSERT ( *i++ == o1); + ASSERT (!i.hasNext()); + + ASSERT ( reg_->forget (o1)); + ASSERT ( reg_->forget (o1)); // failure, because it's already removed + ASSERT (!reg_->forget (o2)); + + o3.reset (oFac()); // another object is another object (it's irrelevant...) + + i = reg_->candidates(q2); + ASSERT (! (*i)); // empty + } + + }; + + + /** Register this test class... */ + LAUNCHER (DefsRegistryImpl_test, "function session"); + + + + } // namespace test + + } // namespace mobject + +} // namespace mobject diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 5f5dbcd9d..5523b8c60 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -842,7 +842,7 @@ This is an very important external Interface, because it links together all thre

[[ProcLayer and Engine]]
 
-
+
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory track regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (storing a weak ref in the ~DefsManager). Alongside with each object successfully queried via "default", the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
 
 !Skeleton
@@ -858,7 +858,7 @@ This is an very important external Interface, because it links together all thre
 #** otherwise, the newly created object is remembered (tagged) as new default, together with the degree of constriction
 
 !!!Implementation details
-Taken precisely, the "degree of constriction" yields only a partial ordering &mdash; but as the "default"-predicate is sort of a existential quantification anyways, its sole purpose is to avoid polluting the session with unnecessary default objects, and we don't need to care for absolute precision. A suitable approximation is to count the number of predicates terms in the query and use a pqueue (separate for each Type) to store weak refs to the the objects tagged as "default"
+Taken precisely, the "degree of constriction" yields only a partial ordering &mdash; but as the "default"-predicate is sort of a existential quantification anyways, its sole purpose is to avoid polluting the session with unnecessary default objects, and we don't need to care for absolute precision. A suitable approximation is to count the number of predicates terms in the query and use a (sorted) set (separate for each Type) to store weak refs to the the objects tagged as "default"
 {{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; just ignoring this issue seems reasonable.
 
 !!!Problems with the (preliminary) mock implementation

From 3d2791b91eafebd495d307d47e03b520db5f1215 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Wed, 2 Apr 2008 04:06:08 +0200
Subject: [PATCH 10/24] WIP some fixes; add test for a utility function

---
 src/proc/mobject/session/defsregistry.hpp     |   6 +-
 tests/components/common/removefromsettest.cpp | 108 ++++++++++++++++++
 .../mobject/session/defsregistryimpltest.cpp  |  69 +++++------
 3 files changed, 146 insertions(+), 37 deletions(-)
 create mode 100644 tests/components/common/removefromsettest.cpp

diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp
index d52867f42..5a2328867 100644
--- a/src/proc/mobject/session/defsregistry.hpp
+++ b/src/proc/mobject/session/defsregistry.hpp
@@ -165,11 +165,6 @@ namespace mobject
       {
         Table table_;
         
-      protected:
-        
-        DefsRegistry () {};
-        friend class DefsManager;
-        
       public:
         /** used for enumerating solutions */
         template
@@ -208,6 +203,7 @@ namespace mobject
           public:
             shared_ptr operator* () { return ptr; }
             bool hasNext ()              { return next || findNext(); }
+            Iter  operator++ (int)       { Iter tmp=*this; operator++(); return tmp; }            
             Iter& operator++ ()           
               { 
                 ptr=findNext();
diff --git a/tests/components/common/removefromsettest.cpp b/tests/components/common/removefromsettest.cpp
new file mode 100644
index 000000000..bf1629378
--- /dev/null
+++ b/tests/components/common/removefromsettest.cpp
@@ -0,0 +1,108 @@
+/*
+  RemoveFromSet(Test)  -  algorithm removing predicated elements from set
+ 
+  Copyright (C)         Lumiera.org
+    2008,               Hermann Vosseler 
+ 
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+ 
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+ 
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ 
+* *****************************************************/
+
+
+#include "common/test/run.hpp"
+#include "common/util.hpp"
+
+#include 
+#include 
+//#include 
+#include 
+#include 
+#include 
+#include 
+
+using std::cout;
+using std::string;
+
+
+
+namespace util
+  {
+  namespace test
+    {
+    using util::for_each;
+    using boost::lambda::_1;
+//    using boost::function;
+    using boost::lexical_cast;
+    using boost::bind;
+    
+  
+    template
+    void 
+    show (COLL const& coll)
+    {
+      cout << "[ ";
+      for_each (coll, cout << _1 << ", ");
+      cout << "]\n";
+    }
+    
+    
+    bool
+    killerselector (string description, uint candidate)
+    {
+      return string::npos != description.find( lexical_cast (candidate));
+    }
+    
+    
+    
+    class RemoveFromSet_test : public Test
+      {
+        virtual void run (Arg arg)
+          {
+            test_remove (" nothing ");
+            test_remove ("0");
+            test_remove ("9");
+            test_remove ("5");
+            test_remove ("0 2 4 6 8");
+            test_remove ("1 3 5 7 9");
+            test_remove ("0 1 2 3 4 5 6 7 8 9");
+            test_remove ("0 1 2 3 4 5 6 7 8  ");
+            test_remove ("  1 2 3 4 5 6 7 8 9");
+            test_remove ("0 1 2 3 4   6 7 8 9");
+          }
+        
+        /** @test populate a test set, remove the denoted elements
+         *        and print the result
+         */
+        test_remove (string elems_to_remove)
+          {
+            std::set theSet;
+            for (int i=0; i<10; ++i)
+              theSet.push_back (i);
+            
+            util::remove_if (theSet, bind( killerselector, elems_to_remove, _1));
+            
+            show (theSet);
+          }
+        
+      };
+    
+      
+      LAUNCHER (RemoveFromSet_test, "unit common");
+
+      
+  } // namespace test
+    
+} // namespace util
+
diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
index 49c2fb343..6a01f15c0 100644
--- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp
+++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp
@@ -34,16 +34,17 @@
 #include 
 
 using lumiera::Query;
+using util::isnil;
 
 using std::tr1::shared_ptr;
 using boost::scoped_ptr;
 using boost::format;
-using util::isnil;
 using std::string;
 using std::rand;
 using std::map;
 
 
+
 namespace mobject
   {
   namespace session
@@ -62,7 +63,7 @@ namespace mobject
       
       /** create a random new ID */
       string 
-      newID (Symbol prefix)
+      newID (string prefix)
       {
         return str (instancePatt % prefix % rand());
       }
@@ -83,9 +84,19 @@ namespace mobject
       
       
       
-      /** fabricating (random) query strings */
       string
-      q_str (int degree=0)
+      garbage_term ()         ///< yields a random string of 3 letters
+      {
+        return str (predicatePatt 
+                   % char ('a'+ rand() % 26)
+                   % rand() % 100
+                   % garbage.substr(rand() % 17 , 3)
+                   );
+      }
+      
+      string
+      q_str (int degree=0)    ///< fabricating (random) query strings
+
       {
         string fake;
         if (!degree) 
@@ -96,15 +107,6 @@ namespace mobject
         return fake;
       }
       
-      string
-      garbage_term ()
-      {
-        return (predicatePatt 
-               % char ('a'+ rand() % 26)
-               % rand() % 100
-               % garbage.substr(rand() % 17 , 3)
-               );
-      }
       
       
       /************************************************************************
@@ -123,15 +125,20 @@ namespace mobject
           
           typedef Query > Q13;
           typedef Query > Q23;
-
+          
+          typedef DefsRegistry::Iter > Iter13;
+          typedef DefsRegistry::Iter > Iter23;
+          
+          
           // fabricating Objects wrapped into smart-ptrs
-          lumiera::factory::RefcountPtr oFac;
-          lumiera::factory::RefcountPtr

pFac; + lumiera::factory::RefcountPtr > oFac; + lumiera::factory::RefcountPtr > pFac; O o1, o2, o3; Q13 q1, q2, q3, q4, q5; map ps; + public: DefsRegistryImpl_test () : o1 (oFac()), o2 (oFac()), o3 (oFac()), q1 (q_str (1)), @@ -165,7 +172,7 @@ namespace mobject reg_->put (o3, q3); reg_->put (o3, q2); reg_->put (o2, q1); - reg_->put (o1, Q13); // the empty query + reg_->put (o1, Q13()); // the empty query ps.clear(); for (int i=0; i<100; ++i) @@ -174,15 +181,14 @@ namespace mobject Q23 qx (q_str()); ps[qx] = px; reg_->put (px, qx); - px.instanceID = qx; + px->instanceID = qx; } } void check_query () { - DefsRegistry::Iter > i; - i = reg_->candidates(Q13 ("irrelevant query")); + Iter13 i (reg_->candidates(Q13 ("irrelevant query"))); ASSERT ( i.hasNext()); ASSERT ( *i++ == o1); // ordered according to the degree of the queries ASSERT ( *i++ == o2); @@ -202,7 +208,7 @@ namespace mobject ASSERT ( *i++ == o1); ASSERT (!i.hasNext()); - i = reg_->candidates(Q13); + i = reg_->candidates(Q13()); ASSERT ( *i++ == o1); // found by direct match to the empty query ASSERT ( *i++ == o1); ASSERT ( *i++ == o2); @@ -214,22 +220,22 @@ namespace mobject uint d=0; uint d_prev=0; - for (i = reg_->candidates(Q23 ("some crap")); - i.hasNext(); ++i ) + Iter23 j = reg_->candidates(Q23 ("some crap")); + for ( ; j.hasNext(); ++j ) { - ASSERT ( *i ); - Q23 qx = (*i)->instanceID; - ASSERT ( ps[qx] == (*i)); + ASSERT ( *j ); + Q23 qx ((*j)->instanceID); + ASSERT ( ps[qx] == (*j)); d = lumiera::query::countPraed (qx); ASSERT ( d_prev <= d ); d_prev = d; } - ASSERT (!i.hasNext()); + ASSERT (!j.hasNext()); // calling with an arbitrary (registered) query // yields the corresponding object at start of the ennumeration - i = reg_->candidates(ps.begin()->first); - ASSERT ( *i == ps.begin()->second); + j = reg_->candidates(ps.begin()->first); + ASSERT ( *j == ps.begin()->second); } @@ -238,8 +244,7 @@ namespace mobject { reg_->forget (o2); - DefsRegistry::Iter > i; - i = reg_->candidates(q4); + Iter13 i (reg_->candidates(q4)); ASSERT ( i.hasNext()); ASSERT ( *i++ == o1); // ordered according to the degree of the queries // but the o2 entries are missing @@ -287,7 +292,7 @@ namespace mobject ASSERT ( reg_->forget (o1)); // failure, because it's already removed ASSERT (!reg_->forget (o2)); - o3.reset (oFac()); // another object is another object (it's irrelevant...) + o3 = oFac(); // another object is another object (it's irrelevant...) i = reg_->candidates(q2); ASSERT (! (*i)); // empty From 162976a4cf84443436ee224690e254b1a8616fff Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 3 Apr 2008 03:50:28 +0200 Subject: [PATCH 11/24] WIP reorganize the ordering relation on assets (should simplify handling of comparisons at various places) --- src/proc/asset.hpp | 95 +++++++++++-------- tests/components/common/removefromsettest.cpp | 26 ++--- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index bc587cc96..b9a85e5ae 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -39,7 +39,7 @@ ** value. For example the asset::Media#getID returns an ID. By using the ** templated query function AssetManager#getAsset, we can get at references to the more ** specific subinterface asset::media just by using the ID value in a typesafe manner. - ** This helps avoiding dynamic typing and switch-on-type, leading to much more robust, + ** This helps avoiding dynamic typing and switch-on-type, leading to more robust, ** extensible and clear code. ** ** (Implementation detail: as g++ is not able to handle member function template @@ -65,6 +65,7 @@ #include #include #include +#include #include @@ -128,12 +129,14 @@ namespace asset * asset::Effect, asset::Codec, asset::Track, asset::Dataset. * @note Assets objects have a strict unique identity and because of this are non-copyable. * You can not create an Asset derived object without registering it with the AssetManager - * automatically. I is possible to copy the PAsset (smart pointer) though. + * automatically. It is possible to copy the PAsset (smart pointer) though. * * @since 09/2007 * @author Ichthyo */ - class Asset : private boost::noncopyable + class Asset + : boost::totally_ordered< Asset, + boost::noncopyable> { public: @@ -142,6 +145,7 @@ namespace asset * sufficiently identifying any given Asset. */ struct Ident + : boost::totally_ordered { /** element ID, comprehensible but sanitized. * The tuple (category, name, org) is unique. @@ -174,20 +178,13 @@ namespace asset const string& o = "lumi", const uint ver=1); + + int compare (const Ident& other) const; + /** @note equality ignores version differences */ - bool operator== (const Ident& other) const - { - return org == other.org - && name == other.name - && category == other.category; - } - bool operator!= (const Ident& other) const - { - return !operator==(other); - } - - int compare (const Ident& other) const; - + bool operator== (const Ident& oi) const { return compare (oi) ==0; } + bool operator< (const Ident& oi) const { return compare (oi) < 0; } + operator string () const; }; @@ -197,11 +194,16 @@ namespace asset public: const Ident ident; ///< Asset identification tuple - virtual const ID& getID() const { return id; } - + virtual const ID& getID() const { return id; } + + + bool operator== (const Asset& oa) const { return ident == oa.ident; } + bool operator< (const Asset& oa) const { return ident < oa.ident; } + virtual operator string () const; + protected: const ID id; ///< Asset primary key. @@ -293,6 +295,20 @@ namespace asset + /* ====== ordering of Assets and Asset-Pointers ====== */ + + /** ordering of Assets is based on the ordering + * of Ident tuples, which are supposed to be unique. + * @note version info is irrelevant */ + inline int + Asset::Ident::compare (const Asset::Ident& oi) const + { + int res; + if (0 != (res=category.compare (oi.category))) return res; + if (0 != (res=org.compare (oi.org))) return res; + return name.compare (oi.name); + } + /** promote subtype-ptr to PAsset, e.g. for comparing */ template inline const PcAsset @@ -304,30 +320,27 @@ namespace asset /** ordering of Asset smart ptrs based on Ident tuple. * @todo currently supporting only smart_ptr. * @todo should better be done using boost::operator */ - inline bool operator== (const PcAsset& a1, const PcAsset& a2) { return a1 && a2 && ( 0==a1->ident.compare(a2->ident));} - inline bool operator< (const PcAsset& a1, const PcAsset& a2) { return a1 && a2 && (-1==a1->ident.compare(a2->ident));} - inline bool operator> (const PcAsset& a1, const PcAsset& a2) { return a2 < a1; } - inline bool operator>= (const PcAsset& a1, const PcAsset& a2) { return !(a1 < a2); } - inline bool operator<= (const PcAsset& a1, const PcAsset& a2) { return !(a1 > a2); } - inline bool operator!= (const PcAsset& a1, const PcAsset& a2) { return !(a1== a2); } + inline bool operator== (PcAsset const& a1, PcAsset const& a2) { return a1 && a2 && ( (*a1) == (*a2));} + inline bool operator< (PcAsset const& a1, PcAsset const& a2) { return a1 && a2 && ( (*a1) < (*a2));} - inline bool operator== (const PAsset& a1, const PAsset& a2) { return pAsset(a1) == pAsset(a2); } - inline bool operator< (const PAsset& a1, const PAsset& a2) { return pAsset(a1) < pAsset(a2); } - inline bool operator> (const PAsset& a1, const PAsset& a2) { return pAsset(a1) > pAsset(a2); } - inline bool operator>= (const PAsset& a1, const PAsset& a2) { return pAsset(a1) >= pAsset(a2); } - inline bool operator<= (const PAsset& a1, const PAsset& a2) { return pAsset(a1) <= pAsset(a2); } - inline bool operator!= (const PAsset& a1, const PAsset& a2) { return pAsset(a1) != pAsset(a2); } - - /** ordering of Asset Ident tuples. - * @note version is irrelevant */ - inline int - Asset::Ident::compare (const Asset::Ident& oi) const - { - int res; - if (0 != (res=category.compare (oi.category))) return res; - if (0 != (res=org.compare (oi.org))) return res; - return name.compare (oi.name); - } + template + inline bool operator== (PA1 const& a1, PA2 const& a2) { return pAsset(a1) == pAsset(a2); } + + template + inline bool operator< (PA1 const& a1, PA2 const& a2) { return pAsset(a1) < pAsset(a2); } + + + /** build ordering of Asset shared pointers on the ordering of the pointed to assets. + * Because we can't define member operators for shared-ptrs, we explicitly instantiate + * the boost ordering concept template for all Asset kinds we want to use... + */ + template + struct asset_total_ordering : public boost::totally_ordered + { }; + + template struct asset_total_ordering; + template struct asset_total_ordering; + /** convienient for debugging */ diff --git a/tests/components/common/removefromsettest.cpp b/tests/components/common/removefromsettest.cpp index bf1629378..c51d02019 100644 --- a/tests/components/common/removefromsettest.cpp +++ b/tests/components/common/removefromsettest.cpp @@ -26,8 +26,7 @@ #include #include -//#include -#include +#include #include #include #include @@ -43,9 +42,9 @@ namespace util { using util::for_each; using boost::lambda::_1; -// using boost::function; + using boost::lambda::bind; using boost::lexical_cast; - using boost::bind; + template @@ -57,7 +56,6 @@ namespace util cout << "]\n"; } - bool killerselector (string description, uint candidate) { @@ -68,31 +66,35 @@ namespace util class RemoveFromSet_test : public Test { - virtual void run (Arg arg) + virtual void + run (Arg arg) { test_remove (" nothing "); test_remove ("0"); test_remove ("9"); test_remove ("5"); - test_remove ("0 2 4 6 8"); - test_remove ("1 3 5 7 9"); + test_remove ("0 2 4 6 8 "); + test_remove (" 1 3 5 7 9"); test_remove ("0 1 2 3 4 5 6 7 8 9"); test_remove ("0 1 2 3 4 5 6 7 8 "); test_remove (" 1 2 3 4 5 6 7 8 9"); test_remove ("0 1 2 3 4 6 7 8 9"); } - /** @test populate a test set, remove the denoted elements - * and print the result - */ + + /** @test populate a test set, + * remove the denoted elements + * and print the result... */ + void test_remove (string elems_to_remove) { std::set theSet; for (int i=0; i<10; ++i) - theSet.push_back (i); + theSet.insert (i); util::remove_if (theSet, bind( killerselector, elems_to_remove, _1)); + cout << "removed " << elems_to_remove << " ---> "; show (theSet); } From 89e71212e17cb65f1984127c502e9c1e0ee1e0b9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Apr 2008 05:45:21 +0200 Subject: [PATCH 12/24] include the tools subdir in default target (=build) --- SConstruct | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SConstruct b/SConstruct index 4d2f2ee9c..6b6350210 100644 --- a/SConstruct +++ b/SConstruct @@ -251,7 +251,7 @@ def defineBuildTargets(env, artifacts): artifacts['plugins'] = env.SharedLibrary('$BINDIR/lumiera-plugin', plugobj) # call subdir SConscript(s) for independent components - SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts') + SConscript(dirs=[SRCDIR+'/tool'], exports='env artifacts core') SConscript(dirs=[TESTDIR], exports='env artifacts core') @@ -264,7 +264,7 @@ def definePostBuildTargets(env, artifacts): il = env.Alias('install-lib', '$DESTDIR/lib') env.Alias('install', [ib, il]) - build = env.Alias('build', artifacts['lumiera']+artifacts['plugins']) + build = env.Alias('build', artifacts['lumiera']+artifacts['plugins']+artifacts['tools']) allbu = env.Alias('allbuild', build+artifacts['testsuite']) env.Default('build') # additional files to be cleaned when cleaning 'build' From 2b529e3fac22a0d5da2a4df72c4005ee90b98fb7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Apr 2008 05:57:54 +0200 Subject: [PATCH 13/24] customized smart-ptr based on std::shared_ptr hopefully resolves long standing problems with odering and type relations of objects handled via smart ptr --- src/common/p.hpp | 128 +++++++ src/tool/SConscript | 4 +- src/tool/try.cpp | 84 ++++- tests/50components.tests | 20 +- .../components/common/customsharedptrtest.cpp | 315 ++++++++++++++++++ 5 files changed, 541 insertions(+), 10 deletions(-) create mode 100644 src/common/p.hpp create mode 100644 tests/components/common/customsharedptrtest.cpp diff --git a/src/common/p.hpp b/src/common/p.hpp new file mode 100644 index 000000000..d4cc2f11c --- /dev/null +++ b/src/common/p.hpp @@ -0,0 +1,128 @@ +/* + P.hpp - customized shared_ptr with ordering and type relationships + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file p.hpp + ** customized refcounting smart pointer. + ** Template derived from std::tr1::shared_ptr adding total ordering and + ** type relationships implemented by forwarding to the pointees. In all other + ** respects, it should behave exactly as shared_ptr and is able to cooperate + ** and share ownership with other shared_ptr instantiations. + ** + ** By default different instantiations of shared_ptr are completely unrelated types, + ** even if using inherintance related type parameters for instantiation: a shared_ptr + ** isn't some kind-of shared_ptr -- we need to do an explicit static_ptr_cast. Another + ** common problem is the definition of equality and ordering relations for shared_ptr: + ** equality is based on the equality of the managed pointers, while ordering is built upon + ** the ref count. While generally this may be a good compromise, in our case it hinders treating + ** the smart ptrs within the application almost as if they were the objects themselfs and proved + ** an obstacle for writing generic helper functions. + ** + ** the P template resolves these problems by implementing the ordering operators in terms of + ** the corresponding operators on the pointee and by allowing to specify a base class smart-ptr + ** as template parameter. + ** + ** @see asset.hpp + ** @see customsharedptrtest.cpp + ** @see orderingofassetstest.cpp + */ + + +#ifndef LUMIERA_P_H +#define LUMIERA_P_H + + +#include + + +namespace lumiera + { + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + /** + * customized refcounting smart pointer template, built upon + * std::tr1::shared_ptr, but forwarding type relationships and + * ordering operators to the pointee objects. + * @param TAR the visible pointee type + * @param the shared-ptr type used as implementation + * @note if the BASE smart-ptr type used as implementation + * implies another pointer type than the one used on + * the interface (=type TAR), then every access to the + * pointee causes an dynamic cast. Thus the pointee types + * need to support RTTI. + */ + template > + class P + : public BASE + { + public: + P ( ) : BASE() {} + template explicit P (Y* p) : BASE(p) {} + template P (Y* p, D d) : BASE(p,d){} + + P (P const& r) : BASE(r) {} + template P (shared_ptr const& r) : BASE(r) {} + template explicit P (weak_ptr const& wr) : BASE(wr) {} + template explicit P (std::auto_ptr & ar) : BASE(ar) {} + + + P& operator= (P const& r) { BASE::operator= (r); return *this; } + template P& operator=(shared_ptr const& sr) { BASE::operator= (sr); return *this; } + template P& operator=(std::auto_ptr & ar) { BASE::operator= (ar); return *this; } + + TAR* get() const { return dynamic_cast (BASE::get()); } + TAR& operator*() const { return *get(); } + TAR* operator->() const { return get(); } + + void swap(P& b) { BASE::swap (b);} + + + private: /* === friend operators injected into enclosing namespace for ADL === */ + template + friend inline bool + operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } + + template + friend inline bool + operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } + + template + friend inline bool + operator< (P const& p, P<_O_> const& q) { return (p && q) && (*p < *q); } + + template + friend inline bool + operator> (P const& p, P<_O_> const& q) { return (p && q) && (*q < *p); } + + template + friend inline bool + operator<= (P const& p, P<_O_> const& q) { return (p && q)? (*p <= *q) : (!p && !q); } + + template + friend inline bool + operator>= (P const& p, P<_O_> const& q) { return (p && q)? (*p >= *q) : (!p && !q); } + + }; + + +} // namespace lumiera +#endif diff --git a/src/tool/SConscript b/src/tool/SConscript index 738483498..bf069ebfb 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -3,11 +3,11 @@ ## SConscript - SCons buildscript for tool subdirectory (called by SConstruct) ## -Import('env','artifacts') +Import('env','artifacts','core') # build the ubiquitous Hello World application (note: C source) artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') - + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature: + + env.Program('#$BINDIR/try', ['try.cpp'] + core) #### to try out some feature: ] diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 349ca7db0..2151f4e84 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -7,18 +7,90 @@ // execute with NOBUG_LOG='ttt:TRACE' bin/try // 1/08 - working out a static initialisation problem for Visitor (Tag creation) // 1/08 - check 64bit longs +// 4/08 - comparison operators on shared_ptr #include #include +#include -#include +#include "proc/asset/media.hpp" using std::string; using std::cout; -NOBUG_CPP_DEFINE_FLAG(test); + +namespace lumiera + { + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + template > + class P + : public BASE + { + public: + P() : BASE() {} + template explicit P (Y* p) : BASE(p) {} + template P (Y* p, D d) : BASE(p,d) {} + + P (P const& r) : BASE(r) {} + template P (shared_ptr const& r) : BASE(r) {} + template explicit P (weak_ptr const& wr) : BASE(wr) {} + template explicit P (std::auto_ptr & ar) : BASE(ar) {} + + private: + // friend operators injected into enclosing namespace and found by ADL: + template + friend inline bool + operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } + + template + friend inline bool + operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } + + template + friend inline bool + operator< (P const& p, P<_O_> const& q) { return (p && q) && (*p < *q); } + + template + friend inline bool + operator> (P const& p, P<_O_> const& q) { return (p && q) && (*q < *p); } + + template + friend inline bool + operator<= (P const& p, P<_O_> const& q) { return (p && q)? (*p <= *q) : (!p && !q); } + + template + friend inline bool + operator>= (P const& p, P<_O_> const& q) { return (p && q)? (*p >= *q) : (!p && !q); } + + }; +} + +namespace asset + { + using lumiera::P; + + + void + doIt() + { + Asset::Ident key2("Try-1", Category(VIDEO)); + P a1 = asset::Media::create(key2, "Name-1"); + + Asset::Ident key3("Try-2", Category(VIDEO)); + P a2 = asset::Media::create(key3, "Name-2"); + + cout << "a1 = " << str(a1); + + bool yes = (a1 >= a2); + enable_if, string>::type yup; + + cout << "\n yes=" << yes << " yup=" << typeid(*a1).name(); + } +} int main (int argc, char* argv[]) @@ -26,12 +98,10 @@ int main (int argc, char* argv[]) NOBUG_INIT; - int64_t lol (1); - cout << sizeof(lol)<< "\n"; + asset::doIt(); + + cout << "\ngulp\n"; - cout << "long: "<< std::numeric_limits::max() - <<" 64: " << std::numeric_limits::max() - <<"\n"; return 0; } diff --git a/tests/50components.tests b/tests/50components.tests index 0377d08c6..10cf575a5 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -2,7 +2,6 @@ TESTING "Component Test Suite: common and basic components" ./test-components -- - TEST "Hello test" HelloWorld_test 3 < [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ] +out: removed 0 ---> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, ] +out: removed 9 ---> [ 0, 1, 2, 3, 4, 5, 6, 7, 8, ] +out: removed 5 ---> [ 0, 1, 2, 3, 4, 6, 7, 8, 9, ] +out: removed 0 2 4 6 8 ---> [ 1, 3, 5, 7, 9, ] +out: removed 1 3 5 7 9 ---> [ 0, 2, 4, 6, 8, ] +out: removed 0 1 2 3 4 5 6 7 8 9 ---> [ ] +out: removed 0 1 2 3 4 5 6 7 8 ---> [ 9, ] +out: removed 1 2 3 4 5 6 7 8 9 ---> [ 0, ] +out: removed 0 1 2 3 4 6 7 8 9 ---> [ 5, ] +END + + TEST "SanitizedIdentifier_test" SanitizedIdentifier_test < 'Word' out: 'a Sentence' --> 'a_Sentence' diff --git a/tests/components/common/customsharedptrtest.cpp b/tests/components/common/customsharedptrtest.cpp new file mode 100644 index 000000000..7be41b801 --- /dev/null +++ b/tests/components/common/customsharedptrtest.cpp @@ -0,0 +1,315 @@ +/* + CustomSharedPtr(Test) - refcounting, equality and comparisons + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "common/test/run.hpp" +#include "common/util.hpp" + +#include "common/p.hpp" + +#include + +using std::string; + + +namespace asset + { + namespace test + { + using lumiera::P; + using std::tr1::shared_ptr; + using std::tr1::weak_ptr; + + + struct X + : boost::totally_ordered + { + long x_; + + X(long x=0) : x_(x) {} + operator long () { return x_; } + + bool operator< (const X& ox) const { return x_ < ox.x_; } + bool operator== (const X& ox) const { return x_ == ox.x_; } + + virtual ~X() {} // using RTTI + }; + + struct XX : public X + { + long xx_; + + XX(long x=0) : X(x), xx_(x+1) {} + }; + + + /**************************************************************** + * @test assure correct behaviour of lumiera's custom shared-ptr, + * including ADL of operators, shared ownership, typing + * and ordering + * @see lumiera::P + */ + class CustomSharedPtr_test : public Test + { + virtual void + run (Arg arg) + { + check_refcounting (); + check_shared_ownership (); + check_type_relations (); + check_ordering (); + } + + + /** @test smart-ptr basic behaviour */ + void + check_refcounting () + { + P p1 (new X(7)); + ASSERT (p1); + ASSERT (1 == p1.use_count()); + ASSERT (7 == p1->x_); + + { + P p2 (new X(9)); + ASSERT (1 == p2.use_count()); + + p2.swap (p1); + ASSERT (1 == p1.use_count()); + ASSERT (1 == p2.use_count()); + + p2 = p1; + ASSERT (2 == p1.use_count()); + ASSERT (2 == p2.use_count()); + } + + ASSERT (1 == p1.use_count()); + ASSERT (9 == p1->x_); + + p1.reset(); + ASSERT (0 == p1.use_count()); + ASSERT (!p1); + } + + + /** @test cooperation with other shared-ptr types */ + void + check_shared_ownership () + { + std::auto_ptr au (new X(22)); + ASSERT (au.get()); + + P pX (au); + ASSERT (!au.get()); + ASSERT (pX); + ASSERT (1 == pX.use_count()); + ASSERT (22 == pX->x_); + + weak_ptr wX (pX); + ASSERT (wX.lock()); + ASSERT (1 == pX.use_count()); + + shared_ptr sp1 (wX); + shared_ptr sp2 (pX); + shared_ptr sp3; sp3 = pX; + + ASSERT (22 == sp3->x_); + ASSERT (4 == pX.use_count()); + ASSERT (*pX == *sp1); + ASSERT (*sp1 == *sp2); + ASSERT (*sp2 == *sp3); + + P pX2; + pX2.swap(pX); + ASSERT (!pX); + ASSERT (0 == pX.use_count()); + ASSERT (4 == pX2.use_count()); + + P > pXX (pX2); // a different type, but compatible pointers + pX2 = pX; + ASSERT (!pX2); + ASSERT (0 == pX2.use_count()); + ASSERT (4 == pXX.use_count()); + + sp3 = sp2 = sp1 = pX; + ASSERT (22 == pXX->x_); + ASSERT (1 == pXX.use_count()); + ASSERT (!sp1); + ASSERT (!sp2); + ASSERT (!sp3); + + ASSERT (22 == wX.lock()->x_); + ASSERT (1 == pXX.use_count()); + + pXX.reset(); + ASSERT (!pXX); + ASSERT (!wX.lock()); + } + + + /** @test building type relationships on smart-ptrs */ + void + check_type_relations () + { + P pX; // Base: shared_ptr + P pX1; // Base: shared_ptr + P > pX2; // Base: P + P > pX3; // Base: shared_ptr + P > pLo;// Base: shared_ptr + P pLoL; // Base: std::string + P pLoLoL; // Base: shared_ptr + + ASSERT (INSTANCEOF (P, &pX)); + ASSERT (INSTANCEOF (shared_ptr, &pX)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX1)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX1)); // doesn't compile (no RTTI) -- that's OK +// ASSERT (!INSTANCEOF (P, &pX1)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX2)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX2)); + ASSERT ( INSTANCEOF (P, &pX2)); + + ASSERT ( INSTANCEOF (shared_ptr, &pX3)); +// ASSERT (!INSTANCEOF (shared_ptr, &pX3)); +// ASSERT (!INSTANCEOF (P, &pX3)); + + ASSERT ( INSTANCEOF (shared_ptr, &pLo)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLo)); +// ASSERT (!INSTANCEOF (P, &pLo)); + +// ASSERT (!INSTANCEOF (shared_ptr, &pLoL)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLoL)); +// ASSERT (!INSTANCEOF (P, &pLoL)); + ASSERT ( INSTANCEOF (string, &pLoL)); + + ASSERT ( INSTANCEOF (shared_ptr, &pLoLoL)); +// ASSERT (!INSTANCEOF (string, &pLoLoL)); +// ASSERT (!INSTANCEOF (shared_ptr, &pLoLoL)); + + pX = pX1; // OK: pointee subtype... + pX = pX2; // invokes shared_ptr::operator= (shared_ptr const&) + pX = pX3; +// pX = pLo; // similar, but long* not asignable to X* +// pX = pLoL; // similar, but string* not asignable to X* +// pX = pLoLoL; // same... + // you can't do much with the "LoLo"-Types, + // their types and pointee types's relations don't match + + pX.reset (new XX(5)); + ASSERT (5 == *pX); // implicit conversion from X to long + + pX2 = pX; // works, because both are implemented in terms of shared_ptr + ASSERT (5 == pX2->x_); + ASSERT (6 == pX2->xx_); // using the XX interface (performing dynamic downcast) + + pX3.reset (new X(7)); // again works because implemented in terms of shared_ptr + pX2 = pX3; // same + ASSERT (pX2); // both contain indeed a valid pointer.... + ASSERT (pX3); + ASSERT (! pX2.get()); // but dynamic cast to XX at access fails + ASSERT (! pX3.get()); + } + + + /** @test equality and ordering operators forwarded to pointee */ + void + check_ordering () + { + typedef P PX; + typedef P PXX; + + PX pX1 (new X(3)); + PX pX2 (new XX(5)); + PX pX3, pX4, pX5, pX6; + PXX pXX (new XX(7)); + + pX3 = pXX; + pX4.reset(new X(*pXX)); + + ASSERT ( (pX1 == pX1)); // reflexivity + ASSERT (!(pX1 != pX1)); + ASSERT (!(pX1 < pX1)); + ASSERT (!(pX1 > pX1)); + ASSERT ( (pX1 <= pX1)); + ASSERT ( (pX1 >= pX1)); + + ASSERT (!(pX1 == pX2)); // compare to same ptr type with larger pointee of subtype + ASSERT ( (pX1 != pX2)); + ASSERT ( (pX1 < pX2)); + ASSERT (!(pX1 > pX2)); + ASSERT ( (pX1 <= pX2)); + ASSERT (!(pX1 >= pX2)); + + ASSERT (!(pX2 == pXX)); // compare to ptr subtype with larger pointee of same subtype + ASSERT ( (pX2 != pXX)); + ASSERT ( (pX2 < pXX)); + ASSERT (!(pX2 > pXX)); + ASSERT ( (pX2 <= pXX)); + ASSERT (!(pX2 >= pXX)); + + ASSERT (!(pX1 == pXX)); // transitively compare to ptr subtype with larger pointee of subtype + ASSERT ( (pX1 != pXX)); + ASSERT ( (pX1 < pXX)); + ASSERT (!(pX1 > pXX)); + ASSERT ( (pX1 <= pXX)); + ASSERT (!(pX1 >= pXX)); + + ASSERT ( (pX3 == pXX)); // compare ptr to subtype ptr both referring to same pointee + ASSERT (!(pX3 != pXX)); + ASSERT (!(pX3 < pXX)); + ASSERT (!(pX3 > pXX)); + ASSERT ( (pX3 <= pXX)); + ASSERT ( (pX3 >= pXX)); + + ASSERT ( (pX4 == pXX)); // compare ptr to subtype ptr both referring to different but equal pointees + ASSERT (!(pX4 != pXX)); + ASSERT (!(pX4 < pXX)); + ASSERT (!(pX4 > pXX)); + ASSERT ( (pX4 <= pXX)); + ASSERT ( (pX4 >= pXX)); + + ASSERT (!(pXX == pX5)); // compare subtype ptr to empty ptr: "unequal but not orderable" + ASSERT ( (pXX != pX5)); + ASSERT (!(pXX < pX5)); + ASSERT (!(pXX > pX5)); + ASSERT (!(pXX <= pX5)); + ASSERT (!(pXX >= pX5)); + + ASSERT ( (pX5 == pX6)); // compare two empty ptrs: "equal, aequivalent but not orderable" + ASSERT (!(pX5 != pX6)); + ASSERT (!(pX5 < pX6)); + ASSERT (!(pX5 > pX6)); + ASSERT ( (pX5 <= pX6)); + ASSERT ( (pX5 >= pX6)); + } + }; + + + /** Register this test class... */ + LAUNCHER (CustomSharedPtr_test, "unit common"); + + + + } // namespace test + +} // namespace asset From dffd635482f6e9d098f00f096299cf54359d1fd9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Apr 2008 07:26:54 +0200 Subject: [PATCH 14/24] switch asset ordering impl to utilize the new custom smart-ptr passes compiler and test suite, finally! TODO: also switch the derived asset kinds to use P, P,.... maybe do the same with MObject? --- src/common/query/mockconfigrules.hpp | 4 +- src/common/test/testoption.cpp | 3 +- src/proc/asset.hpp | 48 +++++------- src/proc/asset/db.hpp | 7 +- src/proc/assetmanager.cpp | 3 +- src/proc/mobject/session/defsregistry.hpp | 4 +- src/tool/SConscript | 2 +- src/tool/try.cpp | 73 ------------------- .../proc/asset/assetdiagnostics.hpp | 8 +- tests/components/proc/asset/basicpipetest.cpp | 9 ++- .../components/proc/asset/createassettest.cpp | 18 ++++- .../proc/asset/dependantassetstest.cpp | 4 +- tests/components/proc/asset/makecliptest.cpp | 2 +- .../proc/asset/orderingofassetstest.cpp | 12 ++- tests/components/proc/asset/testasset.cpp | 6 +- tests/components/proc/asset/testasset.hpp | 2 +- 16 files changed, 70 insertions(+), 135 deletions(-) diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index 5a1531cc9..f01d3268d 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -50,7 +50,6 @@ namespace lumiera { - //using std::string; namespace query @@ -114,8 +113,7 @@ namespace lumiera { const Ret& candidate (any_cast (entry)); if (! solution -// ||(solution && *solution == *candidate) // simulates a real unification -//////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + ||(solution && solution == candidate) // simulates a real unification ) return solution = candidate; } diff --git a/src/common/test/testoption.cpp b/src/common/test/testoption.cpp index aaaff32b8..8b1199887 100644 --- a/src/common/test/testoption.cpp +++ b/src/common/test/testoption.cpp @@ -1,5 +1,5 @@ /* - Suite - helper class for running collections of tests + Suite - handle cmdline for invoking Testsuite Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -45,6 +45,7 @@ namespace test * \code * --help * --group + * --describe * \endcode */ TestOption::TestOption (util::Cmdline& cmdline) diff --git a/src/proc/asset.hpp b/src/proc/asset.hpp index b9a85e5ae..7088b8324 100644 --- a/src/proc/asset.hpp +++ b/src/proc/asset.hpp @@ -59,14 +59,16 @@ #include "proc/asset/category.hpp" #include "common/error.hpp" +#include "common/p.hpp" + +#include +#include +#include #include #include #include #include -#include -#include -#include using std::string; @@ -81,6 +83,8 @@ namespace asset using std::size_t; using std::tr1::shared_ptr; using std::tr1::static_pointer_cast; + + using lumiera::P; /** @@ -109,8 +113,8 @@ namespace asset class Asset; class AssetManager; typedef const ID& IDA; - typedef shared_ptr PAsset; - typedef shared_ptr PcAsset; + typedef P PAsset; + typedef P PcAsset; @@ -135,7 +139,7 @@ namespace asset * @author Ichthyo */ class Asset - : boost::totally_ordered< Asset, + : boost::totally_ordered1< Asset, boost::noncopyable> { public: @@ -299,6 +303,9 @@ namespace asset /** ordering of Assets is based on the ordering * of Ident tuples, which are supposed to be unique. + * By using our customized lumiera::P as smart ptr, + * comparison on P ptrs will be automatically + * forwarded to the Asset comparison operators. * @note version info is irrelevant */ inline int Asset::Ident::compare (const Asset::Ident& oi) const @@ -309,6 +316,7 @@ namespace asset return name.compare (oi.name); } + /** promote subtype-ptr to PAsset, e.g. for comparing */ template inline const PcAsset @@ -316,31 +324,15 @@ namespace asset { return static_pointer_cast (subPtr); } - - /** ordering of Asset smart ptrs based on Ident tuple. - * @todo currently supporting only smart_ptr. - * @todo should better be done using boost::operator */ - inline bool operator== (PcAsset const& a1, PcAsset const& a2) { return a1 && a2 && ( (*a1) == (*a2));} - inline bool operator< (PcAsset const& a1, PcAsset const& a2) { return a1 && a2 && ( (*a1) < (*a2));} - - template - inline bool operator== (PA1 const& a1, PA2 const& a2) { return pAsset(a1) == pAsset(a2); } - - template - inline bool operator< (PA1 const& a1, PA2 const& a2) { return pAsset(a1) < pAsset(a2); } - /** build ordering of Asset shared pointers on the ordering of the pointed to assets. - * Because we can't define member operators for shared-ptrs, we explicitly instantiate - * the boost ordering concept template for all Asset kinds we want to use... - */ - template - struct asset_total_ordering : public boost::totally_ordered - { }; - - template struct asset_total_ordering; - template struct asset_total_ordering; + /** type trait for detecting a shared-ptr-to-asset */ + template + struct is_pAsset : boost::false_type {}; + template + struct is_pAsset > + : boost::is_base_of {}; /** convienient for debugging */ diff --git a/src/proc/asset/db.hpp b/src/proc/asset/db.hpp index 0f385f96d..493a188cf 100644 --- a/src/proc/asset/db.hpp +++ b/src/proc/asset/db.hpp @@ -28,6 +28,7 @@ #include "proc/asset.hpp" #include "common/error.hpp" +#include #include #include @@ -92,9 +93,9 @@ namespace asset public: template - void put (ID hash, shared_ptr& ptr) { table[hash] = static_pointer_cast (ptr); } - void put (ID hash, PAsset& ptr) { table[hash] = ptr; } - bool del (ID hash) { return table.erase (hash); } + void put (ID hash, P& ptr) { table[hash] = static_pointer_cast (ptr); } + void put (ID hash, PAsset& ptr) { table[hash] = ptr; } + bool del (ID hash) { return table.erase (hash); } template shared_ptr diff --git a/src/proc/assetmanager.cpp b/src/proc/assetmanager.cpp index 47b11f52e..fcdf45dc0 100644 --- a/src/proc/assetmanager.cpp +++ b/src/proc/assetmanager.cpp @@ -112,14 +112,13 @@ namespace asset AssetManager::reg (KIND* obj, const Asset::Ident& idi) throw(lumiera::error::Invalid) { - typedef shared_ptr PType; AssetManager& _aMang (AssetManager::instance()); TODO ("check validity of Ident Category"); ID asset_id (getID (idi)); Thread::Lock guard SIDEEFFECT; TODO ("handle duplicate Registrations"); - PType smart_ptr (obj, &destroy); + P smart_ptr (obj, &destroy); _aMang.registry.put (asset_id, smart_ptr); return asset_id; diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 5a2328867..2bfec09ca 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -85,7 +85,7 @@ namespace mobject operator() (const Record& rec) { shared_ptr storedObj (rec.objRef.lock()); - return storedObj && pAsset(storedObj)==pAsset(obj_); //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + return storedObj && (storedObj == obj_); } }; @@ -261,7 +261,7 @@ namespace mobject { shared_ptr storedObj (pos->objRef.lock()); if (storedObj) - return (pAsset(storedObj) == pAsset(obj)); //////////////TODO: not only Assets (i.e. define comparison Operators on Assets!) + return (storedObj == obj); else // use the opportunity and purge the expired entry registry.erase(pos); diff --git a/src/tool/SConscript b/src/tool/SConscript index bf069ebfb..6f4e796a6 100644 --- a/src/tool/SConscript +++ b/src/tool/SConscript @@ -8,6 +8,6 @@ Import('env','artifacts','core') # build the ubiquitous Hello World application (note: C source) artifacts['tools'] = [ env.Program('#$BINDIR/hello-world','hello.c') - + env.Program('#$BINDIR/try', ['try.cpp'] + core) #### to try out some feature: + + env.Program('#$BINDIR/try', 'try.cpp') #### to try out some feature... ] diff --git a/src/tool/try.cpp b/src/tool/try.cpp index 2151f4e84..90bdfadf8 100644 --- a/src/tool/try.cpp +++ b/src/tool/try.cpp @@ -14,83 +14,12 @@ #include #include -#include "proc/asset/media.hpp" using std::string; using std::cout; -namespace lumiera - { - using std::tr1::shared_ptr; - using std::tr1::weak_ptr; - - template > - class P - : public BASE - { - public: - P() : BASE() {} - template explicit P (Y* p) : BASE(p) {} - template P (Y* p, D d) : BASE(p,d) {} - - P (P const& r) : BASE(r) {} - template P (shared_ptr const& r) : BASE(r) {} - template explicit P (weak_ptr const& wr) : BASE(wr) {} - template explicit P (std::auto_ptr & ar) : BASE(ar) {} - - private: - // friend operators injected into enclosing namespace and found by ADL: - template - friend inline bool - operator== (P const& p, P<_O_> const& q) { return (p && q)? (*p == *q) : (!p && !q); } - - template - friend inline bool - operator!= (P const& p, P<_O_> const& q) { return (p && q)? (*p != *q) : !(!p && !q); } - - template - friend inline bool - operator< (P const& p, P<_O_> const& q) { return (p && q) && (*p < *q); } - - template - friend inline bool - operator> (P const& p, P<_O_> const& q) { return (p && q) && (*q < *p); } - - template - friend inline bool - operator<= (P const& p, P<_O_> const& q) { return (p && q)? (*p <= *q) : (!p && !q); } - - template - friend inline bool - operator>= (P const& p, P<_O_> const& q) { return (p && q)? (*p >= *q) : (!p && !q); } - - }; -} - -namespace asset - { - using lumiera::P; - - - void - doIt() - { - Asset::Ident key2("Try-1", Category(VIDEO)); - P a1 = asset::Media::create(key2, "Name-1"); - - Asset::Ident key3("Try-2", Category(VIDEO)); - P a2 = asset::Media::create(key3, "Name-2"); - - cout << "a1 = " << str(a1); - - bool yes = (a1 >= a2); - enable_if, string>::type yup; - - cout << "\n yes=" << yes << " yup=" << typeid(*a1).name(); - } -} int main (int argc, char* argv[]) @@ -98,8 +27,6 @@ int main (int argc, char* argv[]) NOBUG_INIT; - asset::doIt(); - cout << "\ngulp\n"; diff --git a/tests/components/proc/asset/assetdiagnostics.hpp b/tests/components/proc/asset/assetdiagnostics.hpp index 199338599..c7a698db6 100644 --- a/tests/components/proc/asset/assetdiagnostics.hpp +++ b/tests/components/proc/asset/assetdiagnostics.hpp @@ -71,14 +71,14 @@ namespace asset } - template + template inline bool - dependencyCheck (C child, P parent) + dependencyCheck (P child, P parent) { - return (pAsset(child) == pAsset(parent)) + return (child == parent) || (0 < child->getParents().size() && (parent == child->getParents()[0]) - && (contains (parent->getDependant(), child))); + && (contains (parent->getDependant(), child))) ; } diff --git a/tests/components/proc/asset/basicpipetest.cpp b/tests/components/proc/asset/basicpipetest.cpp index fb2541f20..d085f22d4 100644 --- a/tests/components/proc/asset/basicpipetest.cpp +++ b/tests/components/proc/asset/basicpipetest.cpp @@ -154,6 +154,9 @@ namespace asset void dependProcPatt(string pID) { + typedef P PPipe; /////TODO: transition to P<> + typedef P PProcPatt; + PPipe thePipe = Pipe::query ("pipe("+pID+")"); ASSERT (thePipe); PProcPatt thePatt = thePipe->getProcPatt(); @@ -186,11 +189,13 @@ namespace asset ASSERT (!aMang.known (thePipe->getID())); // has been unlinked too, because dependant on pattern2 ASSERT (thePipe); + PProcPatt pattern3 = thePipe->getProcPatt(); /////TODO: transition to P<> ASSERT (thePipe->getProcPatt()); - ASSERT (thePipe->getProcPatt() == pattern2); // but is still valid, as long as the ref is alive.... + ASSERT ( pattern3 == pattern2); // but is still valid, as long as the ref is alive.... PPipe pipe3x = Pipe::query ("pattern(another)"); - ASSERT (pipe3x->getProcPatt() != pattern2); // because pattern2 is already unlinked... + pattern3 = pipe3x->getProcPatt(); /////TODO: transition to P<> + ASSERT (pattern3 != pattern2); // because pattern2 is already unlinked... ASSERT (pipe3x == Session::current->defaults (Query("pattern(another)"))); ASSERT (pipe3x != pipe2x); // ..we got a new default pipe for "pattern(another)" too! diff --git a/tests/components/proc/asset/createassettest.cpp b/tests/components/proc/asset/createassettest.cpp index d9302fc7c..530cb1413 100644 --- a/tests/components/proc/asset/createassettest.cpp +++ b/tests/components/proc/asset/createassettest.cpp @@ -60,7 +60,7 @@ namespace asset - typedef shared_ptr PM; + typedef P PM; /////TODO: transition to P<> /** @test Creating and automatically registering Asset instances. * Re-Retrieving the newly created objects from AssetManager. @@ -78,12 +78,26 @@ namespace asset // Assets have been registered and can be retrieved by ID AssetManager& aMang = AssetManager::instance(); + PM registered; /////TODO: transition to P<> + registered = aMang.getAsset (mm1->getID()); + ASSERT (registered == mm1); + registered = aMang.getAsset (mm2->getID()); + ASSERT (registered == mm2); + registered = aMang.getAsset (mm3->getID()); + ASSERT (registered == mm3); + + registered = aMang.getAsset (mm1->getID()); + ASSERT (registered != mm2); +/* + * TODO: switch back to original version + * once the transition to P ist done... + * ASSERT (aMang.getAsset (mm1->getID()) == mm1); ASSERT (aMang.getAsset (mm2->getID()) == mm2); ASSERT (aMang.getAsset (mm3->getID()) == mm3); ASSERT (aMang.getAsset (mm1->getID()) != mm2); - +*/ PAsset aa1 = aMang.getAsset (ID(mm1->getID())); // note we get an Asset ref ASSERT (aa1 == mm1); PM mX1 = aMang.getAsset (mm1->getID()); // ..and now we get a Media ref diff --git a/tests/components/proc/asset/dependantassetstest.cpp b/tests/components/proc/asset/dependantassetstest.cpp index c9125b5d3..63ea2f57e 100644 --- a/tests/components/proc/asset/dependantassetstest.cpp +++ b/tests/components/proc/asset/dependantassetstest.cpp @@ -194,8 +194,8 @@ namespace asset void checkRealAssetDependencyRegistration () { // -----Media and Clip-------------------------------- - typedef Media::PMedia PM; - typedef Media::PClip PC; + typedef P PM; + typedef P PC; PM mm = asset::Media::create("test-1", VIDEO); PC cc = mm->createClip()->findClipAsset(); ASSERT (dependencyCheck (cc,mm)); diff --git a/tests/components/proc/asset/makecliptest.cpp b/tests/components/proc/asset/makecliptest.cpp index 83702d7c5..136abf05d 100644 --- a/tests/components/proc/asset/makecliptest.cpp +++ b/tests/components/proc/asset/makecliptest.cpp @@ -50,7 +50,7 @@ namespace asset */ class MakeClip_test : public Test { - typedef shared_ptr PM; + typedef P PM; typedef asset::Media::PClipMO PC; virtual void run (Arg arg) diff --git a/tests/components/proc/asset/orderingofassetstest.cpp b/tests/components/proc/asset/orderingofassetstest.cpp index 26d6aa50a..783770348 100644 --- a/tests/components/proc/asset/orderingofassetstest.cpp +++ b/tests/components/proc/asset/orderingofassetstest.cpp @@ -59,22 +59,20 @@ namespace asset { virtual void run(Arg arg) { - typedef shared_ptr PA; - Asset::Ident key1("Au-1", Category(AUDIO), "ichthyo", 5); - PA mm1 = asset::Media::create(key1, "Name-1"); + PAsset mm1 = asset::Media::create(key1, "Name-1"); Asset::Ident key2("Au-1", Category(AUDIO), "ichthyo", 7); - PA mm2 = asset::Media::create(key2, "Name-2"); + PAsset mm2 = asset::Media::create(key2, "Name-2"); Asset::Ident key3("Au-2", Category(AUDIO), "ichthyo", 5); - PA mm3 = asset::Media::create(key3, "Name-3"); + PAsset mm3 = asset::Media::create(key3, "Name-3"); Asset::Ident key4("Au-2", Category(AUDIO), "stega", 5); - PA mm4 = asset::Media::create(key4, "Name-4"); + PAsset mm4 = asset::Media::create(key4, "Name-4"); Asset::Ident key5("Au-1", Category(VIDEO), "ichthyo", 5); - PA mm5 = asset::Media::create(key5, "Name-5"); + PAsset mm5 = asset::Media::create(key5, "Name-5"); // ordering of keys diff --git a/tests/components/proc/asset/testasset.cpp b/tests/components/proc/asset/testasset.cpp index b48b64da1..42ddff7ad 100644 --- a/tests/components/proc/asset/testasset.cpp +++ b/tests/components/proc/asset/testasset.cpp @@ -79,7 +79,7 @@ namespace asset * within AssetManager by the Asset base class ctor */ template - shared_ptr > + P > TestAsset::ptrFromThis () { return static_pointer_cast,Asset> @@ -113,8 +113,8 @@ namespace asset template TestAsset::TestAsset (PAsset& pRef); template TestAsset::TestAsset (PAsset& pRef); - template shared_ptr > TestAsset::ptrFromThis (); - template shared_ptr > TestAsset::ptrFromThis (); + template P > TestAsset::ptrFromThis (); + template P > TestAsset::ptrFromThis (); diff --git a/tests/components/proc/asset/testasset.hpp b/tests/components/proc/asset/testasset.hpp index 05f82c79e..e1abc57d2 100644 --- a/tests/components/proc/asset/testasset.hpp +++ b/tests/components/proc/asset/testasset.hpp @@ -58,7 +58,7 @@ namespace asset static void deleter (TestAsset* aa) { delete aa; } public: - typedef shared_ptr > PA; + typedef P > PA; static PA create () { return (new TestAsset )->ptrFromThis(); } static PA create (PAsset& pRef) { return (new TestAsset (pRef))->ptrFromThis(); } From 24825a13f35a649690f561d2794051d95358f0aa Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 5 Apr 2008 22:52:20 +0200 Subject: [PATCH 15/24] registry for default objects now passing test --- src/proc/mobject/session/defsregistry.hpp | 58 ++++++++++++++----- tests/51asset.tests | 16 ++--- tests/52query.tests | 4 ++ tests/53session.tests | 15 ++++- tests/{52engine.tests => 56engine.tests} | 0 .../components/common/customsharedptrtest.cpp | 8 +-- .../proc/mobject/builder/buildsegmenttest.cpp | 1 + .../mobject/session/defsregistryimpltest.cpp | 11 ++-- 8 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 tests/52query.tests rename tests/{52engine.tests => 56engine.tests} (100%) diff --git a/src/proc/mobject/session/defsregistry.hpp b/src/proc/mobject/session/defsregistry.hpp index 2bfec09ca..4aca10868 100644 --- a/src/proc/mobject/session/defsregistry.hpp +++ b/src/proc/mobject/session/defsregistry.hpp @@ -32,7 +32,9 @@ #include #include #include +#include #include +#include namespace mobject @@ -44,6 +46,10 @@ namespace mobject using lumiera::Thread; using lumiera::Query; + using std::string; + using boost::format; + using boost::lambda::_1; + using boost::lambda::var; namespace // Implementation details { @@ -58,8 +64,11 @@ namespace mobject uint maxSlots (0); ///< number of different registered Types + format dumpRecord ("%2i| %64s --> %s\n"); - /** holding a single "default object" entry */ + /** + * holding a single "default object" entry + */ template struct Record { @@ -90,15 +99,18 @@ namespace mobject }; struct OrderRelation - { - inline bool - operator() (Record one, Record two) ///< @note doesn't touch the objRef - { - return ( one.degree < two.degree - ||(one.degree == two.degree && one.query < two.query) - ); - } - }; + { + inline bool + operator() (Record one, Record two) ///< @note doesn't touch the objRef + { + return ( one.degree < two.degree + ||(one.degree == two.degree && one.query < two.query) + ); + } + }; + + operator string () const { return str (dumpRecord % degree % query % dumpObj()); } + string dumpObj () const { shared_ptr o (objRef.lock()); return o? string(*o):"dead"; } }; /** every new kind of object (Type) creates a new @@ -180,13 +192,14 @@ namespace mobject : p(from), i(from), e(to) { if (i!=e) ++i; // p is next to be tested, i always one ahead - ptr = findNext(); + operator++ (); } Iter (II match, II from, II to) ///< returns direct match first, then ennumerates - : p(match), i(from), e(to), - next(), ptr(findNext()) - { } + : p(match), i(from), e(to) + { + operator++ (); // init to first element (or to null if emty) + } shared_ptr findNext () throw() { @@ -264,10 +277,11 @@ namespace mobject return (storedObj == obj); else // use the opportunity and purge the expired entry - registry.erase(pos); + registry.erase(pos++); } // no existing entry.... registry.insert(pos, entry); + ENSURE (registry.find (entry) != registry.end()); return true; } @@ -284,6 +298,20 @@ namespace mobject Registry& registry = Slot::access(table_); return util::remove_if(registry, SearchFunc (obj)); } + + + /** helper for diagnostics. + * @note to use it, your objects need an operator string() + */ + template + string dump() + { + string res; + util::for_each ( Slot::access(table_) + , var(res) += _1 + ); + return res; + } }; diff --git a/tests/51asset.tests b/tests/51asset.tests index 5e259e95e..0acc9c63a 100644 --- a/tests/51asset.tests +++ b/tests/51asset.tests @@ -11,6 +11,14 @@ return: 0 END +PLANNED "BasicPipe_test" BasicPort_test < pX1; // Base: shared_ptr P > pX2; // Base: P P > pX3; // Base: shared_ptr - P > pLo;// Base: shared_ptr + P > pLo;// Base: shared_ptr (quite a nonsene, but well...) P pLoL; // Base: std::string P pLoLoL; // Base: shared_ptr @@ -182,7 +182,7 @@ namespace asset ASSERT (INSTANCEOF (shared_ptr, &pX)); ASSERT ( INSTANCEOF (shared_ptr, &pX1)); -// ASSERT (!INSTANCEOF (shared_ptr, &pX1)); // doesn't compile (no RTTI) -- that's OK +// ASSERT (!INSTANCEOF (shared_ptr, &pX1)); // doesn't compile (no RTTI) -- that's correct // ASSERT (!INSTANCEOF (P, &pX1)); ASSERT ( INSTANCEOF (shared_ptr, &pX2)); @@ -212,8 +212,8 @@ namespace asset // pX = pLo; // similar, but long* not asignable to X* // pX = pLoL; // similar, but string* not asignable to X* // pX = pLoLoL; // same... - // you can't do much with the "LoLo"-Types, - // their types and pointee types's relations don't match + // you won't be able to do much with the "LoLo"-Types, + // as their types and pointee types's relations don't match pX.reset (new XX(5)); ASSERT (5 == *pX); // implicit conversion from X to long diff --git a/tests/components/proc/mobject/builder/buildsegmenttest.cpp b/tests/components/proc/mobject/builder/buildsegmenttest.cpp index 82de68330..5fd44d988 100644 --- a/tests/components/proc/mobject/builder/buildsegmenttest.cpp +++ b/tests/components/proc/mobject/builder/buildsegmenttest.cpp @@ -51,6 +51,7 @@ namespace mobject { virtual void run(Arg arg) { + UNIMPLEMENTED ("oh my"); } }; diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp index 6a01f15c0..3bdc8f1f2 100644 --- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp +++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp @@ -75,6 +75,7 @@ namespace mobject { static string name; string instanceID; + operator string () const { return instanceID; } Dummy () : instanceID (newID (name)) {} }; @@ -89,14 +90,13 @@ namespace mobject { return str (predicatePatt % char ('a'+ rand() % 26) - % rand() % 100 + % (rand() % 100) % garbage.substr(rand() % 17 , 3) ); } string q_str (int degree=0) ///< fabricating (random) query strings - { string fake; if (!degree) @@ -197,6 +197,7 @@ namespace mobject ASSERT ( *i++ == o2); ASSERT ( *i == o1); ASSERT (!i.hasNext()); + ASSERT (! *++i ); // null after end i = reg_->candidates(q3); ASSERT ( *i++ == o3); // found by direct match @@ -272,7 +273,7 @@ namespace mobject ASSERT ( *i++ == o1); ASSERT (!i.hasNext()); - ASSERT ( reg_->put (o2, q5)); // trying to (re)register o2 with a existing query + ASSERT (!reg_->put (o2, q5)); // trying to (re)register o2 with a existing query // counts as failure (nothing chages) i = reg_->candidates(q5); ASSERT ( *i++ == o1); // direct match @@ -289,8 +290,8 @@ namespace mobject ASSERT (!i.hasNext()); ASSERT ( reg_->forget (o1)); - ASSERT ( reg_->forget (o1)); // failure, because it's already removed - ASSERT (!reg_->forget (o2)); + ASSERT (!reg_->forget (o1)); // failure, because it's already removed + ASSERT ( reg_->forget (o2)); o3 = oFac(); // another object is another object (it's irrelevant...) From b361fc96723d29f12c3b90fb68f480dda52487e3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Apr 2008 05:36:16 +0200 Subject: [PATCH 16/24] supplement tests for some small utils dealing with query expressions --- src/common/query.cpp | 22 ++- src/common/test/run.hpp | 2 +- src/common/util.hpp | 4 +- tests/50components.tests | 4 +- tests/52query.tests | 19 +++ .../common/query/querydiagnostics.hpp | 95 ++++++++++++ .../common/query/queryutilstest.cpp | 146 ++++++++++++++++++ .../common/sanitizedidentifiertest.cpp | 4 +- .../mobject/session/defsregistryimpltest.cpp | 39 ++--- 9 files changed, 293 insertions(+), 42 deletions(-) create mode 100644 tests/components/common/query/querydiagnostics.hpp create mode 100644 tests/components/common/query/queryutilstest.cpp diff --git a/src/common/query.cpp b/src/common/query.cpp index 09c642f1c..d7fb51257 100644 --- a/src/common/query.cpp +++ b/src/common/query.cpp @@ -34,10 +34,9 @@ using boost::regex; using boost::smatch; using boost::regex_search; using boost::sregex_iterator; -using boost::algorithm::is_upper; -using boost::algorithm::is_alpha; using util::contains; +using util::isnil; namespace lumiera @@ -45,16 +44,29 @@ namespace lumiera namespace query { + + namespace // local definitions + { + typedef boost::function ChPredicate; + + ChPredicate is_alpha = boost::algorithm::is_alpha(); + ChPredicate is_upper = boost::algorithm::is_upper(); + } // local defs + void normalizeID (string& id) { id = util::sanitize(id); - REQUIRE (!util::isnil(id)); - REQUIRE (is_alpha() (id[0])); + if (isnil(id) || !is_alpha (id[0])) + id.insert(0, "o"); + + + REQUIRE (!isnil(id)); + REQUIRE (is_alpha (id[0])); char first = id[0]; - if (is_upper() (first)) + if (is_upper (first)) id[0] = std::tolower (first); } diff --git a/src/common/test/run.hpp b/src/common/test/run.hpp index b38729d8a..529896d0b 100644 --- a/src/common/test/run.hpp +++ b/src/common/test/run.hpp @@ -85,7 +85,7 @@ namespace test { public: Launch (string testID, string groups) { Suite::enroll (this,testID,groups); }; - virtual auto_ptr operator() () { return auto_ptr (new TEST ); }; + virtual auto_ptr operator() () { return auto_ptr (new TEST ); }; }; } // namespace test diff --git a/src/common/util.hpp b/src/common/util.hpp index d56a45432..7f8704554 100644 --- a/src/common/util.hpp +++ b/src/common/util.hpp @@ -197,8 +197,8 @@ namespace util "trailing Withespace \t \n" --> 'trailing_Withespace' "with a lot \nof Whitespace" --> 'with_a_lot_of_Whitespace' "with\"much (punctuation)[]!" --> 'withmuch_(punctuation)' - "§&Ω%€ leading garbarge" --> 'leading_garbarge' - "mixed Ω garbarge" --> 'mixed_garbarge' + "§&Ω%€ leading garbage" --> 'leading_garbage' + "mixed Ω garbage" --> 'mixed_garbage' "Bääääh!!" --> 'Bh' \endverbatim */ diff --git a/tests/50components.tests b/tests/50components.tests index 10cf575a5..d2d38e5d0 100644 --- a/tests/50components.tests +++ b/tests/50components.tests @@ -129,8 +129,8 @@ out: ' --> 'trailing_Withespace' out: 'with a lot out: of Whitespace' --> 'with_a_lot_of_Whitespace' out: 'with"much (punctuation)[]!' --> 'withmuch_(punctuation)' -out: '§&Ω%€ leading garbarge' --> 'leading_garbarge' -out: 'mixed Ω garbarge' --> 'mixed_garbarge' +out: '§&Ω%€ leading garbage' --> 'leading_garbage' +out: 'mixed Ω garbage' --> 'mixed_garbage' out: 'Bääääh!!' --> 'Bh' out: '§&Ω%€' --> '' END diff --git a/tests/52query.tests b/tests/52query.tests index 2905bb4b4..ce5f4f31e 100644 --- a/tests/52query.tests +++ b/tests/52query.tests @@ -2,3 +2,22 @@ TESTING "Proc Layer config rules Test Suite" ./test-components --group=query +TEST "QueryUtils_test" QueryUtils_test Query < + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef LUMIERA_TEST_QUERY_QUERYDIAGNOSTICS_H +#define LUMIERA_TEST_QUERY_QUERYDIAGNOSTICS_H + + +//#include "common/factory.hpp" +//#include "common/util.hpp" + +//#include +//#include +#include +//#include +#include + +//using boost::algorithm::join; +//using boost::lexical_cast; +using boost::format; +using std::string; +//using std::cout; +using std::rand; + + + +namespace lumiera + { + namespace query + { + namespace test + { + + namespace // implementation constants + { + + format predicatePatt ("%s_%2i( %s )"); + const string garbage ("asdfghjklqwertzuiop"); + + const uint MAX_DEGREE_RAND = 9; + + } + + + + inline string + garbage_term () ///< yields a random string of 3 letters + { + return str (predicatePatt + % char ('a'+ rand() % 26) + % (rand() % 100) + % garbage.substr(rand() % 17 , 3) + ); + } + + inline string + garbage_query (int degree=0) ///< fabricating (random) query strings + { + string fake; + if (!degree) + degree = 1 + rand() % MAX_DEGREE_RAND; + while (0 < --degree) + fake += garbage_term() + ", "; + fake += garbage_term() + "."; + return fake; + } + + + + + } // namespace test + + } // namespace query + +} // namespace lumiera +#endif diff --git a/tests/components/common/query/queryutilstest.cpp b/tests/components/common/query/queryutilstest.cpp new file mode 100644 index 000000000..6a5b7f375 --- /dev/null +++ b/tests/components/common/query/queryutilstest.cpp @@ -0,0 +1,146 @@ +/* + QueryUtils(Test) - checking various utils provided for dealing with config queries + + Copyright (C) Lumiera.org + 2008, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + +#include "common/test/run.hpp" +#include "common/util.hpp" + +#include "common/cmdline.hpp" +#include "common/query.hpp" +#include "common/query/querydiagnostics.hpp" + +#include +#include +#include + +using lumiera::Query; +using util::Cmdline; +using util::isnil; +using util::contains; +using util::for_each; + +using boost::bind; +using std::string; +using std::cout; + + + +namespace lumiera + { + namespace query + { + namespace test + { + + struct Thing + { + virtual ~Thing() {} // add RTTI for Query.asKey(); + }; + + + + /************************************************************************ + * @test check the various small helpers and utilities we utilize + * for dealing with ConfigQuery + */ + class QueryUtils_test : public Test + { + + virtual void + run (Arg arg) + { + if (isnil(arg)) arg = Cmdline ("Query normalizeID extractID countPraed"); + + if (contains (arg, "Query" )) check_Query (); + if (contains (arg, "normalizeID")) check_normalizeID (); + if (contains (arg, "extractID" )) check_extractID (); + if (contains (arg, "countPraed" )) check_countPraed (); + } + + + + /** @test Query wrapper class basics */ + void + check_Query () + { + cout << Query ("I am writing a test sentence.").asKey() << "\n"; + } + + + + /** @test sanitizing and normalizing various tokens */ + void + check_normalizeID () + { + Cmdline tokens ("a A AA dufte 1a _1 A_A BÄH"); + tokens.push_back (""); + tokens.push_back (" White space "); + tokens.push_back ("§&Ω%€GΩ%€ar Ω baäääääge!!!!! "); + + cout << "..original : " << tokens << " :\n"; + + for_each (tokens, bind ( &normalizeID, _1 )); + + cout << "normalized : " << tokens << " :\n"; + } + + + + /** @test the simple regexp extracting a parameter token */ + void + check_extractID () + { + ASSERT ("tok" == extractID ("pred", "pred(tok)." )); + ASSERT ("tok" == extractID ("pred", " pred( tok )" )); + ASSERT ("tok" == extractID ("pred", "pred(tok), pred(tux)." )); + ASSERT ("tok" == extractID ("pred", "other(xyz) pred(tok) pred(tux)" )); + ASSERT ("tok" == extractID ("pred", "some( pred(tok)" )); + + ASSERT (isnil (extractID ("pred", "pred (tok)"))); + ASSERT (isnil (extractID ("pred", "pred tok)" ))); + ASSERT (isnil (extractID ("pred", "pred(tok " ))); + } + + + + /** @test counting of predicates in a quiery + * (currently 4/08 regexp based...) + */ + void + check_countPraed () + { + for (uint i=1; i <= 30; ++i) + ASSERT ( i == countPraed (garbage_query (i))); + } + }; + + + /** Register this test class... */ + LAUNCHER (QueryUtils_test, "unit query"); + + + + } // namespace test + + } // namespace query + +} // namespace lumiera diff --git a/tests/components/common/sanitizedidentifiertest.cpp b/tests/components/common/sanitizedidentifiertest.cpp index 416d81c58..1e96bc515 100644 --- a/tests/components/common/sanitizedidentifiertest.cpp +++ b/tests/components/common/sanitizedidentifiertest.cpp @@ -45,8 +45,8 @@ namespace util print_clean ("trailing Withespace\n \t"); print_clean ("with a \t lot\n of Whitespace"); print_clean ("with\"much (punctuation)[]!"); - print_clean ("§&Ω%€ leading garbarge"); - print_clean ("mixed Ω garbarge"); + print_clean ("§&Ω%€ leading garbage"); + print_clean ("mixed Ω garbage"); print_clean ("Bääääh!!"); print_clean ("§&Ω%€"); } diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp index 3bdc8f1f2..fa5d2c4c3 100644 --- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp +++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp @@ -28,12 +28,15 @@ #include "common/factory.hpp" #include "common/query.hpp" +#include "common/query/querydiagnostics.hpp" + #include #include #include #include using lumiera::Query; +using lumiera::query::test::garbage_query; using util::isnil; using std::tr1::shared_ptr; @@ -52,13 +55,10 @@ namespace mobject namespace test { - const uint MAX_DEGREE_RAND = 9; - format typePatt ("Dummy<%2i>"); format instancePatt ("obj_%s_%i"); format predicatePatt ("%s_%2i( %s )"); - const string garbage ("asdfghjklqwertzuiop"); /** create a random new ID */ @@ -85,27 +85,6 @@ namespace mobject - string - garbage_term () ///< yields a random string of 3 letters - { - return str (predicatePatt - % char ('a'+ rand() % 26) - % (rand() % 100) - % garbage.substr(rand() % 17 , 3) - ); - } - - string - q_str (int degree=0) ///< fabricating (random) query strings - { - string fake; - if (!degree) - degree = 1 + rand() % MAX_DEGREE_RAND; - while (0 < --degree) - fake += garbage_term() + ", "; - fake += garbage_term() + "."; - return fake; - } @@ -141,11 +120,11 @@ namespace mobject public: DefsRegistryImpl_test () : o1 (oFac()), o2 (oFac()), o3 (oFac()), - q1 (q_str (1)), - q2 (q_str (2)), - q3 (q_str (3)), - q4 (q_str (4)), - q5 (q_str (5)) + q1 (garbage_query (1)), + q2 (garbage_query (2)), + q3 (garbage_query (3)), + q4 (garbage_query (4)), + q5 (garbage_query (5)) { } @@ -178,7 +157,7 @@ namespace mobject for (int i=0; i<100; ++i) { P px (pFac()); - Q23 qx (q_str()); + Q23 qx (garbage_query()); ps[qx] = px; reg_->put (px, qx); px->instanceID = qx; From b53d8655fd334a292cb686f22276259a8c36fa7f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Apr 2008 08:56:18 +0200 Subject: [PATCH 17/24] yet another helper function: remove matching term from query string --- src/common/query.cpp | 38 +++++++++++++++---- src/common/query.hpp | 8 +++- tests/52query.tests | 5 +++ .../common/query/querydiagnostics.hpp | 11 +----- .../common/query/queryutilstest.cpp | 29 +++++++++++++- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/src/common/query.cpp b/src/common/query.cpp index d7fb51257..67873a5e8 100644 --- a/src/common/query.cpp +++ b/src/common/query.cpp @@ -77,8 +77,16 @@ namespace lumiera { map regexTable; - Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\)"; + Symbol matchArgument = "\\(\\s*([\\w_\\.\\-]+)\\s*\\),?\\s*"; regex findPredicate (string("(\\w+)")+matchArgument); + + inline regex& + getTermRegex (Symbol sym) + { + if (!contains (regexTable, sym)) + regexTable[sym] = regex (string(sym)+=matchArgument); + return regexTable[sym]; + } } /** (preliminary) helper: instead of really parsing and evaluating the terms, @@ -90,17 +98,31 @@ namespace lumiera const string extractID (Symbol sym, const string& termString) { - - if (!contains (regexTable, sym)) - regexTable[sym] = regex (string(sym)+=matchArgument); - smatch match; - if (regex_search (termString, match, regexTable[sym])) - return string (match[1]); + if (regex_search (termString, match, getTermRegex (sym))) + return (match[1]); else return ""; + } - } + + /** (preliminary) helper: cut a term with the given symbol. + * The term is matched, removed from the original string and returned + * @note parameter termString will be modified! + */ + const string + removeTerm (Symbol sym, string& termString) + { + smatch match; + if (regex_search (termString, match, getTermRegex (sym))) + { + string res (sym); res += "("+match[1]+")"; + termString.erase (match.position(), match[0].length()); + return res; + } + else + return ""; + } /** @note this is a very hackish preliminary implementation. diff --git a/src/common/query.hpp b/src/common/query.hpp index b7d22498b..14d23982b 100644 --- a/src/common/query.hpp +++ b/src/common/query.hpp @@ -69,7 +69,6 @@ namespace lumiera */ void normalizeID (string& id); - const string extractID (Symbol, const string& termString); /** count the top-level predicates in the query string. * usable for ordering queries, as more predicates usually @@ -77,7 +76,12 @@ namespace lumiera */ uint countPraed (const string&); - + + const string extractID (Symbol, const string& termString); + + const string removeTerm (Symbol, string& termString); + + } // namespace query } // namespace lumiera diff --git a/tests/52query.tests b/tests/52query.tests index ce5f4f31e..2c598b3b0 100644 --- a/tests/52query.tests +++ b/tests/52query.tests @@ -18,6 +18,11 @@ return: 0 END +TEST "QueryUtils_test" QueryUtils_test removeTerm < @@ -25,20 +25,11 @@ #define LUMIERA_TEST_QUERY_QUERYDIAGNOSTICS_H -//#include "common/factory.hpp" -//#include "common/util.hpp" - -//#include -//#include #include -//#include #include -//using boost::algorithm::join; -//using boost::lexical_cast; using boost::format; using std::string; -//using std::cout; using std::rand; diff --git a/tests/components/common/query/queryutilstest.cpp b/tests/components/common/query/queryutilstest.cpp index 6a5b7f375..05cfab7ef 100644 --- a/tests/components/common/query/queryutilstest.cpp +++ b/tests/components/common/query/queryutilstest.cpp @@ -68,11 +68,12 @@ namespace lumiera virtual void run (Arg arg) { - if (isnil(arg)) arg = Cmdline ("Query normalizeID extractID countPraed"); + if (isnil(arg)) arg = Cmdline ("Query normalizeID extractID removeTerm countPraed"); if (contains (arg, "Query" )) check_Query (); if (contains (arg, "normalizeID")) check_normalizeID (); if (contains (arg, "extractID" )) check_extractID (); + if (contains (arg, "removeTerm" )) check_removeTerm (); if (contains (arg, "countPraed" )) check_countPraed (); } @@ -122,6 +123,32 @@ namespace lumiera + /** @test the regexp based cutting of a term with given symbol */ + void + check_removeTerm () + { + // successfull-----Symbol---input-string----------------------extracted------remaining------------- + ASSERT_removeTerm ("pred", "pred(tok).", "pred(tok)", "." ); + ASSERT_removeTerm ("pred", " pred( tok )", "pred(tok)", " " ); + ASSERT_removeTerm ("pred", "pred(tok), pred(tux).", "pred(tok)", "pred(tux)." ); + ASSERT_removeTerm ("pred", "other(xyz) pred(tok) pred(tux)", "pred(tok)", "other(xyz) pred(tux)" ); + ASSERT_removeTerm ("pred", "some( pred(tok)", "pred(tok)", "some( " ); + + // not successfull + ASSERT_removeTerm ("pred", "pred (tok", "", "pred (tok" ); + ASSERT_removeTerm ("pred", "pred tok)", "", "pred tok)" ); + ASSERT_removeTerm ("pred", "pred(tok", "", "pred(tok" ); + } + + void + ASSERT_removeTerm (Symbol sym, string input, Symbol extracted, Symbol modified) + { + ASSERT (extracted == removeTerm (sym, input)); + ASSERT (modified == input); + } + + + /** @test counting of predicates in a quiery * (currently 4/08 regexp based...) */ From 3ed3813be38fb58e29b143a90a55ac809da07189 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 6 Apr 2008 20:11:34 +0200 Subject: [PATCH 18/24] WIP work out how some special cases of config queries can be handled. Including some tricky recursive calls. Even if this is currently a mock implementation it helps me find out how the real implementation (in Prolog) could handle these cases. Doesnt pass the compiler yet (some stubs missing) --- src/common/query/mockconfigrules.cpp | 35 ++++++++++--- src/common/query/mockconfigrules.hpp | 73 ++++++++++++++++++++++++++-- src/proc/asset/structfactoryimpl.hpp | 2 +- wiki/renderengine.html | 4 +- 4 files changed, 101 insertions(+), 13 deletions(-) diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp index 6a4303cd0..d688d9dbd 100644 --- a/src/common/query/mockconfigrules.cpp +++ b/src/common/query/mockconfigrules.cpp @@ -28,9 +28,10 @@ #include "proc/asset/pipe.hpp" -//#include "common/util.hpp" +#include "common/util.hpp" #include "nobugcfg.h" +using util::isnil; namespace lumiera @@ -101,9 +102,33 @@ namespace lumiera item (answer_, "pipe(default)") = item(answer_,"pipe(master), stream(video)"); //TODO killme TODO ("remove the default entries!!! DefaultsManager should find them automatically"); } + + + /* under some circumstances we need to emulate the behaviour * + * of a real resolution engine in a more detailed manner. * + * These case are hard wired in code below */ + + /** special case: create a new pipe with matching pipe and stream IDs on the fly when referred... */ + bool + MockTable::fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID) + { + answer_->insert (entry (q, Struct::create (pipeID, streamID))); + } + /** special case: create/retrieve new rocessing pattern for given stream ID... */ + bool + MockTable::fabricate_ProcPatt_on_demand (Query& q, string const& streamID) + { + typedef const ProcPatt cPP; + typedef typename WrapReturn::Wrapper Ptr; + + Ptr newPP (Struct::create (Query ("make(PP), "+q))); + answer_->insert (entry (q, newPP)); + } + + MockConfigRules::MockConfigRules () { WARN (config, "using a mock implementation of the ConfigQuery interface"); @@ -123,8 +148,8 @@ namespace lumiera * and with capabilities or properties defined by * the query. The real implementation would require * a rule based system (Ichthyo plans to use YAP Prolog), - * while this dummy implementation simply relies on a - * table of pre-fabricated objects. Never fails. + * while this dummy implementation simply relpies based + * on a table of pre-fabricated objects. Never fails. * @return smart ptr (or similar) holding the object, * maybe an empty smart ptr if not found */ @@ -144,9 +169,5 @@ namespace lumiera } // namespace query - - - /** */ - } // namespace lumiera diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index f01d3268d..3ff5d1cff 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -38,6 +38,7 @@ #ifndef LUMIERA_MOCKCONFIGRULES_H #define LUMIERA_MOCKCONFIGRULES_H +#include "proc/mobject/session.hpp" #include "common/configrules.hpp" #include "common/util.hpp" @@ -54,8 +55,10 @@ namespace lumiera namespace query { + using asset::Pipe; using asset::ProcPatt; using asset::PProcPatt; + using mobject::Session; using util::isnil; @@ -88,6 +91,11 @@ namespace lumiera MockTable (); const any& fetch_from_table_for (const string& queryStr); + // special cases.... + bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID); + bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID); + + private: void fill_mock_table (); }; @@ -117,14 +125,73 @@ namespace lumiera ) return solution = candidate; } - - return solution = Ret(); // fail: return default-constructed empty smart ptr + return try_special_case(solution, q); } + + private: + bool + try_special_case (Ret& solution, const Query& q) + { + Query newQuery = q; + if (is_defaults_query (q)) // modified query.. + return solution = Session::current->defaults (newQuery); + // may cause recursion + if (detect_case (newQuery)) + return resolve (solution, newQuery); + + return solution = Ret(); + // fail: return default-constructed empty smart ptr + } + + bool + detect_case (Query& q); }; + /** Hook for treating very special cases for individual types only */ + template + inline bool + LookupPreconfigured::detect_case (Query& q) + { + q.clear(); // end recursion + return false; + } + template + inline bool + LookupPreconfigured::detect_case (Query& q) + { + const string pipeID = extractID("pipe", q); + const string streamID = extractID("stream", q); + if (!isnil(pipeID) && !isnil(streamID)) + return fabricate_matching_new_Pipe (q, pipeID, streamID); + + q.clear(); + return false; + } + template<> + inline bool + LookupPreconfigured::detect_case (Query& q) + { + const string streamID = extractID("stream", q); + if (!isnil(streamID)) + return fabricate_ProcPatt_on_demand (q, streamID); + + // note: we don't handle the case of "make(PP), capabilities....." specially + // because either someone puts a special object into the mock table, or the + // recursive query done by the StructFactory simply fails, resulting in + // the StructFactory issuing a ProcPatt ctor call. + + q.clear(); + return false; + } + + + + + + /** - * Dummy Implementation of the query interface. + * Facade: Dummy Implementation of the query interface. * Provides an explicit implementation using hard wired * values for some types of interest for testing and debugging. */ diff --git a/src/proc/asset/structfactoryimpl.hpp b/src/proc/asset/structfactoryimpl.hpp index fb8e5245f..c2f6f2ab7 100644 --- a/src/proc/asset/structfactoryimpl.hpp +++ b/src/proc/asset/structfactoryimpl.hpp @@ -149,7 +149,7 @@ namespace asset StructFactoryImpl::fabricate (const Query& caps) { TODO ("actually extract properties/capabilities from the query..."); - TODO ("make sure AssetManager detects dublicates (it doesn't currently)"); + TODO ("make sure AssetManager detects dublicates (currently 4/08 it doesn't)"); return new Track (createIdent (caps)); } diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 5523b8c60..6d764e90b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2267,12 +2267,12 @@ The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe poi

-
+
!Identification
 Pipes are distinct objects and can be identified by their asset ~IDs. Besides, as for all [[structural assets|StructAsset]] there are extended query capabilities, including a symbolic pipe-id and a media (stream) type id. Any pipe can accept and deliver exactly one media stream kind (which may be inherently structured though, e.g. spatial sound systems or stereoscopic video)
 
 !creating pipes
-Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes, and further pipes can be created by using an new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]].
+Pipe assets are created automatically by being used and referred. The [[Session]] holds a collection of global pipes, and further pipes can be created by using a new pipe reference in some placement. Moreover, every clip has an (implicit) [[source port|ClipSourcePort]], which will appear as pipe asset when first used (referred) while [[building|BuildProcess]]. Note that creating a new pipe implies using a [[processing pattern|ProcPatt]], which will be queried from the [[Defaults Manager|DefaultsManagement]] (resulting in the use of some preconfigured pattern or maybe the creation of a new ProcPatt object if necessary)
 
 !removal
 Deleting a Pipe is an advanced operation, because it includes finding and "detaching" all references, otherwise the pipe will leap back into existence immediately. Thus, global pipe entries in the Session and pipe references in [[locating pins|LocatingPin]] within any placement have to be removed, while clips using a given source port will be disabled. {{red{todo: implementation deferred}}}

From b86a8605e7ce69dfd320720551922954dfc1a8ef Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 7 Apr 2008 03:19:24 +0200
Subject: [PATCH 19/24] now complete and passing the compiler

---
 src/common/query.hpp                        |  5 ++-
 src/common/query/mockconfigrules.cpp        | 12 +++++-
 src/common/query/mockconfigrules.hpp        | 48 +++++++++++++--------
 src/proc/mobject/session/defsmanager.cpp    | 12 +++++-
 src/proc/mobject/session/mobjectfactory.hpp |  2 +-
 src/proc/mobject/session/track.hpp          |  8 +++-
 6 files changed, 61 insertions(+), 26 deletions(-)

diff --git a/src/common/query.hpp b/src/common/query.hpp
index 14d23982b..db76259db 100644
--- a/src/common/query.hpp
+++ b/src/common/query.hpp
@@ -52,12 +52,13 @@ namespace lumiera
       explicit Query (const string& predicate="") : string(predicate) {}
       explicit Query (format& pattern)            : string(str(pattern)) {}
       
-      
       const string asKey()  const
         {
           return string(typeid(OBJ).name())+": "+*this;
         }
-    };
+      
+      operator string& () { return *this; }      // TODO: needed temporarily by mockconfigrules
+    };                                          //        for calling removeTerm on the string-ref....
 
     
   namespace query
diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp
index d688d9dbd..7589d84f7 100644
--- a/src/common/query/mockconfigrules.cpp
+++ b/src/common/query/mockconfigrules.cpp
@@ -93,6 +93,7 @@ namespace lumiera
       
       typedef const ProcPatt cPP;
       
+      
       // for baiscpipetest.cpp ---------
       answer_->insert (entry_Struct ("stream(video)"));
       answer_->insert (entry_Struct ("stream(teststream)"));
@@ -104,6 +105,8 @@ namespace lumiera
     }
     
     
+    
+    
     /* under some circumstances we need to emulate the behaviour *
      * of a real resolution engine in a more detailed manner.    *
      * These case are hard wired in code below                   */
@@ -112,17 +115,22 @@ namespace lumiera
     bool 
     MockTable::fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID)
     {
-      answer_->insert (entry (q, Struct::create (pipeID, streamID)));
+      typedef WrapReturn::Wrapper Ptr;
+      
+      Ptr newPipe (Struct::create (pipeID, streamID));
+      answer_->insert (entry (q, newPipe));
+      return true; // denotes query will now succeed...
     }
     /** special case: create/retrieve new rocessing pattern for given stream ID... */
     bool 
     MockTable::fabricate_ProcPatt_on_demand (Query& q, string const& streamID)
     {
       typedef const ProcPatt cPP;
-      typedef typename WrapReturn::Wrapper Ptr;
+      typedef WrapReturn::Wrapper Ptr;
       
       Ptr newPP (Struct::create (Query ("make(PP), "+q)));
       answer_->insert (entry (q, newPP));
+      return true;
     }
     
 
diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp
index 3ff5d1cff..c3f6684ca 100644
--- a/src/common/query/mockconfigrules.hpp
+++ b/src/common/query/mockconfigrules.hpp
@@ -67,12 +67,28 @@ namespace lumiera
     
     
     
-    /** a traits-class to define the smart-ptr to wrap the result */
-    template
-    struct WrapReturn             { typedef shared_ptr Wrapper;  };
     
-    template<>
-    struct WrapReturn   { typedef PProcPatt Wrapper;  };
+    
+    namespace // internal details
+      {
+      
+      /** a traits-class to define the smart-ptr to wrap the result */
+      template
+      struct WrapReturn             { typedef shared_ptr Wrapper;  };
+      
+      template<>
+      struct WrapReturn   { typedef PProcPatt Wrapper;  };
+      
+      
+      /** helper detecting if a query actually intended to retrieve a "default" object.
+       *  This implementation is quite crude, of cours it would be necessary to actually
+       *  parse and evaluate the query. @note query is modified if "default" ... */
+      inline bool
+      is_defaults_query (string& query)
+      {
+        return !isnil (removeTerm ("default", query));
+      }
+    } // details (end)
     
     
     /** 
@@ -92,6 +108,8 @@ namespace lumiera
         const any& fetch_from_table_for (const string& queryStr);
         
         // special cases....
+        template 
+        bool detect_case (Query& q);
         bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID);
         bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID);
 
@@ -133,32 +151,28 @@ namespace lumiera
         try_special_case (Ret& solution, const Query& q)
           {
             Query newQuery = q;
-            if (is_defaults_query (q))  // modified query..
+            if (is_defaults_query (newQuery))  // modified query..
               return solution = Session::current->defaults (newQuery);
-                                      //   may cause recursion
+                                             //   may cause recursion
             if (detect_case (newQuery))
               return resolve (solution, newQuery);
             
-            return solution = Ret();  
-                                // fail: return default-constructed empty smart ptr
+            return solution = Ret();     // fail: return default-constructed empty smart ptr
           }
-        
-        bool
-        detect_case (Query& q);
       };
     
     
     /** Hook for treating very special cases for individual types only */
-    template
+    template
     inline bool 
-    LookupPreconfigured::detect_case (Query& q)
+    MockTable::detect_case (Query& q)
     {
       q.clear(); // end recursion
       return false;
     }
-    template
+    template<>
     inline bool 
-    LookupPreconfigured::detect_case (Query& q)
+    MockTable::detect_case (Query& q)
     {
       const string pipeID   = extractID("pipe", q);
       const string streamID = extractID("stream", q);
@@ -170,7 +184,7 @@ namespace lumiera
     }
     template<>
     inline bool 
-    LookupPreconfigured::detect_case (Query& q)
+    MockTable::detect_case (Query& q)
     {
       const string streamID = extractID("stream", q);
       if (!isnil(streamID))
diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp
index 4c5cd9f82..1d6f92e9a 100644
--- a/src/proc/mobject/session/defsmanager.cpp
+++ b/src/proc/mobject/session/defsmanager.cpp
@@ -134,6 +134,8 @@ namespace mobject
 
 #include "proc/asset/procpatt.hpp"
 #include "proc/asset/pipe.hpp"
+#include "proc/asset/track.hpp"
+#include "proc/mobject/session/track.hpp"
 
 namespace mobject
   {
@@ -145,9 +147,15 @@ namespace mobject
     using asset::ProcPatt;
     using asset::PProcPatt;
     
+    using mobject::session::Track;
+    using mobject::session::TrackAsset;
+    using mobject::session::PTrack;
+    using mobject::session::PTrackAsset;
     
-    template PPipe     DefsManager::operator() (const Query&); 
-    template PProcPatt DefsManager::operator() (const Query&); 
+    template PPipe       DefsManager::operator() (const Query&); 
+    template PProcPatt   DefsManager::operator() (const Query&); 
+    template PTrack      DefsManager::operator() (const Query&); 
+    template PTrackAsset DefsManager::operator() (const Query&); 
     
     template bool DefsManager::define (const PPipe&, const Query&);
     template bool DefsManager::forget (const PPipe&);
diff --git a/src/proc/mobject/session/mobjectfactory.hpp b/src/proc/mobject/session/mobjectfactory.hpp
index 02a0f52b3..cff884ca7 100644
--- a/src/proc/mobject/session/mobjectfactory.hpp
+++ b/src/proc/mobject/session/mobjectfactory.hpp
@@ -45,7 +45,7 @@ namespace mobject
     class Track;
     class Effect;
     
-    typedef shared_ptr PTrackAsset;
+    typedef shared_ptr PTrackAsset;
 
 
     class MObjectFactory
diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp
index 3a5517d52..d4005b97d 100644
--- a/src/proc/mobject/session/track.hpp
+++ b/src/proc/mobject/session/track.hpp
@@ -34,7 +34,11 @@ namespace mobject
   {
   namespace session
     {
-    typedef shared_ptr PTrackAsset;
+    class Track;
+    typedef asset::Track TrackAsset;
+    
+    typedef shared_ptr PTrack;
+    typedef shared_ptr PTrackAsset;
 
 
     /**
@@ -57,7 +61,7 @@ namespace mobject
 
       public:
         /** Child tracks in a tree structure */
-        vector > subTracks;  // TODO: Placement!!!!!!!
+        vector > subTracks;  ////TODO: it should really work with Placements! this here is just a decoy!!!!!!!
         
         virtual bool isValid()  const;
       };

From dfefbea769cb1b45284df71af67dc151a0bf37b7 Mon Sep 17 00:00:00 2001
From: Ichthyostega 
Date: Mon, 7 Apr 2008 05:11:09 +0200
Subject: [PATCH 20/24] overdue to write down some documentation about
 ConfigRules (starting to loose track of some relations myself...)

---
 doc/devel/uml/class139653.html           |   3 +-
 doc/devel/uml/class140549.html           |  26 ++
 doc/devel/uml/class140677.html           |  27 ++
 doc/devel/uml/class140805.html           |  26 ++
 doc/devel/uml/class140933.html           |  24 +
 doc/devel/uml/class141061.html           |  22 +
 doc/devel/uml/class141189.html           |  31 ++
 doc/devel/uml/class141317.html           |  24 +
 doc/devel/uml/class141445.html           |  23 +
 doc/devel/uml/class141573.html           |  23 +
 doc/devel/uml/class141701.html           |  20 +
 doc/devel/uml/class141829.html           |  20 +
 doc/devel/uml/classdiagrams.html         |   1 +
 doc/devel/uml/classes.html               |  11 +
 doc/devel/uml/classes_list.html          |  11 +
 doc/devel/uml/collaborationdiagrams.html |   1 +
 doc/devel/uml/componentdiagrams.html     |   1 +
 doc/devel/uml/fig131461.png              | Bin 0 -> 49580 bytes
 doc/devel/uml/fig131589.png              | Bin 0 -> 12406 bytes
 doc/devel/uml/fig131717.png              | Bin 0 -> 14266 bytes
 doc/devel/uml/fig131845.png              | Bin 0 -> 18298 bytes
 doc/devel/uml/index.html                 |  73 ++-
 doc/devel/uml/index_34.html              |  23 +
 doc/devel/uml/index_60.html              |   2 +-
 doc/devel/uml/index_65.html              |   3 +-
 doc/devel/uml/index_67.html              |  42 +-
 doc/devel/uml/index_68.html              |   6 +-
 doc/devel/uml/index_69.html              |   2 +-
 doc/devel/uml/index_70.html              |   3 +-
 doc/devel/uml/index_72.html              |   2 +-
 doc/devel/uml/index_73.html              |   2 +-
 doc/devel/uml/index_76.html              |   1 +
 doc/devel/uml/index_77.html              |   1 +
 doc/devel/uml/index_78.html              |   1 +
 doc/devel/uml/index_79.html              |   2 +-
 doc/devel/uml/index_80.html              |   1 +
 doc/devel/uml/index_81.html              |  27 ++
 doc/devel/uml/index_82.html              |   8 +-
 doc/devel/uml/index_83.html              |   3 +-
 doc/devel/uml/index_84.html              |   8 +-
 doc/devel/uml/index_85.html              |   2 +
 doc/devel/uml/index_86.html              |  14 +-
 doc/devel/uml/index_87.html              |   1 +
 doc/devel/uml/index_89.html              |  23 +
 doc/devel/uml/navig.html                 |   2 +-
 doc/devel/uml/packages.html              |   1 +
 doc/devel/uml/public_operations.html     |   8 +-
 doc/devel/uml/usecasediagrams.html       |   1 +
 doc/devel/uml/usecases.html              |   6 +
 uml/lumiera/128261                       |  11 +-
 uml/lumiera/128517                       |   4 +-
 uml/lumiera/129285                       |   2 +-
 uml/lumiera/131077                       | 551 +++++++++++++++++++++++
 uml/lumiera/131461.diagram               | 196 ++++++++
 uml/lumiera/131589.diagram               |  21 +
 uml/lumiera/131717.diagram               |  45 ++
 uml/lumiera/131845.diagram               |  68 +++
 uml/lumiera/5.session                    |  44 +-
 uml/lumiera/lumiera.prj                  |   4 +-
 wiki/renderengine.html                   |  35 +-
 60 files changed, 1466 insertions(+), 77 deletions(-)
 create mode 100644 doc/devel/uml/class140549.html
 create mode 100644 doc/devel/uml/class140677.html
 create mode 100644 doc/devel/uml/class140805.html
 create mode 100644 doc/devel/uml/class140933.html
 create mode 100644 doc/devel/uml/class141061.html
 create mode 100644 doc/devel/uml/class141189.html
 create mode 100644 doc/devel/uml/class141317.html
 create mode 100644 doc/devel/uml/class141445.html
 create mode 100644 doc/devel/uml/class141573.html
 create mode 100644 doc/devel/uml/class141701.html
 create mode 100644 doc/devel/uml/class141829.html
 create mode 100644 doc/devel/uml/fig131461.png
 create mode 100644 doc/devel/uml/fig131589.png
 create mode 100644 doc/devel/uml/fig131717.png
 create mode 100644 doc/devel/uml/fig131845.png
 create mode 100644 doc/devel/uml/index_34.html
 create mode 100644 doc/devel/uml/index_81.html
 create mode 100644 doc/devel/uml/index_89.html
 create mode 100644 uml/lumiera/131077
 create mode 100644 uml/lumiera/131461.diagram
 create mode 100644 uml/lumiera/131589.diagram
 create mode 100644 uml/lumiera/131717.diagram
 create mode 100644 uml/lumiera/131845.diagram

diff --git a/doc/devel/uml/class139653.html b/doc/devel/uml/class139653.html
index cfdc8392a..641046544 100644
--- a/doc/devel/uml/class139653.html
+++ b/doc/devel/uml/class139653.html
@@ -21,7 +21,8 @@
 
 
Operation currEDL

Declaration :

  • Uml : + currEDL() : EDL
  • C++ : public: EDL currEDL ()

The EDL currently in focus. In most cases, Session and EDL are almost the same, just EDL emphasizes the collection aspect. But generally (for larger editing projects) one Session can contain several EDLs, which may even be nested. At any given time, only one of these EDLs has focus and recieves the editing commands.

Operation getFixture

Declaration :

  • Uml : + getFixture() : Fixture&
  • C++ : public: Fixture& getFixture ()

While the session can be comprised of several EDLs,
there is only one Fixture, which represents the actual
configuration of all Objects to be rendered

-
Relation current (<unidirectional association>)

Declaration :

Standard access path to get at the current session via the Session Manager, which acts as a "PImpl" smart pointer

+
Relation current (<unidirectional association>)

Declaration :

Standard access path to get at the current session via the Session Manager, which acts as a "PImpl" smart pointer

+
Relation defaults (<unidirectional association>)

Declaration :

All public operations : currEDL , getFixture

diff --git a/doc/devel/uml/class140549.html b/doc/devel/uml/class140549.html new file mode 100644 index 000000000..9366aa42a --- /dev/null +++ b/doc/devel/uml/class140549.html @@ -0,0 +1,26 @@ + + + + + + +Class ConfigRules + + + + + +
Class ConfigRules
+

+ + + + +

Declaration :

  • C++ : class ConfigRules : public QueryHandler<>
  • Java : public interface ConfigRules

Directly inherited by : ResolverBase

+

public acces point for running Config Queries

+ +
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+

All public operations : resolve

+ + diff --git a/doc/devel/uml/class140677.html b/doc/devel/uml/class140677.html new file mode 100644 index 000000000..6b32a5b25 --- /dev/null +++ b/doc/devel/uml/class140677.html @@ -0,0 +1,27 @@ + + + + + + +Class QueryHandler + + + + + +
Class QueryHandler
+

+ + + + +

Declaration :

  • C++ : template<class TY> class QueryHandler
  • Java : public interface QueryHandler<TY>

Directly inherited by : ConfigRules

+
+ +
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Operation resolve

Declaration :

  • Uml : + resolve(inout solution : P<TY>, in query : Query<TY>) : bool
  • C++ : public: bool resolve ()
+

All public operations : resolve

+ + diff --git a/doc/devel/uml/class140805.html b/doc/devel/uml/class140805.html new file mode 100644 index 000000000..a9f18e933 --- /dev/null +++ b/doc/devel/uml/class140805.html @@ -0,0 +1,26 @@ + + + + + + +Class TypeHandler + + + + + +
Class TypeHandler
+

+ + + + +

Declaration :

  • C++ : template<class TY> class TypeHandler
  • Java : public interface TypeHandler<TY>

Directly inherited by : TypeHandler<Pipe>

+
+ +
Operation find

Declaration :

  • Uml : abstract, + find(in capabilities : Pred) : TY
  • C++ : public: virtual TY find () = 0
+
Operation make

Declaration :

  • Uml : abstract, + make(in capabilities : Pred) : TY
  • C++ : public: virtual TY make () = 0
+

All public operations : find , make

+ + diff --git a/doc/devel/uml/class140933.html b/doc/devel/uml/class140933.html new file mode 100644 index 000000000..60883f3f2 --- /dev/null +++ b/doc/devel/uml/class140933.html @@ -0,0 +1,24 @@ + + + + + + +Class ResolverBase + + + + + +
Class ResolverBase
+

+ + + + +

Declaration :

Directly inherited by : QueryHandlerImpl

+
+
+

All public operations : resolve

+ + diff --git a/doc/devel/uml/class141061.html b/doc/devel/uml/class141061.html new file mode 100644 index 000000000..db362df0b --- /dev/null +++ b/doc/devel/uml/class141061.html @@ -0,0 +1,22 @@ + + + + + + +Class YAP_Prolog + + + + + +
Class YAP_Prolog
+

+ + + + +

Declaration :

  • C++ : class YAP_Prolog
+
+ + diff --git a/doc/devel/uml/class141189.html b/doc/devel/uml/class141189.html new file mode 100644 index 000000000..ae8237f18 --- /dev/null +++ b/doc/devel/uml/class141189.html @@ -0,0 +1,31 @@ + + + + + + +Class QueryHandlerImpl + + + + + +
Class QueryHandlerImpl
+

+ + + + +

Declaration :

  • C++ : template<class TY> class QueryHandlerImpl : public ResolverBase
+ +
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Relation <association>

Declaration :

+
Operation resolve

Declaration :

  • Uml : + resolve(inout solution : P<TY>, in query : Query<TY>) : bool
  • C++ : public: bool resolve ()
+
Relation <unidirectional association>

Declaration :

+
Relation <unidirectional association>

Declaration :

+
Relation <unidirectional association>

Declaration :

+

All public operations : resolve , resolve

+ + diff --git a/doc/devel/uml/class141317.html b/doc/devel/uml/class141317.html new file mode 100644 index 000000000..96e49881c --- /dev/null +++ b/doc/devel/uml/class141317.html @@ -0,0 +1,24 @@ + + + + + + +Class TypeHandler<Pipe> + + + + + +
Class TypeHandler<Pipe>
+

+ + + + +

Declaration :

  • C++ : template<> class TypeHandler<Pipe> : public TypeHandler<>
+ +
Relation <unidirectional association>

Declaration :

+

All public operations : find , make

+ + diff --git a/doc/devel/uml/class141445.html b/doc/devel/uml/class141445.html new file mode 100644 index 000000000..0f69494d7 --- /dev/null +++ b/doc/devel/uml/class141445.html @@ -0,0 +1,23 @@ + + + + + + +Class DefaultsManager + + + + + +
Class DefaultsManager
+

+ + + + +

Declaration :

  • C++ : class DefaultsManager
+ +
Relation <association>

Declaration :

+ + diff --git a/doc/devel/uml/class141573.html b/doc/devel/uml/class141573.html new file mode 100644 index 000000000..8417d6b5b --- /dev/null +++ b/doc/devel/uml/class141573.html @@ -0,0 +1,23 @@ + + + + + + +Class DefaultsRegistry + + + + + +
Class DefaultsRegistry
+

+ + + + +

Declaration :

  • C++ : class DefaultsRegistry
+ +
Relation <association>

Declaration :

+ + diff --git a/doc/devel/uml/class141701.html b/doc/devel/uml/class141701.html new file mode 100644 index 000000000..232cf2aed --- /dev/null +++ b/doc/devel/uml/class141701.html @@ -0,0 +1,20 @@ + + + + + + +Class User + + + + + +
Class User
+

+ + + + +

Declaration :

  • C++ : class User
+ diff --git a/doc/devel/uml/class141829.html b/doc/devel/uml/class141829.html new file mode 100644 index 000000000..d6c42943f --- /dev/null +++ b/doc/devel/uml/class141829.html @@ -0,0 +1,20 @@ + + + + + + +Class Serializer + + + + + +
Class Serializer
+

+ + + + +

Declaration :

  • C++ : class Serializer
+ diff --git a/doc/devel/uml/classdiagrams.html b/doc/devel/uml/classdiagrams.html index 03c862119..4cf5416cc 100644 --- a/doc/devel/uml/classdiagrams.html +++ b/doc/devel/uml/classdiagrams.html @@ -27,6 +27,7 @@ Media-Asset Relations Proc-Asset Relations Render Entities +Rules access Session structure Struct-Asset Relations diff --git a/doc/devel/uml/classes.html b/doc/devel/uml/classes.html index d2f240eb9..23162b8f6 100644 --- a/doc/devel/uml/classes.html +++ b/doc/devel/uml/classes.html @@ -39,11 +39,14 @@ CompoundMediacompound of several elementary media tracks,
e.g. the individual media streams found in one media file ConditionI provided a reworked Condition class in my Cinelerra2 repository Config +ConfigRulesinterfacepublic acces point for running Config Queries ConManagerConnection Manager, used to build the connections between render engine nodes, if these nodes need to cooperate besides the normal "data pull" operation. Esp., the Connection Manager knows how to wire up the effect's parameters with the corresponding ParamProviders (autmation) in the Session Constraint ControllerFacadeboundaryProvides unified access to the Proc-Subsystem Controller. Especially, this Facade class provides the functions to get a render engine to carry out actual renderings. Datasetmeta asset describing a collection of control data DBImplementation of the registry holding all Asset instances known to the Asset Manager subsystem. As of 8/2007 implemented by a hashtable. +DefaultsManager +DefaultsRegistry DoAttach DoRecurse EDL @@ -102,13 +105,17 @@ ProcNodeinterfaceKey abstraction of the Render Engine: A Data processing Node ProcPattspecial type of structural Asset representing information how to build some part of the render engine's processing nodes network. ProjectorSpecial video processing node used to scale and translate image data. +QueryHandlerinterface +QueryHandlerImpl RelativeLocation RelTypeenumthe possible kinds of RelativePlacements RenderEngine RenderStateEncapsulates the logic used to get a "current render process" in accordance to the currentyl applicable controller settings. The provided StateProxy serves to hold any mutalbe state used in the render process, so the rest of the render engine can be stateless. +ResolverBase Scheduler Segment SegmentationToolTool implementation for deriving a partitioning of the current timeline such, that each segement has a constant configuration. "Constant" means here, that any remaining changes over time can be represented by automation solely, without the need to change the node connections. +Serializeractor SessionPrimary Interface for all editing tasks.
The session contains defaults, all the assets being edited, and a set of EDL with the individual MObjects to be manipulated and rendered. SessionImplImplementation class for the Session interface SessManager @@ -126,13 +133,17 @@ Trackstructural asset holding the configuration of a track in the EDL Track Trafo +TypeHandlerinterface +TypeHandler<Pipe> Unknownplaceholder for unknown or unavailable media source +Useractor VFrame Visitable VRenderRepresentation of a Video render process. (Encapsulates the video buffers for the actual calculations) Wish WriteBuffer WriteBufferPool +YAP_Prolog diff --git a/doc/devel/uml/classes_list.html b/doc/devel/uml/classes_list.html index 41655bf32..6f3005da9 100644 --- a/doc/devel/uml/classes_list.html +++ b/doc/devel/uml/classes_list.html @@ -40,11 +40,14 @@ CompoundMedia
Condition
Config
+ConfigRules
ConManager
Constraint
ControllerFacade
Dataset
DB
+DefaultsManager
+DefaultsRegistry
DoAttach
DoRecurse
EDL
@@ -103,13 +106,17 @@ ProcNode
ProcPatt
Projector
+QueryHandler
+QueryHandlerImpl
RelativeLocation
RelType
RenderEngine
RenderState
+ResolverBase
Scheduler
Segment
SegmentationTool
+Serializer
Session
SessionImpl
SessManager
@@ -127,13 +134,17 @@ Track
Track
Trafo
+TypeHandler
+TypeHandler<Pipe>
Unknown
+User
VFrame
Visitable
VRender
Wish
WriteBuffer
WriteBufferPool
+YAP_Prolog
diff --git a/doc/devel/uml/collaborationdiagrams.html b/doc/devel/uml/collaborationdiagrams.html index 37350c3f2..2d8ab627d 100644 --- a/doc/devel/uml/collaborationdiagrams.html +++ b/doc/devel/uml/collaborationdiagrams.html @@ -16,6 +16,7 @@ +
"default" object
build processThis figure shows the process of building and starting a RenderEngine
diff --git a/doc/devel/uml/componentdiagrams.html b/doc/devel/uml/componentdiagrams.html index 967828869..46ed24795 100644 --- a/doc/devel/uml/componentdiagrams.html +++ b/doc/devel/uml/componentdiagrams.html @@ -17,6 +17,7 @@ +
backend-components
components
OverviewThis drawing shows the top level compoents and relations
proc-components
diff --git a/doc/devel/uml/fig131461.png b/doc/devel/uml/fig131461.png new file mode 100644 index 0000000000000000000000000000000000000000..87241bcda3c02f1b0ef2c1bf09a7249f6c613e77 GIT binary patch literal 49580 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 A+Hh7JxR%cX7JglJPyRqNN6X6MsKe0F zr<<_&a5m(VsvLG@Afcuoz@Sp}VK>U3m&UGCS0h9vJ6i3d zOQH_sR${EZ9hkm@&H++u*MCti@*d2ekzk6T`_SZ}603>CWV)(ruK39x@Nx1C+)(O*Ewj3zd2X#wPana?RuPH6$ ze=WFZpREd%cY^D}O>T2@ap@_Pd#LQR96_DB{p0~1ka9ozY+U@VI=q_qX#I@D&4q!v zcZvm4h_5A5wuF#2x~hlUIa`uf!qhu64=n#6ol1e(P#gM9Y|+Ov=+Ni2`-pbr3J z)6jmrqdO6M$bwky!hFSL&FaGEa*_e{eWI=BX3)IR%#o;ZDL>MOOFtJ2IN;iBW+HFF zWeCI|g}xb;7Ycx?V#)j~Jew=|X_xq=8O|t#?M$327dPQSxrQg(=>TZH5gF@o z<1%jrGezn6>`Y=ohH-Jre`}Tc7~&gjO3;eiI3Q-83#K~JcYvBB+rpC-dcrbOhdq@@ zHYSeW)_NA(7-g5DH1buUOYEX(gc2;qb>=9#bZlf*agf=M-BN)lSB6@<6~RF=MVx8( zEMMRI_$1NZ7&y#WH!-}F#L$Ksbj zPw(`MH7r6pIJLNv?`W+F`$GmS^6&gnR;lki3XE1QZDb)(tfsTVpGJ z0pru-&HEvKVQzre@v-UN`YRX0Il_UQG^0+H>&}-#bqogcC-||Y+`%hnex-ZX3X7!* zMs)X9%#AHqIP^|E>|lH`l;t=<{8|8)YBlh~uK(agxwwmAzV0VS|Avkb)Hl?np>T(} z2JFEh-kxB$#K7sAB<)?TS|x6cu4STvK;B$eXv_S}ec@*i^Zjn)UsvN|zG;7H4$Zs; zf$+YY7AB8*1L{I*F{wEs2;}a6xEJbycBC7>_o9GNldUL`sxI~WT~eK7MG zh&#V7*`Mt|>GNtvXCc1VLIJw@x6DC*^$Z*qt7jnhIkMS zfN!GoXyv!`cH;jA`ayhSH!FL`?h-nm{7>4HmMzQz^n}}sPEU@y5B^m_LymttzV%a7 z*V(wIVf{AZHi1YQfT#=hXBh8!x3=H>w6R7)cuePYp=jv}FnSFn7cJ6CIN9*w{Js&Z&N^Zi5RrO#LXT z8N8kTUS*b@a@-2B@A4rb2?C~}iEnj-k--$Vm-PDW@jksw7zafw(5Afq_s}RF2qsX* zQ}W(vSPm20oB1hwP-t&@GK1!=AYZM1H7Yx$1=wnMu!BmNXavBd=aqnm0vUMrJA=mapBTF?T0 z;yTt|omj%M(A6)ef9R52=54@zsMBz#X8c&FMWhAXNg&pI%e9U3bJ<@wP^YYvlRNHL zn&wCf{AQ?O+#w78M=WtU=`yR|{9uKNjAZK3;`Hy&b&I9`Glp#O?Rp6upgGgz9l_~m z*;}P@6u&2@OXd$6a5}{~jV$C-*XGKSRS47Zq+mhK29E0qipr7ZUQ!ujxRI~#E%mNd z@{2C%X#q(FR5hct2Yo5?vQ%SF*h1ecQsMdgDw0!BYpOzKE>-lYHK|~ zFKC2dR0evCC@oLmE2t6Irg}>n4Lmm?0a?*mn!(7&pj#^I@y6l(#Z-~4aY7UbN%m8- z1O>JT)XuP=hK{ z)V_D)o@45?PE0XZ<+R!K%nW9z0(InL24W>^QG<$55OUpTDcSf^8nO51` zH*ZqP5vP0`c~2Gg51a#UPLacXjxsjm&dKHv7AH7cC-VC9ZJu}yP!K$!_aI#ZMgU%? zuynzR;lIlkf3iAq_CY{K`L%zCAN;^K_Guep7<3%|8&1f-B)cdd{Mz`M@t9nOWnNOv zW`w?ca8TfhsKFSp6^5k8ZyaCQQOF;t6bx#p_=;CvO&^?>zVRLm=E*^(trw0G1v*!2KXJ&YCkRuMULQq zNujhj{k{V#RA0un`)#mT!JpA5`KW_Y+(vH`Py@gxJvCRb6*_By-B-hWkb-%wqRKisISjY$A`fLD_Zw8YJWKReHQp2+@6^>ObG zAi)iHfcuMn(CZe!mlMN3s+RD1r1~(MF)nC5C#hk4k#o?nJFxgL=!#`AC@V{cul9{A zat9|#c7k19nU7O_8T* zEqa5m&+)SIha1Vd&vkTI{aBqBdz*oNJSf=l?-LoU*f0sd0J2Qx=s2mTKLss$`^c*O zhZEH}n4l|#Y`sd@pa-~>)p6dwn z`X``9?+MM!WIEx*nCv$CvHBm+ts-5K)w-v0<{WyoI=pJ+ya>P?HDia|IO{*7j*t{s zxyQ<)HRqJ^(a?y^E}?NJpI{A#)_#hl0?kSBh?myv?@ z^rLvoK%9H$uzFDBuJf}W7H0^ALnfTl(cr4dsZt*;+6e09yrOj4sRlljF4QM$IbfpO zUj@oSW3y#C_h!D2=$0JX^mXT(zqGh1gn~6e4wWVOCCl+eXEjn!{{p&*qP9H3rMhmf z_|0uUiW@g>Om-TiJ~b>uxI2J8UgnUR1@ctoPcMht^z?sI)D? zAqpIBQc3&9GuF{gd9&29&W}vF16oI>j=jkVE8@oW z^fVX+HJozix>9x$`}1)nK6vPuKZU2rGJ}|>DKI(#?bqRj%+fzs@nXq>CKfc!vw=Oe zi9h^zU7h|P$l{vp_V%i~$f4cnw&)noVzoh;?98Q*WI8}^zTWvK!l}VTAg>=-HkJ^& zYoMnO#9q1LvR8n5phdp@(euw~UvAQzg#-nrvvUknIfcoRE!Fkonku_(9^_jK<%T#j zT4{{1@(Xm2zP;=CSGF&-6o7sZrJ&j5#B5uop+d(1KvzX{YWR6Pk}wL-u7Mt$Z$vsE zgso~kB6xNs@lyr$FZ#XzlI8w$Yxw_Dm$t-vf-QDnby{Bg_Pm`Y@=edgTDy!up-^-B wi%P_Q)kmcwSb_89N$u%2_-`8UBTvX51>QwIe;N!BJp`gCulgWQ*67Xu02+cnm;e9( literal 0 HcmV?d00001 diff --git a/doc/devel/uml/fig131717.png b/doc/devel/uml/fig131717.png new file mode 100644 index 0000000000000000000000000000000000000000..b45afa0c17916fb91c682ff320f5371bf3f028ff GIT binary patch literal 14266 zcmeHuXINBSmt`RerGOM7Nl+{h5Q&0hp|FrFphU?aVv!LPlvIdh0Rc%8OOoU$6gel! zIVSi z3#t$ZULy1Y_=~Y89TEb$4Uv8PNcDZp>bT46>&73OHc{7-kVqM;2ubhAa*OAlckdg# zR|z7qZMF2^8}lTlG+SL-ygcD%m6WL+>e)Blz@A&Y+~ODhIHG}EvEj!MTY87eOC`xJ z-m$E3uiDl~h+M7qPIXYE!&;#9k=J6RY^PI|Gs0L?B1JzAJ-2w;zWsLJUtW$C0(t!< z#)1k0`S64h2Z4N+IKYF@d2irD1g}mJLSEC=l0tq7deB3vZJ-FqhZz_e0%?N(cRxt; zW%UB&z?CzCZB#Osg4G!72MJ-oLEcO`$KG@PB2G-~bm9Nq!~V-rMTGK<8bdZnB3i>% z86fuDCIuwTPlPgkdSNeQCR3!u0x!t&*zPgoUf8Ud{+xXwbDL6(8c)56?|(Pf52BW% zSj(|u8%asYTY)Ksh5ZM;A|xav8hQ@NKBrGwYupLv+agtTbaYfzRkgK`=c28@XLkL( zUQ7Ccq~d5PV|gIUX}4daGfuS3da`ozWbIv<{VGrKN-I|tZnKG=2;PA~&*^xU%k{8NMM>ALUk&MV->A`I zq(D|~cyx4m`>@#_ad_D2nvKo48MY+(?%jT- z=8u-vR&z76kZTft(4AmKDz`d}4jB(y-_9bUw)~Vf|?_?>r zuz27<)T4pub58fCzRv)IdMN1R8az@_>8-B*Fu87RW@ct#5vqIzzPB>mJ3cP@@+XWh zdXEzdpX-Rt_`J-5aaLBS!fmyJEut+QadUI4two;XE4LgS9i1HS%d?W+6nUJRDgB`;DT-8iS|DDPp+OJ?pc*v645+ zEdjIw;o;#&&Xw)a0x{n_}e*S!w)fk1EZx@)Pv-x!ION~;#?3zd|3_T}c zx71fO;V6GRkF^|~Yl{rQVCZk4!LWGb=1jS^wzgCcTn=X=v`|*Zd#h$%m6esG2;A%a zwej5lSqY)mT?|JleYQuwvb19jpjz&c!AM+?Q+Com|w! zpFtQ*w}HL#qv9Gkb(pI4d^h2clALV3GL+Ze-hR~S6%Y{c1JyZ^YgkvdUP((#z^M3) z>A`_4f8`BSH+I9vGFYCbccm?HkeU5yYgrWqyfGM57_l9$JY0~U7oA@J$Am2 z>WG;<-7ETQMba0~HtEl%Hs9N?j_`4FzyAF*7z`PF`E<1EBwFhZ3(Ndao{`hu$}`^1 zNM2N(7fJfVczW$ZTdR=*(~cM+UR1{urvSmrQxTyTy+kw8(-}H&TxVNcXFEz~NqUpE ztsEnTrEzg_<>lo*7!t(c?~p9*qSJo;lYX!#i3-n*c%;Odnac-#d^Ub~fJ^GCs@Ewg z9V;f9ZaJ}26N9w!;K2ivh|7*nPO}D6?+!Mmz!?JDn69nWw}Vf3759q}arfT68~0z- z?=1Gx($cOM&s^P2Y4D>e8(#}!(@2jDq!Z$?TT;y#6ftfIt@9@J&eE3~9v|oMczXCr zHBbA%_&`0jxOTnXmpmOsX)~8BACV46v(5$xOcdMBww#`xir?-U7*N;M%{sGquBn-( zyb+=#_AFNDp(Q0HuG<2xKi)1XpR}Q?&OVE*-MxGF z>Dd?vye~}hmD{oFzkmN$Ra3K^taKU5H3T_Us|tMrBB;oElGEe;LcDi&)u|)=v7X)p zZ|6gDn@{-;+jC$MBErHh!>x*^ycDAOnabU9Ui9Dgy%D&nll@8k65CmFrH9W{RLE^^ zMYQsvaR(RG*Y=O~T~BsfTI7mGV|DJ637E&MC&WhdI#$}NQVW`)LLw@crUSU66Qu(= zpXdJiRpUO>6gY{s)K*hdyP`2MFtFDP!f!T0zjE<=v~sZ)wa{nw*GUZ%B#IKvZ#v?> zU&*@4&CA#nJX|$p+~pFayd}xf{L6x<+Kv59JL#hM7MyMRs9KI(QW%Erklcdv?mT0e zo%l_eGR~msfsqJymdWU<|HQVmv*V{G5+ZuvzgiZY75 zB1PCCq3fRnu7r*T`_S|rCG1X@DUWH4<40-+g(Sv>-zp$A{B(qqTCz($gn8UrHEWTyD>F$^&G zVR~CqioJm3Va}_+Oy-`51q@ue$dIpmfX96~gav~*O=0p4rMFQgwg@L^P}$)&FK_jIyH`ZF}VaUs9m zVE`1ROko9~eq`+eoHv9O05wxgm+v)pcjZ_;H%z^Pbl1$ z&~z?arjr}jX7=+K8OSV~HK(YA2b6d(ePKzqu(Gl*F;n>B+AZvCIB7{~eYzOzqmIhk zP+8?;$_j^GRGyypEYRBR%uM=*wPS2fx#P<0TihXI%t+S$Q*J7yurIV=Bv+);#812y z*(*8ww2^^( z^B`u#`L_<`q>9|xL)v`))Od9YWkTQcrm{=D)g>M(alphp`)HxMn#gFKAcVWs8?(93 zr{In;+^h)#!wuqc7@`z%o^EHv~XZlf;V zd^FEU8ej@dO-)J3hJh^YLIsZs7;?<@?8K_*c*H#CWFaxIz!xArF-+(3u49EBrgLy~ zl-6dhIfP|d-VRj8;UB1o&`^*XZwkH^hvvPWLc;3{OnZA&?YOy2e;P@%m`WF- zAwn!SDPq!rg?kMBKq&#N7X+6`3fFlBP`)Mn)}$v%mMrDKxe_GK{r!FY;@5_Y zJ<0LMYEPebb$7#)S5{Wa59gvA8f?fcwMYD#=v^OD6JrYs+M@X6wVi7|UQ$p{z{kf& zS5>8@rRC+_pKe1!UDD<{F;!OM!mP7v<7Fud(Ue$8w*0qKy-hNOUkh3R0Cxt+!sM*Y zzu+?qH>H`WBJJMW5*>g7B)qSxHL%^iEBJ0L=19fLDtmH0P%t?oV_AFhC>%EZyE%kW z`g7l2>-=`Cu$>vevI1kT>-y4EW|OOKQywMc3At7O4U?4Y3r}*`ws>PU?n6WXPRg57^q4;f>PwASUDJ@QWz=Hr|Sga`=; zCP28Xg!QF%LGN5LqyUIyEZ^7<_5y%?&}h7?XSK7l3w*fs)02n_hjtw*($II=?f3LN zM^`9$c|ZR90OK5N&VYwVEyP$_S}L~yfY=!$q!RsHLt`HVAORTQ?c7)GQDBBr(VF_{ zBO@az7_=62^JqC|(r!?9MA*H?U@4uDGWZMLK&apQ_ME&t8-Uws`7Pe>kH2%T`4-B9 zK}OU3A*6p>HV$C?(vA3@-rnA> zu3z9R-cm9$GP1U|u8;JD9Vy${+jo4uUpKRPeqtWJf135W*5mR_V}QIh$@c&u+9v}v zX_O(Ksi-I@VujvF*?E1eF|e_*(bvbo5)u<qZcIw<_OkTpcYUg5Ppn zh+odB(t`#^MC`Zmc0%F&mLmlw-3cHp18<3HwHwinG=&!ne{?3k=m7&!Tr_U4jg`k! znbDyMOZ!-Fa4{;qjla=AR}O%gUJqgapXbML-kZB;NaIy2@o54a@|z-U zb;paUp3+gp|5Ry=86T3@z5RpR-6$UNf!hN!yV!&**&>AGk)ij<2FRX-6|2ikU&y^! z+i6t*eUiCNwa73HkPFLIGCX(kpPaRw>etFnsiL0SNXEvYH{~@tny-Ffg`xR_hw4bF zB1WP=GqA?9&0i*gFh4rb)Vtt&t>@7g_@iVZf75CjB>(fqkMfI-PBb0m+#>@eOZd07 zp$Nj}>f0!tq_I}6=&Yuef8O(<9Dx#uDIYu`gcKg9+;EJ zhlkhCuaTkoQNF)bej*?=5(hOppfCQRdl9nNM8akND&7hM5KlygIyBF$KiwkY(udgY zZFidPGyEX+f6y#nug4^lCM;K&$Qyp$U2YkNkZ$>W=RUX6teidI^ ztr0C#Q<|3oL;L3KPZ(cPyFoS;aoxKpJ(MuMAD`>`R0Ortx~}@uWHK_eY`mJMFWc3S zKw;iN#NRF$k2mBb-5)O#KVWK{aEhQ%H9a5WJkCHb{VOEZ14+Neimdd^w^An(*tBb*po#PKCL+xNeU2V1FiF-OS(2HYO{) z5gcq5KOf=JDY`%Q^}dmuynJhGtANGOZ4W_*t)$L*T0S#XS=r_~QUsli+}(fSVjlmE zO!0*A@$mtoC=NE=2ogHT-5xNI@GVR*&w`12Sc*!6hDRDw;Xw3c`_1eLVFDfQet9$em=;K z2?;L%heh`8F{1PHO_4t|#gc7F!PQC7Ju_I{%}js@Mm)YwlMw^@Nvp|9UAf`@emg)6 z$+EhLO-?uX9UmWa=@jRJjtuUBmV5qe|DQArtFepA$?3^)a;W#!n^6x;zl3g~5XZwH zI|JSa@v7}B=$=?-mC#!up!qg6fBv6X9n}mMK%#+?0hSZ+O?}r-TIT;1Bz&h}wGikJ z^`TY1a^;G!uy9gnY;3H+r1P8lo86t{{F3HF-FtTn60U_;Vhv{`dK$~ZCHu_&g!Mh6 ztd{lt_0gjBw3N8g^~&QusqO7;rMQP_r|G5aN34dDfR`9-iNRoUjKu+}2;Bn2M~uk3 zwMv)cwUYU`rKP1##F=y3*Zca;o1R!Nz>ok!En_f6G4>uiyQ=x}@^XlBoR|v;7A1x5 zobqqq#M>!}2|&F}{O6!=(9#0ds{&L8P=E!fiKWKKP97eyZcV?It{1(MmFhu;-ppS{ z8{ed$1pzi?)wvRlaIG9%u9ywiKE-vOIfH^U-E+*>XMPgO4RBDu9-({9oB0kBTK*?; zhlleRA~BS52XKl^R&F$?0*e&tf zK_T1i2Ztz(i@KT`g%Qfg!eqW977&is6}#%@=H`D&+U{siXCfwHARL1^L%HsuMkotO zJk_s5=H<+BT3U_wSiq$1YC#Y(o>PHDl}ga-u=4ElI1NLf?UVP)t(>nx!E5`@i;EEngym7b0E z@F$IwV5JTD!oThml9b*0Vn3dfnfQW{N0~!aN0r804PXXvY|fdxPZVtF85#8Nf4PA| z+c!7}*pd8#g14(9g-Ga0sE?!?*XO_SB^d}%VVjqC3EXSISD^6IgC>Mmc08c@;^S-n zjpmz}&@03OR&tn~nR(9;U3;9Cq& z{*Ooo1fYL0IY8TqO$^Pky}=EWs1-|-}E z*ai&=xVD&BRSKV-cu$$r&$0IHXkO!HkX(AJiSCzmPqz|J0zI8)DJlB6p0M82%Lr=q zXU}X#EsKoHt~WDWFho+({+`&eWOzGU;94V4USBO9u*&3o#bAh+6FZg`+U00u8c?yI zol{Y4S5_ecw1B)WqQ{gaI@y^o2<1W;wr#Rd|t>f6OPugm8DVGSIwyzWm| z8|IWR?b^mC2JIJNzRg+S2LALn7t}+DJh;Q-u1g*gLxtNwF1Ssm1HH%|5Jw**$a%3A z%9?yixYyP;o32~oXor;#*4x2-c-c+l?=??npap*K`$pwM&E9wT23e#8LyMb{x%o6+ zJ4{g$D-&A+15HbjG{6#lY`eeZ+w}WzK1d8x3m&0cfSW0eAg33#)xgD+OGa zyR6VO1=kAJNr@B^LkSHR0{F3kqKuZE-C4Z+-t?tDa5YN_dN+GBJNpr;nOdGs1Ez&4 zgbGHg+Tze6lGSq46m?XJi!aBl`w-1B{L*nl9H0dnguFSY!@gq;e>fRhxL$0iOpiz( zS8{-j@NFOg6v#V?_wz{3v81k&ufYm3u4#Jx+eQ`vp-1;$dh|DM2~ zeHre3JA;o@b#_EPpn2a{bAYRLj-|+;6B$mg*l`h!n;6u0jg5$yw=DsU7pOHys4vUs zViB`KRH*ZCPt#BP#X~$wN^7p5j)dZ&{$58X8xE(x_w?FRN4v{_SS~FqL$yb#>FMbK zjbvg1u%4h?HTu)2XlZHb=~Wum5&WC}^If60GjsUw%tBVCPD#(qrp`I2aK+_ei(yEP zTX`0M(N@up5pyX$Le&q4f;@)loIfWTaysXiS69_~*Q?IZMWZgOg@fpwZfT^?Iq^W0 zqo0LMvFU$3w{YD0G?nggNF_G7Eh>PnJJPiOyQuva#9}*;fW}Mhlv~Ej-YG@%D`;EF z%gejEy8azH*iP5`juu%*DBIiF>Dmt)kKmN$PTHdWRhP)**gcpD1T_#4-$HNZO#F!8 zebe17%n}o1@W)`(mxw4I@bQrl6Ca$M0uWx8L1@wZ4 zw*p}d7MmupgvX`@9l^QXZlV3RHGBC%hi89T?YBhn&GO z=fkaBwMEpx3eXw=qyTdRB?ocT9au{VkilgF_mOENwD5gFKF!#r_~X|w=*3!WbaXUr z)&@b5)h%#@1Sb8LUf#Q4G_OyIIA*a|Zd0n>-)vzDM@y_W~e(eE(a;?!)H4-<)R4E=G=TByezg zJoRh3tGys{&4xeuKf{Bc?xbQ1_GK9v8U0~0aF5CjGL4MA2Mz0Pcv3e$lz7c4G*}FB z7~FbQIY(t#$dEJ|{5i`*Pwk2mNLbEGjGhVosJTwnIfTGg##M;N&z~A~Kjo{QvxQVG zCQ{tSkFEnSK3~_P?=}vS0u88s>k(4^ev^y08Fiek_Z7&ufy101VMri$kJ*n}4s%Lg z`C73bB{{b~!l=zIutI~qyj#0eRy~#W`g@Pj5cvmKgqTIp7xLL}`k8b`o!8@{d_|M4 zOc4&LrwcnArG{{gMfFYlhIO@s@JNw-nmY#Sym^cNfN0OIpoTNu%|I zbK?b(t`KA7&nrM$hs7HuE&7X!77E2Z7xyAVLp)k%TVBt3c4Ip#{C*PGlD;k=Cbn&q ztbEOeAO8~q{-1r0B6#Fu1CLTo*~XxO%SQrek$ zfs>e8cItZPb&sT zFUSy3387WR1pK}Cj1Wl%clHjj{GuJI=SAH_*@ro6Dn;+>kJaRdQ1az@GPig^mBn*r z06e-68!*%vAFZQf^K}HS!0>eHpU+e7;ZG3tR?P4tB9n>k=2d2SI8XTUvqSdpVW}Y| zWitu_GPL-JjhUiA){8}G?d@7p86sf^%^IbHzkX*Dc6cRz@x@aQ`XF{;hq?M$@%rk( z;R}~-b?->LEPJ{+3?#;4o0(6aJ^`lC+&p7*OW?r+9f#R4hL+Adm*D`n3j&dHdU_hL zxFGp~CY0CM^K;1FfX7L=DBWj(Hxm`V#YaZ0USnDSg1l~#)i|Ix9YK%ea=Zt8BUPh? z<`FfYLie5hC}_RFodl4HSGi^NM}NM|puPX%@ogA1y4axRBPhpi zZg^FblYM=CLqn-@g`hEBTU&!mczJ}WK<@*Fd=fV&pxp95_H$_C?+KD6 z9;>*Pa~?Hkrzazbb3?-Bxrd-`PZ$PGr2=h!02X*?QuRzQwBHq?u}=F|yGQ3vB*4vs zIO7FfWmht%PVqMm^xBvZt1&nA%F^dz7l ziyO2@@c|~{=37xPac6))Jz&6SrJ|yqrM257L0YlZ!Upu8loS+@J`jh^N`M9z5)uN+ zit9{(KtThj_6#0FAo##zvh>e5ipI9UBtG8~1++kS+R%C`k}-(a|Ags&X4JEzgzse= zl<9Q!Ooxpa05GT2i|m#Mz(EC0pPa$tA)~-c?lr(#;pIgT*aRAJz`8g&IVG>%(;e~y zJ_n^3!MuV3)rROO^sj0+dSH|Q_=i!-|Erjbqmj|f&39w*rv>jfUlfg2*v`uFZ3EB8 zcg5JHUB{Fsk+wh$%H5S`Eh__X25ho*uinjw{1%#?Yo2Y~jCL;qYNk!_i%oB?yt*^4 zsC-OUDw=(m+>)(Z_NpW1aMjwi3><2w$>Rwi&h2i?up)rg2QVf}?kR9W*_Vxuj*eTs z>+uYTKI9_Q%4<)GR>(eV7$RY1N+kEGanso1SqJV<;GWO) z2sGFvz#6jZIB08+D!Cr@Mu5ot9&IHzWkyauC;&V*Sp_emWd$T^47aoxaXe4x?UdI7 zP@7ug(=_RI#fndwt^GFM@Gu(1d+1Zrq>)z+-7wTmu4vAbt9oC0Y#7=0P&}9b&4(lj z?_pN}pDgf`UjM@_q?NwAjgKE){muXJ?I++$LeNCd#1ktGOU|jv8?g_+J~g@cNuHJ^ z7QxHwbU-rVXrjGoTcY!a=O@wYBGC*IxWSyvrzreU-EeP%{Qh>}Z$J4z#)JuK)D=aB z+=Se`r^2q^jomiLpKS#)Q$+j&GwfP3S#xHxS+HMcei^Vq9vp|7|7{X;4j@WDl*i}N za9mL7FHcN1tzV>jny1N(dn_YAt1rz%jd*7-` z(pX02?EsuR1yc|crOUzq0sBVARUbc5^kPCuiO{Gdq({-^q1u^oGGt`H@^z0-#G)fw zYQp`|yv?tauTS@=&n{ZDX_1bPd|9mJ%Bq@zt*6k+yWRjA>Lsc(1O2)3TVylw+S)w2 z2gzzW*6GQ#ZuiRjw@Gv9-!Nkug#>ipw0C+HVA+!U&Z^@gOLPh|G;%uZSwFnJ575)q zl8Z72!vPXvg?QmcKoW@wa`nJuErfI;V-ck;AJT@7zni0Uw|^9wxK#QHxURLo_nrQx zB(`Sn7W?&#n%L;r+iLM}`0+}&V#xm4!ZzM_or~1WIifCE; zIPNl$0#%pFj!$@d%if-w4=jFq6`j)|Yl^Db=U6F+=yX+Cpz~`Z8VyE~jtOL_eFUkWvNi#Bj zSQb-J6~Uzk*JnQ3>qyYsv|D7=v0k6@_J~BBWyU$z@=sAY2?&xTKW$uF%pc3U?xpVi z@~hApgWl4QF+BN$WScT<#MbSCu}yOPtgEuq@uLO#5(hLR-a9c|f+XjnN7nmlYUu;G z!_sHokl*?RP9hwRx3$G^{upSFP0Ozb9>5!uiD?G|jdHA4opf!3AlJ@OgGU0FUv|v>W1$oI!ZbUsaUw@!F7R+3d2sqREGQ z^060|0LKN1J$xH|?g@HIRTvZrIYbwaT34OySDm>mgnD^- z0jc@yA&_0mXP;o6RO)-s^Z0sO%}4?^XKxDVCF=@N4_NcU%5*rZCNZE(v=-`}2U=tY zkVM+BP{d0r((%Su3xZ#K7_`F}Jr))hO)V^Tfc_c?{E&GdAr7N3S*fXaSy@@x*pB;i z&`ilItE-k47PyXgl{34J8kx_@w6sp9%?g2gqHOvT8)(|Za2pbQK=aSQVzHT-E2YcX z$G_Z3lR|+5wE`Hd%JtSk?+%=%x~$2a^W?}UvBGNFt_#=U^`&;pje+#^mWhCMc3rnG z1EkA@q#tF;_!{U?x!$7m-%oi0CN?ZQ9KByQ=>k}Rq|hUVb2iym-P-Jk3%%*rohZ$v zTl&Cr{aLztSRNmKhv6T)sNYo4f5J6_Hq-S$p9KY@w3M@Kh39-Y+5i&+paE3(a8BJD ze`W>{;CpUr3scibKJ)M>WqtiB*E8U?0}cW}3EfGKLAaiRktcjw-!>^qQO4#XFOUs#*7p z%n4%Ze%gc<-Vfi~+XMR284%k+r7kT!@@0uDUP{k+!FBDp=%%umUTA}1C%7>{Y17j$ z;Pt?o+S<|rEE9nJuyzH)4e%|~3Rs0IQ(nLR*3Qla_%W^_&%MvUI;myqO-g?Y`yS}! zt+PwZ%WUlI58urBkFJWjo|c1Nud@>Y+#Fi>{sKv)^kO)$eZYyJ+ycC#0AKs4#Vgp9 z==_6zDF`5Ne-zP5am4UeO$@URtR35P?PXJ5w9HCznR}v{GUsf0LVxr<39!sv@e&-H z)Wmgh;#>Z7(n67}pC*7Gc1@Aw3muDkVDg5eRN zK;TK{a>(<}ICX938`OEz&S%v0S*;ndAZ+wgeHID_crBbuAD9vgr^?F9AFAmBBPtp4 zqI(Tsyx2AKld7s*4Gav1^Nr(^`?<2!kC*k&gn=aoiyi3iPh)*E*EXDM=pMS29HFN? zb8dSi?GGK)b5aNpP8Y&=6Mqh`(|bH>Qg9g7($WGR3~UUCzsdW@{~1Mod5bTTax7nN z23@rHD|i?!0gVJetQf``>;(pWl;;07PufdISvhAnqVjk0R0`1Z<^X);&+nklr$&p4 ziUOHuB!Ct;nEpE&%s-|9&N#3&FD;qb)jWaPfVfIiNdcBvR+aB`V$RRyhKGlR0A&=| zq@t!~xAE&yVeKEbhBGtBkSoFR!3a(ckUGFqrKvd#z&GeISpWTGoX-jL=#ex+pYt8u z3=Ir^0WatzIB;NN%Yc#M+^ORy%JBQkm*V$MEE=S**p+w9m4SVg9 zBv_hvDX?rme?1p@S~a>_3D7X&Xn_%451Qy*Pa*r2p_SnufY3YF+~@>hSdJg^ zH5+C~VG)t#r6te@J_C+3(2ATcD!KN6Ll|*&{0e+0qO!oD?+nPw1K2hoAk(j?ZSD(v* z^c35>m;7LB$E<#LfqtSN;hSN}1eD7%JmeXJR9Q>)U(sNw1j!8{qT)=Yl8Vy{kU871 z^1rm(dAcP3;_-8tq~kn;ZNP5bX8rfRsSl9@tn9V5wIb`~KY$?~n4oJn+6n)oudC{4 zX^EOhsvM6Y^G-Z4EfoZa9W5jR-xdrCj^r!QjhQ)WYe$FLf^!QxH&vA#&$O&87w{SY z;A?@F45U%M1#BBrGJX~oIeR+_=%`w;6dZcxL#y^VIwi^#F8>^B870F6giQUK<*Sp# z(kSAGlraucpA%O|JEFpAq_Pbab++AmpJy1`wv$4FGXL0_(ty?id6743RHMGs{OW6A z>EX{iUZQxsfNF$%fc{^+x%6MYO!Pl`7wZ4t@qheI8F8~c-jnuOTjMh5YdWo)L3o_t pm9ka{L|p3r%H{v#ch$}ajl9FQ7q4HS1@GiRWF-|JXCn{a^PTfM@!c`LJO1~MaUG6xmV2+5bFMj``M%HdygTrjyd>@o@*5BY;XaiTSA?J| zLJ)+ugn0#g!nV|+20=8?Q}M^juAkP&J++lpW<<6e9&6Z%9?_TA;vZT zx2drHDo(wlVuuMc-**-+F8dm9G^@;N9-UjfrfD|U`Hk*DqS|EA8L6lGsa)LLlb(m; zV`F0nxP;CAflv{!^FbD z=@SVvh-K3%xfdmE@X8>;m%!9?vAtbZL`0-adt-IA!evwU{G(q~tj)~N5DJUpa+}Gj ziHQjowcNhVSlgHkZgOrDSvNPgYOiw-509HSo+v63P3G2Or}#Y6(#lfJGUketWl+lM zFD@<~J7^3db$4?UJ{WoddPoT~YIsXnho`Kj7D~bQYjjjALz#qx(hu5n@M@5@iGTSq{~WQ20A)=WKR$&$MtL1V)?@e!o$M4`ugZtIkF>II6g>8%g8XL z&d<*iGs^en4J)UleSDVHzq{CJV_{)oZLM2m_QC7Wx1#-1_ttd%;rgf~Q%-jF+C*hV zZ43DiPBLHf-n7Q1CS*~OS@Fox(b2bW_j20@auIF{!*w{FKus&Mpwx1j+O&9+2r-XH0N=k}q7LOZE zJ*UgY*as=Px}7@yvYqG8pYz#Fd=h+R|Lj@UTf)1g!#HZ+GbB15JlD`T?h?Z%gjo7+UCdyD~@0R}iTSEpiOWks6F%FN8x!GW8<8Pea)mme}= zZL{Y%;<+DfOhG}xl*-zss;c_&kt*#6GBz-@5m8Z5pFXWEEJVk~J~VE<>0?c1)u&8+ z!b4F+J7&BPOiN39usO*Q(W2|Quj2FlrL{G$!^T`SH)o=Jdhcb5kI&W1g}D5y{0YBm zrw^-hZ{51JKyd}4@E^1IJ~cJ9x913#l90GwP0zyJ};X~~T=k>ki-nxi>YTx|)d|V3NB}e`Qd8F6*$1=4V$Hyd@KbI z?#oaT5)=fBh1Y82D?+zU$AnoyR(9{;!0T+gNiAD_dULbdsD(&+`RsUCNJwbXbr#ot z^Hl4)&od<@;@h{GqNGz5&6Smvt*qG8cB0gDxA*q;4i6V6+POzw@9gXpyl4U^q$+AC zD;rsaq17x*ufOJP`}GSC4{rnXPEJnl^5T4Tbv5U^{B~W1Dx`mX0Bdw@sNluZr%%C` zHkJF)(y4_b{7+U+_E)1fu0ZNMXZ0l=xw@77RwG5p$;klIOpJ^GT&!j*eFc*Wy#ERJ zO(7YVYf$#h-{E-G_kRr2BR+VPI+(Zg3hT6Md$ckg20 zkaeX=YdSdBEyy~FHs6rMhgdNu1*}6U1)PmcO{pK*H#9aHw}jJ!*)Z18CRb2Lu@SdI zgo4RvM55G7X7i3=thP@g2w@tUsmJ9vpaZPkoP(iCcNaMPO?-TOO3JK1E(Hmzdd+5Q zd;8w*ZbaLBRaKSy;Rd~1XawI$q?^A%iREyzGP9pT+F%oBI~W)62DG*Bz{J(nQxy|L z5Qzjk4a5rV@qD2KI~x_7E1k+<@qSUwIz3olU0<)XpO;RFvelvdZ;%qhhxEj>$>zS> zN8hmj2|AMJP(y18ej8#O)`+8eYJg9R)6?B4lA&0{4BBP(ev|Ycq)G$qB4c6{K0W*} z+8pvdBfXE2j_v^u4_1Sbiu~>e zYr(-dRo9C(g%B5KX=!N-3ky$zNC@G(bWJSm0}AF1CTtvWZpoN9wz~?+XiG z6c6gIPE?-8+f=>UnQP&wJ_{dBRn4-Q`0)t*`}60|gm)!um58lV+xnDNHpj~D0{A2f ztc_US*D@kY%gAVHX$j1dR)5IBvDv{lsSTE>goK2F!E}0(7r#k+?Ah5_mFKB_BOTFG zW8---d?9hnGP_y-fB?zPL?J&m`~6kNa&S&~c=*iB%-o#O1_xHPuIW~tK4oX0Iq&}j zLhs_|%Qti4q$VL}MJehzVMja|T5p`{4#K2U$wSgyHYdPNpjlwpsHW%HxY^p;DlISX z?BF0HEBlGd_}#_x>w|a~O#4KEzGUuPm1qFeFN*WGmbz0obblNjFD8j>HxSp>)?(mO zIl8zkiUo~Tlr~lJ_8N+dH(Z>b8gmV22D5R;IoYHCVfP&@%m8O+rY@H{EX7h!E%;WOHM@K>yLA^~U)W*vj?Cw?c3U!NXH ztYSa{0b>^UAriaw_#&?E?rlbeZ{EBCQ_*X%(6~(^lq%=hy}J7P!?odht2?}wLt!v6 z=k*b%`PN%yD4b`t*_O)@LNr#n5o3#A>wNrN(R;w3!pPFFBburR!iVG2)25~-Vz}Xp7b?vCWe$rt{PhWx zmy$~98qc4@qNEQuCp(e!Yc*gq#iJHxjcAEx*J-6TdnQukvrx@CPUwy2eTb~#Dxd0I zcSQt&N%3+pcV0J72Fu7s%97l1tDmB#U)>2!e0Djl~w3mAG9eC4b}YeR$kgINgz52 zTJH2M6}xhk)W{@pXV+bqJ@jDT(|U6v6nDf}qonFWO`&r_Vw8;~3*770yQNz!&bv_p z0->Y>EqqN*FWRDIQ@IxWNQn+MMQV#@)+wF#Ev(XmDZOurT0bV8PHvO#Y3o$b)xF?` z&CSOkt;dcGU%fKX9NBrb%S#C7;vOV`-?*VzVkPjydi>M7ciG=fU0C(3Hy-hv!4T)G z@sgnzv*}4(3P}@gM_ZdNG8uW7Q3h}_hKBC#mYN!L3cjD@R($N%r)$H+t!>=Kg~BIM zATEiYy-`%O#IEfsAn4Iv@B-a8tgep8Z^j^_FXe?KPL12tu5XtUj&qp@8L93`h4BKWtdgiY%G4}bg+ z{P81S3bO>K44pFgeh6ZXf>3bdD|X-DWveHd=Twab8k!FYn_DzNczEI7RBgn>#B7Tr-MdJ{T)QP$&yqs$asyOgOX-#+JG{m~M7Y zPqQ4XX_ypIc%GC}Sh+0zqIW;=V2zQakUb3^XyGw^wj0UxILTA>t7<@zgEu5z$2?&U zb>HXKD&L&^#;!$obI{Z2`}fXk7^jCD<5_8Gxroc#`}<8JnpfKj6B9o)Hg4KdYs^ho zmFvIQx*&<&qnS2hN;|P!!TzC|HBNc3^KMS}?Y-}4BOq74d|}(Y8Em`qttC3cqbEGjC!Pvj%O`4PJ4#}I zMWY%Y&q@AVRbcuhUq;_-8%M;br>m^2I{1rmfKg)sLw;V3#g8A5{Q^N@TKZ_tTz*%) ze|e9+k@sb~2{}Go?OXen;%*8CfoT2I^>L3hHm%~js~f3dL}kFMI`WT zzwl+F1nueazS5=0MQs9LY@awgvpJS*LmbZ|tuk*z5P!6peb69s^K!IG$htG}g>P79 z=Etrs`WsJ*q;F|ypIwE~@oS1<-sUzjaC5V+_UiTY=)3X6QL*QAtl&)fDdJZHB@tOE z4fk$j(&VoFtD?M32lqp-c2d{9=NeC+qQmA+H(4-n!t5yI`m4P>{gwII*sUwNxr~kb znN_mkgq4Pk*17m~N>eMth4e}p!OM|^*B}D!?RgSd!~ZrCCR(KNUW8R&?02L{UT*g) z#HB0P2zfjV<=`}y?&?li#DqP1q$eLo)L`{MHi}ClX>04gf>5foeAQw{{=x!DO23J54jWsJpv#7$;mm6g@P7IfGUm+3 z7Z{<0?CFt~l*9nX$0z&s66*e!53w#OS(VahKJ1xwc0te(|8@@XKwn>Dqg4{ODJ_id z;gAT={$03~^Fdsh1K%>KfZfa+31VUr-prz+cV~wJ3cp20;}1Q{)z#VB3VkrDr&dO| z85me$2D-Wvd?)UVa^#exNFi3s$m0`S%r*cI>FJ9^a4s(4+1A)cfEFVllC$nd!5N;{ z{|=mS2zxQY=y;tArEj@!j8)?@Q}bqDhQYqau(@0BEydh?x3sl&%l&}0_qX?+ouMdBfD8NZ_r(`wf*+GK<9=&F-T737GU(xy{v}`+`uqC!H$_saF4S4@*TPO~ zuR=qR-H&SHEWQ28%~!*^_E#Z68oRmZ4(+P{29wyv??TWALP_gkoZn%GZ5%Lwgv1?- zT!0V6{4JqG|BVhb?BBE*UTMNagFcUE^C9=D)ziAoScixqD4$B5@!FqMAqqw;$?Elo z(4Y}4(r0Z`i7(#NVnAjrbJ6rL5M_?{f^vMv3TdS{An#MS zwFV_C;3r~V%Y5ajYXCF(=3MkX+zk!FgdwvjqjAxoz3GqluEe^cL0e&3$*=dWKs3#4 z-L-`4n$-7-2<0@~M>Z;1(&U<~q51O$SU$>ySUM8W+!Z1Wv4KC?WNW=hp| zE0$Q5xdJ)fZ%3}ltTO7xCZl;f!L5hyg1f&ZwQut(zUcJ_eafe5W~&%T6Rk^ML8~F3 zlD>xOI$%|6H&M?>1_*wMYKOXrwp$GWs;wVk$eM>>3{ZD_{nSc_*V5Af@^%)puJv@B zir00O!GWmmY$#wJcBi>h*wfp&i$P0*M~56mssXGNhOOuj{>@$2TxF4q`VSCw8Revzs9HDL3ShM^Jmcv5}6`%-N|deUo9~)F#wMe zkeJry=EqAZp+A275WYOyb>1tH12pl`ps}&>#>NIcJ$+h4ta?4eKnA9o>%&Yb;R@GQ}P!T7IM+iiL=Lu0B%bU zan6$p!XqERXzgroznc3X2?j@uDJLT%@ZGy`Krm%IU}nxtOY_e%I6K;w45dnqEMy(6 zMqJudpR1}?~YRb+EDC%^bv=qyNwCy_SPsmkfSEk=cz znat-F8qsXb%srd_7ZZTK7Tj&;W^KEe=RgdX+TBgvJKe0#%gW+jIs+65(sGzq@U9P= zZ&+z*X#lmCdWN#s={m9w#&xN56-wEn=waqh*w`P`OX9Wqcp(9og2UlTO1*%n!@hCj zUMeYvE)FKDh|b#r(*#gdDjCXv z;a=n;_mz{836f4d8J)aHQ)ULV7E@|dC^fa9>()$T5aMVWqiV}PUiQJ|6MDUHVGBKugu*+9(tSl`_ zF@?N)N6u%ht*F=o;suUqW2RK1z&S9&W3K_3J(t_;8%Dur?ds+R@`oQ_DU2d*_%ta} zi`4RTkv5YHU@}bh^)*~}E1#bPJgem?*gt=uM*(Z-dwr7dz=yz)^9P{p@NfB$gy0cg z&YGE-P5JNjtLe@A@5ShOX@LE{y87}X&&$lLtbHv)`S7ZXlb0A60pCqKceFG$H46(1 z-}w04rKK%RZ96$}b8&ImHEnRp`10koq8sQfm5>|ee>&>9Hd#G6ISEt_z0#oSsy#ru z6cl_=Y%OF(Xq;V~Zi0~fbnSbm{pLC#9F#6^^nW*1sPYE{b0Xj?x;{T1%+upj0gMlz zpR0AIQ^%Cg0e6~apCxzxKne7y{k6m>52w?Lve$6_moL+!Hq{+1Ez^q+uFFwS(w;Na z)zwuU%>)4wN3YT?O0nGY^Z*c5UOO#}+Ptg83(*-N;QpattRLFaq$+3i^FMqTD{?n8 zMr(f`utnkz#TPJsdtYxKg@lA~#buY23@qthoN#%a9ew>uC%rsfA5hlM3p%&qc5;3= z>2Ue>eQ>Y`0Fq+O9NQ;L<1UkcJGFFmEzKCYmkJ&}FM_H602w^6IW_f6M<*dZK1)wM zy>~mkE6wX;2AH7>q84kKmid_n;B7oj?O9v1WUst=!!ID9sU?E3tn$BlrKqU*GnBfs zT1bM4jh#JF$lX~~v<6Jmvi8{`2IRYU?{rRZrkaMlvq); z6$SEHAcYiD9+rakPLY`ffUqDI`|v51sqpm=0IH{TY+lxl8nZja)9 z@b5h2WMK(X)clPbZ>hf8X$(`V2TT6br%!m4{6?9)(#vx#5uKkNrA+2NrX$M%oaQlr zQ=1>#*Y#X~r|f4GzWn*x`i6?t$;kZt{1imaivU%cr(1~$11tNxNym)y?FUwlZ};MX zl1A$b81wqifq>en-2O@N<;xe7a)bQ)JUl#tf@eT*IB_WEs5*Lc>Ea)ki3!X5vGWde zy1E)+Zk}QLwjG}wBJN{&v&Ko8|IKmHEAakMeu{DXpmz<>SRi#4r) zsH3S7)m;akv_4slIIY6LfbaqRO%kxQw8U-t3vhihE?-yAF7R21^%QxU^lqnvDPR?9u9lJD=l z0y9t`h5PvU7^q0Z46;kXt!PtKVOlhJrAQcL3l11SKp|@V`e^FIJ?H?Z`jSQ(?5jg; zb0lxcda!HoU`WwDu;uEjZDHe=Kp+x2eLZ$%6vRmR7|m??JRtJOw>rYbpzsYO^~M#m ze%%3rq#?RFk|9*QF+_GI?XzCrfL#+!gRIe|se~-A+GDR_gP5cNuex5}g>(A~0S`Et z=_{$X)Fbust5Eb~rqoFLWpw;p%DcnEJ|Yea?Rb{={!3pK%2ev?w!SBW9<$!~Or%zwDhI7l%g z`ybk=qK=M`_1Hrj8yldrsH-=}kXB7uH2+;Zm_Ohb5D)>o61C?ZAC6LDfD>%%2tp`;w7izS2j}8DaH@ z7vQgI>oaM9tGqT7vP=NSP@*vfe?6%-E>YmuqM}GeOO@YlH#Kgp6Oy?_E zd8HrytgyWJBmb^Ag#TM*H3$g6r>?S@6qYszQ40WGKOs_74^1=ky>2V{(0CO5MO!$o zH*JsIo(bYWiQ+);2_j*e34mYPrld4o-Ygxg$;HO5&2{TLT;~EC_t%`2Ha}2>bwBr&_M|@^}RwYz_$UR8&-B&z7jI z9ruost{j#FVfeanuygxQ$M239(A32Pa@<41j*mR+xxN)c1+1e(LrZIGvP|b7dcuPJ zuD~L3n3FEJ_06hZ(FAvZB+?=gDwcFAcu!AH$a#--e7jABA z;~t5A4tjX@(t!SYTu$$@dNj|Ht+=?aECV(^K4K*!Jv<`9PY#oXkk@mv?nD;%3Ly@8 zQ*G_z%gXyXs%?xn5bW0j;>m?J()BZg|L_Z&pR2g|)pW?DH0 z&z`EQ4*?kurS7I{0W$(B{-y-8(pMq{3mcn-jg8)|w!V<{5Uw5=hgX!A-~E`A-wf;` zkv-rUAky5W0O3;gADTwnWlF=;)_n8M}lU!xw|tmFj$Y4gb)-0 zOs)^W2SP=vOibF>ueObUN(R0>tXd_OAgTb17ihgeZaS$*lTXl8S3f%2?Toj`;=y4a zE`B)}#iBNc+Q;&;vw?6y=&YX7eeK${Z_1f~$3!45kB(g7&G{ft!MSaiNyJTQCTEc1 zqwm-S-r!foWe`5>OHQpLCv$Uifo6Ndudctpe`z}BwvfSXZV}4(*Fc7d9&SI?`b~fk zY37HivyFLW%W+Qr)mBqeWn|>RYu3fehkUBIT-4BTo}>3EFj!kizXk`6S?^26R>KL~ z*oYV#XDlm|+c|hIEpytStdeE{Rro!rqss(pV)$jy3pp&i8^Eu~C|T&Po0RTr9gMHN#$%~9kjRe@}R_vA|%PYqapQf6Zlas^f%PHG@vVR-2wzPEJH*Coa z7iQER>j$hWi`##t$foB4jfYQ8^n`@m9T0QaOECb)v2b;gpNVHF7c@4C5CCyN;rLjH zBS!M{L>TF^*_6PK2PONDT@l#XMCjeVZ%A|51UQc7<|%aSi{HlIq zeg_MdrP>kRmQq&v+_D2c7(2+?E(G;L?knj3$P+wq_C&9RM5?X0@F2$Gy!@ znbljcI%q2U@uusf+Z&(r-d+bR*zMatMN+ye-T8z^f5!^DlRY7YZA>5z$eq6hUJ_4} zjWIdjuv#BITc`Eg#Kh5IoIok2P?Dpl7IJ1OvVRL@Q!jvSCVKLgR$UAN{)>ml39!L2 zdKbIFw-w}KIqfgb*zC(b5)j>d@PMBFqH2F_tV}#d7bI&K0DAQ^Ws`(A#)5{1T9{S9 zH-}@h?DWRVd&{dOmMwcbJ3~mDTQH;H!bOQtuRwyHlz`qup|%l6-JCd^Vuo@~o2$=j zI-b--JN0-b7XDiZ;QxW;spFiKt}5TQww`Q2?}A;wUgZkp!^))}NDTgrh1_b@60R^# zr>kv%r>R`3AuDw&b{;9}fAWN0EI>H?LxEz(-ukzXPte-J^);H=ym=#}g{sban(MJ% z2Uz+5?&J#8w)D;NrLl@WH!#-eS8hd900IUQ-7B}(2seNO#qf;!IG$;85QvKt?l#6O z*gm&G7-v`72HEm6kM7x=O){JwP)b@mVS=cP>s%&EBBelILG)&@$*5;NtB}y)CgQR> znk2{P>UY=eqEwlb%&eED9Ve3J=BX7GZD24Y-tWy)F%bYNoY8bvhJ${7QIFR58al3= zyza{M^iNJ$JRgTE@WO!vU@y*8LN@8*W?P~CE62x=-zl$j-!^5oDD5PML@BoTE=*&? zieFCB`&y1VyN;JXx^+v#v~xqfN-&C`&~|ztkXTYhwW``~HYkX+`pF&@lm5!_RW`PA zY|;rld}@a^bu`%5{Cx2*u4G?~Z!Q3rp%slbBP+9XM`v}ByNkTtHa;7hCQ8R!yxhE0 zC4e)?-{Eo2R;db|;2!VW#h?qP@+D9kOsx0c@9eDJ ztHm&_J~GrA+|zReK~0xasXJ1uoV2S}{!{7a?gn4K5N5U&^8E?kZTZdQwga;V_tDTeGL;Kny&^q7KW%K< z94lUpst>3RYe{3%DfdCt)#MwT2jIk2z8nNbKkbONe*3cW!kzN1>qOpcTXYxPj#M*fm_7^H;va#^%Sya>^XmjZ8b-hDdxm&7dNX z4%CDb=s;F}V^{Jy#Ysiur_R}+wt9CRRXl}T`OLr=n&rJzsOg&NU0ll#^DeG=0~3Au zyLB|59Zg>+U(@XBme?AwMWCsZW|Lfr4FEUdf6M;7-YDxk7@lmxWNbkAl6XhK9nr{8 zPtkqL-d#&;Kng+tbslk2Z?r#br!^x6t@I@7bZg|`sf41AXn+ldX1tfKOo8W#U+p&T z$d?)dEGDqAE?ogvzr{($Ub|zDE$Ts_GjKh~R|l&MoksdCd%o5mc2;k7gH~gDa8)C? ziq?PWih%Ff6#`_5#30C8CZAHwL!Cdyc(0q0HRe& zN}-nn*}xz9o`Ns8q@?8gDMKGR_Dh`-=YS`5AonBjW-~j_ehG|oA3l8GPXJ*#Yxo3aniNCS{N1cLq*Tkd!kqG1cjS5E$H|H(f#R zhiieC3N{Jr*1-Wg2gl`ddJ=fBp4&3Lb)K!ew)PmfSzB8haG*Y(2Y31O(9RIpfItvk zcH$RMz1JgC>3*0Cm_!zq3eQtsly^XHqSWrit5>fyHAjBD+T9S~q9KW$zA_Z4%C1$w z8Xp%|X43HyP=Mzze)l5nt#%{2`}*eQ=IAWn7{vPfUsqEbEHCHJP(A_1svVWG(o$s= zm9mNoVz~8qIeknU1vxpHKkzVJA9dm%2W}@|JF*9MD&0!AWxi=}ygtICrluxsE+*y! z7`%L7)lvJq8?+l_^1xJ@dtFjeGDr3Fvk!~+6L?UK2X;+W!_S|EtgBN~1h;Q%sH&Re z$6&MRf)M|LI^ol&cD0SEshYxUFO};tP6_f&l?2v{_&0x%JMkv~{2|SH_wGhYz$$fP zr3p~Xext!Gjg3=}6Mf!-xop|$sfG<4Nv?ZK&*9A=c~)VL$}s%X;N!?L3aH1FNZ=F$ z#H1?1{ojGOtQ9^Up1gtrBM|J3$dsvwhzfy_4{y#;N(Th_i@hyi%~Z)!<>BLdS*#f% zA%=+sPM4Dd`o5`YXMzASit!4qC~z{y9`_3a{suUI1ciivHwT2>vXnfC3lCrh;G1yu znY`!m1u+F|lhj^E1{)5yPmF={7dQw3Tdm9tsLRZpocV(rNWH&WIeE>JfBf(OYz{aF z_>XVhx}9?X41U0fxWBcvwY@!DWKIm5`zHZ4$`iIQbz;t>{s}!_PUm<)CZH%PA%XJF zoiSi)0?ZZ&%?AeuU8E-Y@h8R#Z)Ou;8Dd~A(K5dy;LOUzl#!9KoP(U;`tFV84IoBC zheQY1O)+l=1d zfRkJmy9^{@1Ud_Lfqu86cv#F35 zeC{4#+Y|5vz6#J5@{qXto4wzcrktt+0avck4MpF7})`3ZMoc>TdYKQx$KKk`lvvdV5dzR~KhzQ=Z)e z9zaZ3cXzjMSWS(nfq^GZeYRe;E_2k+#zsQ8uOFCf$GeMIFa^Qw27nBhFu>&G))uu4 z4{NBan3$v`B_(OIAMWo1gKeHyjBn*FD)u{Q29v&WW7OzS*Ncln{ZUd}B~#OjY~UQ# z*VhNE-jyp?s*V>Zyxf7CZ=nOw^^YDsdj0yfhYFx-0HXjVAltorpCiG#yH7*ot;p|j zv;~;j=hDFF2Ig4qWKn+p<3syDHHD)8wU=}H5xFw3h5pqZ&taa4JPE*-6dmGAk&87w zB}M>wbQk-v#G$6g`ciGN&4)0>a{byj=-4EJCrc^78oXJ3c`>Mm=vcS6vnz96|Cp7< zl3MiTOKnF-NEYyt2>@D}_QL~SUVC~c5(o~W@iV~L7JP3^i2}pB?Bd; z2=FJP4uGr;=>E=p8#fo%4Qy-?pi13;@pEo*5jgV)ryk&DDrWWn-5`&}hip@}*~ce+ z@11WW5ntM=Aiaj@hdy%`@szLeBKOV#BLwW0KqXta0IY|$mKMtgse7q0>^h3fQ76TZ z1Oz63yy_huj#E4V^9j@;$j5O#VTb@MHa_`-=D|TV5hMtx4!V@5qoYam^d^Uf)cxSk zm6iMQ^s0dk(R#k?Gm#Hqb5m1O7k?$RwX~R9ShNCG-z9E2$4dc6qGl-&Grlx*K(-aC z!HcUFCR;0h9Tb?M#EGx~ycXb$0A-OHxiO050Brswk3pU9Z(Kdl6pk8&s!FzJn;hKn zsfF8fLid-tXMy7JB@$R~iQwwd^R26n{00UoLUX!gY=S^l$WV5hZ;b+)5E#ZB?Xn&- z`s*ryv{g}4Q<4Vn$=!OS!KLLdW5ubkhZ?Ef*fOKhr`E*_KPG_wB}w(Fp|_Wtj_zxA zcJL%fMJZ$OZ`fZOKi z{>iA7Kso1dp*#Q-v#aN;r5$u6J5RL%s@Wn=l-SQhqF3;TxB<5W?5srZP}T?jpweXQhAZH_1k7%9N>pnS+D=kHFh2u|qrBx!&%4oS|16Ud zRm&e?A|=6kf&tFcX|PDaZa_IFbTGQn@lluRv}PuQOASF->78q$(v6;s$^o%O9UoVK zj?{3mQt7DE-DCpYR}XFk7h{5Jey_rkDeQ|xJrr~j!mhV~6`|^`Sn<2jb5M|;)fLt4 z_XPeNj0_05v#~W%9aFv5jedwaf3;^A^=$$8_V+rjnic(Gf_4xRd-JS(U8{X>hJ%BBxZ`UAaQkdc3<=N~D-iTU*_0+apYg$6X+eMuJgv~$74XeduYn|4Q^QoCyf!do zd~HLxW!#epxY0Ot5$EeAs?82ANk$8yR_m}TxiP%oUboAp*FbAH$e%ZBMuOc=*VR^6 z-uL%YIygkwau5+2Qh$Z#vw}{L8ODb3O$^+W9wM=uE$B_9+tYHjfu6DFZGjd3O{!dwBS8gy4sa4a)(B1!l> zqcLc5257@5-|XqXt-88yOu)OKtD>?Q6Gd6-S@9vyV7e}cnDKpPwg+PNCuy1VKTN!d z(wHsn+G3bY_oN6_U-khL*u4g zdj)A-e@kKdZU1gWts3oG{+4_#pkY^5-rMEbOj_T0Xsi0<$pFYR)@OH{o&+V{8W6)I zJdmy?x(H2^&&gOWjZkvAxD$e>49I8SoUN<5BACDNz@x|KT((J!5NAv6YXgmF_YNH~ zjc6;&v%cYDd|)3zR6WJ)MqGP$@iO7PlkJh`ll0^n($pTle{dIFHd5+2&(C&MWrbXy zRM#(*l^@*U%|-xoAHMLz(?-%!+5X;-x`c4qgpZ&dmA{Bn^5haKyv$w;gXQK;Xl|bL zOj=1vgj_&1qgv?io;gBzU7bXZW^sC1*^{h(^9RcwEn#5>z${Gw3bRh!eti;$HwO}v zKKK}$pZ`2HRYiQ$T#>cyM%H>5-sS0}u?Zd)KJ|BldUsu2D)3ig)!Z`=v_e?v>i*QC zwGDs>{RYIvZbXdQocku_WWkHTivz$jW8f$50d8AXFyCsF7A|GK)3dUonc=7jyqlGk zr#tj6>u&SFTWwTm4(zj1PXif!|IMP%`QlIvAezI5>nsGm)+-1ah7e|uqex~Y-fwm@QHCH2?DTtMBs0`dgf zfc0$FmsHdP=7dsFfw5rZPB9I@gfX9<&d$VhWbLS?YAz+Eyv(W5au22C`)v1}mL}@2 zR7-9sKU}>NJRh#kIEUg-{!uk69D@e=s!uL1L9&2~sS!GK=`wpjvW8XTX^=qhpZeQP z8(=vzB-5U;BtUUu|5qz-_4Ua73mi$OUIQ@yzgFox)^s?D*qoW^NfR^q^@(Y0$T;+Z ztDj&p3vW3vKYT##rK5-DD0^zrIL1{-o!kGueIR#vcQ8n8K}FFC2xhGKTg_Oi&kwXh zsf0B3o>s(Jyxmj@*b{es|6gUelLa||XMwyQwMDZ5Iy=`^;D42UfPx$>7zp@J8ZlwN ze*LPe1MaVONor4V%rB96WSmnKn^nZo(^xJb+|1~=fJ;4oT>I^LN|7ep;In@u9FOxp zB_xzp<@_gM0hLC7RQ~$awr7--r~(}hXv?x`s#z~zzARe@9MQqvp2u22Q`PB)0K6F# zp!y1B=jKxJS${$zk%}3ttgH`~p1MDSAPVC>qQSJ)31HTE_~Iu{w6UKgA)JVa2xPe^ zMeEr!QKm}QZ9`E}@5d}2_cuTR)8ABJHVKwFOqT*y0pLvm0V{|v5nz>ir2JhvHpnZz zTwGXCRZ#)5b%-LrdC%ANQKXVIsF*dkt@^joYHV5yX>F{*3#y17Jm~AglK5M>+0gr> zH`C3?Q(y{bIrJ`b+F$AS_VxzEObTfFpOz*OVp~VY2WEa8KY?+t3e<(Ar<=;iv;iOA z&qn|58;>MhqGDnyDk?6|HxYxIS|AYs%ofNKFZS~g1*|MHymYN<7xl(J4zICnvv`3R1n5?v#KG;z%hvZBSxcR%_X-@E8+SBKgyEw}TH% z9AH&iBWR8WN01}~}KPwA6D{fiLC+SoGF=(?jEKglt%to5u;bYyg@B6oj4uwRx8wRIRP8tdJ8E zM}V^0m>AP4HIMn_)YF$tOn-K93IDxAwv|E2`X|Mz- zT;?n4kQ7kUU+7URY(A-nBdkaqm7zF`Uq%RL3VW_RoMEl%( zALqp~yCbExvVaz!j0*&U{M00^a%`Q~25{DK^w9t(_a~exR?7 zx_Gq5e!;-|1k_Ws)oH!OMMT^9i$Ik3a#Glgv{nl&nCVR}9&DTsEk*w`LjSEM1Pe#IM&)!Vr7%4F2E6%jQVoxcW|lWCeeqzG zZ@ft|=b1PoePySWQEb@$D)-kOb341ykZ7jAl*T#i@*1pa3u%=YF-P6k;jtG*C5q(} z$Gek!ycfU{)FpCx9O!u}PXPa?raUc}qNVHgYV~IbM|6y7S}I!-pA9jLj@tnj_VqK3 zUzT%g{$eLN6g=E!scR$V&dV*;XOOR{gkCl-naIh$n$M3|vyqvE02vmfC zK=|Id_CG>rz0%*K-VIxW+kr-P9Mwh@r4J{o#)q;7#jS(72R6rAqNLoG63m-NieHuu zmg-3`)_NvFpJmL8%dcX{>sEWc1f_=7GS=1xsskfEJ*^Il?DPy&W)>E|T88h9)fHO4 zm`(rn3-LJ;bV1Ib!`hmNG2J>fH3|g$;Mn?wc!P9Ywu0+XY@b&Ywv>FH!T_n58|*_=e!|D)i|pMZAk?*((0 zS{c2UMonLzrp(Ti1o(x_Ac3m!pZYmj!8aZ0{^WfgMW`igW>2K6vj zqa=0#_`k=s9`h*q@5HlM+tT8SZzQDH|k6b1?#}e z!nnvO9}~sMB>VzG-D*sNCum$XNb!Kqetz2dEl*^i_3HM8DrwCrs)-FpPE)4`8rm7G z&ZrDoF}busuToRMYGfrjIsA(lbxSkgr%p7>CAUPt_mG%kMpKL)0}w>@@1O$OzyDwi zN*P?bVR%Lc10gU8TxKYmOGB_IO?`~Mr?wp?P!ROSr2TU76Y QvK8p*6M6Amxc-~}4=4CJsQ>@~ literal 0 HcmV?d00001 diff --git a/doc/devel/uml/index.html b/doc/devel/uml/index.html index 406fc303d..bbecbcbc0 100644 --- a/doc/devel/uml/index.html +++ b/doc/devel/uml/index.html @@ -112,7 +112,7 @@ Documentation
Artifact Lumiera

Depends on common

Depends on gui

Depends on proc

Depends on backend

the main executable to be built

-

executable associated with : exitnode, pathmanager, track, paramprovider, mask, main, conmanager, clip, meta, fixedlocation, relativelocation, mobject, source, frame, placement, sessionimpl, builderfacade, controllerfacade, processor, pluginadapter, effect, buildertool, segmentationtool, aframe, assembler, trafo, explicitplacement, auto, glrender, link, parameter, renderengine, allocation, vframe, toolfactory, arender, renderstate, label, glbuf, procnode, stateproxy, hub, buildable, abstractmo, nodecreatertool, projector, interpolator, edl, fixture, glpipe, vrender

+

executable associated with : aframe, assembler, trafo, explicitplacement, auto, glrender, link, parameter, renderengine, allocation, vframe, toolfactory, arender, renderstate, label, glbuf, procnode, stateproxy, hub, buildable, abstractmo, nodecreatertool, projector, interpolator, edl, fixture, glpipe, vrender, exitnode, pathmanager, track, paramprovider, mask, main, conmanager, clip, meta, fixedlocation, relativelocation, mobject, source, frame, placement, sessionimpl, builderfacade, controllerfacade, processor, pluginadapter, effect, buildertool, segmentationtool

Artifact main

Artifact source

@@ -1137,8 +1137,71 @@ reuse exiting Engine

Selection :

    Transformation

    GUI is here just a container to hold any entities considered to be User Interface related, which is not in focus for this Design draft

    5 Package CommonLib

    + +

    5.1 Package ConfigQuery

    +
    + +

    5.1.1 Component View Query System overview

    +
    + +

    +

    components



    + +
    Component ConfigRules
    +

    A system for accessing various kinds of preconfigured objects by querying for capabilities.

    + +
    Component Resolver
    + +
    Component Rule Base
    + +
    Component DefaultsManager
    +
    + +

    5.1.2 Class View query

    +
    + +

    +

    Rules access



    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +

    5.1.3 Use Case View query use

    +
    + +

    +

    when to query



    + +

    5.1.3.1 Use Case create specific object

    + +

    5.1.3.2 Use Case use "default" object

    +
    +
    + +

    5.1.3.3 Use Case load object from session

    + +

    5.1.3.4 Use Case add new object to session

    +
    Class User
    +
    + +

    5.1.3.5 Use Case ConfigQuery

    + +

    5.1.3.6 Use Case need sub object

    + +

    +

    "default" object



    + +
    Class instance predicate impl

    type :TypeHandler

    +
    -

    5.1 Class View error

    +

    5.2 Class View error

    @@ -1152,7 +1215,7 @@ reuse exiting Engine

    Selection :

      Transformation

      -

      5.2 Class View Service Components

      +

      5.3 Class View Service Components

      Class Tool
      @@ -1162,7 +1225,7 @@ reuse exiting Engine

      Selection :

        Transformation
        Class Appconfig

        -

        5.3 Class View Posix Threads Abstraction

        +

        5.4 Class View Posix Threads Abstraction

        C++ wrapers for pthreads

        Class Thread
        @@ -1170,7 +1233,7 @@ reuse exiting Engine

        Selection :

          Transformation
          Class Mutex

          -

          5.4 Class View SmartPointers

          +

          5.5 Class View SmartPointers

          diff --git a/doc/devel/uml/index_34.html b/doc/devel/uml/index_34.html new file mode 100644 index 000000000..54f1d1d1f --- /dev/null +++ b/doc/devel/uml/index_34.html @@ -0,0 +1,23 @@ + + + + + + +" + + + + + +
          "
          +

          + + + + + + +
          NameKindDescription
          "default" objectcollaboration diagram
          + + diff --git a/doc/devel/uml/index_60.html b/doc/devel/uml/index_60.html index a873c25b0..4909e634c 100644 --- a/doc/devel/uml/index_60.html +++ b/doc/devel/uml/index_60.html @@ -30,8 +30,8 @@ <flow>transition <flow>transition <flow>transition -<flow>transition <flow>transition +<flow>transition <flow>transition <flow>transition <flow>transition diff --git a/doc/devel/uml/index_65.html b/doc/devel/uml/index_65.html index cb80221e6..3fce72879 100644 --- a/doc/devel/uml/index_65.html +++ b/doc/devel/uml/index_65.html @@ -23,6 +23,7 @@ access Fileuse case activity finalactivity final activity finalactivity final +add new object to sessionuse case AFrameclass aframeartifacta buffer and render process holding a Audio frame allocationartifact @@ -55,8 +56,8 @@ aud_aclass instance audioclass instance audio1class instance -audio1class instance audio1class instance +audio1class instance autoartifactMedia Object holding automation data AutoclassAutomation data for some parameter (i.e. a time varying function) Automation Entitiesclass diagram diff --git a/doc/devel/uml/index_67.html b/doc/devel/uml/index_67.html index 93dc372e2..08c04b765 100644 --- a/doc/devel/uml/index_67.html +++ b/doc/devel/uml/index_67.html @@ -23,42 +23,42 @@ Categoryclasstree like classification of Assets categoryartifacttree like classification of Assets causeattributea copy of the first exception encountered in this exception chain -chainoperationcreate and add another Placement for this media object, thus increasingly constraining the (possible) position of this object. chainrelationChain of additional Placements further constraining the position of this MObject +chainoperationcreate and add another Placement for this media object, thus increasingly constraining the (possible) position of this object. checked_inrelationchecked_in objects are subject of cache aging and must be not in use checked_outrelationthis list keeps all mappings which are in use, and thus prevents them from Cache aging -class instanceclass instance -class instanceclass instance -class instanceclass instance -class instanceclass instance class instanceclass instance class instanceclass instance +class instanceclass instance class instanceclass instance +class instanceclass instance +class instanceclass instance class instanceclass instance +class instanceclass instance class instanceclass instance class instanceclass instance -class instanceclass instance -class instanceclass instance -class instanceclass instance -class instanceclass instance +class instanceclass instance class instanceclass instance -class instanceclass instance -class instanceclass instance class instanceclass instance -class instanceclass instance class instanceclass instance +class instanceclass instance +class instanceclass instance +class instanceclass instance +class instanceclass instance class instanceclass instance +class instanceclass instance +class instanceclass instance +class instanceclass instance +class instanceclass instance class instanceclass instance +class instanceclass instance class instanceclass instance class instanceclass instance -class instanceclass instance -class instanceclass instance -class instanceclass instance -class instanceclass instance +class instanceclass instance clearoperationclear current session contents
          without resetting overall session config.
          Afterwards, the session will contain only one
          empty EDL, while all Assets are retained.
          Clipclassbookkeeping (asset) view of a media clip. -clipartifacta Media Clip clipartifactbookkeeping (asset) view of a media clip. +clipartifacta Media Clip Clipclass clipsrelation Codecclassdescription of some media data decoder or encoder facility @@ -71,6 +71,7 @@ CommonLibpackage complete Render Engineactivity object componentsrelation +componentscomponent diagram compoundclipartifactcompound of several clips (multichannel) CompoundClipclassClip MObject which is actually a compound of several elementary clips,
          e.g. the several streams found within multichannels media. CompoundMediaclasscompound of several elementary media tracks,
          e.g. the individual media streams found in one media file @@ -78,6 +79,10 @@ ConditionclassI provided a reworked Condition class in my Cinelerra2 repository Configclass config examplesuse case view +ConfigQuerypackage +ConfigQueryuse case +ConfigRulesclasspublic acces point for running Config Queries +ConfigRulescomponentA system for accessing various kinds of preconfigured objects by querying for capabilities. configureoperation configure Renderactivity configure Toolsopaque activity action @@ -88,14 +93,15 @@ constraintartifactLocatingPin representing an directive by the user that
          must not be violated Constraintclass Controllercomponent -controllerpackagesourcecode package

          The Processing and Render Controller,
          located within the MObject Subsystem Controllerpackage +controllerpackagesourcecode package

          The Processing and Render Controller,
          located within the MObject Subsystem Controller Entitiesclass diagram Controller Workingsclass view ControllerFacadeclassProvides unified access to the Proc-Subsystem Controller. Especially, this Facade class provides the functions to get a render engine to carry out actual renderings. controllerfacadeartifactFacade and service access point for the Proc Layer Controller ControllerFacadecomponent create ProcNodeopaque activity action +create specific objectuse case createClipoperationcreate a (possibly compound) Clip refering to this media, ready to be added to the EDL. currEDLoperationThe EDL currently in focus. In most cases, Session and EDL are almost the same, just EDL emphasizes the collection aspect. But generally (for larger editing projects) one Session can contain several EDLs, which may even be nested. At any given time, only one of these EDLs has focus and recieves the editing commands. currentrelationStandard access path to get at the current session via the Session Manager, which acts as a "PImpl" smart pointer diff --git a/doc/devel/uml/index_68.html b/doc/devel/uml/index_68.html index 68da1dfbd..90bc7ba4d 100644 --- a/doc/devel/uml/index_68.html +++ b/doc/devel/uml/index_68.html @@ -22,14 +22,18 @@ datasrcrelationThe predecessor in a processing pipeline, i.e. a source to get data to be processed DBclassImplementation of the registry holding all Asset instances known to the Asset Manager subsystem. As of 8/2007 implemented by a hashtable. dbartifactregistry holding known Asset instances. +defaultsrelation +DefaultsManagerclass +DefaultsManagercomponent +DefaultsRegistryclass define segmentopaque activity action descriptorrelationtype of this frame descriptorrelation designpackage designpackageAll things concering the big picture.
          Not a real code package, rather a container for design drafts, specifications, decisions. detect Channelsuse case -determine Render Paramsopaque activity action determine Render Paramsexpansion region +determine Render Paramsopaque activity action devnullclass instance Dispatchercomponent dispatchOpoperation diff --git a/doc/devel/uml/index_69.html b/doc/devel/uml/index_69.html index 1f6fd3f19..6c17d406d 100644 --- a/doc/devel/uml/index_69.html +++ b/doc/devel/uml/index_69.html @@ -24,8 +24,8 @@ EDL Example2object diagramMore complex example showing the Object graph in the EDL and how it is linked into the Fixture to yield the actual locations. In this example, an HUE Effect is applied on a part of the Clip edlsrelation EffectclassEffect or media processing component -effectartifactEffect or media processing component effectartifactEDL representation of a pluggable and automatable effect. +effectartifactEffect or media processing component Effectclass elementsrelationrelevant MObjects comprising this segment. TODO: actually necessary?? enableoperationchange the enabled status of this asset. Note the corresponding #isActive predicate may depend on the enablement status of parent assets as well diff --git a/doc/devel/uml/index_70.html b/doc/devel/uml/index_70.html index b36d60aff..78b986b1b 100644 --- a/doc/devel/uml/index_70.html +++ b/doc/devel/uml/index_70.html @@ -29,12 +29,13 @@ FileProviderclassThis is the Factory for Files, whenever something wants to use some file (or temporary storage), This Factory will hand out some smart/shared pointer to a File object which will be used to retrieve Frames. FileReferenceclass filesrelation +findoperation fixedlocationartifactimplements fixed placement of a MObject FixedLocationclass Fixtureactivity object fixtureartifactthe (low level) representation of the EDL with concrete placement data -Fixtureclass Fixturecomponent +Fixtureclass fork activity nodefork activity node FrameclassFrames are just a low level lump of continous memory, most parts are opaque. Frames are memory sensitive, they will be small constant sized structures which can be efficently managed in a pool. Framenode diff --git a/doc/devel/uml/index_72.html b/doc/devel/uml/index_72.html index bf8262c8c..0abaaac68 100644 --- a/doc/devel/uml/index_72.html +++ b/doc/devel/uml/index_72.html @@ -24,8 +24,8 @@ howtoProcoperation@return descriptor how to build a render pipeline corresponding to this media Hubclass hubartifactspecial ProcNode used to build data distributing connections -HUEclass instance HUEclass instance +HUEclass instance diff --git a/doc/devel/uml/index_73.html b/doc/devel/uml/index_73.html index 27e37a604..798945020 100644 --- a/doc/devel/uml/index_73.html +++ b/doc/devel/uml/index_73.html @@ -20,9 +20,9 @@ idattributeAsset primary key. In Memory Databaseclass diagram inFixtureactivity action pin -inputclass instance inputclass instance inputclass instance +inputclass instance instanceoperation instructionsrelation Interfaceclass view diff --git a/doc/devel/uml/index_76.html b/doc/devel/uml/index_76.html index 0b4ef97db..75cbabe50 100644 --- a/doc/devel/uml/index_76.html +++ b/doc/devel/uml/index_76.html @@ -25,6 +25,7 @@ linkartifactforwarding, adapting or connecting ProcNode loadoperationreplace the current session by a new
          session loaded from serialized state. load Mediause case +load object from sessionuse case locatingpinartifactChaining and constraining the Placement of a Media Object LocatingPinclassAn element with value semantics, which actually implements the placement of some MObject by positioning it in some way. Lockclass diff --git a/doc/devel/uml/index_77.html b/doc/devel/uml/index_77.html index 3e0b915cc..e6d8e8031 100644 --- a/doc/devel/uml/index_77.html +++ b/doc/devel/uml/index_77.html @@ -18,6 +18,7 @@ + diff --git a/doc/devel/uml/index_78.html b/doc/devel/uml/index_78.html index eeaf4b78d..1cf51971d 100644 --- a/doc/devel/uml/index_78.html +++ b/doc/devel/uml/index_78.html @@ -18,6 +18,7 @@
          NameKindDescription
          mainartifact
          makeoperation
          mappingrelation
          mappingsrelationweak pointers
          Maskclass
          + diff --git a/doc/devel/uml/index_79.html b/doc/devel/uml/index_79.html index d1fdc83c0..4ac4c3b33 100644 --- a/doc/devel/uml/index_79.html +++ b/doc/devel/uml/index_79.html @@ -19,9 +19,9 @@ - + diff --git a/doc/devel/uml/index_80.html b/doc/devel/uml/index_80.html index 2b01f816d..ba34ae763 100644 --- a/doc/devel/uml/index_80.html +++ b/doc/devel/uml/index_80.html @@ -40,6 +40,7 @@ + diff --git a/doc/devel/uml/index_81.html b/doc/devel/uml/index_81.html new file mode 100644 index 000000000..5a521bd2f --- /dev/null +++ b/doc/devel/uml/index_81.html @@ -0,0 +1,27 @@ + + + + + + +Q + + + + + +
          Q
          +

          + + + +
          NameKindDescription
          nameattributeelement ID, comprehensible but sanitized. The tuple (category, name, org) is unique.
          need sub objectuse case
          nextrelationnext additional LocatingPin, if any
          nodecreatertoolartifactcentral Tool implementing the Renderengine building
          NodeCreatorToolclassThis Tool implementation plays the central role in the buld process: given a MObject from Session, it is able to attach ProcNodes to the render engine under construction such as to reflect the properties of the MObject in the actual render.
          NameKindDescription
          offsetattributeOffset the actual position by this (time) value relative to the anchor point. TODO: Representation?
          orgattributeorigin or authorship id. Can be a project abbreviation, a package id or just the authors nickname or UID. This allows for the compnent name to be more generic (e.g. "blur"). Default for all assets provided by the core Lumiera codebase is "lumi".
          ouputclass instance
          ouputclass instance
          ouputclass instance
          ouputclass instance
          outPortrelationthe Port this MObject wants to be conected to
          outputrelation
          Overviewcomponent diagramThis drawing shows the top level compoents and relations
          pnodenode
          pointattributeidentifying the point where the nodes should be attached
          Posix Threads Abstractionclass viewC++ wrapers for pthreads
          predicate implclass instance
          Prefetchclass
          Previewclassalternative version of the media data, probably with lower resolution
          previewartifactalternative version of the media data, probably with lower resolution
          + + + + + + +
          NameKindDescription
          queryclass view
          Query System overviewcomponent view
          query useuse case view
          QueryHandlerclass
          QueryHandlerImplclass
          + + diff --git a/doc/devel/uml/index_82.html b/doc/devel/uml/index_82.html index a76e8f590..1bade53e8 100644 --- a/doc/devel/uml/index_82.html +++ b/doc/devel/uml/index_82.html @@ -22,8 +22,8 @@ registryrelation@internal Table or DB holding all registered asset instances. relativelocationartifactPlacement implemnetaion providing various ways of attaching a MObject to another one RelativeLocationclass -relTypeattributethe kind of relation denoted by this Placement RelTypeclassthe possible kinds of RelativePlacements +relTypeattributethe kind of relation denoted by this Placement removeoperationremove the given asset <i>together with all its dependants</i> from the internal DB Render Entitiesclass diagram Render Requestactivity parameter @@ -38,7 +38,13 @@ reprattributehuman readable representation of the condition characterizing this allocaton, e.g. "t >= 10" resetoperationreset all session config and
          start with a pristine default session. resolveoperationcreate an actual (explicit) placement while trying to satisfy the network of adjacent objects and placements. +resolveoperation +resolveoperation +Resolvercomponent +ResolverBaseclass rootCauseoperationIf this exception was caused by a chain of further exceptions,
          return the first one registered in this throw sequence.
          This works only, if every exceptions thrown as a consequence
          of another exception is propperly constructed by passing
          the original exception to the constructor +Rule Basecomponent +Rules accessclass diagram diff --git a/doc/devel/uml/index_83.html b/doc/devel/uml/index_83.html index 18c49405b..e2f2fcea3 100644 --- a/doc/devel/uml/index_83.html +++ b/doc/devel/uml/index_83.html @@ -28,11 +28,12 @@ segmentsactivity object segmentsactivity object segmentsrelationthe partitioning of the Timeline to be created by this tool. +Serializerclass Service Componentsclass view Sessioncomponent sessionartifactInterface: the session edited by the user -sessionpackagesourcecode package

          Everything concerning the EDL and Session, within the MObject Subsystem Sessionclass view +sessionpackagesourcecode package

          Everything concerning the EDL and Session, within the MObject Subsystem SessionclassPrimary Interface for all editing tasks.
          The session contains defaults, all the assets being edited, and a set of EDL with the individual MObjects to be manipulated and rendered. Session structureclass diagram sessionimplartifactholds the complete session data to be edited by the user diff --git a/doc/devel/uml/index_84.html b/doc/devel/uml/index_84.html index 692cb2cac..14d928057 100644 --- a/doc/devel/uml/index_84.html +++ b/doc/devel/uml/index_84.html @@ -34,21 +34,23 @@ trackrelation trackattribute trackrelation -trackartifactstructural asset holding the configuration of a track in the EDL trackartifactA grouping device within the EDL. The corresponding Placement
          by which this Track object is refered defines fallback placing
          properties to be used by all objects placed on this track in
          case they don't specify more concrete placements.
          Typically, tracks are used do make default Port connections,
          define a layer or pan for sound and for for disabling groups
          of clips. Note tracks are grouped in a tree like fashion.
          +trackartifactstructural asset holding the configuration of a track in the EDL Trackclass tracksrelationelementary media assets comprising this compound Trafoclass trafoartifacttransforming processing Node treatoperation treatoperationThis operation is to be overloaded for the specific MObject subclasses to be treated. -treatoperation treatoperation treatoperation treatoperation +treatoperation +treatoperation treatoperation treatoperation -treatoperation +TypeHandlerclass +TypeHandler<Pipe>class diff --git a/doc/devel/uml/index_85.html b/doc/devel/uml/index_85.html index 6075e15d1..dafc717cb 100644 --- a/doc/devel/uml/index_85.html +++ b/doc/devel/uml/index_85.html @@ -20,7 +20,9 @@ Unknownclassplaceholder for unknown or unavailable media source unknownartifactplaceholder for unknown or unavailable media source usageuse case view +use "default" objectuse case useFileoperationAnnounces that the application intends to use this file with mode (READ|WRITE|READWRITE) +Userclass useTemporaryStorageoperationProvides a pool for interminate frames diff --git a/doc/devel/uml/index_86.html b/doc/devel/uml/index_86.html index f75b93690..e3c5f01a9 100644 --- a/doc/devel/uml/index_86.html +++ b/doc/devel/uml/index_86.html @@ -20,23 +20,23 @@ versionattributeversion number of the thing or concept represented by this asset. Of each unique tuple (name, category, org) there will be only one version in the whole system. Version 0 is reserved for internal purposes. Versions are considered to be ordered, and any higher version is supposed to be fully backwards compatible to all previous versions. VFrameclass vframeartifacta buffer and render process holding a Video frame -vid1class instance vid1class instance -vid_aclass instance -vid_aclass instance -vid_Aclass instance +vid1class instance vid_Aclass instance +vid_Aclass instance +vid_aclass instance +vid_aclass instance vid_Aclass instance videoclass instance -videoclass instance videoclass instance videoclass instance +videoclass instance +video1class instance video1class instance video1class instance video1class instance -video1class instance -video1class instance video1class instance +video1class instance Visitableclass visitorpackagesub-namespace for visitor library implementation visitorartifactAcyclic Visitor library diff --git a/doc/devel/uml/index_87.html b/doc/devel/uml/index_87.html index 748f57b37..bd2bf3399 100644 --- a/doc/devel/uml/index_87.html +++ b/doc/devel/uml/index_87.html @@ -19,6 +19,7 @@ NameKindDescription whatoperation whatoperationthe base class of all exceptions thrown by the standard library +when to queryuse case diagram wiringTemplaterelation wishartifactLocatingPin representing a low-priority directive by the user,
          to be fulfilled only if possible (and after satisfying the
          more important LocatingPins) Wishclass diff --git a/doc/devel/uml/index_89.html b/doc/devel/uml/index_89.html new file mode 100644 index 000000000..bb8528187 --- /dev/null +++ b/doc/devel/uml/index_89.html @@ -0,0 +1,23 @@ + + + + + + +Y + + + + + +
          Y
          +

          + + + + + + +
          NameKindDescription
          YAP_Prologclass
          + + diff --git a/doc/devel/uml/navig.html b/doc/devel/uml/navig.html index 74b812388..fd08b1b7d 100644 --- a/doc/devel/uml/navig.html +++ b/doc/devel/uml/navig.html @@ -12,6 +12,6 @@

          -Top- -Classes- -Public Operations- -Public properties- -Packages- -Use Cases- -Activities- -Class Diagrams- -Object Diagrams- -Activity Diagrams- -Use Case Diagrams- -Collaboration Diagrams- -Component Diagrams- -Deployment Diagrams-

          -

          < A B C D E F G H I K L M N O P R S T U V W ~

          +

          " < A B C D E F G H I K L M N O P Q R S T U V W Y ~

          diff --git a/doc/devel/uml/packages.html b/doc/devel/uml/packages.html index c9321815b..49c0f4f80 100644 --- a/doc/devel/uml/packages.html +++ b/doc/devel/uml/packages.html @@ -25,6 +25,7 @@ codegenThis package is used to organize code generation by BOUML. It is considered useless after having generated the initial code skeleton. commonsrcsourcecode package

          Common library and helper classes CommonLib +ConfigQuery Controller controllersrcsourcecode package

          The Processing and Render Controller,
          located within the MObject Subsystem design diff --git a/doc/devel/uml/public_operations.html b/doc/devel/uml/public_operations.html index d0aedd4c7..697d67b52 100644 --- a/doc/devel/uml/public_operations.html +++ b/doc/devel/uml/public_operations.html @@ -29,6 +29,7 @@ currEDLSessionThe EDL currently in focus. In most cases, Session and EDL are almost the same, just EDL emphasizes the collection aspect. But generally (for larger editing projects) one Session can contain several EDLs, which may even be nested. At any given time, only one of these EDLs has focus and recieves the editing commands. dispatchOpVisitable enableAssetchange the enabled status of this asset. Note the corresponding #isActive predicate may depend on the enablement status of parent assets as well +findTypeHandler getAppconfigaccess the configuation value for a given key.
          @return empty string for unknown keys, else the corresponding configuration value get_reprAllocation getAssetAssetManagerfind and return corresponging object @@ -48,20 +49,23 @@ isActiveAssetweather this asset is swithced on and consequently included in the fixture and participates in rendering knownAssetManager@return true if the given id is registered in the internal asset DB loadSessManagerreplace the current session by a new
          session loaded from serialized state. +makeTypeHandler playRenderEngineTODO: will probably be handled differently (see Cehteh) removeAssetManagerremove the given asset <i>together with all its dependants</i> from the internal DB resetSessManagerreset all session config and
          start with a pristine default session. resolvePlacementcreate an actual (explicit) placement while trying to satisfy the network of adjacent objects and placements. +resolveQueryHandler +resolveQueryHandlerImpl rootCauseErrorIf this exception was caused by a chain of further exceptions,
          return the first one registered in this throw sequence.
          This works only, if every exceptions thrown as a consequence
          of another exception is propperly constructed by passing
          the original exception to the constructor saveSessManagercreate a complete, serialized representation
          of the current session config and contents.
          @todo how to serialize, prameters, return value? treatApplicable treatBuilderToolThis operation is to be overloaded for the specific MObject subclasses to be treated. +treatNodeCreatorTool treatNodeCreatorTool treatNodeCreatorTool treatNodeCreatorTool -treatNodeCreatorTool -treatSegmentationTool treatSegmentationTool +treatSegmentationTool treatSegmentationTool useFileFileProviderAnnounces that the application intends to use this file with mode (READ|WRITE|READWRITE) useTemporaryStorageFileProviderProvides a pool for interminate frames diff --git a/doc/devel/uml/usecasediagrams.html b/doc/devel/uml/usecasediagrams.html index 62cd42a0e..c6ce03c64 100644 --- a/doc/devel/uml/usecasediagrams.html +++ b/doc/devel/uml/usecasediagrams.html @@ -17,6 +17,7 @@ +
          backend use cases
          when to query
          diff --git a/doc/devel/uml/usecases.html b/doc/devel/uml/usecases.html index d55241488..9b7d44a88 100644 --- a/doc/devel/uml/usecases.html +++ b/doc/devel/uml/usecases.html @@ -18,8 +18,14 @@ + + + + + +
          access Channel
          access File
          add new object to session
          ConfigQuery
          create specific object
          detect Channels
          load Media
          load object from session
          need sub object
          use "default" object
          diff --git a/uml/lumiera/128261 b/uml/lumiera/128261 index c30e7ce9e..6394cfca5 100644 --- a/uml/lumiera/128261 +++ b/uml/lumiera/128261 @@ -1,6 +1,6 @@ format 40 "MObject" // ProcessingLayer::MObject - revision 29 + revision 30 modified_by 5 "hiv" // class settings //class diagram settings @@ -104,6 +104,15 @@ configuration of all Objects to be rendered" classrelation_ref 144773 // current () b multiplicity "" parent class_ref 139781 // SessManager end + + classrelation 150917 // defaults () + relation 148101 ---> + a role_name "defaults" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 150917 // defaults () + b multiplicity "" parent class_ref 141445 // DefaultsManager + end end class 128005 "SessionImpl" diff --git a/uml/lumiera/128517 b/uml/lumiera/128517 index 14fa5def7..5eed4e170 100644 --- a/uml/lumiera/128517 +++ b/uml/lumiera/128517 @@ -1,6 +1,6 @@ format 40 "CommonLib" // CommonLib - revision 11 + revision 12 modified_by 5 "hiv" // class settings //class diagram settings @@ -26,6 +26,8 @@ format 40 package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default show_infonote default drawing_language default + package_ref 131077 // ConfigQuery + classview 128773 "error" //class diagram settings draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default diff --git a/uml/lumiera/129285 b/uml/lumiera/129285 index 17a54b8b9..1a005d8e9 100644 --- a/uml/lumiera/129285 +++ b/uml/lumiera/129285 @@ -1,6 +1,6 @@ format 40 "ProcessingLayer" // ProcessingLayer - revision 12 + revision 13 modified_by 5 "hiv" // class settings //class diagram settings diff --git a/uml/lumiera/131077 b/uml/lumiera/131077 new file mode 100644 index 000000000..c996ebb90 --- /dev/null +++ b/uml/lumiera/131077 @@ -0,0 +1,551 @@ +format 40 +"ConfigQuery" // CommonLib::ConfigQuery + revision 1 + modified_by 5 "hiv" + // class settings + //class diagram settings + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //component diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default + //deployment diagram settings + package_name_in_tab default show_context default write_horizontally default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default + //state diagram settings + package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default + show_activities default region_horizontally default drawing_language default + //activity diagram settings + package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default + show_infonote default drawing_language default + + componentview 128261 "Query System overview" + //component diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default + componentdiagram 131589 "components" + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + draw_component_as_icon default show_component_req_prov default show_component_rea default + size A4 + end + + component 130565 "ConfigRules" + comment "A system for accessing various kinds of preconfigured objects by querying for capabilities." + end + + component 130693 "Resolver" + end + + component 130821 "Rule Base" + end + + component 130949 "DefaultsManager" + end + end + + classview 129157 "query" + //class diagram settings + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //state diagram settings + package_name_in_tab default show_context default auto_label_position default write_trans_label_horizontally default show_trans_definition default draw_all_relations default shadow default + show_activities default region_horizontally default drawing_language default + //class settings + //activity diagram settings + package_name_in_tab default show_context default show_opaque_action_definition default auto_label_position default write_flow_label_horizontally default draw_all_relations default shadow default + show_infonote default drawing_language default + + classdiagram 131461 "Rules access" + draw_all_relations no hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + size A4 + end + + class 140549 "ConfigRules" + abstract visibility public stereotype "interface" + nactuals 1 + actual class class_ref 140677 // QueryHandler + rank 0 explicit_value "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "${comment}${@}${visibility}interface ${name}${extends} { +${members}} +" + idl_decl "${comment}${abstract}${local}interface ${name}${inherit} { +${members}}; +" + explicit_switch_type "" + + comment "public acces point for running Config Queries" + classrelation 148357 // + relation 146181 ---|> + a public + cpp default "${type}" + classrelation_ref 148357 // + b multiplicity "" parent class_ref 140677 // QueryHandler + end + + classrelation 148485 // + relation 146309 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 148485 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 148613 // + end + + classrelation 148741 // + relation 146437 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 148741 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 148869 // + end + end + + class 140677 "QueryHandler" + abstract visibility public stereotype "interface" + nformals 1 + formal name "TY" type "class" explicit_default_value "" + explicit_extends "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "${comment}${@}${visibility}interface ${name}${extends} { +${members}} +" + idl_decl "${comment}${abstract}${local}interface ${name}${inherit} { +${members}}; +" + explicit_switch_type "" + + classrelation 148613 // + relation_ref 146309 // + end + + classrelation 148869 // + relation_ref 146437 // + end + + operation 135301 "resolve" + public explicit_return_type "bool" + nparams 2 + param inout name "solution" explicit_type "P" + param in name "query" explicit_type "Query" + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + end + end + + class 140805 "TypeHandler" + abstract visibility public stereotype "interface" + nformals 1 + formal name "TY" type "class" explicit_default_value "" + explicit_extends "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "${comment}${@}${visibility}interface ${name}${extends} { +${members}} +" + idl_decl "${comment}${abstract}${local}interface ${name}${inherit} { +${members}}; +" + explicit_switch_type "" + + operation 135045 "find" + abstract cpp_virtual public explicit_return_type "TY" + nparams 1 + param in name "capabilities" explicit_type "Pred" + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + + + end + + operation 135173 "make" + abstract cpp_virtual public explicit_return_type "TY" + nparams 1 + param in name "capabilities" explicit_type "Pred" + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + + + end + end + + class 140933 "ResolverBase" + abstract visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 148997 // + relation 146565 -_-|> + a public + cpp default "${type}" + classrelation_ref 148997 // + b multiplicity "" parent class_ref 140549 // ConfigRules + end + end + + class 141061 "YAP_Prolog" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 150405 // + relation 147717 -_-> + a default + cpp default "Generated" + classrelation_ref 150405 // + b multiplicity "" parent class_ref 140805 // TypeHandler + end + end + + class 141189 "QueryHandlerImpl" + visibility package + nformals 1 + formal name "TY" type "class" explicit_default_value "" + explicit_extends "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 149125 // + relation 146693 ---|> + a public + cpp default "${type}" + classrelation_ref 149125 // + b multiplicity "" parent class_ref 140933 // ResolverBase + end + + classrelation 149253 // + relation 146821 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149253 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149381 // + end + + classrelation 149381 // + relation_ref 146821 // + end + + classrelation 149509 // + relation 146949 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149509 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149637 // + end + + classrelation 149637 // + relation_ref 146949 // + end + + operation 135429 "resolve" + public explicit_return_type "bool" + nparams 2 + param inout name "solution" explicit_type "P" + param in name "query" explicit_type "Query" + cpp_decl " ${comment}${friend}${static}${inline}${virtual}${type} ${name} ${(}${)}${const}${volatile} ${throw}${abstract};" + cpp_def "${comment}${inline}${type} +${class}::${name} ${(}${)}${const}${volatile} ${throw}${staticnl} +{ + ${body} +} + +" + + + end + + classrelation 149765 // + relation 147077 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149765 // + b multiplicity "" parent class_ref 141061 // YAP_Prolog + end + + classrelation 150149 // + relation 147461 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 150149 // + b multiplicity "" parent class_ref 141061 // YAP_Prolog + end + + classrelation 150277 // + relation 147589 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 150277 // + b multiplicity "" parent class_ref 141061 // YAP_Prolog + end + end + + class 141317 "TypeHandler" + visibility package + nactuals 1 + actual class class_ref 140805 // TypeHandler + rank 0 explicit_value "" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 149893 // + relation 147205 ---> + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 149893 // + b multiplicity "" parent class_ref 138117 // Pipe + end + + classrelation 150021 // + relation 147333 -_-|> + a public + cpp default "${type}" + classrelation_ref 150021 // + b multiplicity "" parent class_ref 140805 // TypeHandler + end + end + + class 141445 "DefaultsManager" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 150533 // + relation 147845 -_-> + a default + cpp default "Generated" + classrelation_ref 150533 // + b multiplicity "" parent class_ref 140549 // ConfigRules + end + + classrelation 150661 // + relation 147973 ---- + a role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 150661 // + b role_name "" multiplicity "" protected + cpp default " ${comment}${static}${mutable}${volatile}${const}${type}* ${name}${value}; +" + classrelation_ref 150789 // + end + end + + class 141573 "DefaultsRegistry" + visibility package + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + classrelation 150789 // + relation_ref 147973 // + end + end + end + + usecaseview 128389 "query use" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + usecasediagram 131717 "when to query" + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + size A4 + end + + usecase 128517 "create specific object" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + end + + usecase 128645 "use \"default\" object" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + simplerelation 129541 + ---|> + on usecase_ref 128517 // create specific object + end + end + + usecase 128773 "load object from session" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + end + + usecase 128901 "add new object to session" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + end + + class 141701 "User" + visibility package stereotype "actor" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + end + + class 141829 "Serializer" + visibility package stereotype "actor" + cpp_decl "${comment}${template}class ${name}${inherit} + { +${members} }; +${inlines} +" + java_decl "" + idl_decl "" + explicit_switch_type "" + + end + + usecase 129029 "ConfigQuery" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + end + + usecase 129157 "need sub object" + //use case diagram settings + package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + //sequence diagram settings + show_full_operations_definition default write_horizontally default class_drawing_mode default drawing_language default draw_all_relations default shadow default + //collaboration diagram settings + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + //object diagram settings + write_horizontally default package_name_in_tab default show_context default auto_label_position default draw_all_relations default shadow default + end + + collaborationdiagram 131845 "\"default\" object" + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + size A4 + end + + classinstance 135941 "predicate impl" + type class_ref 140805 // TypeHandler + attributes + end + relations + end + end + end +end diff --git a/uml/lumiera/131461.diagram b/uml/lumiera/131461.diagram new file mode 100644 index 000000000..2afd439c2 --- /dev/null +++ b/uml/lumiera/131461.diagram @@ -0,0 +1,196 @@ +format 40 + +packagecanvas 128005 + package_ref 131077 // ConfigQuery + xyzwh 194 34 1980 445 495 +classcanvas 128133 class_ref 140549 // ConfigRules + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + color verylightorange + xyz 268 364 2005 + end +classcanvas 128261 class_ref 140677 // QueryHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 217 237 2005 + end +classcanvas 128389 class_ref 140677 // QueryHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 264 218 2005 + end +classcanvas 128517 class_ref 140677 // QueryHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 302 205 2005 + end +classcanvas 128645 class_ref 140805 // TypeHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 262 88 2005 + end +classcanvas 128901 class_ref 140805 // TypeHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 215 98 2010 + end +classcanvas 129029 class_ref 140805 // TypeHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 307 77 1995 + end +classcanvas 129541 class_ref 140933 // ResolverBase + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 451 223 2000 + end +classcanvas 129669 class_ref 141061 // YAP_Prolog + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 554 155 2000 + end +classcanvas 130437 class_ref 141189 // QueryHandlerImpl + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 439 280 2000 + end +classcanvas 130693 class_ref 141189 // QueryHandlerImpl + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 439 360 2000 + end +classcanvas 130821 class_ref 141189 // QueryHandlerImpl + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 439 442 2000 + end +classcanvas 132613 class_ref 138117 // Pipe + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 84 123 2000 + end +classcanvas 132741 class_ref 141317 // TypeHandler + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 18 244 2000 + end +classcanvas 133381 class_ref 136581 // AssetManager + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 19 25 2005 + end +fragment 133637 "Rule Base" + xyzwh 425 67 1985 202 78 +end +classcanvas 135941 class_ref 141445 // DefaultsManager + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 58 415 2000 + end +classcanvas 136069 class_ref 141573 // DefaultsRegistry + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 59 481 2000 + end +classcanvas 136709 class_ref 139653 // Session + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 19 313 2005 + end +textcanvas 137093 "queries" + xyzwh 201 414 2000 38 18 +textcanvas 137221 "invokes" + xyzwh 557 500 2005 40 18 +textcanvas 137349 "invokes" + xyzwh 549 386 2005 40 18 +textcanvas 137477 "provide type specific implementation" + xyzwh 83 207 2005 102 39 +note 137605 "Prolog rules use TypeHandler interface to imlement predicates" + xyzwh 503 38 2000 176 59 +classcanvas 137733 class_ref 137989 // Track + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 104 99 1995 + end +classcanvas 137861 class_ref 138757 // ProcPatt + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 118 75 1990 + end +classcanvas 137989 class_ref 129029 // Effect + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 134 47 1985 + end +classcanvas 138117 class_ref 137861 // Codec + draw_all_relations default hide_attributes default hide_operations default show_members_full_definition default show_members_visibility default show_members_stereotype default show_parameter_dir default show_parameter_name default package_name_in_tab default class_drawing_mode default drawing_language default show_context_mode default auto_label_position default show_infonote default shadow default + xyz 144 22 1975 + end +relationcanvas 128773 relation_ref 146181 // + from ref 128133 z 2004 to ref 128261 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 129157 relation_ref 146309 // + from ref 128133 z 2004 to ref 128389 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 129285 relation_ref 146437 // + from ref 128133 z 1999 to ref 128517 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 130565 relation_ref 146693 // + from ref 130437 z 1999 to ref 129541 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 130949 relation_ref 146821 // + from ref 130821 z 1999 to ref 130693 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 131077 relation_ref 146949 // + from ref 130693 z 1999 to ref 130437 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 131973 relation_ref 146693 // + from ref 130693 z 1999 to ref 129541 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 132101 relation_ref 146693 // + from ref 130821 z 1999 to ref 129541 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 132869 relation_ref 147205 // + from ref 132741 z 1999 to point 73 205 + line 133509 z 1999 to ref 132613 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 132997 relation_ref 147333 // + from ref 132741 z 1999 to point 73 226 + line 133253 z 1999 to point 168 226 + line 133125 z 1999 to ref 128901 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 133765 relation_ref 147077 // + from ref 130693 z 1999 to point 542 417 + line 133893 z 1999 to point 589 403 + line 134021 z 1999 to ref 129669 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 134149 relation_ref 146565 // + from ref 129541 z 1999 to point 491 182 + line 134661 z 1999 to point 434 182 + line 134533 z 1999 to point 348 473 + line 134405 z 1999 to point 304 473 + line 134277 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 134789 relation_ref 147461 // + from ref 130821 z 1999 to point 543 502 + line 135045 z 1999 to point 589 490 + line 134917 z 1999 to ref 129669 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 135173 relation_ref 147589 // + from ref 130437 z 1999 to point 543 337 + line 135429 z 1999 to point 589 320 + line 135301 z 1999 to ref 129669 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 135557 relation_ref 147717 // + from ref 129669 z 1994 to point 588 116 + line 135685 z 1994 to point 440 116 + line 135813 z 1994 to ref 129029 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136197 relation_ref 147845 // + from ref 135941 z 1999 to ref 128133 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136581 relation_ref 147973 // + from ref 135941 z 1999 to ref 136069 + no_role_a no_role_b + no_multiplicity_a no_multiplicity_b +relationcanvas 136837 relation_ref 148101 // + from ref 136709 z 1999 to point 105 348 + line 136965 z 1999 to ref 135941 + role_a_pos 117 390 3000 no_role_b + no_multiplicity_a no_multiplicity_b +end diff --git a/uml/lumiera/131589.diagram b/uml/lumiera/131589.diagram new file mode 100644 index 000000000..81414854d --- /dev/null +++ b/uml/lumiera/131589.diagram @@ -0,0 +1,21 @@ +format 40 + +componentcanvas 128005 component_ref 130565 // ConfigRules + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 207 133 2000 259 219 +componentcanvas 128133 component_ref 130693 // Resolver + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 416 17 2005 155 63 +componentcanvas 128261 component_ref 130821 // Rule Base + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 227 15 2000 156 82 +componentcanvas 128389 component_ref 128645 // AssetManagement + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 461 366 2000 187 120 +componentcanvas 128517 component_ref 128133 // Session + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 23 15 2000 190 108 +componentcanvas 128645 component_ref 130949 // DefaultsManager + draw_component_as_icon default show_component_req_prov default show_component_rea default + xyzwh 23 146 2005 155 63 +end diff --git a/uml/lumiera/131717.diagram b/uml/lumiera/131717.diagram new file mode 100644 index 000000000..b633daae5 --- /dev/null +++ b/uml/lumiera/131717.diagram @@ -0,0 +1,45 @@ +format 40 + +usecasecanvas 128005 usecase_ref 128517 // create specific object + xyzwh 461 161 3005 64 32 label_xy 442 193 +usecasecanvas 128133 usecase_ref 128645 // use "default" object + xyzwh 462 267 3005 64 32 label_xy 448 299 +usecasecanvas 128261 usecase_ref 128773 // load object from session + xyzwh 159 259 3005 64 32 label_xy 132 291 +usecasecanvas 128389 usecase_ref 128901 // add new object to session + xyzwh 159 116 3005 64 32 label_xy 128 148 +classcanvas 128517 class_ref 141701 // User + xyz 40 56 3005 label_xy 48 96 +classcanvas 128645 class_ref 141829 // Serializer + xyz 40 255 3005 label_xy 38 295 +usecasecanvas 128773 usecase_ref 129029 // ConfigQuery + color lightorange + xyzwh 337 211 3005 64 32 label_xy 338 243 +usecasecanvas 129797 usecase_ref 129157 // need sub object + xyzwh 358 61 3005 64 32 label_xy 352 93 +packagecanvas 130437 + package_ref 131077 // ConfigQuery + xyzwh 304 18 3010 281 390 +fragment 130693 "EDL/Session" + xyzwh 94 40 3009 181 368 +end +line 128901 ---> + from ref 128517 z 3004 to ref 128389 +line 129029 ---> + from ref 128389 z 3004 to ref 128773 +simplerelationcanvas 129157 simplerelation_ref 129541 + from ref 128133 z 3004 to ref 128005 +line 129285 ---> + from ref 128773 z 3004 to ref 128005 +line 129413 ---> + from ref 128773 z 3004 to ref 128133 +line 129541 ---> + from ref 128261 z 3004 to ref 128773 +line 129669 ---> + from ref 128645 z 3004 to ref 128261 +line 129925 ---> + from ref 128005 z 1999 to point 489 87 + line 130053 z 1999 to ref 129797 +line 130309 ---> + from ref 129797 z 3004 to ref 128773 +end diff --git a/uml/lumiera/131845.diagram b/uml/lumiera/131845.diagram new file mode 100644 index 000000000..aa4bf61a3 --- /dev/null +++ b/uml/lumiera/131845.diagram @@ -0,0 +1,68 @@ +format 40 + +classinstance 128005 class_ref 140549 // ConfigRules + name "" xyz 245 192 2000 +classinstance 128133 class_ref 141445 // DefaultsManager + name "" xyz 51 83 2000 +classinstance 128261 class_ref 141573 // DefaultsRegistry + name "" xyz 244 58 2000 +classinstancecanvas 128517 classinstance_ref 135941 // predicate impl + xyz 344 347 2000 + end +classinstance 128901 class_ref 135301 // Factory + name "" xyz 439 206 2000 +classinstance 129797 class_ref 136581 // AssetManager + name "" xyz 376 447 2000 +textcanvas 130437 "recursive invocation of sub queries" + xyzwh 332 185 2004 172 18 +linkcanvas 128389 + from ref 128133 z 1999 to ref 128261 +dirscanvas 129413 z 1000 linkcanvas_ref 128389 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + forward_label "1 get registered default object +6 store new default object" xyz 92 30 3000 +linkcanvas 128645 + from ref 128133 z 1999 to ref 128005 +dirscanvas 129541 z 1000 linkcanvas_ref 128645 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + forward_label "2 resolve(inout solution : Query) : bool" xyz 94 157 3000 +linkcanvas 128773 + from ref 128005 z 1999 to ref 128517 +dirscanvas 129669 z 1000 linkcanvas_ref 128773 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + forward_label "3 run prolog code" xyz 335 248 3000 +linkcanvas 129029 + from ref 128517 z 1999 to ref 128901 +dirscanvas 130181 z 1000 linkcanvas_ref 129029 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + forward_label "5 create new instances" xyz 448 275 3000 +linkcanvas 129925 + from ref 128517 z 1999 to ref 129797 +dirscanvas 130053 z 1000 linkcanvas_ref 129925 + show_full_operations_definition default show_hierarchical_rank default write_horizontally default drawing_language default package_name_in_tab default show_context default draw_all_relations default shadow default + forward_label "4 retrieve existing object" xyz 436 403 3000 +linkcanvas 130309 + from ref 128901 z 1999 to ref 128005 +msgs + explicitmsg "get registered default object" + forward ranks 1 "1" dirscanvas_ref 129413 + no_msg + explicitmsg "resolve(inout solution : Query) : bool" + forward ranks 2 "2" dirscanvas_ref 129541 + msgs + explicitmsg "run prolog code" + forward ranks 3 "2.1" dirscanvas_ref 129669 + msgs + explicitmsg "retrieve existing object" + forward ranks 4 "2.1.1" dirscanvas_ref 130053 + no_msg + explicitmsg "create new instances" + forward ranks 5 "2.1.2" dirscanvas_ref 130181 + no_msg + msgsend + msgsend + explicitmsg "store new default object" + forward ranks 6 "3" dirscanvas_ref 129413 + no_msg +msgsend +end diff --git a/uml/lumiera/5.session b/uml/lumiera/5.session index 2d78dfa96..89c760008 100644 --- a/uml/lumiera/5.session +++ b/uml/lumiera/5.session @@ -3,34 +3,34 @@ diagrams classdiagram_ref 130309 // Asset Kinds 860 633 100 4 158 0 classdiagram_ref 128133 // Session structure - 860 633 100 4 289 0 + 860 633 100 4 349 0 classdiagram_ref 128389 // Render Entities 743 538 100 4 184 0 - active classdiagram_ref 131205 // Struct-Asset Relations - 555 620 100 4 0 0 + classdiagram_ref 131205 // Struct-Asset Relations + 555 620 100 4 60 0 + classdiagram_ref 131461 // Rules access + 688 627 100 4 0 0 + componentdiagram_ref 131589 // components + 688 544 100 4 0 0 + active usecasediagram_ref 131717 // when to query + 624 495 100 4 0 0 + collaborationdiagram_ref 131845 // "default" object + 626 551 100 4 0 0 end show_stereotypes selected -package_ref 129 // lumiera + package_ref 129 // lumiera open - componentview_ref 128133 // interfaces - deploymentview_ref 128133 // EXE Deployment - deploymentview_ref 128645 // gen - class_ref 137477 // Unknown - class_ref 137605 // Preview - class_ref 137989 // Track - class_ref 128005 // SessionImpl - class_ref 128133 // EDL - class_ref 128261 // Fixture - class_ref 128389 // Track - class_ref 128645 // Placement - class_ref 129413 // RelativeLocation - class_ref 129541 // Allocation - class_ref 140421 // Plug - class_ref 139909 // LocatingPin - class_ref 134021 // Buildable - class_ref 134149 // BuilderTool - class_ref 134405 // NodeCreatorTool + + package_ref 128005 // design + + package_ref 129285 // ProcessingLayer + componentview_ref 128261 // Query System overview + class_ref 140677 // QueryHandler + class_ref 140805 // TypeHandler + class_ref 140933 // ResolverBase + class_ref 141189 // QueryHandlerImpl + usecaseview_ref 128389 // query use classview_ref 128773 // error class_ref 140165 // Visitable end diff --git a/uml/lumiera/lumiera.prj b/uml/lumiera/lumiera.prj index 555a8d3ba..8eec4bb57 100644 --- a/uml/lumiera/lumiera.prj +++ b/uml/lumiera/lumiera.prj @@ -1,6 +1,6 @@ format 40 "lumiera" - revision 42 + revision 43 modified_by 5 "hiv" cpp_root_dir "../../src/" @@ -57,7 +57,7 @@ format 40 2008, Christian Thaeter " - key "html dir" value "/mnt/Lager/heim/devel/cin3/doc/devel/uml/" + key "html dir" value "/mnt/Lager/heim/devel/lumi/doc/devel/uml/" key "" value "" package_ref 128005 // design diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 6d764e90b..c70827a4f 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -797,6 +797,33 @@ Error: #f88 &rarr; [[Configuration Rules system|ConfigRules]]
          +
          +
          * planning to embed a YAP Prolog engine
          +* currently just integrated by a table driven mock
          +* the baseline is a bit more clear by now (4/08)
          +
          +&rarr; see also ConfigRules
          +&rarr; see also DefaultsManagement
          +
          +!Use cases
          +[<img[when to run config queries|uml/fig131717.png]]
          +
          +The key idea is that there is a Rule Base &mdash; partly contained in the session (building on a stock of standard rules supplied with the application). Now, whenever there is the need to get a new object, for adding it to the session or for using associated with another object &mdash; then instead of creating it by a direct hard wired ctor call, we issue a ConfigQuery requesting an object of the given type with some //capabilities// defined by predicates. The same holds true when loading an existing session: some objects won't be loaded back blindly, rather they will be re-created by issuing the config queries again. Especially important is the case of (re)creating a [[processing pattern|ProcPatt]] guiding how to wire up the processing pipeline for some given media.
          +
          +At various places, instead of requiring a fixed set of capabilities, it is possible to request a "default configured" object instead, specifying just those capabilities we really need to be configured in a specific way. This is done by using the [Defaults Manager|DefaultsManagement] accessible on the [[Session]] interface. Such a default object query may either retrieve an already existing object instance, run further config queries, and finally result in the invocation of a factory for creating new objects &mdash; just as necessary and guarded by the rules.
          +
          +@@clear(left):display(block):@@
          +
          +!Components and Relations
          +[>img[participating classes|uml/fig131461.png]]
          +
          +@@clear(right):display(block):@@
          +
          +!when querying for a [["default"|DefaultsManagement]] object
          +[<img[colaboration when issuing a defaults query|uml/fig131845.png]]
          +
          +@@clear(left):display(block):@@
          +
          Many features can be implemented by specifically configuring and wiring some unspecific components. Rather than tie the client code in need of some given feature to these configuration internals, in Lumiera the client can //query // for some kind of object providing the //needed capabilities. // Right from start (summer 2007), Ichthyo had the intention to implement such a feature using sort of a ''declarative database'', e.g. by embedding a Prolog system. By adding rules to the basic session configuration, users should be able to customize the semi-automatic part of Lumiera's behaviour to great extent.
           
          @@ -1140,7 +1167,7 @@ For this Lumiera design, we could consider making GOP just another raw media dat
           &rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
           
          -
          +
          This wiki page is the entry point to detail notes covering some technical decisions, details and problems encountered in the course of the implementation of the Lumiera Renderengine, the Builder and the related parts.
           
           * [[Packages, Interfaces and Namespaces|InterfaceNamespaces]]
          @@ -1154,6 +1181,7 @@ For this Lumiera design, we could consider making GOP just another raw media dat
           * [[using the Visitor pattern?|VisitorUse]] &mdash; resulting in [[»Visiting-Tool« library implementation|VisitingToolImpl]]
           * [[Handling of Tracks and render Pipes in the EDL|TrackPipeEDL]]. [[Handling of Tracks|TrackHandling]] and [[Pipes|PipeHandling]]
           * [[getting default configured|DefaultsManagement]] Objects relying on [[rule-based Configuration Queries|ConfigRules]]
          +* [[integrating the Config Query system|ConfigQueryIntegration]]
           * [[identifying the basic Builder operations|BasicBuildingOperations]] and [[planning the Implementation|PlanningNodeCreatorTool]]
           * [[how to handle »attached placement«|AttachedPlacementProblem]]
           
          @@ -2465,12 +2493,13 @@ Besides, they provide an important __inward interface__ for the [[ProcNode]]s, w
          -
          +
          The Render Engine is the part of the application doing the actual video calculations. Its operations are guided by the Objects and Parameters edited by the user in [[the EDL|EDL]] and it retrieves the raw audio and video data from the [[Data backend|backend.html]]. Because the inner workings of the Render Engine are closely related to the structures used in the EDL, this design covers [[the aspect of objects placed into the EDL|MObjects]] as well.
           <<<
           ''Status'': started out as design draft in summer '07, Ichthyo is now in the middle of a implementing the foundations and main structures in C++
           * basic AssetManager working
           * currently impmenenting the Builder (&rarr;[[more|PlanningNodeCreatorTool]])
          +* intermittently working out how to deal with &rarr; [[ConfigQueries / default objects|ConfigQueryIntegration]])
           <<<
           
           !Summary
          @@ -2486,7 +2515,7 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin
           &rarr; [[Overview Render Engine|OverviewRenderEngine]]
           &rarr; BuildProcess and RenderProcess
           &rarr; [[Two Examples|Examples]] (Object diagrams) 
          -&rarr; how [[Automation]] works  {{red{to be defined in more detail}}}
          +&rarr; how [[Automation]] works
           &rarr; [[Problems|ProblemsTodo]] to be solved and notable [[design decisions|DesignDecisions]]
           &rarr; [[Implementation Details|ImplementationDetails]] {{red{WIP}}}
           
          From ed2799b76f80cf0323b945c3b50fff5ba40b9c23 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 7 Apr 2008 08:03:22 +0200 Subject: [PATCH 21/24] getting the first test of defsmanagerimpltest.cpp to run ..adding some backdoors and bashing at the mock implementation to fake the behaviour of a real resolution engine --- src/common/configrules.cpp | 17 +++++++++ src/common/configrules.hpp | 14 ++++++++ src/common/query/mockconfigrules.cpp | 11 ++++++ src/common/query/mockconfigrules.hpp | 36 ++++++++++++------- src/proc/mobject/session/defsmanager.cpp | 3 +- .../mobject/session/defsmanagerimpltest.cpp | 3 ++ .../mobject/session/defsregistryimpltest.cpp | 2 +- 7 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/common/configrules.cpp b/src/common/configrules.cpp index 504df330f..3a1ae1bdd 100644 --- a/src/common/configrules.cpp +++ b/src/common/configrules.cpp @@ -51,4 +51,21 @@ namespace lumiera SingletonSub ConfigRules::instance (typeinfo); + + + + namespace query + { + namespace // local definitions: implementing a backdoor for tests + { + string fakeBypass; + } + + void setFakeBypass(string const& q) { fakeBypass = q; } + bool isFakeBypass (string const& q) { return q == fakeBypass; } + + } // namespace query + + + } // namespace lumiera diff --git a/src/common/configrules.hpp b/src/common/configrules.hpp index 890a36a86..81da041ae 100644 --- a/src/common/configrules.hpp +++ b/src/common/configrules.hpp @@ -222,5 +222,19 @@ namespace lumiera }; + + + namespace query + { + + /** backdoor for tests: the next config query with this query string + * will magically suceed with every candidate object provided. This + * is currently necessary to get objects into the defaults manager, + * as the query system is not able to do real query resolution */ + void setFakeBypass(string const& q); + bool isFakeBypass (string const& q); + + } // namespace query + } // namespace lumiera #endif diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp index 7589d84f7..f05f3c745 100644 --- a/src/common/query/mockconfigrules.cpp +++ b/src/common/query/mockconfigrules.cpp @@ -133,6 +133,17 @@ namespace lumiera return true; } + /** for entering "valid" solutions on-the-fly from tests */ + template + bool + MockTable::set_new_mock_solution (Query& q, typename WrapReturn::Wrapper& obj) + { + answer_->insert (entry (q, obj)); + return true; + } + // generate the necessary specialisations----------------------------- + template bool MockTable::set_new_mock_solution (Query&, PPipe&); + diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index c3f6684ca..c4f56aeef 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -109,9 +109,12 @@ namespace lumiera // special cases.... template - bool detect_case (Query& q); + bool detect_case (typename WrapReturn::Wrapper&, Query& q); bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID); bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID); + + template + bool set_new_mock_solution (Query& q, typename WrapReturn::Wrapper& candidate); private: @@ -150,11 +153,14 @@ namespace lumiera bool try_special_case (Ret& solution, const Query& q) { + if (solution && isFakeBypass(q)) // backdoor for tests + return solution; + Query newQuery = q; if (is_defaults_query (newQuery)) // modified query.. return solution = Session::current->defaults (newQuery); // may cause recursion - if (detect_case (newQuery)) + if (detect_case (solution, newQuery)) return resolve (solution, newQuery); return solution = Ret(); // fail: return default-constructed empty smart ptr @@ -165,35 +171,39 @@ namespace lumiera /** Hook for treating very special cases for individual types only */ template inline bool - MockTable::detect_case (Query& q) + MockTable::detect_case (typename WrapReturn::Wrapper&, Query& q) { q.clear(); // end recursion return false; } template<> inline bool - MockTable::detect_case (Query& q) + MockTable::detect_case (WrapReturn::Wrapper& candidate, Query& q) { const string pipeID = extractID("pipe", q); const string streamID = extractID("stream", q); + + if (candidate && pipeID == candidate->getPipeID()) + return set_new_mock_solution (q, candidate); // "learn" this solution to be "valid" + if (!isnil(pipeID) && !isnil(streamID)) - return fabricate_matching_new_Pipe (q, pipeID, streamID); + return fabricate_matching_new_Pipe (q, pipeID, streamID); q.clear(); return false; } template<> inline bool - MockTable::detect_case (Query& q) + MockTable::detect_case (WrapReturn::Wrapper& candidate, Query& q) { - const string streamID = extractID("stream", q); - if (!isnil(streamID)) - return fabricate_ProcPatt_on_demand (q, streamID); + if (!isnil (extractID("make", q))) + return false; // let the query fail here, + // so the invoking factory will go ahead + // and create a new object. - // note: we don't handle the case of "make(PP), capabilities....." specially - // because either someone puts a special object into the mock table, or the - // recursive query done by the StructFactory simply fails, resulting in - // the StructFactory issuing a ProcPatt ctor call. + const string streamID = extractID("stream", q); + if (!candidate && !isnil(streamID)) + return fabricate_ProcPatt_on_demand (q, streamID); q.clear(); return false; diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp index 1d6f92e9a..c84e5116d 100644 --- a/src/proc/mobject/session/defsmanager.cpp +++ b/src/proc/mobject/session/defsmanager.cpp @@ -58,9 +58,8 @@ namespace mobject shared_ptr res; QueryHandler& typeHandler = ConfigRules::instance(); for (DefsRegistry::Iter i = defsRegistry->candidates(capabilities); - i.hasNext(); ++i ) + res = *i ; ++i ) { - shared_ptr res (*i); typeHandler.resolve (res, capabilities); if (res) return res; diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp index ff94f8a85..8aa773d5f 100644 --- a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp +++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp @@ -105,7 +105,10 @@ namespace asset ASSERT (!find (pipe2->getPipeID()), "accidental clash of random test-IDs"); // now declare that these objects should be considered "default" +lumiera::query::setFakeBypass(""); /////////////////////////////////////////////////TODO mock resolution ASSERT (Session::current->defaults.define (pipe1, Query (""))); // unrestricted default + +lumiera::query::setFakeBypass("stream("+sID+")"); ///////////////////////////////////TODO mock resolution ASSERT (Session::current->defaults.define (pipe2, Query ("stream("+sID+")"))); ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default"); diff --git a/tests/components/proc/mobject/session/defsregistryimpltest.cpp b/tests/components/proc/mobject/session/defsregistryimpltest.cpp index fa5d2c4c3..b67b6cbe2 100644 --- a/tests/components/proc/mobject/session/defsregistryimpltest.cpp +++ b/tests/components/proc/mobject/session/defsregistryimpltest.cpp @@ -201,7 +201,7 @@ namespace mobject uint d=0; uint d_prev=0; Iter23 j = reg_->candidates(Q23 ("some crap")); - for ( ; j.hasNext(); ++j ) + for ( ; *j ; ++j ) { ASSERT ( *j ); Q23 qx ((*j)->instanceID); From 0b5b9b593e3cb9f04fa36df6244d2455624a3b00 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 8 Apr 2008 03:21:24 +0200 Subject: [PATCH 22/24] DefsManagerImpl_test now running --- src/common/query/mockconfigrules.cpp | 15 ++++++++++++--- src/common/query/mockconfigrules.hpp | 13 ++++++++++--- tests/53session.tests | 5 +++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp index f05f3c745..ddc7bd57a 100644 --- a/src/common/query/mockconfigrules.cpp +++ b/src/common/query/mockconfigrules.cpp @@ -97,11 +97,10 @@ namespace lumiera // for baiscpipetest.cpp --------- answer_->insert (entry_Struct ("stream(video)")); answer_->insert (entry_Struct ("stream(teststream)")); - item (answer_, "stream(default)") = item (answer_,"stream(video)"); //TODO killme + item (answer_, "stream(default)") = item (answer_,"stream(video)"); // set up a default stream answer_->insert (entry_Struct ("pipe(master), stream(video)")); - item (answer_, "pipe(default)") = item(answer_,"pipe(master), stream(video)"); //TODO killme - TODO ("remove the default entries!!! DefaultsManager should find them automatically"); + item (answer_, "default(P)") = item(answer_,"pipe(master), stream(video)"); } @@ -121,6 +120,16 @@ namespace lumiera answer_->insert (entry (q, newPipe)); return true; // denotes query will now succeed... } + /** special case: create a new pipe for a specific stream ID */ + bool + MockTable::fabricate_new_Pipe_for_stream (Query& q, string const& streamID) + { + typedef WrapReturn::Wrapper Ptr; + + Ptr newPipe (Struct::create (Query ("make(PP), "+q))); + answer_->insert (entry (q, newPipe)); + return true; + } /** special case: create/retrieve new rocessing pattern for given stream ID... */ bool MockTable::fabricate_ProcPatt_on_demand (Query& q, string const& streamID) diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index c4f56aeef..dba328754 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -111,6 +111,7 @@ namespace lumiera template bool detect_case (typename WrapReturn::Wrapper&, Query& q); bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID); + bool fabricate_new_Pipe_for_stream (Query& q, string const& streamID); bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID); template @@ -180,6 +181,11 @@ namespace lumiera inline bool MockTable::detect_case (WrapReturn::Wrapper& candidate, Query& q) { + if (!isnil (extractID("make", q))) + return false; // let the query fail here, + // so the invoking factory will go ahead + // and create a new object. + const string pipeID = extractID("pipe", q); const string streamID = extractID("stream", q); @@ -188,6 +194,9 @@ namespace lumiera if (!isnil(pipeID) && !isnil(streamID)) return fabricate_matching_new_Pipe (q, pipeID, streamID); + + if (!candidate && !isnil(streamID)) + return fabricate_new_Pipe_for_stream (q, streamID); q.clear(); return false; @@ -197,9 +206,7 @@ namespace lumiera MockTable::detect_case (WrapReturn::Wrapper& candidate, Query& q) { if (!isnil (extractID("make", q))) - return false; // let the query fail here, - // so the invoking factory will go ahead - // and create a new object. + return false; // failure triggers creation... const string streamID = extractID("stream", q); if (!candidate && !isnil(streamID)) diff --git a/tests/53session.tests b/tests/53session.tests index dbf85bc75..64946fcf2 100644 --- a/tests/53session.tests +++ b/tests/53session.tests @@ -6,11 +6,12 @@ PLANNED "AddClip_test" AddClip_test < Date: Tue, 8 Apr 2008 04:39:07 +0200 Subject: [PATCH 23/24] Defaults Manager finished for now ...passes high level test (with help by the mock 'resolution engine') ;-) --- src/common/query/mockconfigrules.cpp | 5 ++-- src/common/query/mockconfigrules.hpp | 6 ++--- tests/53session.tests | 3 ++- .../proc/mobject/session/defsmanagertest.cpp | 26 +++++++++++++++++-- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/common/query/mockconfigrules.cpp b/src/common/query/mockconfigrules.cpp index ddc7bd57a..c8b9f774b 100644 --- a/src/common/query/mockconfigrules.cpp +++ b/src/common/query/mockconfigrules.cpp @@ -100,7 +100,7 @@ namespace lumiera item (answer_, "stream(default)") = item (answer_,"stream(video)"); // set up a default stream answer_->insert (entry_Struct ("pipe(master), stream(video)")); - item (answer_, "default(P)") = item(answer_,"pipe(master), stream(video)"); + item (answer_, "") = item(answer_,"pipe(master), stream(video)");// use as default } @@ -122,7 +122,7 @@ namespace lumiera } /** special case: create a new pipe for a specific stream ID */ bool - MockTable::fabricate_new_Pipe_for_stream (Query& q, string const& streamID) + MockTable::fabricate_just_new_Pipe (Query& q ) { typedef WrapReturn::Wrapper Ptr; @@ -147,6 +147,7 @@ namespace lumiera bool MockTable::set_new_mock_solution (Query& q, typename WrapReturn::Wrapper& obj) { + answer_->erase (q.asKey()); answer_->insert (entry (q, obj)); return true; } diff --git a/src/common/query/mockconfigrules.hpp b/src/common/query/mockconfigrules.hpp index dba328754..f5bea3264 100644 --- a/src/common/query/mockconfigrules.hpp +++ b/src/common/query/mockconfigrules.hpp @@ -111,7 +111,7 @@ namespace lumiera template bool detect_case (typename WrapReturn::Wrapper&, Query& q); bool fabricate_matching_new_Pipe (Query& q, string const& pipeID, string const& streamID); - bool fabricate_new_Pipe_for_stream (Query& q, string const& streamID); + bool fabricate_just_new_Pipe (Query& q); bool fabricate_ProcPatt_on_demand (Query& q, string const& streamID); template @@ -195,8 +195,8 @@ namespace lumiera if (!isnil(pipeID) && !isnil(streamID)) return fabricate_matching_new_Pipe (q, pipeID, streamID); - if (!candidate && !isnil(streamID)) - return fabricate_new_Pipe_for_stream (q, streamID); + if (!candidate && (!isnil(streamID) || !isnil(pipeID))) + return fabricate_just_new_Pipe (q); q.clear(); return false; diff --git a/tests/53session.tests b/tests/53session.tests index 64946fcf2..887cbdf4c 100644 --- a/tests/53session.tests +++ b/tests/53session.tests @@ -6,7 +6,8 @@ PLANNED "AddClip_test" AddClip_test <
        • retrieving a "default" object repeatedly
        • *
        • retrieving a more constrained "default" object
        • *
        • failure registers a new "default"
        • @@ -101,7 +105,7 @@ namespace asset ASSERT (pipe2 == pipe1); pipe2 = asset::Struct::create (Query ()); ASSERT (pipe2 == pipe1); - pipe2 = asset::Struct::create (Query ("default(X)")); + pipe2 = asset::Struct::create (Query ("default(P)")); ASSERT (pipe2 == pipe1); } @@ -144,12 +148,30 @@ namespace asset } + /** verify the defaults manager holds only weak refs, + * so if an object goes out of scope, any defaults entries + * are purged silently + */ void verifyRemoval (string pID) { Query query_for_pID ("pipe("+pID+")"); size_t hash; { PPipe pipe1 = Session::current->defaults (query_for_pID); +// +// this is fine but doesn't work as long as there is another entry in the mock table... +// ...for now we use hack to overwrite the reference in the mock table +// + ASSERT (3 == pipe1.use_count()); // that's the problem; it should be 2 + + QueryHandler& typeHandler = ConfigRules::instance(); + PPipe pipe2 = asset::Struct::create (pID, "quatsch"); + + typeHandler.resolve (pipe2, query_for_pID); // in the mock impl this has the side effect + ASSERT (pipe2); // of replacing the mock entry + //////////////////////////////////////////// so from now on the test works as intended.... + + ASSERT (2 == pipe1.use_count()); hash = pipe1->getID(); } // now AssetManager should hold the only ref From 97821a0fa2b453e9d7c18b8240dbf6c275cab3c6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 11 Apr 2008 06:24:27 +0200 Subject: [PATCH 24/24] Design: clarified various details regarding Track handling --- src/proc/asset/track.hpp | 9 +++- src/proc/mobject/session/track.hpp | 10 ++++ wiki/renderengine.html | 75 ++++++++++++++++++++++++++---- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/src/proc/asset/track.hpp b/src/proc/asset/track.hpp index 6a0ab6c88..a36a6242c 100644 --- a/src/proc/asset/track.hpp +++ b/src/proc/asset/track.hpp @@ -1,5 +1,5 @@ /* - TRACK.hpp - structural asset holding the configuration of a track in the EDL + TRACK.hpp - structural asset used as global track identifier Copyright (C) Lumiera.org 2008, Hermann Vosseler @@ -33,7 +33,12 @@ namespace asset /** - * Structural Asset holding the configuration of a track in the EDL + * Structural Asset using as a global identifier for placing + * some object onto a given track. Not to be confused with the "track-MO": + * To actually use a track within an EDL, we need to attach a + * Placement to the tree-of-tracks of this EDL. + * Thus, we have one global track-identifier (this class here), but + * maybe several instances (track-MO) within various EDLs */ class Track : public Struct { diff --git a/src/proc/mobject/session/track.hpp b/src/proc/mobject/session/track.hpp index d4005b97d..2b8f090b9 100644 --- a/src/proc/mobject/session/track.hpp +++ b/src/proc/mobject/session/track.hpp @@ -49,6 +49,16 @@ namespace mobject * Typically, tracks are used do make default processing pipe connections, * define a layer or pan for sound and for for disabling groups * of clips. Note tracks are grouped in a tree like fashion. + * \par + * This Media Object (often refered to as "track-MO") is allways dealt with + * locally within one EDL. Client code normally doesn't have to care for creating + * or retrieving track-MO. Rather, it referes to the global track-asset-ID. The same + * holds true when placing some other Media Object onto a track: the corresponding + * placement just refers the global trackID, while the builder automatically retrieves + * the matching track-MO for the EDL in question. If some EDL contains several instances + * (track-MO) refering to the same trackID (asset), then this causes all objects placed + * onto this track to be included several times in the resulting render nodes network + * (possibliy with varying placement properties) */ class Track : public Meta { diff --git a/wiki/renderengine.html b/wiki/renderengine.html index c70827a4f..c60cbb53b 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -792,12 +792,13 @@ TertiaryMid: #99a TertiaryDark: #667 Error: #f88
          -
          +
          Configuration Queries are requests to the system to "create or retrieve an object with //this and that // capabilities". They are resolved by a rule based system ({{red{planned feature}}}) and the user can extend the used rules for each Session. Syntactically, they are stated in ''prolog'' syntax as a conjunction (=logical and) of ''predicates'', for example {{{stream(mpeg), pipe(myPipe)}}}. Queries are typed to the kind of expected result object: {{{Query<Pipe> ("stream(mpeg)")}}} requests a pipe excepting/delivering mpeg stream data &mdash; and it depends on the current configuration what "mpeg" means. If there is any stream data producing component in the system, which advertises to deliver {{{stream(mpeg)}}}, and a pipe can be configured or connected with this component, then the [[defaults manager|DefaultsManagement]] will create/deliver a [[Pipe|PipeHandling]] object configured accordingly.
           &rarr; [[Configuration Rules system|ConfigRules]]
          +&rarr; [[accessing and integrating configuration queries|ConfigQueryIntegration]]
           
          -
          +
          * planning to embed a YAP Prolog engine
           * currently just integrated by a table driven mock
           * the baseline is a bit more clear by now (4/08)
          @@ -810,21 +811,33 @@ Error: #f88
          The key idea is that there is a Rule Base &mdash; partly contained in the session (building on a stock of standard rules supplied with the application). Now, whenever there is the need to get a new object, for adding it to the session or for using associated with another object &mdash; then instead of creating it by a direct hard wired ctor call, we issue a ConfigQuery requesting an object of the given type with some //capabilities// defined by predicates. The same holds true when loading an existing session: some objects won't be loaded back blindly, rather they will be re-created by issuing the config queries again. Especially important is the case of (re)creating a [[processing pattern|ProcPatt]] guiding how to wire up the processing pipeline for some given media. -At various places, instead of requiring a fixed set of capabilities, it is possible to request a "default configured" object instead, specifying just those capabilities we really need to be configured in a specific way. This is done by using the [Defaults Manager|DefaultsManagement] accessible on the [[Session]] interface. Such a default object query may either retrieve an already existing object instance, run further config queries, and finally result in the invocation of a factory for creating new objects &mdash; just as necessary and guarded by the rules. +At various places, instead of requiring a fixed set of capabilities, it is possible to request a "default configured" object instead, specifying just those capabilities we really need to be configured in a specific way. This is done by using the [[Defaults Manager|DefaultsManagement]] accessible on the [[Session]] interface. Such a default object query may either retrieve an already existing object instance, run further config queries, and finally result in the invocation of a factory for creating new objects &mdash; just as necessary and guarded by the rules. @@clear(left):display(block):@@ !Components and Relations [>img[participating classes|uml/fig131461.png]] +<br/> +<br/> +<br/> +<br/> +<br/> +<br/> +<br/> + +Access point is the interface {{{ConfigRules}}}, which allowes to resolve a ConfigQuery resulting in an object with properties condigured such as to fulfill the query. This whole subsystem employes quite some generic programming, because actually we don't deal with "objects", but rather with similar instantiations of the same functionality for a collection of different object types. For the purpose of resolving these queries, the actual kind of object is not so much of importance, but on the caller sinde, of course we want to deal with the result of the queries in a typesafe manner. +Examples for //participating object kinds// are [[pipes|Pipe]], [[processing patterns|ProcPatt]], effect instances, [[tags|Tag]], [[labels|Label]], [[automation data sets|AutomationData]],... @@clear(right):display(block):@@ +For this to work, we need each of the //participating object types// to implement a generic interface {{{TypeHandler}}}, which will provide actual C/C++ implementations for the predicates usable on objects of this type within the Prolog rules. The implementation has to make sure that alongside with each config query, there are additional //type constraints// to be regarded. For example, if the client code runs a {{{Query<Pipe>}}}, an additional //type guard// (implemented by a predicate {{{type(pipe)}}} has to be inserted, so only rules and facts in accordance with this type will be used for resolution. + !when querying for a [["default"|DefaultsManagement]] object [<img[colaboration when issuing a defaults query|uml/fig131845.png]] @@clear(left):display(block):@@
          -
          +
          Many features can be implemented by specifically configuring and wiring some unspecific components. Rather than tie the client code in need of some given feature to these configuration internals, in Lumiera the client can //query // for some kind of object providing the //needed capabilities. // Right from start (summer 2007), Ichthyo had the intention to implement such a feature using sort of a ''declarative database'', e.g. by embedding a Prolog system. By adding rules to the basic session configuration, users should be able to customize the semi-automatic part of Lumiera's behaviour to great extent.
           
           [[Configuration Queries|ConfigQuery]] are used at various places, when creating and adding new objects, as well when building or optimizing the render engine node network.
          @@ -843,6 +856,7 @@ Actually posing such an configuration query, for example to the [[Defaults Manag
           !Implementation
           At start and for debugging/testing, there is an ''dummy'' implementation using a map with predefined queries and answers. But for the real system, the idea is to embed a ''YAP Prolog'' engine to run the queries. This includes the task of defining and loading a set of custom predicates, so the rule system can interact with the object oriented execution environment, for example by transforming some capability predicate into virtual calls to a corresponding object interface. We need a way for objects to declare some capability predicates, together with a functor that can be executed on an object instance (and further parameters) in the cause of the evaluation of some configuration query. Type safety and diagnostics play an important role here, because effectively the rule base is a pool of code open for arbitray additions from the user session.
           &rarr; [[considerations for a Prolog based implementation|QueryImplProlog]]
          +&rarr; [[accessing and integrating configuration queries|ConfigQueryIntegration]]
           &rarr; see {{{src/common/query/mockconfigrules.cpp}}} for the table with the hard wired (mock) answers
           
           
          @@ -988,8 +1002,9 @@ After beeing processed by the Builder, we get the following Render Engine config
           [img[Example1: generated Render Engine|uml/fig129029.png]]
           
          -
          -
          This Example showes the //high level// EDL as well. This needs to be transformed into a Fixture by some facility still to be designed. Basically, each [[Placement]] needs to be queried for this to get the corresponding ExplicitPlacement. The difficult part is to handle possible Placement constraints, e.g. one clip can't be placed at a timespan covered by another clip on the same track. In the current Cinelerra2, all of this is done directly by the GUI actions.
          +
          +
          {{red{TODO: seemingly this example is slightly outdated, as the implementation of placements is now indirect via LocatingPin objects}}}
          +This Example showes the //high level// EDL as well. This needs to be transformed into a Fixture by some facility still to be designed. Basically, each [[Placement]] needs to be queried for this to get the corresponding ExplicitPlacement. The difficult part is to handle possible Placement constraints, e.g. one clip can't be placed at a timespan covered by another clip on the same track. In the current Cinelerra2, all of this is done directly by the GUI actions.
           
           The &raquo;Timeline&laquo; is a sequence of ~MObjects -- note: using the same Object instances -- but now with the calculated ExplicitPlacement, locating the clip at a given time and track. The effect is located absolutely in time as well, but because it is the same Instance, it has the pointer to the ~RelativePlacement, wich basically attaches the effect to the clip. This structure may look complicated, but is easy to process if we go "backward" and just rely on the information contained in the ExplicitPlacement.
           [img[Example2: Clip with Effect and generated Fixture for this EDL|uml/fig128901.png]]
          @@ -2283,10 +2298,10 @@ DAMAGE.
           <html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
           ***/
          -
          +
          Pipes play an central role within the Proc Layer, because for everything placed and handled within the EDL, the final goal is to get it transformed into data which can be retrieved at some pipe's exit port. Pipes are special facilities, rather like inventory, separate and not treated like all the other objects.
           We don't distinguish between "input" and "output" ports &mdash; rather, pipes are thought to be ''hooks for making connections to''. By following this line of thought, each pipe has an input side and an output side and is in itself something like a ''Bus'' or ''processing chain''. Other processing entities like effects and transitions can be placed (attached) at the pipe, resulting them to be appended to form this chain. Likewise, we can place [[wiring requests|WiringRequest]] to the pipe, meaning we want it connected so to send it's output to another destination pipe. The [[Builder]] may generate further wiring requests to fulfil the placement of other entities.
          -Thus //Pipes are the basic building blocks// of the whole render network. We distinguish ''global available'' Pipes, which are like the sum groups of a mixing console, and the ''lokal pipe'' or [[source port|ClipSourcePort]] of the individual clips, which exist only within the duration of the corresponding clip. The design //limits the possible kinds of pipes // to these two types &mdash; thus we can build local processing chains at clips and global processing chains at the global pipes of the session and that's all we can do. (because of the flexibility which comes with the concept of [[placements|Placement]], this is no real limitation)
          +Thus //Pipes are the basic building blocks// of the whole render network. We distinguish ''global available'' Pipes, which are like the sum groups of a mixing console, and the ''lokal pipe'' or [[source ports|ClipSourcePort]] of the individual clips, which exist only within the duration of the corresponding clip. The design //limits the possible kinds of pipes // to these two types &mdash; thus we can build local processing chains at clips and global processing chains at the global pipes of the session and that's all we can do. (because of the flexibility which comes with the concept of [[placements|Placement]], this is no real limitation)
           
           The GUI can connect the viewer(s) to some pipe (and moreover can use [[probe points|ProbePoint]] placed like effects and connected to some pipe), and likewise, when starting a ''render'', we get the opportunity to specify the pipes to pull the data from. Pulling data from some pipe is the (only) way to activate the render nodes network reachable from this pipe.
           
          @@ -2524,13 +2539,13 @@ The system is ''open'' inasmuch every part mirrors the structure of correspondin
           
          A data processing node within the Render Engine. Its key feature is the possibility to pull from it one (freely addressable) [[Frame]] of calculated data. Further, each ~ProcNode has the ability to be wired with other nodes and [[Parameter Providers|ParamProvider]]
           
          -
          +
          This special type of [[structural Asset|StructAsset]] represents information how to build some part of the render engine's processing nodes network. It can be thought of as a template or blueprint for construction. Most notably, it is used for creating nodes reading, decoding and delivering source media material to the render network. Each [[media Asset|MediaAsset]] has associated processing patterns describing the codecs and other transformations needed to get at the media data of this asset. (and because media assets are typically compound objects, the referred ~ProcPatt will be compound too). Obviously, the possibilities opened by using processing patterns go far beyond.
           
           Technically, a processing pattern is a list of building instructions, which will be //executed// by the [[Builder]] on the render node network under construction. This implies the possibility to define further instruction kinds when needed in future; at the moment the relevant sorts of instructions are
           * attach the given sequence of nodes to the specified point
           * recursively execute a nested ~ProcPatt
          -More specifically, a sequence of nodes is given by a sequence of prototypical effect and codec assets (and from each of them we can create the corresponding render node). And the point to attach these nodes is given by an identifier &mdash; in most cases just "{{current}}", denoting the point the builder was just working at, when he treated some MObject which in turn yielded this processing pattern in question.
          +More specifically, a sequence of nodes is given by a sequence of prototypical effect and codec assets (and from each of them we can create the corresponding render node). And the point to attach these nodes is given by an identifier &mdash; in most cases just "{{{current}}}", denoting the point the builder was just working at, when he treated some MObject which in turn yielded this processing pattern in question.
           
           Like all [[structural assets|StructAsset]], ~ProcPatt employs a special naming scheme within the asset name field, which directly mirrors its purpose and allows to bind to existing processing pattern instances when needed. The idea is letting all assets in need of a similar processing pattern refer to one shared ~ProcPatt instance. For example, within a MPEG video media asset, at some point there will be a ~ProcPatt labeled "{{{stream(mpeg)}}}". In consequence, all MPEG video will use the same pattern of node wiring. And, of course, this pattern could be changed, either globally, or by binding a single clip to some other processing pattern (for making a punctual exception from the general rule)
           
          @@ -4121,6 +4136,38 @@ Of course, we can place other ~MObjects relative to some track (that's the main
           &rarr; [[Handling of Pipes|PipeHandling]]
           
          +
          +
          What //exactly&nbsp;// is denoted by &raquo;Track&laquo; &mdash; //basically&nbsp;// a working area to group media objects placed at this track at various time positions &mdash; varies depending on context:
          +* viewed as [[structural asset|StructAsset]], tracks are nothing but global identifiers (possibly with attached tags and description)
          +* regarding the structure //within each [[EDL]],// tracks form a tree-like grid, the individual track being attached to this tree by a [[Placement]], thus setting up properties of placement (time reference origin, output connection, layer, pan) which will be inherited down to any objects located on this track and on child tracks, if not overridden more locally.
          +* with respect to //object identity,// a given track-ID can have an incarnation or manifestation as real track-object within several [[EDLs|EDL]] (meaning you could select, disable or choose for rendering all objects in any EDL placed onto this track). Moreover, the track-//object// and the //placement&nbsp;// of this track within the tree of tracks of a given EDL are two distinguishable entities (meaning a given track &mdash; with a couple of objects located on it &mdash; could be placed differently several times within the same EDL, for example with different start offset or with different layering, output mode or pan position)
          +
          +!Identification
          +Tracks thus represent a blend of several concepts, but depending on the context it is allways clear which aspect is meant. Seen as [[assets|Asset]], tracks are known by a unique track-ID, which can be either [[queried|ConfigQuery]], or directly refered to by use of the asset-ID (which is a globally known hash). Usually, all referrals are via track-ID, including when you [[place|Placement]] an object onto a track. 
          +Under some cincumstances though, especially from within the [[Builder]], we refer to a {{{Placement<Track>}}} rather, denoting a specific instantiation located at a distinct node within the tree of tracks of a given EDL. These latter referrals are always done by direct object reference, e.g. while traversing the track tree (generally there is no way to refer to a placement by name).
          +
          +!creating tracks
          +Similar to [[pipes|Pipe]] and [[processing patterns|ProcPatt]], track-assets need not be created, but rather leap into existence on first referral. On the contrary, you need to explicitly create the {{{Placement<Track>}}} for attaching it to some node within the tree of tracks of an EDL. The public access point for creating such a placement is {{{asset::Struct::create(trackID}}} (i.e. the {{{asset::StructFactory}}}), which, as a convenience shortcut, can also be accessed from the interface of the track-objects within the EDL for adding new child tracks to a given track.
          +
          +!removal
          +Deleting a Track is an operation with drastic consequences, as it will cause the removal of all child tracks and the deletion of //all object placements to this track,// which could cause the resepctive objects to go out of scope (being deleted automatically by the placements or other smart pointer classes in charge of them). On the contrary, removing a {{{Placement<Track>}}} from the tree of tracks of an EDL will just cause all objects placed onto this track to disappear (because they are no longer reachable for the build process). On re-adding it, they will show up again. (This is how things behave based on how we defined the relations of the entities to be. Another question is if we want to make this functionality available to the user. Judging from the use of Ardour's &laquo;Playlists&raquo;, such a feature may be quite helpful).
          +
          +!using Tracks
          +The '''Track Asset''' is a rather static object with limited capabilities. It's main purpose is to be a point of referral. Track assets have a description field and you may assign a list of [[tags|Tag]] to them (which could be used for binding ConfigRules).  Note that track assets are globally known within the session, they can't be limited to just one EDL (but you are allways free not to refer to some track from a given EDL). By virtue of this global nature, you can utilize the track assets to enable/disable a bunch of objects irrespective of what EDL they are located in, and probably it's a good idea to allow the selection of specific tracks for rendering.
          +Matters are quite different for the placement of a Track within the tree of tracks of a given EDL, and for placing some media object onto a given track. The track placement defines properties which will be inherited to all objects on this track and on all child tracks and thus plays a key role for wiring the objects up to some output pipe. Typically, the top level track of each EDL has a placement-to "the" video and "the" audio master pipe.
          +
          +!!!!details to note
          +* Tracks are global, but the placement of a track is local within one EDL
          +* when objects are placed onto a track, this is done by referal to the global track asset ID. But because this placement of some media object is allways inherently contained within one EDL, the //meaning&nbsp;// of such a placement is to connect to the properties of any track-placement of this given track //within this EDL.//
          +* thus tracks-as-ID appear as something global, but tracks-as-propperty-carrier appear to the user as something local and object-like.
          +* in an extreme case, you'll add two different placements of a track at different points within the track tree of an EDL. And because the objects placed onto a track refer to the global track-ID, every object "on" this track //within this EDL&nbsp;// will show up two times independently and possibly with different inherited properties (output pipe, layering mode, pan, temporal position)
          +* an interesting configuration results from the fact that you can use an EDL as a [["meta clip" or "virtual clip"|VirtualClip]] within another EDL. In this case, you'll probably configure the tracks of the "inner" EDL such as to send their output not to a global pipe but rather to the [[source ports|ClipSourcePort]] of the virtual clip (which are effectively local pipes). Thus, within the "outer" EDL, you could attach effects to the virutal clip, combine it with transitions and place it onto another track, and any missing properties of this latter placement are to be resolved within the "outer" EDL <br/>(it would be perfectly legal to construct a contrieved example when using the same track-ID within "inner" and the "outer" EDL. Because the Placement of this track will probably be different in the both ~EDLs, the behaviour of this placement could be quite different in the "inner" and the "outer" EDL. All of this may seem weird when discussed here in a textual and logical manner, but when viewed within the context and meaning of the various entities of the application, it's rather the way you'd expect it to be: you work locally and things behave as defined locally)
          +* note further, the root of the tree of tracks within each EDL //is itself again a //{{{Placement<Track>}}}. There is no necessitiy for doing it this way, but it seemed more stright forward and logical to Ichthyo, as it allowes for an easy way of configuring some things (like ouput connections) as a default within one EDL. As every track can have a list of child tracks, you'll get the "list of tracks" you'd expect.
          +* a nice consequence of the latter is: if you create a new EDL, it automatically gets one top-level track to start with, and this track will get a default configured placement (according to what is defined as [[default|DefaultsManagement]] within the current ConfigRules) &mdash; typically starting at t=0 and being plugged into the master video and master audio pipe
          +* nothing prevents us from putting several objects at the same temporal location within one track. If the builder can't derive any additional layering information (which could be provided by some other configuration rules), then //there is no layering precedence// &mdash; simply the object encountered first (or last) wins.
          +* obviously, one wants the __edit function__ used to create such an overlapping placement&nbsp; also to create an [[transition|TransitionsHandling]] between the overlapping objects. Meaning this edit function will automatically create an transition processor object and provide it with a placement such as to attach it to the region of overlap.
          +
          +
          ''towards a definition of »Track«''. We don't want to tie ourself to some naive and overly simplistic definition, just because it is convenient. For classical (analogue) media, tracks are physical entities dictated by the nature of the process by which the media works. Especially, Tape machines have read/writing heads, which creates fixed tracks to which to route the signals. This is a practical geometric necessity. For digital media, there is no such necessity. We are bound primarily by the editor's habits of working.
           
          @@ -4164,6 +4211,14 @@ Using transitions is a very basic task and thus needs viable support by the GUI.
           Because of this experience, ichthyo wants to support a more general case of transitions, which have N output connections, behave similar to their "simple" counterpart, but leave out the mixing step. As a plus, such transitions can be inserted at the source ports of N clips or between any intermediary or final output pipes as well. Any transition processor capable of handling this situation should provide some flag, in order to decide if he can be placed in such a manner. (wichin the builder, encountering a  inconsistently placed transition is just an [[building error|BuildingError]])
           
          +
          +
          A ''~Meta-Clip'' or ''Virtual Clip'' (both are synonymous) denotes a clip which doesn't just pull media streams out of a source media asset, but rather provides the results of rendering a complete sub-network. In all other respects it behaves exactly like a "real" clip, i.e. it has [[source ports|ClipSourcePort]], can have attached effects (thus forming a local render pipe) and can be placed and combined with other clips. Depending on what is wired to the source ports, we get two flavours:
          +* a __placeholder clip__ has no "embedded" content. Rather, by virtue of placements and wiring requests, the output of some other pipe somewhere in the session will be wired to the clip's source ports. Thus, pulling data from this clip will effectively pull from these source pipes wired to it.
          +* a __nested EDL__ is like the other ~EDLs in the Session, just that any missing placement properties will be derived from the Virtual Clip, which is thought as to "contain" the objects of the nested EDL. Typically, you'd also [[configure the tracks|TrackHandling]] of the "inner" EDL such as to connect any output to the source ports of the Virtual Clip.
          +
          +Like any "real" clip, Virtual Clips have a start offset and a length, which will simply translate into an offset of the frame number pulled from the Virtual Clip's source connection or embedded EDL, making it possible to cut, splice, trim and roll them as usual. This of course implies we can have several instances of the same virtual clip with different start offset and length placed differently. The only limitation is that we can't handle cyclic dependencies for pulling data (which has to be detected and flagged as an error by the builder)
          +
          +
          The ''Visitor Pattern'' is a special form of //double dispatch// &mdash; selecting the function actually to be executed at runtime based both on the concrete type of some tool object //and // the target this tool is applied to. The rationale is to separate some specific implementation details from the basic infrastructure and the global interfaces, which can be limited to describe the fundamental properties and operations, while all details relevant only for some specific sub-problem can be kept together encapsulated in a tool implementation class. Typically, there is some iteration mechanism, allowing to apply these tools to all objects in a given container, a collection or object graph, without knowing the exact type of the target and tool objects. See the [[Visitor Pattern design discussion|VisitorUse]]