finialise simple state manager implementation

...and rearrange storage interface to suit
This commit is contained in:
Fischlurch 2016-02-13 17:09:57 +01:00
parent 92d9a7323f
commit 94576af4df
5 changed files with 77 additions and 46 deletions

View file

@ -39,9 +39,12 @@
** remembers those messages, always retaining the latest state information observed for any
** property of any [tangible interface element](\ref tangible.hpp) encountered thus far.
**
** @todo as of 2/2016 this is complete WIP-WIP-WIP
** @todo as of 2/2016 this is still preliminary.
** In the end, we want to capture and restore presentation state
** in dependency to the current perspective and work site
**
** @see ////TODO_test usage example
** @see BusTerm_test::captureStateMark() usage example
** @see BusTerm_test::replayStateMark() usage example
**
*/
@ -50,12 +53,8 @@
#define GUI_INTERACT_PRESENTATION_STATE_MANAGER_H
#include "lib/error.hpp"
//#include "gui/ctrl/bus-term.hpp"
//#include "lib/idi/entry-id.hpp"
#include "lib/idi/entry-id.hpp"
#include "lib/diff/gen-node.hpp"
//#include "lib/symbol.hpp"
//#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <string>
@ -64,20 +63,24 @@
namespace gui {
namespace interact {
// using lib::HashVal;
// using util::isnil;
using std::string;
/**
* Interface: handling of persistent interface state.
* @todo write type comment...
* Operations to retrieve previously captured state and
* to re-play this state towards the originating UI-elements.
* It is assumed that the actual implementation is connected
* to the UI-Bus and captures *state mark notifications*.
* State is first grouped by ID of the originating interface
* element, and recorded per distinct property within each
* element.
*/
class PresentationStateManager
: boost::noncopyable
{
protected:
virtual ~PresentationStateManager(); ///< this is an interface
virtual ~PresentationStateManager(); ///< this is an interface
using ID = lib::idi::BareEntryID const&;
using StateMark = lib::diff::GenNode const&;

View file

@ -74,9 +74,11 @@ namespace interact {
struct StateMapGroupingStorage
: boost::noncopyable
{
using Record = std::set<GenNode, GenNodeComparator>;
using StateData = std::set<GenNode, GenNodeComparator>;
using Storage = std::unordered_map<BareEntryID, Record, BareEntryID::UseEmbeddedHash>;
using Storage = std::unordered_map<BareEntryID, StateData, BareEntryID::UseEmbeddedHash>;
using Record = Storage::value_type;
Storage elmTable_;
@ -116,19 +118,19 @@ namespace interact {
}
static BareEntryID const&
getID (iterator entry)
getID (Record entry)
{
return entry->first;
return entry.first;
}
static Record const&
getState (iterator entry)
static StateData const&
getState (Record entry)
{
return entry->second;
return entry.second;
}
static GenNode const&
getState (iterator entry, string propertyKey)
getState (Record entry, string propertyKey)
{
UNIMPLEMENTED ("fetch property data from given element record");
}

View file

@ -27,9 +27,23 @@
** groups recorded message by elementID and simply overwrites
** previous state info for a given attribute with later one.
**
** @todo as of 2/2016 this is complete WIP-WIP-WIP
** \par performance
** The storage layout was chosen under the assumption that we'll get
** many elements with only few properties per element. The implementing
** hash table relies on the hash function for BareEntryID, which uses
** the embedded hash, which in turn is based on hashing the symbolicID
** plus a hashed type string.
**
** @see ////TODO_test usage example
** @todo as of 2/2016 noting but a simple data retrieval happens here.
** Actually, on the long run, we want "intelligent" handling of
** presentation state, we want to capture and restore state
** with sensitivity to perspective and work site. Probably
** this means to layer a second level of aggregation on top.
** @warning state mark entries are added, never discarded.
** Once we start actually persisting this state,
** this might get us into trouble.
**
** @see BusTerm_test::captureStateMark usage example
**
*/
@ -39,24 +53,18 @@
#include "lib/error.hpp"
//#include "gui/ctrl/bus-term.hpp"
#include "lib/idi/entry-id.hpp"
#include "lib/diff/gen-node.hpp"
#include "gui/ctrl/bus-term.hpp"
#include "gui/interact/presentation-state-manager.hpp"
#include "gui/interact/state-map-grouping-storage.hpp"
#include "gui/ctrl/bus-term.hpp"
//#include "lib/symbol.hpp"
//#include "lib/util.hpp"
#include <boost/noncopyable.hpp>
#include <string>
namespace gui {
namespace interact {
// using lib::HashVal;
// using util::isnil;
using gui::ctrl::BusTerm;
using lib::diff::GenNode;
using lib::diff::Ref;
@ -68,14 +76,22 @@ namespace interact {
/**
* Simple map based implementation of the
* PresentationStateManager interface.
* Requires a permanent connection to the UI-Bus,
* which is given as reference at construction.
* The intention is to use such an implementation
* embedded within the gui::ctrl::CoreService,
* which in turn then manages the lifecycle of
* this UI-Bus connection. Besides, a derived
* mock implementation is available through
* the test::Nexus
*
* @todo write type comment...
* @see StateMapGroupingStorage storage implementation
*/
class StateRecorder
: public PresentationStateManager
{
using Storage = StateMapGroupingStorage;
using Iter = Storage::iterator;
using Record = Storage::Record const&;
BusTerm& uiBus_;
Storage storage_;
@ -103,15 +119,18 @@ namespace interact {
virtual void
replayAllState() override
{
for (Iter entry = storage_.begin(); entry!=storage_.end(); ++entry)
for (Record entry : storage_)
replayPropertiesOf (entry);
}
/** replay all captured state from any element,
* but captured especially for the given property
*/
virtual void
replayAllState (string propertyKey) override
{
for (Iter entry = storage_.begin(); entry!=storage_.end(); ++entry)
for (Record entry : storage_)
{
StateMark state = Storage::getState (entry, propertyKey);
if (state != Ref::NO)
@ -120,36 +139,43 @@ namespace interact {
}
/** replay all captured state from the given element. */
virtual void
replayAllProperties (ID uiElm) override
{
Iter entry = storage_.find (uiElm);
auto entry = storage_.find (uiElm);
if (entry != storage_.end())
replayPropertiesOf (entry);
replayPropertiesOf (*entry);
}
virtual void
clearState() override
{
storage_.clear();
}
public:
StateRecorder (BusTerm& busConnection)
: uiBus_(busConnection)
, storage_()
{ }
/** Interface for the operating facility (CoreService)
* to feed state mark messages to be remembered.
* @param uiElm originator of the state notification
* @param stateMark state information to record
*/
void
record (ID uiElm, StateMark stateMark)
recordState (ID uiElm, StateMark stateMark)
{
storage_.record (uiElm, stateMark);
}
private:
void
replayPropertiesOf (Iter entry)
replayPropertiesOf (Record entry)
{
ID uiElm = Storage::getID (entry);
for (auto& stateMark : Storage::getState (entry))

View file

@ -117,7 +117,7 @@ namespace test {
* from the Hub.
*/
void
attachNewBusTerm ()
attachNewBusTerm()
{
MARK_TEST_FUN;
// our dummy will be linked with this identity
@ -180,7 +180,7 @@ namespace test {
/** @test perform the full command binding and invocation protocol */
void
commandInvocation ()
commandInvocation()
{
MARK_TEST_FUN
gui::test::Nexus::startNewLog();
@ -238,7 +238,7 @@ namespace test {
* We use a test version of the PresentationStateManager,
* based on the same building blocks as _the real thing_ */
void
captureStateMark ()
captureStateMark()
{
MARK_TEST_FUN
gui::test::Nexus::startNewLog();
@ -279,7 +279,7 @@ namespace test {
/** @test replay previously captured state information" */
void
replayStateMark ()
replayStateMark()
{
MARK_TEST_FUN
PresentationStateManager& stateManager = gui::test::Nexus::getMockStateManager();
@ -316,28 +316,28 @@ namespace test {
void
verifyNotifications ()
verifyNotifications()
{
UNIMPLEMENTED ("send notifications to a distinct element");
}
void
clearStates ()
clearStates()
{
UNIMPLEMENTED ("broadcast state reset");
}
void
pushDiff ()
pushDiff()
{
UNIMPLEMENTED ("push a mutation diff towards an interface element");
}
void
destroy ()
destroy()
{
UNIMPLEMENTED ("detach and destroy the test BusTerm");
}

View file

@ -590,7 +590,7 @@ namespace test{
testNexus().installStateMarkHandler(
[&](ID const& elementID, lib::diff::GenNode const& stateMark)
{
stateManager().record (elementID, stateMark);
stateManager().recordState (elementID, stateMark);
});
return getMockStateManager();