DOC: constituting elements of the TreeMutator

write down a first draft for a definiton section,
to describe the fundamental parts involved, when
applying a diff message onto implementation defined
data structures

After a break of tree weeks, I found it difficult to find may way
amidst all those various levels of abstraction. In addition to this
definition, we'll probably also need a high level overview of the
whole diff system operation.
This commit is contained in:
Fischlurch 2016-04-17 03:53:10 +02:00
parent 8167fbff77
commit 69c63045e6
2 changed files with 101 additions and 6 deletions

View file

@ -457,9 +457,9 @@
using Payload = _DefaultPayload<Elm>;
static bool
disable_selector (GenNode const&)
ignore_selector (GenNode const&)
{
return false;
return true; // by default apply diff unconditionally
}
static bool
@ -479,7 +479,7 @@
= CollectionBindingBuilder<Coll
,decltype(&Payload::match)
,decltype(&Payload::construct)
,decltype(&disable_selector)
,decltype(&ignore_selector)
,decltype(&disable_assignment)
,decltype(&disable_childMutation)
>;
@ -490,7 +490,7 @@
return FallbackBindingConfiguration{ coll
, Payload::match
, Payload::construct
, disable_selector
, ignore_selector
, disable_assignment
, disable_childMutation
};

View file

@ -8214,7 +8214,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="201604162306" tags="Model Concepts GuiPattern design draft" changecount="65">
<div title="TreeMutator" creator="Ichthyostega" modifier="Ichthyostega" created="201503292115" modified="201604170104" tags="Model Concepts GuiPattern design draft" changecount="79">
<pre>The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
within the [[diff framework|TreeDiffModel]], this is a crucial joint, since here the abstract, generic, ~DOM-like ExternalTreeDescription meeds opaque, local and undisclosed data structures.
@ -8243,7 +8243,102 @@ Incidentally, the ''diff language'' itself is meant to be readable and expressiv
The canonical example are changes to the editing session. After maybe running some resolution steps, an actual effective change is determined within the session. At this point, a minimal diff message to describe this effective change will be created. This diff message can be logged, to make the change persistent. And then this message will be &quot;cast towards the UI&quot;. There is a communication structure, the UI-Bus, which allows to &quot;direct&quot; such a message towards an element only known and designated by its ID. In fact, this receiver element will be a widget within the UI, and this widget exposes a custom configured TreeMutator to consume this message and effect the corresponding changes within the widget structure of the UI.
!Semantics
The TreeMutator is an //adapter,// allowing the diff language to perform some abstracted generic operations, and at the same time enabling access to an (not further disclosed) implementation data structure in a standardised way.</pre>
The TreeMutator is an //adapter,// allowing the diff language to perform some abstracted generic operations, and at the same time enabling access to an (not further disclosed) implementation data structure in a standardised way. The underlying idea is to deal with //structured data//, i.e. anything which can be described in a DOM like tree. More specifically, our ExternalTreeDescription is the blueprint and exemplifies the generic notion of //structural data to receive a change by diff.// Anything, which can be brought into that form -- after sufficient abstraction -- can be handled through a TreeMutator with appropriate binding, and thus is able to receive diff messages. To be yet more specific
* data is arranged in an order retaining collection
* the type of data within that collection is possibly mixed
* each element within that collection has an unique ID (identity)
* but this uniqueness is only required locally, within a scope at hand
* data can be classified in two ways
** elements might be named (&quot;attributes&quot;) or unnamed (&quot;children&quot;)
** elements might be primitive values or nested records (&quot;objects&quot;)
* and such an &quot;object&quot;
** is recursively again &quot;data&quot; as defined here
** can, optionally, carry a type field (metadata)
** is expected to be spelled out as //attributes first, then children.//
When it comes to applying a diff, we assume that this data abstractly behaves in a specific way, which is outlined by the ''mutation primitives'' -- these are the operations on the TreeMutator interface. Again, to be more specific...
* data elements are //value-like,// but have an attached identity.
* data elements can be moved.
* when the diff application starts, all existing data elements will be moved into a temporary buffer
* the sequence order of the data elements is retained in that move
* after that, the data in the temporary buffer represents the //old state//, while the new state arrives in the form of diff instructions.
* &quot;applying&quot; those diff instructions will transform the exiting data into new shape, possibly insert and delete some elements.
* in the end, the data will be back into the original location, but reshaped and transformed.
!!!mutation primitives
the building blocks, from which such a diff application can be combined, are embodied into the TreeMutator interface
;lifecycle
:the TreeMutator is a one-way, &quot;throwaway&quot; object. It starts (with moving content into the old state buffer), is operated and then disposed.
;ownership
:the TreeMutator //only referres// to the data, but during the application process, it owns the data in the old state buffer
;failure guarantee
:''none'' given. If diff application fails, data is possibly partially transformed, partially in the old state buffer. Thus data is //corrupted.//
;old sequence position
:during diff application, the old state sequence is traversed once. There is an implicit position.
;operation {{{emptySrc}}}
:determine if the old state sequence is empty, i.e. there are no further elements waiting from the old state sequence
;operation {{{skipSrc}}}
:skip the next element in the old state sequence, effectively dropping it into obsolescence
;operation {{{injectNew}}}
:create and inject a new element into the transformed data, based on the given //spec// (from the verb in the diff message)
;operation {{{matchSrc}}}
:decide if the next element in the old state sequence //matches// the given //spec// from the diff verb. This spec is given as GenNode
;operation {{{acceptSrc}}}
:move the next element from the old state as-is into the new transfomed data, after verifying match with the provided //spec//.
;operation {{{accept_until}}}
:repeatedly //accept// elements from the old state buffer, until hitting a condition. The condition is given within the //spec//
:* when the spec is {{{Ref::END}}}, then accept all (remaining) elements, i.e. the remainder of the old sequence as-is.
:* otherwise, accept //until after// finding an element that matches the //spec//. That is, the matching element will be the last one accepted
;operation {{{findSrc}}}
:without changing the current position, delve further into the old state sequence, to find an element //matching the given spec.//
:this element shall than be //accepted// (moved) into the new transformed data, leaving a //waste position// in the old state buffer.
:later, when encountering this //waste position// during regular traversal, it //must be skipped//.
:The regular diff language messages will ensure this to happen in proper order
;operation {{{assignElm}}}
:locate an element //already accepted into// the new transformed data. Then assign a new value corresponding to the given //spec//.
:The search order is to look at the last added element first, then to start search from the beginning of the new transformed data sequence;
:a match is identified by matching the ID part of the given //spec// against the //identity// of the data element in question.
;operation {{{mutateChild}}}
:likewise locate an element //already accepted//. This element is assumed to be a //nested scope// (object)
:for this element, a new TreeMutator shall be //fabricated recursively// and placed into a given buffer. This new TreeMutator is assumed
:to be attached to the child element in question, and can be used to delve into mutation of sub scopes
!!!ways of binding
With ''binding'' we denote the way to attach a TreeMutator to concrete, implementation local data structures. It is accomplished using the ''builder interface''.
Besides being a base class (with default {{{NOP}}} implementation of all mutation primitives), through this builder interface the TreeMutator offers several
pre configured ways of binding to actual data, thereby easing the process to provide a suitably customised TreeMutator instance. Moreover, by using this
builder interface, several ways of binding can be //layered on top of each other,// so the actual TreeMutator is structured into multiple //onion layers,// where
each layer is responsible for a specific aspect of the binding. This is crucial to deal with &quot;objects&quot; comprised of a mixture of children, like e.g. the [[Pipe]]
attached to a clip, or the mixture of clips, effects and labels found within a [[Fork]] (&quot;track&quot;).
;the selector
:this is a lambda {{{isApplicableIf : bool(GenNode const&amp;)}}}, provided by each //onion layer//
:* the selector is //optional// and only necessary when there is more than one binding layer
:* this predicate decides if a given //spec,// as taken from a //diff verb// to be applied, is of relevance for the given //onion layer//
:* if this onion layer is not concerned or affected by this message, it will be passed down to further layers, possibly ignoring it at the end
:* thereby, the meaning of a diff message is //segregated// into various domains or type scopes, each of which corresponds to a //onion layer// at the implementation side
;test mutation target
:this is an &quot;onion layer&quot; for unit tests and diagnostic purposes. It is assumed to be layered on top of other, real bindings.
:it acts like a //wire tap// and logs any encountered primitives into a {{{TestEventLog}}}.
;STL collection binding
:this is the most important case, allowing to bind to objects managed within a STL collection.
:all non trivial and non default aspects of this binding need to be given by installing some ''closures'' (lambdas)
:* lambda {{{matchElement : bool(GenNode const&amp; spec, Elm const&amp; elm)}}}
:** implements the concrete meaning when a local data element can be considered to match the given spec
:* lambda {{{constructFrom : Elm(GenNode const&amp; spec)}}}
:** create a new element to conform to the given spec
:** return this element by value
:* lambda {{{isApplicableIf : bool(GenNode const&amp;)}}}
:** decide applicability of the given //spec// to //this collection binding layer.//
:* lambda {{{assignElement : bool(Elm&amp;, GenNode const&amp;)}}}
:** implement assignment of a new value to the (implementation) data element passed in as reference
:** might return false to indicate that assignment can not be handled
:* lambda {{{buildChildMutator : bool(Elm&amp;, GenNode::ID const&amp;, TreeMutator::MutatorBuffer)}}}
:** fabricate a recursive sub-TreeMutator to deal with the (implementation data) element passed as reference
:** the ID, as taken from the (diff) spec, is also passed, for orientation
:** the lambda is expected to //place// the generated mutator into the given buffer smart handle, which takes ownership of this object
You should note that this entire design is recursive: only after understanding the part, where, for handling a sub-structure, a nested mutator is fabricated and placed into a given buffer, it becomes clear to what effect we're creating a customised mutator: we always need some (relative) parent scope, which happens to know more about the actual data to be treated with a TreeMutator. This scope assists with creating a suitable binding. Obviously, from within that binding, it is known what the sub-structures and children of that local data are all about and what semantics to give to the fundamental operations.
</pre>
</div>
<div title="TreeMutatorEvolution" creator="Ichthyostega" modifier="Ichthyostega" created="201603160255" modified="201603160313" tags="Model Concepts design draft" changecount="5">
<pre>The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.