LUMIERA.clone/src/proc/mobject/session/scope-path.hpp

254 lines
8 KiB
C++

/*
SCOPE-PATH.hpp - logical access path down from Session root
Copyright (C) Lumiera.org
2009, Hermann Vosseler <Ichthyostega@web.de>
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 <b>by value</b> (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 <vector>
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<ScopePath>
{
size_t refcount_;
std::vector<Scope> path_;
typedef vector<Scope> _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