/* SCOPE-PATH.hpp - logical access path down from Session root 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. */ /** @file scope-path.hpp ** An Object representing a sequence of nested scopes within the Session. ** MObjects are being attached to the model by Placements, and each Placement ** is added as belonging \em into another Placement, which defines the Scope ** of the addition. There is one (formal) root element, containing the timelines; ** from there a nested sequence of scopes leads down to each Placement. ** Ascending this path yields all the scopes to search or query in proper order ** to be used when resolving some attribute of placement. Placements use visibility ** rules comparable to visibility of scoped definitions in common programming languages ** or in cascading style sheets, where a local definition can shadow a global one. ** In a similar way, properties not defined locally may be resolved by querying ** up the sequence of nested scopes. ** ** A scope path is represented as sequence of scopes, where each Scope is implemented ** by a PlacementRef pointing to the »scope top«, i.e. the placement in the session ** constituting this scope. The leaf of this path can be considered the current scope. ** ScopePath is intended to remember a \em current location within the model, to be ** used for resolving queries and discovering contents. ** ** \par operations and behaviour ** ** In addition to some search and query functions, a scope path has the ability to ** \em navigate to a given target scope, which must be reachable by ascending and ** descending into the branches of the overall tree or DAG (in the general case). ** Navigating changes the current path, which usually happens when the current ** "focus" shifts while operating on the model. ** ** - ScopePath can be default constructed, yielding an \em invalid path. ** - When created with a given target Scope, a connection to the current Session ** is created behind the scenes to discover the path starting from this target ** Scope up to model root. This is the core "locating" operation. It may fail. ** - There is a pre defined \c ScopePath::INVALID token ** - ScopePaths are intended to be handled by value (as are Scopes and ** PlacementRefs). They are equality comparable and provide several specialised ** relation predicates. ** - all implementations are focused on clarity, not uttermost performance, as ** the assumption is for paths to be relatively short and path operations to ** be executed rather in a GUI action triggered context. ** - the iteration (Lumiera Forward Iterator) yields the path elements in ** \em ascending order, starting with the leaf element ** - a path containing just the root element evaluates to \c bool(false) ** (rationale is that any valid, usable path is below just root). ** - an empty (nil) path doesn't even contain the root element and ** may throw on many operations. ** ** \par relation to ScopeLocator ** ** ScopeLocator holds the QueryFocusStack, which contains ScopePath objects. ** Each of these stack frames represents the current location for some evaluation ** context; it is organised as stack to allow intermediate evaluations. Management ** of these stack frames is automated, with the assistance of ScopePath by ** incorporating a ref-count. ** ** @see scope-path-test.cpp ** @see Scope ** @see ScopeLocator ** */ #ifndef MOBJECT_SESSION_SCOPE_PATH_H #define MOBJECT_SESSION_SCOPE_PATH_H #include "proc/mobject/session/scope.hpp" #include "lib/bool-checkable.hpp" #include "lib/iter-adapter.hpp" #include "lib/error.hpp" #include 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. * Implemented as vector of Scope elements. Providing * state and relation predicates, and the ability to * \em navigate the current location, as represented * by the current leaf element of the path. * * Incorporates a ref count to be utilised by ScopeLocator * and QueryFocus to establish the \em current focus (path). */ class ScopePath : public lib::BoolCheckable { size_t refcount_; std::vector path_; typedef vector _VType; typedef _VType::const_reverse_iterator _VIter; typedef lib::RangeIter<_VIter> _IterType; public: ~ScopePath (); ScopePath (); ScopePath (Scope const& leaf); static const ScopePath INVALID; /* == state diagnostics == */ bool isValid() const; bool empty() const; size_t size() const; size_t length() const; size_t ref_count()const; ////////////////////////////////////////TICKET #429 : diagnostic output to be added later /// Iteration is always ascending from leaf to root typedef _IterType iterator; iterator begin() const; iterator end() const; /* == relations == */ Scope const& getLeaf() const; bool endsAt (Scope const&) const; bool contains (Scope const&) const; bool contains (ScopePath const&) const; friend ScopePath commonPrefix (ScopePath const&, ScopePath const&); friend bool disjoint (ScopePath const&, ScopePath const&); friend bool operator== (ScopePath const&, ScopePath const&); friend void intrusive_ptr_add_ref (ScopePath*); friend void intrusive_ptr_release (ScopePath*); /* == mutations == */ void clear(); Scope& moveUp(); Scope& goRoot(); void navigate (Scope const&); private: bool hasValidRoot() const; PlacementMO const& currModelRoot() const; void appendScope (Scope const&); }; inline bool operator== (ScopePath const& path1, ScopePath const& path2) { return path1.path_ == path2.path_; } inline bool operator!= (ScopePath const& path1, ScopePath const& path2) { return !(path1 == path2); } /** management function for boost::intrusive_ptr * to be picked up by ADL */ inline void intrusive_ptr_add_ref (ScopePath* pathFrame) { REQUIRE (pathFrame); ++(pathFrame->refcount_); } inline void intrusive_ptr_release (ScopePath* pathFrame) { REQUIRE (pathFrame); if (0 < pathFrame->refcount_) --(pathFrame->refcount_); } inline size_t ScopePath::ref_count() const { return refcount_; } inline size_t ScopePath::length() const { return path_.size(); } inline size_t ScopePath::size() const { 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 { return iterator (path_.rbegin(), path_.rend()); } inline ScopePath::iterator ScopePath::end() const { return iterator(); } }} // namespace mobject::session #endif