2009-10-14 05:48:24 +02:00
/*
ScopePath - 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 0213 9 , USA .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-11-22 04:36:14 +01:00
# include "include/logging.h"
2009-10-14 05:48:24 +02:00
# include "proc/mobject/session/scope-path.hpp"
2009-11-22 01:16:33 +01:00
# 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"
2009-11-21 20:55:14 +01:00
# include "lib/error.hpp"
# include "lib/util.hpp"
# include <tr1/functional>
2009-11-22 01:16:33 +01:00
# include <algorithm>
2009-10-14 05:48:24 +02:00
namespace mobject {
namespace session {
2009-11-22 01:16:33 +01:00
using std : : reverse ;
2009-11-21 20:55:14 +01:00
using std : : tr1 : : bind ;
using std : : tr1 : : function ;
using std : : tr1 : : placeholders : : _1 ;
2009-11-22 01:16:33 +01:00
using lib : : append_all ;
2009-11-21 20:55:14 +01:00
using util : : and_all ;
using namespace lumiera ;
2009-10-14 05:48:24 +02:00
2009-11-22 01:16:33 +01:00
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 .
2009-11-21 20:55:14 +01:00
*/
2009-10-14 05:48:24 +02:00
ScopePath : : ScopePath ( )
2009-11-22 03:48:52 +01:00
: refcount_ ( 0 )
, path_ ( )
2009-10-14 05:48:24 +02:00
{
2009-11-22 01:16:33 +01:00
clear ( ) ;
2009-11-21 20:55:14 +01:00
}
2009-11-22 01:16:33 +01:00
/**
* 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
2009-11-21 20:55:14 +01:00
*/
ScopePath : : ScopePath ( Scope const & leaf )
2009-11-22 03:48:52 +01:00
: refcount_ ( 0 )
, path_ ( )
2009-11-21 20:55:14 +01:00
{
2009-11-22 01:16:33 +01:00
if ( ! leaf . isValid ( ) ) return ; // invalid leaf defines invalid path....
append_all ( discoverScopePath ( leaf ) , path_ ) ;
reverse ( path_ . begin ( ) , path_ . end ( ) ) ;
2009-11-21 20:55:14 +01:00
}
2009-11-22 03:48:52 +01:00
ScopePath : : ~ ScopePath ( )
{
2009-11-22 04:36:14 +01:00
WARN_IF ( refcount_ , session , " Destroying a scope path frame with ref-count=%lu " , refcount_ ) ;
2009-11-22 03:48:52 +01:00
}
2009-11-21 20:55:14 +01:00
/** constant \em invalid path token. Created by locating an invalid scope */
2009-12-13 04:27:57 +01:00
const ScopePath ScopePath : : INVALID = ScopePath ( Scope ( ) ) ; ///////////////////////TODO can this initialisation be deferred? it causes creation of an default session very early
2009-11-21 20:55:14 +01:00
/** 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
*/
2009-11-22 01:16:33 +01:00
inline bool
2009-11-21 20:55:14 +01:00
ScopePath : : isValid ( ) const
{
2009-11-22 01:16:33 +01:00
return ( 0 < length ( ) )
# ifndef NDEBUG
& & hasValidRoot ( )
# endif
;
2009-10-14 05:48:24 +02:00
}
2009-11-21 20:55:14 +01:00
bool
2009-11-22 01:16:33 +01:00
ScopePath : : hasValidRoot ( ) const
{
REQUIRE ( 0 < length ( ) ) ;
return path_ [ 0 ] = = currModelRoot ( ) ;
}
PlacementMO const &
ScopePath : : currModelRoot ( ) const
2009-11-21 20:55:14 +01:00
{
2009-11-22 01:16:33 +01:00
return SessionServiceExploreScope : : getScopeRoot ( ) ;
2009-11-21 20:55:14 +01:00
}
2009-10-14 05:48:24 +02:00
2009-11-22 01:16:33 +01:00
2009-11-21 20:55:14 +01:00
/* == Relations == */
2009-11-22 01:16:33 +01:00
Scope const &
2009-10-29 04:32:00 +01:00
ScopePath : : getLeaf ( ) const
{
2009-11-22 01:16:33 +01:00
___check_notBottom ( this , " Inspecting " ) ;
return path_ . back ( ) ;
2009-10-29 04:32:00 +01:00
}
2009-11-21 20:55:14 +01:00
/** verify the scope in question is equivalent
* to our leaf scope . Equivalence of scopes means
* they are defined by the same scope top placement ,
2009-11-22 01:16:33 +01:00
* i . e . registered with the same Placement - ID .
2009-11-21 20:55:14 +01:00
*/
bool
ScopePath : : endsAt ( Scope const & aScope ) const
{
2009-11-22 01:16:33 +01:00
return aScope = = getLeaf ( ) ;
2009-11-21 20:55:14 +01:00
}
bool
ScopePath : : contains ( Scope const & aScope ) const
{
2009-11-22 01:16:33 +01:00
for ( iterator ii = this - > begin ( ) ; ii ; + + ii )
if ( aScope = = * ii )
return true ;
return false ;
2009-11-21 20:55:14 +01:00
}
bool
ScopePath : : contains ( ScopePath const & otherPath ) const
{
if ( empty ( ) ) return false ;
if ( ! otherPath . isValid ( ) ) return true ;
if ( ! isValid ( ) ) return false ;
ASSERT ( 1 < length ( ) ) ;
ASSERT ( 1 < otherPath . length ( ) ) ;
for ( iterator ii = otherPath . begin ( ) ; ii ; + + ii )
if ( ! this - > contains ( * ii ) )
return false ;
return true ;
}
ScopePath
commonPrefix ( ScopePath const & path1 , ScopePath const & path2 )
{
2009-11-22 01:16:33 +01:00
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 ;
2009-11-21 20:55:14 +01:00
}
bool
disjoint ( ScopePath const & path1 , ScopePath const & path2 )
{
if ( path1 . empty ( ) | | path2 . empty ( ) ) return false ;
2009-11-22 01:16:33 +01:00
2009-11-21 20:55:14 +01:00
return ( path1 . isValid ( ) & & path2 . isValid ( ) )
& & ( path1 . path_ [ 1 ] ! = path2 . path_ [ 1 ] ) // no common prefix
;
}
2009-11-22 01:16:33 +01:00
2009-10-29 04:32:00 +01:00
/* == Mutations == */
void
ScopePath : : clear ( )
{
2009-11-22 01:16:33 +01:00
path_ . clear ( ) ;
path_ . push_back ( currModelRoot ( ) ) ;
ENSURE ( ! empty ( ) ) ;
ENSURE ( ! isValid ( ) ) ;
ENSURE ( hasValidRoot ( ) ) ;
2009-10-29 04:32:00 +01:00
}
2009-11-21 20:55:14 +01:00
Scope &
ScopePath : : moveUp ( )
{
2009-11-22 01:16:33 +01:00
___check_notBottom ( this , " Navigating " ) ;
static Scope invalidScope ;
path_ . resize ( length ( ) - 1 ) ;
if ( empty ( ) ) return invalidScope ;
else return path_ . back ( ) ;
2009-11-21 20:55:14 +01:00
}
Scope &
ScopePath : : goRoot ( )
{
2009-11-22 01:16:33 +01:00
___check_notBottom ( this , " Navigating " ) ;
ENSURE ( hasValidRoot ( ) ) ;
path_ . resize ( 1 ) ;
return path_ . back ( ) ;
2009-11-21 20:55:14 +01:00
}
void
ScopePath : : navigate ( Scope const & target )
{
2009-11-22 01:16:33 +01:00
___check_notBottom ( this , " Navigating " ) ;
2009-11-21 20:55:14 +01:00
* this = ScopePath ( target ) ; //////////////////////////////TICKET #424
}
2009-10-14 05:48:24 +02:00
2009-11-22 01:16:33 +01:00
void
ScopePath : : appendScope ( Scope const & child ) ///< @internal backdoor for #commonPrefix
{
path_ . push_back ( child ) ;
}
2009-10-14 05:48:24 +02:00
} } // namespace mobject::session