remove the now obsolete, dedicated first diff implementation
yay! this piece of code has served its purpose: it was the blueprint to build a way better design and implementation, which can now cover this "generic tree" case as a special case as well
This commit is contained in:
parent
7a29e260e9
commit
a066650eb7
2 changed files with 141 additions and 397 deletions
|
|
@ -103,7 +103,6 @@
|
|||
|
||||
|
||||
#include "lib/diff/tree-diff.hpp"
|
||||
#include "lib/diff/tree-diff-traits.hpp"
|
||||
#include "lib/diff/tree-diff-mutator-binding.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
|
|
@ -124,375 +123,8 @@ namespace diff{
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* Interpreter for the tree-diff-language to work on arbitrary
|
||||
* opaque target data structures. A concrete strategy to apply a structural diff
|
||||
* to otherwise undisclosed, recursive, tree-like target data. The only requirement
|
||||
* is for this target structure to expose a hook for building a customised
|
||||
* TreeMutator able to work on and transform the private target data.
|
||||
*
|
||||
* In the extended configuration for tree-diff-application to given opaque target
|
||||
* data, the setup uses the [metaprogramming adapter traits](\ref TreeDiffTraits)
|
||||
* to pave a way for building the custom TreeMutator implementation, wired internally
|
||||
* to the given opaque target. Moreover, based on the concrete target type, a suitable
|
||||
* ScopeManager implementation can be provided. Together, these two dynamically created
|
||||
* adapters allow the generic TreeDiffMutatorBinding to perform all of the actual
|
||||
* diff application and mutation task.
|
||||
*
|
||||
* @throws lumiera::error::State when diff application fails due to the
|
||||
* target sequence being different than assumed by the given diff.
|
||||
* @see DiffComplexApplication_test usage example of this combined machinery
|
||||
* @see #TreeDiffInterpreter explanation of the verbs
|
||||
*/
|
||||
template<class TAR>
|
||||
class DiffApplicationStrategy<TAR, enable_if<TreeDiffTraits<TAR>>>
|
||||
: public TreeDiffMutatorBinding
|
||||
{
|
||||
using Scopes = StackScopeManager<TreeMutatorSizeTraits<TAR>::siz>;
|
||||
|
||||
|
||||
TAR& subject_;
|
||||
Scopes scopes_;
|
||||
|
||||
|
||||
TreeMutator*
|
||||
buildMutator (DiffMutable& targetBinding)
|
||||
{
|
||||
scopes_.clear();
|
||||
TreeMutator::Handle buffHandle = scopes_.openScope();
|
||||
targetBinding.buildMutator (buffHandle);
|
||||
return buffHandle.get();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit
|
||||
DiffApplicationStrategy(TAR& subject)
|
||||
: TreeDiffMutatorBinding()
|
||||
, subject_(subject)
|
||||
, scopes_()
|
||||
{ }
|
||||
|
||||
void
|
||||
initDiffApplication()
|
||||
{
|
||||
auto target = mutatorBinding (subject_);
|
||||
buildMutator (target);
|
||||
TreeDiffMutatorBinding::scopeManger_ = &scopes_;
|
||||
TreeDiffMutatorBinding::treeMutator_ = &scopes_.currentScope();
|
||||
REQUIRE (this->treeMutator_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Interpreter for the tree-diff-language to work on GenNode elements
|
||||
* A concrete strategy to apply a structural diff to a target data structure
|
||||
* made from #Record<GenNode> elements. This data structure is assumed to be
|
||||
* recursive, tree-like. But because Record elements are conceived as immutable
|
||||
* and value-like, the tree diff application actually works on a Rec::Mutator
|
||||
* wrapping the target record to be altered through consuming the diff.
|
||||
* @throws lumiera::error::State when diff application fails due to the
|
||||
* target sequence being different than assumed by the given diff.
|
||||
* @see #TreeDiffInterpreter explanation of the verbs
|
||||
*/
|
||||
template<>
|
||||
class DiffApplicationStrategy<Rec::Mutator>
|
||||
: public TreeDiffInterpreter
|
||||
{
|
||||
using Mutator = Rec::Mutator;
|
||||
using Content = Rec::ContentMutator;
|
||||
using Iter = Content::Iter;
|
||||
|
||||
struct ScopeFrame
|
||||
{
|
||||
Mutator& target;
|
||||
Content content;
|
||||
|
||||
ScopeFrame(Mutator& toModify)
|
||||
: target(toModify)
|
||||
, content()
|
||||
{ }
|
||||
|
||||
void init()
|
||||
{
|
||||
target.swapContent (content);
|
||||
content.resetPos();
|
||||
if (not target.empty()) // re-entrance:
|
||||
{ // discard garbage from previous usage.
|
||||
Rec pristineSequence; // must start new sequence from scratch
|
||||
target.swap (pristineSequence);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Storage: a stack of workspaces
|
||||
* used to handle nested child objects */
|
||||
std::stack<ScopeFrame> scopes_;
|
||||
|
||||
|
||||
Mutator& out() { return scopes_.top().target; }
|
||||
Content& src() { return scopes_.top().content; }
|
||||
Iter& srcPos() { return scopes_.top().content.pos; }
|
||||
bool endOfData() { return srcPos() == src().end(); }
|
||||
Rec& alteredRec() { return out(); }
|
||||
|
||||
|
||||
void
|
||||
__expect_in_target (GenNode const& elm, Literal oper)
|
||||
{
|
||||
if (endOfData())
|
||||
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
|
||||
"no (further) elements in target sequence") % oper % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
|
||||
if (elm.matches(Ref::CHILD) and not srcPos()->isNamed())
|
||||
return; // allow for anonymous pick or delete of children
|
||||
|
||||
if (not srcPos()->matches(elm))
|
||||
throw error::State(_Fmt("Unable to %s element %s from target as demanded; "
|
||||
"found element %s on current target position instead")
|
||||
% oper % elm % *srcPos()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_further_elements (GenNode const& elm)
|
||||
{
|
||||
if (endOfData())
|
||||
throw error::State(_Fmt("Premature end of target sequence, still expecting element %s; "
|
||||
"unable to apply diff further.") % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_found (GenNode const& elm, Iter const& targetPos)
|
||||
{
|
||||
if (targetPos == src().end())
|
||||
throw error::State(_Fmt("Premature end of sequence; unable to locate "
|
||||
"element %s in the remainder of the target.") % elm
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_successful_location (GenNode const& elm)
|
||||
{
|
||||
if (endOfData()
|
||||
and not ( elm.matches(Ref::END) // after(_END_) -> its OK we hit the end
|
||||
or (elm.matches(Ref::ATTRIBS) and src().children.empty()))) // after(_ATTRIBS_) -> if there are no children, it's OK to hit the end
|
||||
throw error::State(_Fmt("Unable locate position 'after(%s)'") % elm.idi
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_valid_parent_scope (GenNode::ID const& idi)
|
||||
{
|
||||
if (scopes_.empty())
|
||||
throw error::State(_Fmt("Unbalanced child scope bracketing tokens in diff; "
|
||||
"When leaving scope %s, we fell out of root scope.") % idi.getSym()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
|
||||
if (alteredRec().empty())
|
||||
throw error::State(_Fmt("Corrupted state. When leaving scope %s, "
|
||||
"we found an empty parent scope.") % idi.getSym()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
__expect_end_of_scope (GenNode::ID const& idi)
|
||||
{
|
||||
if (not endOfData())
|
||||
throw error::State(_Fmt("Incomplete diff: when about to leave scope %s, "
|
||||
"not all previously existing elements have been confirmed by the diff. "
|
||||
"At least one spurious element %s was left over") % idi.getSym() % *srcPos()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
|
||||
Iter
|
||||
find_in_current_scope (GenNode const& elm)
|
||||
{
|
||||
Iter end_of_scope = src().currIsAttrib()? src().attribs.end()
|
||||
: src().children.end();
|
||||
return std::find_if (srcPos()
|
||||
,end_of_scope
|
||||
,[&](auto& entry)
|
||||
{
|
||||
return entry.matches(elm);
|
||||
});
|
||||
}
|
||||
|
||||
GenNode const&
|
||||
find_child (GenNode::ID const& idi)
|
||||
{
|
||||
if (alteredRec().empty())
|
||||
throw error::State(_Fmt("Attempt to mutate element %s, but current target data scope is empty. "
|
||||
"Sender and receiver out of sync?") % idi.getSym()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
|
||||
// Short-cut-mutation: look at the last element.
|
||||
// this should be the one just added. BUT NOTE: this fails
|
||||
// when adding an attribute after entering the child scope.
|
||||
// Since attributes are typically values and not mutated,
|
||||
// this inaccuracy was deemed acceptable
|
||||
auto& current = out().accessLast();
|
||||
if (Ref::THIS.matches(idi) or current.matches(idi))
|
||||
return current;
|
||||
|
||||
for (auto & child : alteredRec())
|
||||
if (child.idi == idi)
|
||||
return child;
|
||||
|
||||
throw error::State(_Fmt("Attempt to mutate non existing child record; unable to locate child %s "
|
||||
"after applying the diff. Current scope: %s") % idi.getSym() % alteredRec()
|
||||
, LUMIERA_ERROR_DIFF_CONFLICT);
|
||||
}
|
||||
|
||||
void
|
||||
move_into_new_sequence (Iter pos)
|
||||
{
|
||||
if (src().currIsAttrib())
|
||||
out().appendAttrib (move(*pos)); //////////////TICKET #969 was it a good idea to allow adding attributes "after the fact"?
|
||||
else
|
||||
out().appendChild (move(*pos));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* == Implementation of the list diff application primitives == */
|
||||
|
||||
virtual void
|
||||
ins (GenNode const& n) override
|
||||
{
|
||||
if (n.isNamed())
|
||||
if (n.isTypeID())
|
||||
out().setType (n.data.get<string>());
|
||||
else
|
||||
out().appendAttrib(n); //////////////TICKET #969 dto.
|
||||
else
|
||||
{
|
||||
out().appendChild(n);
|
||||
if (src().currIsAttrib())
|
||||
src().jumpToChildScope();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
del (GenNode const& n) override
|
||||
{
|
||||
__expect_in_target(n, "remove");
|
||||
++src();
|
||||
}
|
||||
|
||||
virtual void
|
||||
pick (GenNode const& n) override
|
||||
{
|
||||
__expect_in_target(n, "pick");
|
||||
move_into_new_sequence (srcPos());
|
||||
++src();
|
||||
}
|
||||
|
||||
virtual void
|
||||
skip (GenNode const& n) override
|
||||
{
|
||||
__expect_further_elements (n);
|
||||
++src();
|
||||
} // assume the actual content has been moved away by a previous find()
|
||||
|
||||
virtual void
|
||||
find (GenNode const& n) override
|
||||
{
|
||||
__expect_further_elements (n);
|
||||
Iter found = find_in_current_scope(n);
|
||||
__expect_found (n, found);
|
||||
move_into_new_sequence (found);
|
||||
} // consume and leave waste, expected to be cleaned-up by skip() later
|
||||
|
||||
|
||||
|
||||
/* == Implementation of the tree diff application primitives == */
|
||||
|
||||
/** cue to a position behind the named node,
|
||||
* thereby picking (accepting) all traversed elements
|
||||
* into the reshaped new data structure as-is */
|
||||
virtual void
|
||||
after (GenNode const& n) override
|
||||
{
|
||||
if (n.matches(Ref::ATTRIBS))
|
||||
while (not endOfData() and srcPos()->isNamed())
|
||||
{
|
||||
move_into_new_sequence (srcPos());
|
||||
++src();
|
||||
}
|
||||
else
|
||||
if (n.matches(Ref::END))
|
||||
while (not endOfData())
|
||||
{
|
||||
move_into_new_sequence (srcPos());
|
||||
++src();
|
||||
}
|
||||
else
|
||||
while (not (endOfData() or srcPos()->matches(n)))
|
||||
{
|
||||
move_into_new_sequence (srcPos());
|
||||
++src();
|
||||
}
|
||||
|
||||
__expect_successful_location(n);
|
||||
|
||||
if (not endOfData() and srcPos()->matches(n))
|
||||
{
|
||||
move_into_new_sequence (srcPos());
|
||||
++src(); // get /after/ an explicitly given position
|
||||
}
|
||||
}
|
||||
|
||||
/** assignment of changed value in one step */
|
||||
virtual void
|
||||
set (GenNode const& n) override
|
||||
{
|
||||
GenNode const& elm = find_child (n.idi);
|
||||
unConst(elm).data = n.data;
|
||||
}
|
||||
|
||||
/** open nested scope to apply diff to child object */
|
||||
virtual void
|
||||
mut (GenNode const& n) override
|
||||
{
|
||||
GenNode const& child = find_child (n.idi);
|
||||
Rec const& childRecord = child.data.get<Rec>();
|
||||
|
||||
TRACE (diff, "tree-diff: ENTER scope %s", cStr(childRecord));
|
||||
scopes_.emplace (mutateInPlace (unConst(childRecord)));
|
||||
scopes_.top().init();
|
||||
}
|
||||
|
||||
/** finish and leave child object scope, return to parent */
|
||||
virtual void
|
||||
emu (GenNode const& n) override
|
||||
{
|
||||
TRACE (diff, "tree-diff: LEAVE scope %s", cStr(alteredRec()));
|
||||
|
||||
__expect_end_of_scope (n.idi);
|
||||
scopes_.pop();
|
||||
__expect_valid_parent_scope (n.idi);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
explicit
|
||||
DiffApplicationStrategy(Rec::Mutator& mutableTargetRecord)
|
||||
: scopes_()
|
||||
{
|
||||
scopes_.emplace(mutableTargetRecord);
|
||||
}
|
||||
|
||||
void
|
||||
initDiffApplication()
|
||||
{
|
||||
REQUIRE (1 == scopes_.size());
|
||||
scopes_.top().init();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}} // namespace lib::diff
|
||||
|
|
|
|||
|
|
@ -22,27 +22,71 @@
|
|||
|
||||
|
||||
/** @file tree-diff-mutator-binding.hpp
|
||||
** Concrete implementation to apply structural changes to unspecific
|
||||
** private data structures with hierarchical nature. This is a variation
|
||||
** of the generic [tree diff applicator](\ref tree-diff-application.hpp),
|
||||
** using the same implementation concept, while relying on an abstract
|
||||
** adapter type, the \ref TreeMutator. Similar to the generic case, when
|
||||
** combined with the generic #DiffApplicator, this allows to receive
|
||||
** linearised structural diff descriptions and apply them to a given
|
||||
** target data structure, which in this case is even a decoupled
|
||||
** private data structure.
|
||||
** Concrete implementation to apply structural changes to hierarchical
|
||||
** data structures. Together with the generic #DiffApplicator, this allows
|
||||
** to receive linearised structural diff descriptions and apply them to
|
||||
** a given target data structure, to effect the corresponding changes.
|
||||
**
|
||||
** ## Design considerations
|
||||
** So this use case is implemented on the same conceptual framework used for
|
||||
** the generic tree diff application, which in turn is -- conceptually -- an
|
||||
** extension of applying a list diff. But, again, we follow the route _not_ to
|
||||
** explicate those conceptual relations in the form of inheritance. This would
|
||||
** While -- conceptually -- our tree diff handling can be seen as an extension
|
||||
** and generalisation of list diffing, the decision was \em not to embody this
|
||||
** extension into the implementation technically, for sake of clarity. This would
|
||||
** be implementation re-use, as opposed to building a new viable abstraction.
|
||||
** No one outside the implementation realm would benefit from such an abstraction,
|
||||
** so we prefer to understand the tree diff language as the abstraction, which
|
||||
** needs to embodied into two distinct contexts of implementation.
|
||||
** needs to embodied into two distinct contexts of implementation. So the list diff
|
||||
** application strategy can be seen as blueprint and demonstration of principles.
|
||||
**
|
||||
** ### Yet another indirection
|
||||
** ### Use cases
|
||||
** Initially, we'd have to distinguish two usage situations
|
||||
** - apply a diff to a generic tree representation, based on Record<GenNode>
|
||||
** - apply a diff to some tree shaped implementation data structure
|
||||
** _Conceptually_ we use the former as blueprint and base to define the semantics
|
||||
** of our »tree-diff language«, while the latter is an extension and can be supported
|
||||
** within the limits of precisely these tree-diff semantics. That is, we support diff
|
||||
** application to all implementation data structures which are _conceptually congruent_
|
||||
** to the generic tree representation. This extension happens in accordance to the
|
||||
** goals of our "diff framework", since we want to allow collaboration between
|
||||
** loosely coupled subsystems, without the need of a shared data structure.
|
||||
**
|
||||
** ### Implementation
|
||||
** On the implementation level though, relations are the other way round: the
|
||||
** framework and technique to enable applying a diff onto private implementation data
|
||||
** is used also to apply the diff onto the (likewise private) implementation of our
|
||||
** generic tree representation. Because the common goal is loose coupling, we strive
|
||||
** at imposing as few requirements or limitations onto the implementation as possible.
|
||||
**
|
||||
** Rather we require the implementation to provide a _binding,_ which can then be used
|
||||
** to _execute_ the changes as dictated by the incoming diff. But since this binding
|
||||
** has to consider intricate details of the diff language's semantics and implementation,
|
||||
** we provide a *Builder DSL*, so the client may assemble the concrete binding from
|
||||
** preconfigured building blocks for the most common cases
|
||||
** - binding "attributes" to object fields
|
||||
** - binding "children" to a STL collection of values
|
||||
** - binding especially to a collection of GenNode elements,
|
||||
** which basically covers also the generic tree representation.
|
||||
**
|
||||
** #### State and nested scopes
|
||||
** For performance reasons, the diff is applied _in place_, directly mutating the
|
||||
** target data structure. This makes the diff application _stateful_ -- and in case of
|
||||
** a *diff conflict*, the target *will be corrupted*.
|
||||
**
|
||||
** Our tree like data structures are conceived as a system of nested scopes. Within
|
||||
** each scope, we have a list of elements, to which a list-diff is applied. On start
|
||||
** of diff application, a one time adapter and intermediary is constructed: the TreeMutator.
|
||||
** This requires the help of the target data structure to set up the necessary bindings,
|
||||
** since the diff applicator as such has no knowledge about the target data implementation.
|
||||
** At this point, the existing (old) contents of the initial scope are moved away into an
|
||||
** internal _source sequence buffer,_ from where they may be "picked" and moved back into
|
||||
** place step by step through the diff. After possibly establishing a new order, inserting
|
||||
** or omitting content within a given "object" (Record), the tree diff language allows in
|
||||
** a second step to _open_ some of the child "objects" by entering nested scope, to effect
|
||||
** further changes within the selected child node. This is done within the `mut(ID)....emu(ID)`
|
||||
** bracketing construct of the diff language. On the implementation side, this recursive
|
||||
** descent and diff application is implemented with the help of a stack, where a new
|
||||
** TreeMutator is constructed whenever we enter (push) a new nested scope.
|
||||
**
|
||||
** #### Yet another indirection
|
||||
** Unfortunately this leads to yet another indirection layer: Implementing a
|
||||
** language in itself is necessarily a double dispatch (we have to abstract the
|
||||
** verbs and we have to abstract the implementation side). And now we're decoupling
|
||||
|
|
@ -51,8 +95,8 @@
|
|||
** functors) to translate the _implementation actions_ underlying the language into
|
||||
** _concrete actions_ working on local data.
|
||||
**
|
||||
** ### Generic and variable parts
|
||||
** So this is a link between generic [»tree diff language«](\ref tree-diff.hpp)
|
||||
** #### Generic and variable parts
|
||||
** So there is a link between generic [»tree diff language«](\ref tree-diff.hpp)
|
||||
** interpretation and the concrete yet undisclosed private data structure, and
|
||||
** most of this implementation is entirely generic, since the specifics are
|
||||
** abstracted away behind the TreeMutator interface. For this reason, most of
|
||||
|
|
@ -62,19 +106,18 @@
|
|||
** for the given concrete target data.
|
||||
**
|
||||
** ### the TreeMutator DSL
|
||||
** In the end, this concrete TreeMutator needs to be built or provided within
|
||||
** the realm of the actual data implementation anyway, so the knowledge about the
|
||||
** actual data layout remains confined there. Unfortunately, implementing a TreeMutator
|
||||
** is quite an involved and technical task, requiring intimate knowledge of structure
|
||||
** and semantics of the diff language. On a second thought, it turns out that most
|
||||
** data implementation will rely on some very common representation techniques,
|
||||
** like using object fields as "attributes" and a STL collection to hold the
|
||||
** "children". Based on this insight, it is possible to provide standard adapters
|
||||
** and building blocks, in the form of an DSL, to generate the actual TreeMutator.
|
||||
** The usage site thus needs to supply only some lambda expressions to specify
|
||||
** how to deal with the representation data values
|
||||
** In the end, for each target structure, a concrete TreeMutator needs to be built
|
||||
** or provided within the realm of the actual data implementation, so the knowledge
|
||||
** about the actual data layout remains confined there. While this requires some
|
||||
** understanding regarding structure and semantics of the diff language, most data
|
||||
** implementation will rely on some very common representation techniques, like using
|
||||
** object fields as "attributes" and a STL collection to hold the "children". Based
|
||||
** on this insight, we provide a DSL with standard adapters and building blocks,
|
||||
** to ease the task of generating ("binding") the actual TreeMutator. The usage site
|
||||
** needs to supply only some functors or lambda expressions to specify how to deal
|
||||
** with the actual representation data values:
|
||||
** - how to construct a new entity
|
||||
** - when the binding actually becomes relevant
|
||||
** - when the binding actually becomes active
|
||||
** - how to determine a diff verb "matches" the actual data
|
||||
** - how to set a value or how to recurse into a sub scope
|
||||
**
|
||||
|
|
@ -97,6 +140,7 @@
|
|||
#include "lib/diff/tree-diff.hpp"
|
||||
#include "lib/diff/tree-mutator.hpp"
|
||||
#include "lib/diff/diff-mutable.hpp"
|
||||
#include "lib/diff/tree-diff-traits.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/format-string.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
|
@ -267,5 +311,73 @@ namespace diff{
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Interpreter for the tree-diff-language to work on arbitrary
|
||||
* opaque target data structures. A concrete strategy to apply a structural diff
|
||||
* to otherwise undisclosed, recursive, tree-like target data. The only requirement
|
||||
* is for this target structure to expose a hook for building a customised
|
||||
* TreeMutator able to work on and transform the private target data.
|
||||
*
|
||||
* This generic setup for diff application covers especially the case where the
|
||||
* target data is a "GenNode tree", and the root is accessible as Rec::Mutator
|
||||
* (We use the Mutator as entry point, since GenNode trees are by default immutable).
|
||||
*
|
||||
* In the extended configuration for tree-diff-application to given opaque target
|
||||
* data, the setup uses the [metaprogramming adapter traits](\ref TreeDiffTraits)
|
||||
* to pave a way for building the custom TreeMutator implementation, wired internally
|
||||
* to the given opaque target. Moreover, based on the concrete target type, a suitable
|
||||
* ScopeManager implementation can be provided. Together, these two dynamically created
|
||||
* adapters allow the generic TreeDiffMutatorBinding to perform all of the actual
|
||||
* diff application and mutation task.
|
||||
*
|
||||
* @throws lumiera::error::State when diff application fails due to the
|
||||
* target sequence being different than assumed by the given diff.
|
||||
* @see DiffComplexApplication_test usage example of this combined machinery
|
||||
* @see #TreeDiffInterpreter explanation of the verbs
|
||||
*/
|
||||
template<class TAR>
|
||||
class DiffApplicationStrategy<TAR, enable_if<TreeDiffTraits<TAR>>>
|
||||
: public TreeDiffMutatorBinding
|
||||
{
|
||||
using Scopes = StackScopeManager<TreeMutatorSizeTraits<TAR>::siz>;
|
||||
|
||||
|
||||
TAR& subject_;
|
||||
Scopes scopes_;
|
||||
|
||||
|
||||
TreeMutator*
|
||||
buildMutator (DiffMutable& targetBinding)
|
||||
{
|
||||
scopes_.clear();
|
||||
TreeMutator::Handle buffHandle = scopes_.openScope();
|
||||
targetBinding.buildMutator (buffHandle);
|
||||
return buffHandle.get();
|
||||
}
|
||||
|
||||
public:
|
||||
explicit
|
||||
DiffApplicationStrategy(TAR& subject)
|
||||
: TreeDiffMutatorBinding()
|
||||
, subject_(subject)
|
||||
, scopes_()
|
||||
{ }
|
||||
|
||||
void
|
||||
initDiffApplication()
|
||||
{
|
||||
auto target = mutatorBinding (subject_);
|
||||
buildMutator (target);
|
||||
TreeDiffMutatorBinding::scopeManger_ = &scopes_;
|
||||
TreeDiffMutatorBinding::treeMutator_ = &scopes_.currentScope();
|
||||
REQUIRE (this->treeMutator_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_DIFF_MUTATOR_BINDING_H*/
|
||||
|
|
|
|||
Loading…
Reference in a new issue