Implement ScopePath functionality. Closes #322
This commit is contained in:
parent
27390b5732
commit
36e342fc11
4 changed files with 160 additions and 52 deletions
|
|
@ -22,49 +22,90 @@
|
|||
|
||||
|
||||
#include "proc/mobject/session/scope-path.hpp"
|
||||
#include "proc/mobject/session/scope-locator.hpp"
|
||||
#include "proc/mobject/session/session-service-explore-scope.hpp"
|
||||
#include "proc/mobject/mobject.hpp"
|
||||
#include "lib/itertools.hpp"
|
||||
#include "lib/symbol.hpp"
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/util.hpp"
|
||||
//#include "proc/mobject/session/track.hpp"
|
||||
|
||||
//#include "proc/mobject/placement.hpp"
|
||||
//#include "proc/mobject/session/mobjectfactory.hpp"
|
||||
//#include "proc/asset/track.hpp"
|
||||
#include <tr1/functional>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mobject {
|
||||
namespace session {
|
||||
|
||||
using std::reverse;
|
||||
|
||||
using std::tr1::bind;
|
||||
using std::tr1::function;
|
||||
using std::tr1::placeholders::_1;
|
||||
using lib::append_all;
|
||||
using util::and_all;
|
||||
|
||||
using namespace lumiera;
|
||||
|
||||
|
||||
/** by default, a scope path just contains
|
||||
* the root scope of the current session (PlacementIndex).
|
||||
* @note invoking this function accesses the session and thus
|
||||
* may cause an empty default session to be created.
|
||||
LUMIERA_ERROR_DEFINE (EMPTY_SCOPE_PATH, "Placement scope not locatable (empty model path)");
|
||||
|
||||
|
||||
|
||||
namespace { // Helpers and shortcuts....
|
||||
|
||||
/** issue a query to discover the path to root,
|
||||
* starting with the given scope */
|
||||
inline ScopeQuery<MObject>::iterator
|
||||
discoverScopePath (Scope const& leaf)
|
||||
{
|
||||
return ScopeLocator::instance().locate<MObject> (leaf);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
___check_notBottom (const ScopePath *path, lib::Literal operation_descr)
|
||||
{
|
||||
REQUIRE (path);
|
||||
if (path->empty())
|
||||
throw error::Invalid (operation_descr+" an empty placement scope path"
|
||||
, LUMIERA_ERROR_EMPTY_SCOPE_PATH);
|
||||
}
|
||||
}//(End) helpers
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create an \em empty path.
|
||||
* By default, a scope path just contains
|
||||
* the root scope of the current session (PlacementIndex).
|
||||
* @note invoking this function accesses the session and thus
|
||||
* may cause an empty default session to be created.
|
||||
*/
|
||||
ScopePath::ScopePath ()
|
||||
: path_()
|
||||
{
|
||||
UNIMPLEMENTED ("default path just containing root");
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
/** When creating a path to a given (leaf) scope,
|
||||
* the complete sequence of nested scopes leading to
|
||||
* this special scope is discovered, using the query service
|
||||
* exposed by the session (through ScopeLocator).
|
||||
* @note when locating the default (invalid) scope,
|
||||
* a special empty ScopePath is created
|
||||
* @throw error::Invalid if the given target scope
|
||||
* can't be connected to the (implicit) root
|
||||
/**
|
||||
* When creating a path to a given (leaf) scope,
|
||||
* the complete sequence of nested scopes leading to
|
||||
* this special scope is discovered, using the query service
|
||||
* exposed by the session (through ScopeLocator).
|
||||
* @note when locating the default (invalid) scope,
|
||||
* a special empty ScopePath is created
|
||||
* @throw error::Invalid if the given target scope
|
||||
* can't be connected to the (implicit) root
|
||||
*/
|
||||
ScopePath::ScopePath (Scope const& leaf)
|
||||
: path_()
|
||||
{
|
||||
UNIMPLEMENTED ("initialise by discovering complete scope sequence");
|
||||
if (!leaf.isValid()) return; // invalid leaf defines invalid path....
|
||||
|
||||
append_all (discoverScopePath(leaf), path_);
|
||||
reverse (path_.begin(), path_.end());
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -72,54 +113,66 @@ namespace session {
|
|||
const ScopePath ScopePath::INVALID = ScopePath(Scope());
|
||||
|
||||
|
||||
/* == Diagnostics == */
|
||||
|
||||
/** a \em valid path consists of more than just the root element.
|
||||
* @note contrary to this, an \em empty path doesn't even contain a root element
|
||||
*/
|
||||
bool
|
||||
inline bool
|
||||
ScopePath::isValid() const
|
||||
{
|
||||
UNIMPLEMENTED ("validity self check: more than just root");
|
||||
return (0 < length())
|
||||
#ifndef NDEBUG
|
||||
&& hasValidRoot()
|
||||
#endif
|
||||
;
|
||||
}
|
||||
|
||||
/** an empty path doesn't even contain a root element.
|
||||
* Many operations throw when invoked on such a path.
|
||||
* Navigating up from an root path creates an empty path.
|
||||
*/
|
||||
bool
|
||||
ScopePath::empty() const
|
||||
ScopePath::hasValidRoot() const
|
||||
{
|
||||
UNIMPLEMENTED ("empty == no elements, even no root!");
|
||||
REQUIRE (0 < length());
|
||||
return path_[0] == currModelRoot();
|
||||
}
|
||||
|
||||
PlacementMO const&
|
||||
ScopePath::currModelRoot() const
|
||||
{
|
||||
return SessionServiceExploreScope::getScopeRoot();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* == Relations == */
|
||||
|
||||
Scope&
|
||||
Scope const&
|
||||
ScopePath::getLeaf() const
|
||||
{
|
||||
UNIMPLEMENTED ("access end node of current path");
|
||||
___check_notBottom (this, "Inspecting");
|
||||
return path_.back();
|
||||
}
|
||||
|
||||
|
||||
/** verify the scope in question is equivalent
|
||||
* to our leaf scope. Equivalence of scopes means
|
||||
* they are defined by the same scope top placement,
|
||||
* i.e. registered with the same Placement-ID.
|
||||
* i.e. registered with the same Placement-ID.
|
||||
*/
|
||||
bool
|
||||
ScopePath::endsAt(Scope const& aScope) const
|
||||
{
|
||||
UNIMPLEMENTED ("verify the scope in question is identical (same ID) to our leaf scope");
|
||||
return aScope == getLeaf();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ScopePath::contains (Scope const& aScope) const
|
||||
{
|
||||
UNIMPLEMENTED ("containment check");
|
||||
for (iterator ii = this->begin(); ii; ++ii)
|
||||
if (aScope == *ii)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -144,52 +197,83 @@ namespace session {
|
|||
ScopePath
|
||||
commonPrefix (ScopePath const& path1, ScopePath const& path2)
|
||||
{
|
||||
UNIMPLEMENTED ("determine the common prefix, if any");
|
||||
typedef std::vector<Scope>::iterator VIter;
|
||||
ScopePath prefix (ScopePath::INVALID);
|
||||
uint len = std::min (path1.length(), path2.length());
|
||||
for (uint pos = 0; pos<len; ++pos)
|
||||
if (path1.path_[pos] == path2.path_[pos])
|
||||
prefix.appendScope (path1.path_[pos]);
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
bool
|
||||
disjoint (ScopePath const& path1, ScopePath const& path2)
|
||||
{
|
||||
if (path1.empty() || path2.empty()) return false;
|
||||
|
||||
|
||||
return (path1.isValid() && path2.isValid())
|
||||
&& (path1.path_[1] != path2.path_[1]) // no common prefix
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* == Mutations == */
|
||||
|
||||
void
|
||||
ScopePath::clear()
|
||||
{
|
||||
UNIMPLEMENTED ("reset the current path to default state (just root)");
|
||||
path_.clear();
|
||||
path_.push_back (currModelRoot());
|
||||
|
||||
ENSURE (!empty());
|
||||
ENSURE (!isValid());
|
||||
ENSURE ( hasValidRoot());
|
||||
}
|
||||
|
||||
|
||||
Scope&
|
||||
ScopePath::moveUp()
|
||||
{
|
||||
UNIMPLEMENTED ("navigate one level up, then return leaf");
|
||||
___check_notBottom (this, "Navigating");
|
||||
static Scope invalidScope;
|
||||
|
||||
path_.resize (length()-1);
|
||||
|
||||
if (empty()) return invalidScope;
|
||||
else return path_.back();
|
||||
}
|
||||
|
||||
|
||||
Scope&
|
||||
ScopePath::goRoot()
|
||||
{
|
||||
UNIMPLEMENTED ("navigate up to the root scope");
|
||||
___check_notBottom (this, "Navigating");
|
||||
ENSURE (hasValidRoot());
|
||||
|
||||
path_.resize (1);
|
||||
return path_.back();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopePath::navigate (Scope const& target)
|
||||
{
|
||||
___check_notBottom (this, "Navigating");
|
||||
*this = ScopePath(target); //////////////////////////////TICKET #424
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScopePath::appendScope (Scope const& child) ///< @internal backdoor for #commonPrefix
|
||||
{
|
||||
path_.push_back (child);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}} // namespace mobject::session
|
||||
|
|
|
|||
|
|
@ -83,22 +83,19 @@
|
|||
#ifndef MOBJECT_SESSION_SCOPE_PATH_H
|
||||
#define MOBJECT_SESSION_SCOPE_PATH_H
|
||||
|
||||
//#include "proc/mobject/mobject.hpp"
|
||||
//#include "proc/mobject/placement-ref.hpp"
|
||||
#include "proc/mobject/session/scope.hpp"
|
||||
#include "lib/bool-checkable.hpp"
|
||||
#include "lib/iter-adapter.hpp"
|
||||
|
||||
#include <vector>
|
||||
//#include <string>
|
||||
|
||||
using std::vector;
|
||||
//using std::string;
|
||||
|
||||
namespace mobject {
|
||||
namespace session {
|
||||
|
||||
|
||||
LUMIERA_ERROR_DECLARE (EMPTY_SCOPE_PATH); ///< Placement scope not locatable (empty model path)
|
||||
|
||||
|
||||
/**
|
||||
* Sequence of nested scopes within the high-level model.
|
||||
|
|
@ -113,7 +110,7 @@ namespace session {
|
|||
class ScopePath
|
||||
: public lib::BoolCheckable<ScopePath>
|
||||
{
|
||||
vector<Scope> path_;
|
||||
std::vector<Scope> path_;
|
||||
|
||||
typedef vector<Scope> _VType;
|
||||
typedef _VType::const_reverse_iterator _VIter;
|
||||
|
|
@ -139,7 +136,7 @@ namespace session {
|
|||
|
||||
|
||||
/* == relations == */
|
||||
Scope& getLeaf() const;
|
||||
Scope const& getLeaf() const;
|
||||
bool endsAt (Scope const&) const;
|
||||
bool contains (Scope const&) const;
|
||||
bool contains (ScopePath const&) const;
|
||||
|
|
@ -156,8 +153,17 @@ namespace session {
|
|||
Scope& goRoot();
|
||||
void navigate (Scope const&);
|
||||
|
||||
|
||||
private:
|
||||
bool hasValidRoot() const;
|
||||
PlacementMO const& currModelRoot() const;
|
||||
void appendScope (Scope const&);
|
||||
};
|
||||
///////////////////////////TODO currently just fleshing the API
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline bool
|
||||
|
|
@ -186,6 +192,16 @@ namespace session {
|
|||
return path_.size();
|
||||
}
|
||||
|
||||
/** an empty path doesn't even contain a root element.
|
||||
* Many operations throw when invoked on such a path.
|
||||
* Navigating up from an root path creates an empty path.
|
||||
*/
|
||||
inline bool
|
||||
ScopePath::empty() const
|
||||
{
|
||||
return path_.empty();
|
||||
}
|
||||
|
||||
|
||||
inline ScopePath::iterator
|
||||
ScopePath::begin() const
|
||||
|
|
|
|||
|
|
@ -57,6 +57,14 @@ namespace session {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Implementation-level service for issuing contents/discovery queries.
|
||||
* Actually, the implementation of this service is backed by the PlacementIndex
|
||||
* within the current session, but this link isn't disclosed to client code.
|
||||
* The exposed QueryResolver is able to handle typed DiscoveryQuery instances.
|
||||
* Usually, on invocation, a search scope needs to be specified. The root Scope
|
||||
* of the current model (session datastructure) can be obtained by #getScopeRoot
|
||||
*/
|
||||
struct SessionServiceExploreScope
|
||||
{
|
||||
static QueryResolver const& getResolver();
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace session {
|
|||
}
|
||||
|
||||
|
||||
/** */
|
||||
/** Re-define the implicit PlacementIndex temporarily, e.g. for unit tests. */
|
||||
void
|
||||
SessionServiceMockIndex::reset_PlacementIndex (PPIdx const& alternativeIndex)
|
||||
{
|
||||
|
|
@ -69,7 +69,7 @@ namespace session {
|
|||
}
|
||||
|
||||
|
||||
/** */
|
||||
/** @return resolver for DiscoveryQuery instances, actually backed by PlacementIndex */
|
||||
QueryResolver const&
|
||||
SessionServiceExploreScope::getResolver()
|
||||
{
|
||||
|
|
@ -77,7 +77,7 @@ namespace session {
|
|||
}
|
||||
|
||||
|
||||
/** */
|
||||
/** @return root scope of the current model (session datastructure) */
|
||||
PlacementMO&
|
||||
SessionServiceExploreScope::getScopeRoot()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue