diff --git a/src/proc/mobject/mobject-ref.hpp b/src/proc/mobject/mobject-ref.hpp index bdccf1fb6..4523b3448 100644 --- a/src/proc/mobject/mobject-ref.hpp +++ b/src/proc/mobject/mobject-ref.hpp @@ -64,6 +64,7 @@ #include "lib/lumitime.hpp" #include "proc/mobject/placement.hpp" #include "proc/mobject/placement-ref.hpp" +#include "proc/mobject/session/session-service-mutate.hpp" #include @@ -134,6 +135,38 @@ namespace mobject { } + /** attach a child element to the model + * @param newPlacement to be copied into the model, placed + * into the scope of the object denoted by this MORef + * @return MORef designing the newly created and attached object instance + */ + template + MORef + attach (Placement const& newPlacement) + { + if (!isValid()) + throw lumiera::error::State("Attempt to attach a child to an inactive MObject ref" + , LUMIERA_ERROR_BOTTOM_MOBJECTREF); + MORef newInstance; + PlacementMO::ID thisScope = pRef_; + return newInstance.activate ( + session::SessionServiceMutate::attach_toModel (newPlacement, thisScope)); + } + + + /** detach this object instance from model, + * including all child elements + */ + void + purge () + { + if (isValid()) + session::SessionServiceMutate::detach_and_clear (pRef_); + + ENSURE (!isValid()); + } + + /* === Lifecycle === */ diff --git a/src/proc/mobject/session.hpp b/src/proc/mobject/session.hpp index c73697c4e..f0071ad36 100644 --- a/src/proc/mobject/session.hpp +++ b/src/proc/mobject/session.hpp @@ -121,18 +121,18 @@ namespace mobject { static session::SessManager& current; - DefaultsAccess defaults; ///< manages default configured objects - TimelineAccess timelines; ///< collection of timelines (top level) - SequenceAccess sequences; ///< collection of sequences + DefaultsAccess defaults; ///< manages default configured objects + TimelineAccess timelines; ///< collection of timelines (top level) + SequenceAccess sequences; ///< collection of sequences - virtual bool isValid () = 0; - virtual void attach (PMO& placement) = 0; - virtual bool detach (PMO& placement) = 0; + virtual bool isValid () = 0; + virtual MObjectRef attach (PMO& placement) = 0; + virtual bool detach (PMO& placement) = 0; - virtual MObjectRef getRoot() = 0; + virtual MObjectRef getRoot() = 0; - virtual session::PFix& getFixture () = 0; - virtual void rebuildFixture () = 0; + virtual session::PFix& getFixture () = 0; + virtual void rebuildFixture () = 0; }; diff --git a/src/proc/mobject/session/placement-index.cpp b/src/proc/mobject/session/placement-index.cpp index 036fff3f4..08484be2b 100644 --- a/src/proc/mobject/session/placement-index.cpp +++ b/src/proc/mobject/session/placement-index.cpp @@ -296,6 +296,16 @@ namespace session { return true; } + void + removeAll (ID scopeID) + { + remove_all_from_scope (scopeID); // recursive + remove_base_entry (scopeID); // discard storage + + ENSURE (!util::contains(scopeTab_, scopeID)); + ENSURE (!contains (scopeID)); + } + /* == access for self-test == */ @@ -349,6 +359,21 @@ namespace session { NOTREACHED(); } + void + remove_all_from_scope (ID scopeID) + { + typedef ScopeTable::const_iterator Pos; + pair searchRange = scopeTab_.equal_range(scopeID); + + Pos pos = searchRange.first; + Pos end = searchRange.second; + for ( ; pos!=end; ++pos) + removeAll (pos->second); // depth-first recursion + + scopeTab_.erase (pos,end); // assumed to be NOP for pos==end + } + + /** Helper for building a scope exploring iterator * for PlacementIndex: our "reverse index" (#scopeTab_) @@ -499,6 +524,23 @@ namespace session { } + /** recursively kill a complete scope, + * including the given element and all children. + * @note as an exception, when specifying model root, + * any sub-elements are cleared but root is retained + */ + void + PlacementIndex::clear (ID targetScope) + { + if (targetScope == getRoot().getID()) + pTab_->clear(); + else + pTab_->removeAll (targetScope); + + ENSURE (isValid()); + } + + void PlacementIndex::clear() { diff --git a/src/proc/mobject/session/placement-index.hpp b/src/proc/mobject/session/placement-index.hpp index 8323b6cbb..b94cf0e2b 100644 --- a/src/proc/mobject/session/placement-index.hpp +++ b/src/proc/mobject/session/placement-index.hpp @@ -223,7 +223,8 @@ namespace session { PlacementIndex(PlacementMO const&); ~PlacementIndex() ; - void clear(); + void clear (ID targetScope); + void clear (); }; @@ -304,6 +305,7 @@ namespace session { * immediately followed by creating a typed-ID, * allowing to retain the original typed context * @todo this solution is half-baked ///////////////////////////////////TICKET #523 + * @todo is this API used in application code? or just used in tests? */ template typename BuildID::Type diff --git a/src/proc/mobject/session/session-impl.cpp b/src/proc/mobject/session/session-impl.cpp index 0d830f016..4a689063a 100644 --- a/src/proc/mobject/session/session-impl.cpp +++ b/src/proc/mobject/session/session-impl.cpp @@ -75,7 +75,7 @@ namespace session { } - void + MObjectRef SessionImpl::attach (PMO& placement) { UNIMPLEMENTED ("add Placement to the current Session"); diff --git a/src/proc/mobject/session/session-impl.hpp b/src/proc/mobject/session/session-impl.hpp index 93f447026..e9618bede 100644 --- a/src/proc/mobject/session/session-impl.hpp +++ b/src/proc/mobject/session/session-impl.hpp @@ -84,8 +84,8 @@ namespace session { /* ==== Session API ==== */ virtual bool isValid (); - virtual void attach (PMO& placement); - virtual bool detach (PMO& placement); + virtual MObjectRef attach (PMO& placement); + virtual bool detach (PMO& placement); virtual MObjectRef getRoot(); @@ -131,6 +131,45 @@ namespace session { + template + struct ServiceAccessPoint + : IMPL + { + PMO::ID const& + insertCopy (PMO const& newPlacement, PMO::ID const& scope) + { + return index().insert (newPlacement,scope); + } + + bool + purgeScopeRecursively (PMO::ID const& scope) + { + size_t siz = index().size(); + if (index().contains (scope)) + index().clear (scope); + + ENSURE (!index().contains (scope) || (scope == index().getRoot().getID())); + ENSURE (siz >= index().size()); + return siz != index().size(); + } + + bool + detachElement (PMO::ID const& placementID) + { + return index().remove (placementID); + } + + private: + PlacementIndex& + index() + { + return IMPL::getPlacementIndex(); + } + }; + + + + template struct ServiceAccessPoint : IMPL @@ -225,6 +264,7 @@ namespace session { * global Session PImpl */ typedef SessionServices< Types< SessionServiceFetch + , SessionServiceMutate , SessionServiceExploreScope , SessionServiceMockIndex , SessionServiceDefaults diff --git a/src/proc/mobject/session/session-service-mutate.hpp b/src/proc/mobject/session/session-service-mutate.hpp index 50f62c7c5..a1cdcb4e2 100644 --- a/src/proc/mobject/session/session-service-mutate.hpp +++ b/src/proc/mobject/session/session-service-mutate.hpp @@ -1,8 +1,8 @@ /* - SESSION-SERVICE-FETCH.hpp - session implementation service API: fetch PlacementRef + SESSION-SERVICE-MUTATE.hpp - session implementation service API: add/remove session contents Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2010, 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 @@ -21,12 +21,14 @@ */ -/** @file session-service-fetch.hpp - ** Implementation level session API: resolve a Placement by hash-ID. - ** This specialised service is intended to be used by PlacementRef, - ** in order to (re)-access the Placement instance within the session, - ** given the hash-ID of this placement. An implementation of this - ** service is available through the SessionServices access mechanism. +/** @file session-service-mutate.hpp + ** Implementation level session API: add or remove Session contents. + ** This specialised service is intended to be used by MObjectRef, + ** in order to attach a new Placement to the session or to detach + ** and purge an existing Placement. An implementation of this service + ** service is available through the SessionServices access mechanism, + ** and delegates the actual implementation to the PlacementIndex, + ** which is the core session datastructure. ** ** @see session-impl.hpp implementation of the service ** @see session-services.cpp implementation of access @@ -34,8 +36,8 @@ */ -#ifndef MOBJECT_SESSION_SESSION_SERVICE_FETCH_H -#define MOBJECT_SESSION_SESSION_SERVICE_FETCH_H +#ifndef MOBJECT_SESSION_SESSION_SERVICE_MUTATE_H +#define MOBJECT_SESSION_SESSION_SERVICE_MUTATE_H //#include "proc/mobject/session.hpp" //#include "lib/meta/generator.hpp" @@ -47,9 +49,6 @@ namespace mobject { namespace session { -// using lumiera::typelist::InstantiateChained; -// using lumiera::typelist::InheritFrom; -// using lumiera::typelist::NullType; /** * Implementation-level service for resolving an Placement-ID. @@ -58,13 +57,15 @@ namespace session { * this index may be overlaid temporarily, by using the * SessionServiceMockIndex API. */ - class SessionServiceFetch + class SessionServiceMutate { public: - static PlacementMO& resolveID (PlacementMO::ID const&) ; - static bool isRegisteredID (PlacementMO::ID const&) ; + typedef PlacementMO const& PMO; + typedef PlacementMO::ID const& PID; - static bool isAccessible() ; + static PID attach_toModel (PMO, PID) ; + static bool detach_and_clear (PID) ; + static bool detach (PID) ; }; diff --git a/src/proc/mobject/session/session-services.cpp b/src/proc/mobject/session/session-services.cpp index ba6fc861c..674f92e11 100644 --- a/src/proc/mobject/session/session-services.cpp +++ b/src/proc/mobject/session/session-services.cpp @@ -22,6 +22,7 @@ #include "proc/mobject/session/session-service-fetch.hpp" +#include "proc/mobject/session/session-service-mutate.hpp" #include "proc/mobject/session/session-service-explore-scope.hpp" #include "proc/mobject/session/session-service-mock-index.hpp" #include "proc/mobject/session/session-service-defaults.hpp" @@ -76,6 +77,46 @@ namespace session { } + /** attach an object by placement onto the session. + * Implemented by registering a copy of the Placement into the + * PlacementIndex in the session. This copy establishes a new kind of + * "object instance", represented by a new placement-ID, which is returned + * and can be used to refer to this "instance" within the session from now on. + * @param scope the (existing) parent scope where to attach the new element + */ + PlacementMO::ID const& + SessionServiceMutate::attach_toModel(PMO newPlacement, PID scope) + { + return SessionImplAPI::current->insertCopy (newPlacement,scope); + } + + + /** detach the denoted element from the model including all children. + * @return true if actually erased something + * @note when specifying model root, all sub-elements will be cleared, + * but model root itself will be retained. + */ + bool + SessionServiceMutate::detach_and_clear (PID scope) + { + return SessionImplAPI::current->purgeScopeRecursively (scope); + } + + + /** detach the denoted leaf element from the model. + * @return true if actually erased something + * @throw error::Fatal when attempting to remove the model root + * @throw error::State when the given element contains sub elements + */ + bool + SessionServiceMutate::detach (PID leafElement) + { + return SessionImplAPI::current->detachElement (leafElement); + } + + + + namespace { // deleter function to clean up test/mock PlacementIndex void remove_testIndex (PlacementIndex* testIdx) diff --git a/tests/components/proc/mobject/session/placement-index-test.cpp b/tests/components/proc/mobject/session/placement-index-test.cpp index 749c32a70..38a6964ee 100644 --- a/tests/components/proc/mobject/session/placement-index-test.cpp +++ b/tests/components/proc/mobject/session/placement-index-test.cpp @@ -240,6 +240,31 @@ namespace test { ASSERT (index.remove(e133)); // but can remove an scope, after emptying it ASSERT (!index.contains(e133)); + + // build a complete new subtree + uint siz = index.size(); + ID e1321 = index.insert (testObj, e132); + ID e13211 = index.insert (testObj, e1321); + ID e13212 = index.insert (testObj, e1321); + ID e13213 = index.insert (testObj, e1321); + ID e13214 = index.insert (testObj, e1321); + ID e132131 = index.insert (testObj, e13213); + ID e132132 = index.insert (testObj, e13213); + ID e132133 = index.insert (testObj, e13213); + ID e132134 = index.insert (testObj, e13213); + ID e132141 = index.insert (testObj, e13214); + ID e132142 = index.insert (testObj, e13214); + ID e132143 = index.insert (testObj, e13214); + ID e132144 = index.insert (testObj, e13214); + + // ...and kill it recursively in one sway + index.clear (e1321); + ASSERT (!index.contains (e1321)); + ASSERT (!index.contains (e13211)); + ASSERT (!index.contains (e13213)); + ASSERT (!index.contains (e132131)); + ASSERT (!index.contains (e132144)); + ASSERT (siz == index.size()); } diff --git a/tests/components/proc/mobject/session/session-modify-parts-test.cpp b/tests/components/proc/mobject/session/session-modify-parts-test.cpp index 2687f845e..6c49bf2b5 100644 --- a/tests/components/proc/mobject/session/session-modify-parts-test.cpp +++ b/tests/components/proc/mobject/session/session-modify-parts-test.cpp @@ -64,6 +64,7 @@ namespace test { using lumiera::Query; typedef MORef RClip; + typedef PlacementMO::ID PID; @@ -303,7 +304,10 @@ namespace test { CHECK (focus.getObject() == sess->getRoot()); + PID currRoot = sess->getRoot.getPlacement().getID(); sess->getRoot().purge(); // purging the root scope effectively resets the session to defaults + CHECK (currRoot == sess->getRoot.getPlacement.getID); + // but the root element itself is retained CHECK (sess->isValid()); CHECK (1 == sess->timelines.size()); CHECK (1 == sess->sequences.size());