WIP: first draft -- properties of an external symbolic record type

This Record type is intended to play a role in the
diff description / exchange of GUI data structures.
This commit is contained in:
Fischlurch 2015-06-04 19:26:45 +02:00
parent 7fcee74960
commit b91734b0a6
8 changed files with 250 additions and 45 deletions

View file

@ -121,7 +121,7 @@ namespace diff{
using Handler = HandlerFun<Interpreter,Val>;
};
template<class I, typename E>
template<class I, typename E> ///< alternatively derive value and interpreter from a Handler binding
struct InterpreterScheme<HandlerFun<I,E>>
{
using Val = E;
@ -266,10 +266,10 @@ namespace diff{
/**
* generic builder to apply a list diff to a given target sequence.
* generic builder to apply a diff description to a given target data structure.
* The usage pattern is as follows
* #. construct a DiffApplicator instance, wrapping the target sequence
* #. feed the list diff (sequence of diff verbs) to the #consume function
* #. construct a DiffApplicator instance, wrapping the target data
* #. feed the diff (sequence of diff verbs) to the #consume function
* #. the wrapped target sequence has been altered, to conform to the given diff
* @note a suitable DiffApplicationStrategy will be picked, based on the type
* of the concrete target sequence given at construction. (Effectively

View file

@ -34,7 +34,7 @@
**
** \par Anatomy of a GenNode
**
** GenNode is a polymorphic value with well defined identity and type-ID.
** GenNode is a polymorphic value with well defined identity and type.
** Each element is conceived to be »unique within context« -- as defined
** by the immediately visible scope within a tree like structure.
** Beyond this identity metadata, each GenNode carries a DataCap, which
@ -44,7 +44,7 @@
** will be referred by a suitable reference representation (PlacementID).
** The DataCap is what creates the polymorphic nature, where the common
** interface is mostly limited to managemental tasks (copying of values,
** external representation). Besides, there is are special flavours of
** external representation). Besides, there are special flavours of
** the DataCap to represent \em sub-collections of GenNode elements.
** Especially, the \ref Record type is a kind of collection suitable
** to represent object-like structures, since it both holds several
@ -63,7 +63,8 @@
** - moreover, the elements need to be values, able to be copied and handled at will
** - it will be beneficial for these values to support move semantics explicitly
** - in addition, the tree diffing suggests a mechanism to re-gain the fully
** typed context, based on some kind of embedded type tag
** typed context, either based on some kind of embedded type tag, or
** alternatively by visitation and matching
** - finally, the handling of changes prompts us to support installation
** of a specifically typed <i>change handling closure</i>.
**
@ -79,6 +80,10 @@
** dependency management, we solve this requirement with the help of a trait type,
** expecting the actual usage to supply the necessary specialisations on site.
**
** @todo the purpose and goal of the monadic approach is not clear yet (5/2015).
** To begin with, for the task of diff detection and application, it is sufficient
** to get the children as traversable collection
**
** @see diff-index-table-test.cpp
** @see diff-list-generation-test.cpp
** @see DiffDetector

View file

@ -32,9 +32,32 @@
** which in turn serve as the standard handle to refer to other elements, entities,
** attributes or references within the "backbone" of the Lumiera GUI.
**
** @see diff-index-table-test.cpp
** @see diff-list-generation-test.cpp
** @see DiffDetector
** \par design decisions
** The Record type is shaped from its intended use: It serves to symbolically represent
** \em objects in the "external tree description". Here, \em objects means objects for
** real, i.e. with types, fields and an enclosed scope. But \em external means that we
** do not work on these objects right here, we only represent them, for later referral,
** \em symbolically.
**
** This leads to the following decisions
** - the Record entity is itself an object and thus has an inner side, privately.
** The entrails of the Record can be reworked and tuned for performance
** - yet the Record has an external appearance, which makes it look flat and passive.
** This is to say, a Record has no visible functionality.
** - the parts or \em realms within this symbolic representation are distinguished
** by convention solely
**
** * metadata is very limited and boils down to magic attributes known by name
** * children (scope contents) can be recognised by \em not bearing a name
**
** Record entities are meant to be immutable. The proper way to alter a Record is
** to apply a \link tree-diff.hpp diff \endlink
**
** \par rationale
** The underlying theme of this design is negative, dialectical: we do not want to
** build yet another object system. The object model of C++ is deemed adequate.
**
** @see GenericRecordRepresentation_test
**
*/

View file

@ -193,7 +193,7 @@ namespace asset {
/** case-1: auto generated symbolic ID */
EntryID()
: BareEntryID (idi::generateSymbolID<TY>(), getTypeHash())
: BareEntryID (idi::generateSymbolID<TY>(), getTypeHash()) /////////////TICKET #565 : how to organise access; this is not thread safe
{ }
/** case-2: explicitly specify a symbolic ID to use.

View file

@ -156,7 +156,7 @@ namespace asset{
static format namePattern ("%s.%03d");
////////////////////////////////////////////////////////////////////////////////TICKET #166 : needs to be pushed down into a *.cpp
return str(namePattern % StructTraits<STRU>::namePrefix() % (++i) );
return str(namePattern % StructTraits<STRU>::namePrefix() % (++i) ); //////////TICKET #565 : how to organise access; this ought to be thread safe (-> EntryID )
}

View file

@ -24,14 +24,19 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/diff/record.hpp"
#include "lib/util.hpp" //////TODO necessary?
#include <iostream>
//#include <utility>
//#include <string>
//#include <vector>
#include <vector>
//using std::string;
//using std::vector;
using util::isSameObject;
using std::vector;
//using std::swap;
using std::cout;
using std::endl;
namespace lib {
@ -46,6 +51,8 @@ namespace test{
}//(End)Test fixture
using Seq = vector<string>;
using RecS = Record<String>;
@ -53,10 +60,9 @@ namespace test{
/*****************************************************************************//**
/*************************************************************************************//**
* @test Verify properties of a special collection type meant for external representation
* of object-loke data.
* of object-like data.
*
* @see IndexTable
* @see DiffListApplication_test
@ -68,40 +74,198 @@ namespace test{
run (Arg)
{
simpleUsage();
verifySnapshot();
sequenceIteration();
duplicateDetection();
verifyCreation();
verifyMutations();
copy_and_move();
equality();
}
void
simpleUsage()
{
RecS enterprise("starship"
, {"Name = USS Enterprise"
,"Registry = NCC-1701-D"
,"Class = Galaxy"
,"Owner = United Federation of Planets"
,"built=2363"
}
, {"Picard", "Riker", "Data", "Worf", "Troi", "Crusher", "La Forge"}
);
CHECK (enterprise.getType() == "starship");
CHECK (enterprise.get("Registry") == "NCC-1701-D");
CHECK (enterprise.hasAttribute("Owner"));
CHECK (!enterprise.hasAttribute("owner"));
CHECK (!enterprise.hasAttribute("Owner "));
CHECK (enterprise.contains("Data"));
CHECK (!contains (enterprise, "Woof"));
CHECK (util::contains (enterprise, "Worf"));
VERIFY_ERROR (INVALID, enterprise.get("warp10"));
cout << "enterprise = " << string(enterprise)<<endl;
for (string elm : enterprise) cout << elm<<endl;
for (string mbr : enterprise.scope()) cout << mbr<<endl;
for (auto attr : enterprise.attributes()) cout << attr<<endl;
}
void
verifySnapshot()
{
}
void
sequenceIteration()
{
}
void
duplicateDetection()
verifyCreation()
{
RecS nil;
CHECK (isnil(nil));
CHECK ("NIL" == nil.getType());
CHECK (!nil.begin());
CHECK (nil.begin() == nil.end());
RecS untyped({"x"});
CHECK (!isnil(untyped));
CHECK ("NIL" == untyped.getType());
CHECK (Seq{"x"} == contents(untyped));
CHECK (Seq{"x"} == contents(untyped.scope()));
CHECK (isnil (untyped.attributes()));
RecS untyped2({"x=y", "z"});
CHECK (!isnil(untyped2));
CHECK ("NIL" == untyped2.getType());
CHECK (Seq({"x=y", "z"}) == contents(untyped2));
CHECK (Seq{"x"} == contents (untyped2.keys()));
CHECK (Seq{"y"} == contents (untyped2.values()));
CHECK (Seq{"z"} == contents (untyped2.scope()));
RecS something({"type=thing", "a=1", "b=2", "c", "d"});
CHECK (!isnil(something));
CHECK ("thing" == something.getType());
CHECK (Seq({"type=thing", "a=1", "b=2", "c", "d"}) == contents(something));
CHECK (Seq({"a", "b"}) == contents (something.keys()));
CHECK (Seq({"1", "2"}) == contents (something.values()));
CHECK (Seq({"c", "d"}) == contents (something.scope()));
}
void
copy_and_move()
{
RecS a({"type=thing", "a=1", "b=2", "c", "d"});
RecS b(a);
CHECK (a.getType() == b.getType());
CHECK (contents(a) == contents(b));
CHECK (contents(a.attributes()) == contents(b.attributes()));
CHECK (!isSameOject (a.get("a"), b.get("a")));
CHECK (!isSameOject (*a.scope(), *b.scope()));
string& c = *b.scope();
CHECK ("c" == c);
RecS bb;
CHECK (isnil(bb));
bb = move(b);
CHECK ("b" == bb.get("b"));
CHECK (isSameObject(c, *bb.scope()));
swap (a, bb);
CHECK (!isSameObject(c, *bb.scope()));
CHECK ( isSameObject(c, *a.scope()));
CHECK (isnil (b));
b = bb;
CHECK (!isnil (b));
CHECK (!isSameObject(*b.get("a"), *bb.get("a")));
CHECK (!isSameObject(*b.scope(), *bb.scope()));
}
void
equality()
{
RecS a({"a"});
RecS aa({"a","aa"});
RecS aaa({"a","a"});
RecS ax({"type=a","a"});
RecS ay({"a=a","a"});
CHECK (a != aa); CHECK (aa != a);
CHECK (aa != aaa); CHECK (aaa != aa);
CHECK (a != aaa); CHECK (aaa != a);
CHECK (a != ax); CHECK (ax != a);
CHECK (a != ay); CHECK (ay != a);
CHECK (ax != ay); CHECK (ay != ax);
CHECK (aaa != ay); CHECK (ay != aaa);
RecS a2({"a","aa"});
CHECK (aa == a2); CHECK (a2 == aa);
RecS o1("oo", {"a=α", "b=β"}, {"γ", "δ", "ε"});
RecS o2({"type=oo", "a = α", "b = β", "γ", "δ", "ε"});
RecS o3({"type=oO", "a = α", "b = β", "γ", "δ", "ε"});
RecS o4({"type=oo", "a = α", "b = β", "c=γ", "δ", "ε"});
RecS o5({"type=oo", "a = α", "b = β", "γ", "ε", "δ"});
RecS o6({"type=oo", "a = α", "b = β", "γ", "δ"});
CHECK (o1 == o2); CHECK (o2 == o1);
CHECK (o2 != o3); CHECK (o3 != o2);
CHECK (o3 != o4); CHECK (o4 != o3);
CHECK (o4 != o5); CHECK (o5 != o4);
CHECK (o5 != o6); CHECK (o6 != o5);
CHECK (o1 != o3); CHECK (o3 != o1);
CHECK (o1 != o4); CHECK (o4 != o1);
CHECK (o1 != o5); CHECK (o5 != o1);
CHECK (o1 != o6); CHECK (o6 != o1);
CHECK (o2 != o4); CHECK (o4 != o2);
CHECK (o2 != o5); CHECK (o5 != o2);
CHECK (o2 != o6); CHECK (o6 != o2);
CHECK (o3 != o5); CHECK (o5 != o3);
CHECK (o3 != o6); CHECK (o6 != o3);
CHECK (o4 != o6); CHECK (o6 != o4);
RecS o7({"type=oo", "b = β", "a = α", "γ", "δ", "ε"});
CHECK (o2 != o7); CHECK (o7 != o2);
// ideally, they would be equal, but this would require
// a way more expensive implementation
}
void
verifyMutations()
{
RecS a;
CHECK (isnil (a));
CHECK ("NIL" == a.getType());
RecS::Mutator mut(a);
mut.setType("u");
mut.appendChild("a");
mut.set("a", "1");
RecS aa(mut);
CHECK (a != aa);
CHECK ("u" == aa.getType());
CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa));
CHECK (Seq({"a"}) == contents (aa.keys()));
CHECK (Seq({"1"}) == contents (aa.values()));
CHECK (Seq({"a"}) == contents (aa.scope()));
CHECK (mut == aa);
mut.prependChild("");
mut.set("b", "β");
mut.set("a", "α");
CHECK (mut != aa);
mut.replace(a);
CHECK (isnil (mut));
CHECK (Seq({"type=u", "a=α", "a=β", "", "a"}) == contents(a));
CHECK (Seq({"type=u", "a=1", "a"}) == contents(aa));
}
};

View file

@ -55,7 +55,7 @@ namespace test{
/***********************************************************************************//**
* @test Demonstrate how to build, discover and traverse tree-like data representation.
* @test Demonstrate how to build, explore and traverse tree-like data representation.
*
* @see IndexTable
* @see DiffListApplication_test

View file

@ -1970,7 +1970,7 @@ It would thus be desirable to have a fixed-sized allocation, able to hold the pl
!!!Storage
Explicit placements are value objects and stored at the respective usage site, most notably the [[Segmentation]]. They are //not// attached to the placement index in the session, nor do they bear any referential or indexing semantics. The only dynamic side effect of an explicit placement is to keep the reference count of the corresponding MObject up and thus keep it alive and accessible.</pre>
</div>
<div title="ExternalTreeDescription" creator="Ichthyostega" modifier="Ichthyostega" created="201505081647" modified="201505090059" tags="Concepts Model design spec draft" changecount="19">
<div title="ExternalTreeDescription" creator="Ichthyostega" modifier="Ichthyostega" created="201505081647" modified="201506041327" tags="Concepts Model design spec draft" changecount="21">
<pre>//to symbolically represent hierarchically structured elements, without actually implementing them.//
The purpose of this »external« description is to remove the need of a central data model to work against. We consider such a foundation data model as a good starting point, yet harmful for the evolution of any larger structure to be built. According to the subsidiarity principle, we prefer to turn the working data representation into a local concern. Which leaves us with the issue of collaboration. Any collaboration requires, as an underlying, some kind of common understanding. And any formalised, mechanical collaboration requires to represent that common point of attachment -- at least as symbolic representation. The ExternalTreeDescription is shaped to fulfil this need: //in theory,// the whole field could be represented, symbolically, as a network of hierarchically structured elements. Yet, //in practice,// all we need is to conceive the presence of such a representation, as a backdrop to work against. And we do so -- we work against that symbolic representation, by describing ''changes'' made to the structure and its elements. Thus, the description of changes, the ''diff language'', refers to and partially embodies such symbolically represented elements and relations.
&amp;rarr; TreeDiffFundamentals
@ -2019,7 +2019,7 @@ This decision more or less limits the usefulness of simple values as children to
The discriminating ID of any GenNode can serve as a name, and indeed will be used as the name of an attribute within a record.
Now, the interesting question is: what constitues the full identity? Is it the ~ID-string? does it also include some kind of type information, so that two children with the same name, but different types would be considered different? And, moreover, we could even go as far as to make the path part of the identity, so that two otherwise identical elements would be different, when located at different positions within the graph. But since we did not rule out cyclic graphs, the latter idea would necessitate the notion of an //optimal path// --
A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be focribly ordered, or just retaining the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to &quot;supply&quot; a list of children for symbolic representation as they see fit.
A somewhat similar question is the ordering and uniqueness of children. While attributes -- due to the usage of the attribute node's ID as name-key -- are bound to be unique within a given Record, children within the scope of a record could be required to be unique too, making the scope a set. And, of course, children could be focribly ordered, or just retain the initial ordering, or even be completely unordered. On a second thought, it seems wise not to impose any guarantees in that regard, beyond the simple notion of retaining an initial sequence order, the way a »stable« sorting algorithm does. All these more specific ordering properties can be considered the concern of some specific kinds of objects -- which then just happen to &quot;supply&quot; a list of children for symbolic representation as they see fit.
</pre>
</div>
<div title="Factories" modifier="Ichthyostega" created="200708100401" modified="201310132317" tags="def Concepts" changecount="6">
@ -2316,9 +2316,9 @@ For this Lumiera design, we could consider making GOP just another raw media dat
&amp;rarr;see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]]
</pre>
</div>
<div title="GenNode" creator="Ichthyostega" modifier="Ichthyostega" created="201501171413" modified="201505081546" tags="Model GuiIntegration GuiPattern def draft" changecount="11">
<div title="GenNode" creator="Ichthyostega" modifier="Ichthyostega" created="201501171413" modified="201505312131" tags="Model GuiIntegration GuiPattern def draft" changecount="12">
<pre>//Abstract generic node element to build a ~DOM-like rendering of Lumiera's [[session model|HighLevelModel]].//
GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere &quot;outside&quot;, at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual point of reference to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere &quot;outside&quot;, at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual reference frame to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
To be more specific, within the actual model there are [[Placements|Placement]]. These refer to [[M-Objects|MObject]]. Which in turn rely on [[Assets|Asset]]. Moreover, we have some processing rules, and -- last but not least -- the &quot;objects&quot; encountered in the model have state, visible as attributes of atomic value type (integral, floating point, string, boolean, time, time ranges and [[quantised time entities|TimeQuant]]).
A generic node may //represent any of these kind// -- and it may have ~GenNode children, forming a tree. Effectively all of this together makes ~GenNode a ''Monad''.
@ -2444,7 +2444,7 @@ The workspace starts out with a single element, corresponding to the »model roo
Speaking of implementation, this state and update mechanics relies on two crucial provisions: Lumiera's framework for [[tree diff representation|TreeDiffModel]] and the ExternalTreeDescription, which is an abstracted, ~DOM-like rendering of the relevant parts of the session; this model tree is comprised of [[generic node elements|GenNode]] acting as proxy for [[calls into|SessionInterface]] the [[session model|HighLevelModel]] proper.
</pre>
</div>
<div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201503221633" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="30">
<div title="GuiModelUpdate" creator="Ichthyostega" modifier="Ichthyostega" created="201410250121" modified="201506040022" tags="GuiIntegration GuiPattern design decision discuss draft" changecount="31">
<pre>Considerations regarding the [[structure of custom timeline widgets|GuiTimelineWidgetStructure]] highlight again the necessity of a clean separation of concerns and an &quot;open closed design&quot;. For the purpose of updating the timeline(s) to reflect the HighLevelModel in Proc-Layer, several requirements can be identified
* we need incremental updates: we must not start redrawing each and everything on each tiny change
* we need recursive programming, since this is the only sane way to deal with tree like nested structures.
@ -2479,6 +2479,9 @@ A relevant question to be settled is as to where the core of each change is cons
* we might, at the moment of performing the update, acquire a lock from the ProcDispatcher. The update process may then effectively query down into the session datastructure proper, even through the proxy of a diffing process. The obvious downside is that GUI response might block waiting on an extended operation in Proc, especially when a new build process was started meanwhile. A remedy might be to abort the update in such cases, since its effects will be obsoleted by the build process anyway.
* alternatively, we might incorporate a complete snapshot of all information relevant for the GUI into the GuiModel. Update messages from Proc must be complete and self contained in this case, since our goal is to avoid callbacks. Following this scheme, the first stage of any update would be a push from Proc to the GuiModel, followed by a callback pull from within the individual widgets receiving the notification later. This is the approach we choose for the Lumiera GUI.
!!!information to represent and to derive
The purpose of the GuiModel is to represent an anchor point for the structures //actually relevant for the UI.// To put that into context, the model in the session is not bound to represent matters exactly in the way to be rendered within the GUI. All we can expect is for the //build process// -- upon completion -- to generate a view of the actually altered parts, detailing the information relevant for presentation. Thus we do retain an ExternalTreeDescription of all the information received this way within the GuiModel. Whenever a completed build process sends an updated state, we use the diff framework to determine the actually relevant differences -- both for triggering the corresponding GUI widgets, and for forwarding this focussed diff information to these widgets when they call back from the UI event thread to pull the actual changes.
!!!switch of typed sub-context
When dealing with structural (tree) diffing, there is a specific twist regarding child nodes of mixed type: In the general case, we can not assume that all children of a given node are of the same kind. The classical example is (X)HTML, where a node has //attributes,// various //nested tags// and //nested text content.// The //generic node// thus must be modelled as having several collections of children -- both ordered and un-ordered collections are possible -- and the content of each such sub-collection is itself polymorphic. This constitutes a challenge for the representation of data within the tree diff format. These difficulties can be overcome as follows
#anything, even nested &quot;raw&quot; content is represented //as node//
@ -7514,7 +7517,7 @@ Thus no server and no network connection is needed. Simply open the file in your
* see [[Homepage|http://tiddlywiki.com]], [[Wiki-Markup|http://tiddlywiki.org/wiki/TiddlyWiki_Markup]]
</pre>
</div>
<div title="TimeMutation" modifier="Ichthyostega" created="201101231344" modified="201105261700" tags="spec discuss draft">
<div title="TimeMutation" modifier="Ichthyostega" created="201101231344" modified="201506032222" tags="spec discuss draft" changecount="1">
<pre>Simple time points are just like values and thus easy to change. The difficulties arise when time values are to be //quantised to an existing time grid.// The first relevant point to note is that for quantised time values, the effect of a change can be disproportional to the cause. Small changes below the threshold might be accumulated, and a tiny change might trigger a jump to the next grid point. While this might be annoying, the yet more complex questions arise when we acknowledge that the amount of the change itself might be related to a time grid.
The problem with modification of quantised values highlights an inner contradiction or conflicting goals within the design
@ -7590,7 +7593,7 @@ Rationale: allowing mutations for Time bears the danger of making ~TimeVar obsol
* err, because MObject will include a Duration separate from the start time in the Placement, Duration needs to be mutable too
!!!usage considerations
{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, while the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
{{red{Question 5/11}}} when moving a clip taken from 50fps media, the new position might be quantised to the 50fps grid established by the media, even when the target timeline runs with 25fps, allowing for finer adjustments based on the intermediate frames present in the source material.
!!!design draft
The special focus of this problem seems to lead itself to a __visitor pattern__ based implementation. Because the basic hierarchy of applicable types is fixed, but the behaviour is open ended (and not yet fully determined). A conventional implementation would scatter this behaviour over all the time entities, thus making it hard to understand and reason about. The classical ~GoF cyclic visitor solution to the contrary allows us to arrange closely related behaviour into thematically grouped visitor classes. As a plus, the concrete action can be bound dynamically, allowing for more flexibility when it comes to dealing with the intricate situations when a quantised time span (= a clip) recieves a quantised mutation (= is re-alligend to a possibly different frame grid)
@ -7940,7 +7943,7 @@ Used this way, diff representation helps to separate structure and raw data in e
:Chunks of raw data are attached inline to the structural diff, assuming that each element implicitly knows the kind of data to expect
</pre>
</div>
<div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201504290114" tags="Model GuiPattern design draft" changecount="31">
<div title="TreeDiffImplementation" creator="Ichthyostega" modifier="Ichthyostega" created="201412210015" modified="201506040013" tags="Model GuiPattern design draft" changecount="33">
<pre>//This page details decisions taken for implementation of Lumiera's diff handling framework//
This topic is rather abstract, since diff handling is multi purpose within Lumiera: Diff representation is seen as a meta language and abstraction mechanism; it enables tight collaboration without the need to tie and tangle the involved implementation data structures. Used this way, diff representation reduces coupling and helps to cut down overall complexity -- so to justify the considerable amount of complexity seen within the diff framework implementation.
@ -7993,6 +7996,16 @@ This design prefers the //pull// approach, with a special twist: we provide a co
!!!representation of objects
It should be noted, that the purpose of this whole architecture is to deal with »remote« stuff -- things we somehow need to refer and deal with, but nothing we can influence immediately, right here: every actual manipulation has to be turned into a message and sent //elsewhere.// This is the only context, where some, maybe even partial, generic and introspective object representation makes sense.
{{red{open questions 6/15}}}
* do we need to //alter// object contents -- or do we just replace?
* to what degree is the distinction between attributes and children even relevant -- beyond the ability to address attributes by-name?
* how do we describe an object from scratch?
* how do we represent the break between attributes and children in this linearised description?
** using a separator element?
** by convention through the element names?
** as additional metadata information sent beforehand?
* we need an object-reference element, since we do not want to copy whole subtrees while processing a diff
Within this framework, we represent //object-like// entities through a special flavour of the GenNode: Basically, an object is a flat collection of children, yet given in accordance to a distinct protocol. The relevant ''meta'' information is spelled out first, followed by the ''attributes'' and finally the ''children''. The distinction between these lies in the mode of handling. Meta information is something we need to know before we're able to deal with the actual stuff. Prominent example is the type of the object. Attributes are considered unordered, and will typically be addressed by-name. Children are an ordered collection of recursive instances of the same data structure. (Incidentally, we do not rule out the possibility that also an attribute holds a recursive subtree; only the mode of access is what makes the distinction).
!!!handling of actual mutation
@ -8064,7 +8077,7 @@ On receiving the terms of this &quot;diff language&quot;, it is possible to gene
i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes.
</pre>
</div>
<div title="TreeMutator" creator="Ichthyostega" modifier="Ichthyostega" created="201503292115" modified="201505310130" tags="Model Concepts GuiPattern design draft" changecount="50">
<div title="TreeMutator" creator="Ichthyostega" modifier="Ichthyostega" created="201503292115" modified="201505312250" tags="Model Concepts GuiPattern design draft" changecount="52">
<pre>The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
!Motivation
@ -8117,7 +8130,7 @@ This construction pattern can be extended to offer several optional extension ho
* how to integrate typed children
!!!working with children
Handling tree structured object data imposes some additional constraints, in comparision to generic changes done to a flat list. One notable difference is that there are pre-existing //attributes,// which can not be added and deleted, are known by-name, not by positional order. Another point worth noting is the fact that child objects may be segregated into several collections by type. Since our goal is to provide an intermediary with the ability to map to arbitrary structures, we need to define the primitive operations necessary for implementing the structural operations represented in the form of a diff
Handling tree structured object data imposes some additional constraints, in comparision to generic changes done to a flat list. One notable difference is that there are pre-existing //attributes,// which can not be added and deleted, are known by-name, not by positional order. Another point worth noting is the fact that child objects may be segregated into several collections by type. Since our goal is to provide an intermediary with the ability to map to arbitrary structures, we need to define the primitive operations necessary for implementing the concrete structural operations represented in the form of a diff
* add a child
* remove a child
* step to the next child
@ -8129,7 +8142,7 @@ All these basic operations are implicitly stateful, i.e. they work against an as
To ease the repetitive part of the wiring, which is necessary for each individual application case, we can allow for some degree of //duck typing,// as far as building the TreeMutator is concerned. If there is a type, which provides the above mentioned functions for child management, these can be hooked up automatically into a suitable adapter. Otherwise, the client may supply closures, using the same definition pattern as shown for the attributes above. Here, the ID argument is optional and denotes a //type filter,// whereas the closure itself must accept a name-ID argument. The purpose of this construction is the ability to manage collections of similar children. For example
{{{
.addChild(&quot;Fork&quot;), [&amp;](string type, string id) {
ForkType kind = determineForkkType(type);
ForkType kind = determineForkType(type);
this.forks_.push_back(MyForkImpl(kind, id);
})
.mutateChild(&quot;Fork&quot;), [&amp;](string id) {