diff --git a/src/proc/mobject/session/placement-index-query-resolver.cpp b/src/proc/mobject/session/placement-index-query-resolver.cpp new file mode 100644 index 000000000..efc6224f8 --- /dev/null +++ b/src/proc/mobject/session/placement-index-query-resolver.cpp @@ -0,0 +1,365 @@ +/* + placementIndexQueryResolver - using PlacementIndex to resolve scope queries + + Copyright (C) Lumiera.org + 2009, 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 "pre.hpp" +#include "proc/mobject/session/placement-index-query-resolver.hpp" +#include "proc/mobject/session/scope-query.hpp" +#include "proc/mobject/placement.hpp" +#include "proc/mobject/session/clip.hpp" +#include "proc/mobject/session/effect.hpp" + +#include +#include + + +namespace mobject { +namespace session { + + using boost::scoped_ptr; + + + + typedef PlacementIndex::ID PID; + typedef Goal::QueryID QueryID; + typedef QueryID const& QID; + + typedef PlacementIndex::iterator PIter; + + /** @note all of this search implementation works on Placement refs. + * Only finally, when accessing the iterator, a downcast to a more specific + * object type may happen. In this case, there is also a ContentFilter to + * prevent accessing a placement of a non-matching object type, by trying + * a dynamic cast beforehand. The instantiation of a suitably typed + * PlacementIndexQueryResolver::resolutionFunction ensures that these + * types match reliably the type of the issued query. + */ + typedef PlacementMO Pla; + + + + /** Interface: strategy for exploring the structure */ + class Explorer + { + public: + virtual ~Explorer() { }; + virtual bool exhausted () =0; + virtual Pla& step () =0; + }; + + + + /* ==== special strategies to choose from ==== */ + + /** + * Strategy: explore the structure + * just by following the given iterator; + * usually this yields an element's children + */ + class ChildExplorer + : public Explorer + { + PIter tip_; + + bool + exhausted() + { + return !tip_; + } + + Pla& + step () + { + REQUIRE (tip_); + Pla& pos = *tip_; + ++tip_; + return pos; + } + + public: + ChildExplorer(PIter start) + : tip_(start) + { } + }; + + /** + * Strategy: explore the structure depth first. + * After returning an element, delve into the scope + * defined by this element and so on recursively. + */ + class DeepExplorer + : public Explorer + { + PPIdx index_; + std::stack scopes_; + + bool + exhausted() + { + while (!scopes_.empty() && !scopes_.top()) + scopes_.pop(); + return scopes_.empty(); + } + + Pla& + step () + { + REQUIRE (!scopes_.empty() && scopes_.top()); + Pla& pos = *scopes_.top(); + ++scopes_.top(); + scopes_.push(index_->getReferrers(pos.getID())); + return pos; + } + + public: + DeepExplorer(PIter start, PPIdx idx) + : index_(idx) + { + scopes_.push(start); + } + }; + + + /** + * Strategy: explore the structure upwards, + * ascending until reaching the root element. + */ + class UpExplorer + : public Explorer + { + PPIdx index_; + Pla* tip_; + + bool + exhausted() + { + return !tip_; + } + + Pla& + step () + { + REQUIRE (tip_); + Pla& pos = *tip_; + tip_ = &index_->getScope(*tip_); + if (tip_ == &pos) + tip_ = 0; + return pos; + } + + public: + UpExplorer(Pla& start, PPIdx idx) + : index_(idx) + , tip_(&start) + { } + }; + + + typedef function ContentFilter; + typedef function ExplorerBuilder; + + + /** + * on query, an individual result set is prepared + * to be explored by the invoking client code. + * It is built wrapping the low-level scope iterator + * obtained from the index, controlled by an + * exploration strategy. Embedded into the iterator + * there is a smart-ptr managing this ResultSet. + */ + class ResultSet + : public Resolution + { + ContentFilter acceptable_; + ExplorerBuilder buildExploartion_; + scoped_ptr explore_; + + + virtual Result + prepareResolution() + { + explore_.reset (buildExploartion_()); + + Result cursor; + exploreNext (cursor); + return cursor; + } + + virtual void + nextResult(Result& pos) + { + exploreNext (pos); + } + + void + exploreNext (Result& res) + { + typedef Query::Cursor Cursor; + Cursor& cursor = static_cast (res); + + while (!explore_->exhausted() ) + { + Pla& elm (explore_->step()); + if (acceptable_(elm)) + { + cursor.point_at (elm); + return; + } + } + + ASSERT (explore_->exhausted()); + cursor.point_at (0); + } + + public: + ResultSet (ExplorerBuilder b + ,ContentFilter a) + : acceptable_(a) + , buildExploartion_(b) + , explore_() + { } + }; + + + + + namespace { // Helpers for wiring up a suitable resolutionFunction.... + + + bool acceptAllObjects_(Pla) { return true; } + + /** use filter predicate provided by the concrete query */ + template + ContentFilter + getContentFilter (QUERY query) + { + return query.contentFilter(); + } + + /** especially queries for MObjects need not be filtered */ + ContentFilter + getContentFilter (ScopeQuery) + { + static ContentFilter acceptAllObjects = bind (&acceptAllObjects_, _1); + return acceptAllObjects; + } + + /** shortcut for a suitable QueryID */ + template + QueryID + whenQueryingFor() + { + QueryID qID = {Goal::DISCOVERY, getResultTypeID >()}; + return qID; + } + + } //(END) Helpers + + + + + PlacementIndexQueryResolver::PlacementIndexQueryResolver (PPIdx theIndex) + : index_(theIndex) + { + defineHandling(); + defineHandling(); + defineHandling(); + /////////////////////////////////////////////////////////////////TICKET #414 + } + + bool + PlacementIndexQueryResolver::canHandleQuery(QID qID) const + { + return qID.kind == Goal::DISCOVERY + &&( qID.type == getResultTypeID >() + ||qID.type == getResultTypeID >() + ||qID.type == getResultTypeID >() + /////////////////////////////////////////////////////////////////TICKET #414 + ); + } + + + template + void + PlacementIndexQueryResolver::defineHandling() + { + installResolutionCase( whenQueryingFor() + , bind (&PlacementIndexQueryResolver::resolutionFunction, + this, _1 ) + ); + } + + + /** an instance of this function is installed for each specifically typed + * kind of query to be handled. This allows the client code to retrieve + * just placements of this special type (e.g. Placement) in a + * typesafe manner. We ensure a suitable ContentFilter will be installed, + * dropping any other query results (of other type) before the point + * where they may get casted to the desired result type. The key for + * picking the right resolutionFunction is getResultTypeID() + */ + template + Resolution* + PlacementIndexQueryResolver::resolutionFunction (Goal const& goal) + { + QID qID = goal.getQID(); + REQUIRE (qID == whenQueryingFor()); + REQUIRE (INSTANCEOF(ScopeQuery, &goal)); + + ScopeQuery const& query = static_cast const&> (goal); + Literal direction = query.searchDirection(); + PID scopeID = query.searchScope().getID(); ///////////////////////////////TICKET #411 + + return new ResultSet( bind (&PlacementIndexQueryResolver::setupExploration, + this, scopeID, direction) + , getContentFilter(query) + ); + } + + + /** the builder function used to set up the actual result set object + * when issuing the query. It is preconfigured by the resolutionFunction. + * The object returned from this function is taken over and managed by a + * smart-ptr, which is embedded within the iterator given to the client. + */ + Explorer* + PlacementIndexQueryResolver::setupExploration (PID startID, Literal direction) + { + if (direction == "contents") + return new DeepExplorer(index_->getReferrers(startID), index_); + + else if (direction == "children") + return new ChildExplorer(index_->getReferrers(startID)); + + else if (direction == "parents") + return new UpExplorer(index_->getScope(startID),index_); + + else if (direction == "path") + return new UpExplorer(index_->find(startID),index_); + + else + throw lumiera::error::Invalid("unknown query direction"); //////TICKET #197 + } + + + +}} // namespace mobject::session diff --git a/src/proc/mobject/session/placement-index-query-resolver.hpp b/src/proc/mobject/session/placement-index-query-resolver.hpp index 117c9dc6b..cbb847da8 100644 --- a/src/proc/mobject/session/placement-index-query-resolver.hpp +++ b/src/proc/mobject/session/placement-index-query-resolver.hpp @@ -22,7 +22,29 @@ /** @file placement-index-query-resolver.hpp - ** TODO WIP-WIP + ** Implementing resolution of "discover contents"-queries based on PlacementIndex. + ** This wrapper adds a service to resolve queries for exploring the contents or + ** the parent path of a given scope; the actual implementation is based on the + ** basic operations provided by the PlacementIndex; usually this wrapper is + ** instantiated as one of the SessionServices for use by Proc-Layer internals. + ** The PlacementIndex to use for the implementation is handed in to the ctor. + ** + ** As any of the QueryResolver services, the actual resolution is completely + ** decoupled from the querying client code, which retrieves the query results + ** through an iterator. Parametrisation is transmitted to the resolver using a + ** special subclass of Goal, a ScopeQuery. Especially, besides a filter to apply + ** on the results to retrieve, the direction and way* to search can be parametrised: + ** - ascending to the parents of the start scope + ** - enumerating the immediate child elements of the scope + ** - exhaustive depth-first search to get any content of the scope + ** + ** \par how the actual result set is created + ** On initialisation, a table with preconfigured resolution functions is built, + ** in order to re-gain the fully typed context when receiving a query. From within + ** this context, the concrete Query instance can be investigated to define a + ** constructor function for the actual result set, to determine the way how further + ** results will be searched and extracted. The further exploration is driven by the + ** client pulling values from the iterator until exhaustion. ** ** @see PlacementRef ** @see PlacementIndex_test @@ -35,337 +57,55 @@ #define MOBJECT_SESSION_PLACEMENT_INDEX_QUERY_RESOLVER_H //#include "pre.hpp" -//#include "proc/mobject/session/locatingpin.hpp" -//#include "proc/asset/pipe.hpp" -//#include "lib/util.hpp" -//#include "lib/factory.hpp" -//#include "proc/mobject/placement.hpp" #include "proc/mobject/session/placement-index.hpp" #include "proc/mobject/session/query-resolver.hpp" -#include "proc/mobject/session/scope-query.hpp" +#include "lib/symbol.hpp" -///////////TODO -#include "proc/mobject/session/clip.hpp" -#include "proc/mobject/session/effect.hpp" - -//#include -//#include -#include -//#include -#include namespace mobject { - -// class MObject; /////////////////////////////////////???? - namespace session { -// using lib::factory::RefcountFac; -// using std::tr1::shared_ptr; - using boost::scoped_ptr; -// using std::vector; - - class Clip; - class Effect; + using lib::Literal; + class Explorer; /** - * TODO type comment + * Wrapper for the PlacementIndex, allowing to resolve scope contents discovery + * - handles queries for placements of + * * MObjcect + * * Clip + * * Effect + * - is able to process + * * ContentsQuery for retrieving full contents of a scope depth-first + * * PathQuery for retrieving all the parent scopes + * * more generally, any ScopeQuery with these properties, in some variations */ class PlacementIndexQueryResolver : public session::QueryResolver { PPIdx index_; - typedef PlacementIndex::ID PID; - typedef Goal::QueryID QueryID; - typedef QueryID const& QID; - typedef PlacementIndex::iterator PIter; + virtual bool canHandleQuery(Goal::QueryID const&) const; + + virtual operator string() const { return "PlacementIndex"; } - operator string() const { return "PlacementIndex"; } - - - /** Interface: strategy for exploring the structure */ - class Explorer - { - public: - virtual ~Explorer() { }; - virtual bool exhausted () =0; - virtual PlacementMO& step () =0; - }; - - /** - * Strategy: explore the structure - * just by following the given iterator; - * usually this yields an element's children - */ - class ChildExplorer - : public Explorer - { - PIter tip_; - - bool - exhausted() - { - return !tip_; - } - - PlacementMO& - step () - { - REQUIRE (tip_); - PlacementMO& pos = *tip_; - ++tip_; - return pos; - } - - public: - ChildExplorer(PIter start) - : tip_(start) - { } - }; - - /** - * Strategy: explore the structure depth first. - * After returning an element, delve into the scope - * defined by this element and so on recursively. - */ - class DeepExplorer - : public Explorer - { - PPIdx index_; - std::stack scopes_; - - bool - exhausted() - { - while (!scopes_.empty() && !scopes_.top()) - scopes_.pop(); - return scopes_.empty(); - } - - PlacementMO& - step () - { - REQUIRE (!scopes_.empty() && scopes_.top()); - PlacementMO& pos = *scopes_.top(); - ++scopes_.top(); - scopes_.push(index_->getReferrers(pos.getID())); - return pos; - } - - public: - DeepExplorer(PIter start, PPIdx idx) - : index_(idx) - { - scopes_.push(start); - } - }; - - - /** - * Strategy: explore the structure upwards, - * ascending until reaching the root element. - */ - class UpExplorer - : public Explorer - { - PPIdx index_; - PlacementMO* tip_; - - bool - exhausted() - { - return !tip_; - } - - PlacementMO& - step () - { - REQUIRE (tip_); - PlacementMO& pos = *tip_; - tip_ = &index_->getScope(*tip_); - if (tip_ == &pos) - tip_ = 0; - return pos; - } - - public: - UpExplorer(PlacementMO& start, PPIdx idx) - : index_(idx) - , tip_(&start) - { } - }; - - typedef PlacementMO Pla; - typedef function ContentFilter; - typedef function ExplorerBuilder; - - - /** - * on query, an individual result set is prepared - * to be explored by the invoking client code. - * It is built wrapping the low-level scope iterator - * obtained from the index, controlled by an - * exploration strategy. - */ - struct ResultSet - : Resolution - { - - ContentFilter acceptable_; - ExplorerBuilder buildExploartion_; - scoped_ptr explore_; - - void - exploreNext (Result& res) - { - typedef Query::Cursor Cursor; - Cursor& cursor = static_cast (res); - - while (!explore_->exhausted() ) - { - Pla& elm (explore_->step()); - if (acceptable_(elm)) - { - cursor.point_at (elm); - return; - } - } - - ASSERT (explore_->exhausted()); - cursor.point_at (0); - } - - Result - prepareResolution() - { - explore_.reset (buildExploartion_()); - - Result cursor; - exploreNext (cursor); - return cursor; - } - - void - nextResult(Result& pos) - { - exploreNext (pos); - } - - public: - ResultSet (ExplorerBuilder b - ,ContentFilter a) - : acceptable_(a) - , buildExploartion_(b) - , explore_() - { } - }; - - Explorer* - setupExploration (PID startID, Literal direction) - { - if (direction == "contents") - return new DeepExplorer(index_->getReferrers(startID), index_); - - else if (direction == "children") - return new ChildExplorer(index_->getReferrers(startID)); - - else if (direction == "parents") - return new UpExplorer(index_->getScope(startID),index_); - - else if (direction == "path") - return new UpExplorer(index_->find(startID),index_); - - else - throw lumiera::error::Invalid("unknown query direction"); //////TICKET #197 - } + Explorer* setupExploration (PlacementIndex::ID startID, Literal direction); template - void - defineHandling() - { - installResolutionCase( whenQueryingFor() - , bind (&PlacementIndexQueryResolver::resolutionFunction, - this, _1 ) - ); - } + void defineHandling(); template - Resolution* - resolutionFunction (Goal const& goal) - { - QID qID = goal.getQID(); - REQUIRE (qID == whenQueryingFor()); - REQUIRE (INSTANCEOF(ScopeQuery, &goal)); - ScopeQuery const& query = static_cast const&> (goal); - - Literal direction = query.searchDirection(); - PID scopeID = query.searchScope().getID(); ///////////////////////////////TICKET #411 - - return new ResultSet( bind (&PlacementIndexQueryResolver::setupExploration, - this, scopeID, direction) - , getContentFilter(query) - ); - } + Resolution* resolutionFunction (Goal const& goal); - template - ContentFilter - getContentFilter (QUERY query) ///< use filter predicate provided by the concrete query - { - return query.contentFilter(); - } - - ContentFilter - getContentFilter (ScopeQuery) ///< especially queries for MObjects need not be filtered - { - static ContentFilter acceptAllObjects = bind (&acceptAllObjects_, _1); - return acceptAllObjects; - } - - static bool acceptAllObjects_(Pla) { return true; } - - - - template - static QueryID - whenQueryingFor() - { - QueryID qID = {Goal::DISCOVERY, getResultTypeID >()}; - return qID; - } - - virtual bool - canHandleQuery(QID qID) const - { - return qID.kind == Goal::DISCOVERY - &&( qID.type == getResultTypeID >() - ||qID.type == getResultTypeID >() - ||qID.type == getResultTypeID >() - ///////////////////////////////////////TICKET #414 - ); - } public: - PlacementIndexQueryResolver (PPIdx theIndex) - : index_(theIndex) - { - defineHandling(); - defineHandling(); - defineHandling(); - ///////////////////////////////////////TICKET #414 - } + PlacementIndexQueryResolver (PPIdx theIndex); }; - - - - - }} // namespace mobject::session #endif