diff --git a/src/lib/diff/tree-diff-application.hpp b/src/lib/diff/tree-diff-application.hpp index b91a33b8f..322a953c3 100644 --- a/src/lib/diff/tree-diff-application.hpp +++ b/src/lib/diff/tree-diff-application.hpp @@ -2,7 +2,7 @@ TREE-DIFF-APPLICATION.hpp - consume and apply a tree diff Copyright (C) Lumiera.org - 2014, Hermann Vosseler + 2016, 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 @@ -30,67 +30,99 @@ ** ## Design considerations ** 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. More so, - ** since the Record, which serves as foundation for our »External Tree Description«, - ** was made to look and behave like a list-like entity, but built with two distinct - ** scopes at implementation level: the attribute scope and the contents scope. This - ** carries over to the fine points of the list diff language semantics, especially - ** when it comes to fault tolerance and strictness vs fuzziness in diff application. - ** The implementation is thus faced with having to deal with an internal focus and - ** a switch from scope to scope, which adds a lot of complexity. So the list diff + ** 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. So the list diff ** application strategy can be seen as blueprint and demonstration of principles. ** - ** Another point in question is whether to treat the diff application as - ** manipulating a target data structure, or rather building a reshaped copy. - ** The fact that GenNode and Record are designed as immutable values seems to favour - ** the latter, yet the very reason to engage into building this diff framework was - ** how to handle partial updates within a expectedly very large UI model, reflecting - ** the actual session model in Proc-Layer. So we end up working on a Mutator, - ** which clearly signals we're going to reshape and re-rig the target data. + ** ### Use cases + ** Initially, we'd have to distinguish two usage situations + ** - apply a diff to a generic tree representation, based on Record + ** - 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. ** - ** \par related - ** Closely related to this generic application of tree changes is the situation, - ** where we want to apply structural changes to some non-generic and private data - ** structure. In fact, it is possible to _use the same tree diff language_ for - ** this specific case, with the help of an _adapter_. Thus, within our diff - ** framework, we provide a _similar binding_ for the DiffApplicator, but - ** then targeted towards such an [structure adapter](\ref TreeMutator) + ** ### 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. ** - ** ## State and nested scopes - ** Within the level of a single #Record, our tree diff language works similar to - ** the list diff (with the addition of the \c after(ID) verb, which is just a - ** shortcut to accept parts of the contents unaltered). But after possibly rearranging - ** the contents of an "object" (Record), the diff might open some of its child "objects" - ** by entering a nested scope. This is done with the \c mut(ID)....emu(ID) bracketing - ** construct. On the implementation side, this means we need to use a stack somehow. - ** The decision was to manage this stack explicitly, as a std::stack (heap memory). - ** Each entry on this stack is a "context frame" for list diff. Which makes the - ** tree diff applicator a highly statefull component. + ** 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. ** - ** Even more so, since -- for \em performance reasons -- we try to alter the - ** tree shaped data structure \em in-place. We want to avoid the copy of possibly - ** deep sub-trees, when in the end we might be just rearranging their sequence order. - ** This design decision comes at a price tag though - ** - it subverts the immutable nature of \c Record and leads to - ** high dependency on data layout and implementation details of the latter. - ** This is at least prominently marked by working on a diff::Record::Mutator, - ** so the client has first to "open up" the otherwise immutable tree - ** - the actual list diff on each level works by first \em moving the entire - ** Record contents away into a temporary buffer and then \em moving them - ** back into new shape one by one. In case of a diff conflict (i.e. a - ** mismatch between the actual data structure and the assumptions made - ** for the diff message on the sender / generator side), an exception - ** is thrown, leaving the client with a possibly corrupted tree, where - ** parts might even still be stashed away in the temporary buffer, - ** and thus be lost. - ** We consider this unfortunate, yet justified by the very nature of applying a diff. - ** When the user needs safety or transactional behaviour, a deep copy should be made - ** before attaching the #DiffApplicator + ** #### 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*. ** - ** @note as of 2/2016, there is the possibility this solution will become part - ** of a more generic solution, currently being worked out in tree-diff-mutator-binding.hpp + ** 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 + ** the implementation side from a concrete data structure. Which means, that the user + ** will have to provide a set of closures (which might even partially be generated + ** functors) to translate the _implementation actions_ underlying the language into + ** _concrete actions_ working on local data. + ** + ** #### 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 + ** this _delegating implementation_ can be emitted right here, within the + ** library module. With the sole exception of the ctor, which needs to + ** figure out a way how to "get" a suitable TreeMutator implementation + ** for the given concrete target data. + ** + ** ### the TreeMutator DSL + ** 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 active + ** - how to determine a diff verb "matches" the actual data + ** - how to set a value or how to recurse into a sub scope ** ** @see DiffTreeApplication_test + ** @see DiffComplexApplication_test ** @see DiffListApplication_test ** @see GenNodeBasic_test ** @see tree-diff.hpp @@ -103,7 +135,9 @@ #include "lib/diff/tree-diff.hpp" -#include "lib/diff/tree-diff-mutator-binding.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" @@ -114,6 +148,104 @@ namespace lib { namespace diff{ + /** + * Management interface to deal with storage for + * TreeMutators dedicated to nested scopes + */ + class ScopeManager + : boost::noncopyable + { + public: + virtual ~ScopeManager(); ///< this is an interface + + virtual TreeMutator::Handle openScope() =0; + virtual TreeMutator& closeScope() =0; + virtual void clear() =0; + + virtual size_t depth() const =0; + }; + + + + /** + * Typical standard implementation of the ScopeManager. + * Using Heap memory for the nested scopes, we create a stack + * of opaque InPlaceBuffers for each scope, which allows the + * PlantingHandle mechanism to let the target object corresponding + * to this scope build its own TreeMutator implementation into + * this buffer space for this scope. + */ + template + class StackScopeManager + : public ScopeManager + { + using MutatorBuffer = InPlaceBuffer; + using MutatorStack = std::stack; + + /** Allocate Heap Storage for nested TreeMutator(s) */ + MutatorStack scopes_; + + + public: + StackScopeManager() + : scopes_() + { } + + TreeMutator& + currentScope() const + { + if (0 == depth()) + throw error::Logic("Attempt to access the current scope " + "without establishing a root scope beforehand." + , error::LUMIERA_ERROR_LIFECYCLE); + return *scopes_.top(); + } + + + /* ==== ScopeManager interface ==== */ + + virtual TreeMutator::Handle + openScope() + { + scopes_.emplace(); + TreeMutator::Handle placementHandle (scopes_.top()); + + static_assert (buffSiz >= sizeof(typename MutatorStack::value_type) + ,"insufficient working buffer for TreeMutator"); + return placementHandle; + } + + virtual TreeMutator& + closeScope() + { + scopes_.pop(); + REQUIRE (0 < depth(), "attempt to return beyond root scope"); + return *scopes_.top(); + } + + virtual void + clear() + { + while (0 < scopes_.size()) + scopes_.pop(); + + ENSURE (scopes_.empty()); + } + + + virtual size_t + depth() const + { + return scopes_.size(); + } + }; + + + + + + /* ======= Implementation of Tree Diff Application via TreeMutator ======= */ + using util::unConst; using util::cStr; using util::_Fmt; @@ -121,11 +253,138 @@ namespace diff{ using std::swap; + /** + * Implementation of the tree-diff-language to work on arbitrary tree-like data. + * This is the core part of the implementation, which maps the _diff verbs_ + * onto the corresponding _primitive operations_ of the TreeMutator interface. + * The concrete implementation of TreeMutator then is responsible of translating + * those operations into the correct manipulation of target data. + * @note implementation of these functions is emitted in tree-diff.cpp and thus + * within the library module. For an actual diff-applicator, we also need + * to bind to a concrete TreeMutator, for which we need to instantiate + * the template DiffApplicationStrategy with the concrete target type + * as parameter (see below). This concrete instantiation happens + * inline from within the usage context, while inheriting the + * actual implementation logic from this baseclass here + * + * @throws lumiera::error::State when diff application fails structurally. + * @throws _unspecified errors_ when delegated operations fail. + * @see TreeDiffInterpreter explanation of the verbs + * @see DiffComplexApplication_test demonstration of usage + */ + class TreeDiffMutatorBinding + : public TreeDiffInterpreter + { + protected: + TreeMutator* treeMutator_; + ScopeManager* scopeManger_; + + private: + + /* == error handling helpers == */ + + void __failMismatch (Literal oper, GenNode const& spec); + void __expect_further_elements (GenNode const& elm); + void __fail_not_found (GenNode const& elm); + void __expect_end_of_scope (GenNode::ID const& idi); + void __expect_valid_parent_scope (GenNode::ID const& idi); + + + + + /* == Implementation of the list diff application primitives == */ + + virtual void ins (GenNode const& n) override; + virtual void del (GenNode const& n) override; + virtual void pick (GenNode const& n) override; + virtual void skip (GenNode const& n) override; + virtual void find (GenNode const& n) override; + + + /* == Implementation of the tree diff application primitives == */ + + virtual void after(GenNode const& n) override; + virtual void set (GenNode const& n) override; + virtual void mut (GenNode const& n) override; + virtual void emu (GenNode const& n) override; + + + public: + TreeDiffMutatorBinding() + : treeMutator_(nullptr) + , scopeManger_(nullptr) + { } + }; + /** + * 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 DiffApplicationStrategy>> + : public TreeDiffMutatorBinding + { + using Scopes = StackScopeManager::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_APPLICATION_H*/ diff --git a/src/lib/diff/tree-diff-mutator-binding.hpp b/src/lib/diff/tree-diff-mutator-binding.hpp deleted file mode 100644 index 0e7176318..000000000 --- a/src/lib/diff/tree-diff-mutator-binding.hpp +++ /dev/null @@ -1,383 +0,0 @@ -/* - TREE-DIFF-MUTATOR-BINDING.hpp - consume a tree diff, but target arbitrary private data - - Copyright (C) Lumiera.org - 2016, 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 - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - - -/** @file tree-diff-mutator-binding.hpp - ** 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 - ** 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. So the list diff - ** application strategy can be seen as blueprint and demonstration of principles. - ** - ** ### Use cases - ** Initially, we'd have to distinguish two usage situations - ** - apply a diff to a generic tree representation, based on Record - ** - 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 - ** the implementation side from a concrete data structure. Which means, that the user - ** will have to provide a set of closures (which might even partially be generated - ** functors) to translate the _implementation actions_ underlying the language into - ** _concrete actions_ working on local data. - ** - ** #### 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 - ** this _delegating implementation_ can be emitted right here, within the - ** library module. With the sole exception of the ctor, which needs to - ** figure out a way how to "get" a suitable TreeMutator implementation - ** for the given concrete target data. - ** - ** ### the TreeMutator DSL - ** 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 active - ** - how to determine a diff verb "matches" the actual data - ** - how to set a value or how to recurse into a sub scope - ** - ** @todo this is WIP as of 2/2016 -- in the end it might be merged back or even - ** replace the tree-diff-application.hpp - ** - ** @see DiffComplexApplication_test - ** @see DiffTreeApplication_test - ** @see DiffListApplication_test - ** @see GenNodeBasic_test - ** @see tree-diff.hpp - ** - */ - - -#ifndef LIB_DIFF_TREE_DIFF_MUTATOR_BINDING_H -#define LIB_DIFF_TREE_DIFF_MUTATOR_BINDING_H - - -#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" - -#include -#include - -namespace lib { -namespace diff{ - - /** - * Management interface to deal with storage for - * TreeMutators dedicated to nested scopes - */ - class ScopeManager - : boost::noncopyable - { - public: - virtual ~ScopeManager(); ///< this is an interface - - virtual TreeMutator::Handle openScope() =0; - virtual TreeMutator& closeScope() =0; - virtual void clear() =0; - - virtual size_t depth() const =0; - }; - - - - /** - * Typical standard implementation of the ScopeManager. - * Using Heap memory for the nested scopes, we create a stack - * of opaque InPlaceBuffers for each scope, which allows the - * PlantingHandle mechanism to let the target object corresponding - * to this scope build its own TreeMutator implementation into - * this buffer space for this scope. - */ - template - class StackScopeManager - : public ScopeManager - { - using MutatorBuffer = InPlaceBuffer; - using MutatorStack = std::stack; - - /** Allocate Heap Storage for nested TreeMutator(s) */ - MutatorStack scopes_; - - - public: - StackScopeManager() - : scopes_() - { } - - TreeMutator& - currentScope() const - { - if (0 == depth()) - throw error::Logic("Attempt to access the current scope " - "without establishing a root scope beforehand." - , error::LUMIERA_ERROR_LIFECYCLE); - return *scopes_.top(); - } - - - /* ==== ScopeManager interface ==== */ - - virtual TreeMutator::Handle - openScope() - { - scopes_.emplace(); - TreeMutator::Handle placementHandle (scopes_.top()); - - static_assert (buffSiz >= sizeof(typename MutatorStack::value_type) - ,"insufficient working buffer for TreeMutator"); - return placementHandle; - } - - virtual TreeMutator& - closeScope() - { - scopes_.pop(); - REQUIRE (0 < depth(), "attempt to return beyond root scope"); - return *scopes_.top(); - } - - virtual void - clear() - { - while (0 < scopes_.size()) - scopes_.pop(); - - ENSURE (scopes_.empty()); - } - - - virtual size_t - depth() const - { - return scopes_.size(); - } - }; - - - - /* ======= Implementation of Tree Diff Application via TreeMutator ======= */ - - using util::unConst; - using util::cStr; - using util::_Fmt; - using std::move; - using std::swap; - - - /** - * Interpreter for the tree-diff-language to work on arbitrary, undisclosed - * local data structures. The key point to note is that this local data is - * not required to implement any specific interface. The only requirement is - * the ability somehow to support the basic operations of applying a structural - * diff. This is ensured with the help of a _customisable adapter_ the TreeMutator. - * @throws lumiera::error::State when diff application fails structurally. - * @throws _unspecified errors_ when delegated operations fail. - * @see TreeDiffInterpreter explanation of the verbs - * @see DiffComplexApplication_test demonstration of usage - */ - class TreeDiffMutatorBinding - : public TreeDiffInterpreter - { - protected: - TreeMutator* treeMutator_; - ScopeManager* scopeManger_; - - private: - - /* == error handling helpers == */ - - void __failMismatch (Literal oper, GenNode const& spec); - void __expect_further_elements (GenNode const& elm); - void __fail_not_found (GenNode const& elm); - void __expect_end_of_scope (GenNode::ID const& idi); - void __expect_valid_parent_scope (GenNode::ID const& idi); - - - - - /* == Implementation of the list diff application primitives == */ - - virtual void ins (GenNode const& n) override; - virtual void del (GenNode const& n) override; - virtual void pick (GenNode const& n) override; - virtual void skip (GenNode const& n) override; - virtual void find (GenNode const& n) override; - - - /* == Implementation of the tree diff application primitives == */ - - virtual void after(GenNode const& n) override; - virtual void set (GenNode const& n) override; - virtual void mut (GenNode const& n) override; - virtual void emu (GenNode const& n) override; - - - public: - TreeDiffMutatorBinding() - : treeMutator_(nullptr) - , scopeManger_(nullptr) - { } - }; - - - - - - - /** - * 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 DiffApplicationStrategy>> - : public TreeDiffMutatorBinding - { - using Scopes = StackScopeManager::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*/ diff --git a/src/lib/diff/tree-diff-mutator-binding.cpp b/src/lib/diff/tree-diff.cpp similarity index 96% rename from src/lib/diff/tree-diff-mutator-binding.cpp rename to src/lib/diff/tree-diff.cpp index 4d6e37db9..301767708 100644 --- a/src/lib/diff/tree-diff-mutator-binding.cpp +++ b/src/lib/diff/tree-diff.cpp @@ -1,5 +1,5 @@ /* - TreeDiffMutatorBinding - implementation of diff application to opaque data + TreeDiff - implementation of diff application to opaque data Copyright (C) Lumiera.org 2016, Hermann Vosseler @@ -21,7 +21,7 @@ * *****************************************************/ -/** @file tree-diff-mutator-binding.cpp +/** @file tree-diff.cpp ** Implementation of diff application to unspecific private data structures. ** This binding is the link between a generic interpreter for our ** »tree diff language« and a concrete TreeMutator implementation, @@ -33,15 +33,15 @@ ** up some specifics of the concrete usage situation and thus needs to be ** generated in usage context. ** - ** @see tree-diff.cpp - ** @see tree-diff-mutator-binding.cpp + ** @see tree-diff.hpp + ** @see tree-diff-application.hpp ** @see DiffComplexApplication_test ** */ #include "lib/error.hpp" -#include "lib/diff/tree-diff-mutator-binding.hpp" +#include "lib/diff/tree-diff-application.hpp" @@ -54,6 +54,8 @@ namespace diff{ + + /* ======= Implementation of Tree Diff Application via TreeMutator ======= */ using util::unConst; @@ -79,6 +81,8 @@ namespace diff{ + + /* == Forwarding: error handling == */ @@ -129,6 +133,7 @@ namespace diff{ + /* == Implementation of the list diff application primitives == */ void diff --git a/src/lib/diff/tree-diff.hpp b/src/lib/diff/tree-diff.hpp index 2ba598cf3..bd09f6686 100644 --- a/src/lib/diff/tree-diff.hpp +++ b/src/lib/diff/tree-diff.hpp @@ -49,6 +49,12 @@ ** is represented by "opening" the nested record, followed by a recursive diff. ** By implementing the #TreeDiffInterpreter interface (visitor), a concrete usage ** can receive a diff description and possibly apply it to suitable target data. + ** @remarks the standard usage is to create a DiffApplicator(target) and fed + ** a diff sequence to it. We provide a standard implementation of the + ** DiffApplicator + DiffApplicationStrategy, based on a _custimisable intermediary,_ + ** the TreeMutator. This allows to apply a given tree to any suitably compatible + ** target data structure; especially there is a preconfigured setup for our + ** _"generic tree representation"_, diff::Record. ** ** @see diff-language.cpp ** @see tree-diff-application.cpp @@ -116,12 +122,7 @@ namespace diff{ * altered target structure. The `mut(ID)` verb then opens this nested * record for diff handling, and all subsequent diff verbs are to be * interpreted relative to this scope, until the corresponding - * \c emu(ID) verb is encountered. As a special notation, right - * after handling an element with the list diff verbs (i.e. \c ins - * or \c pick or \c find), it is allowed immediately to open the - * nested scope with \c mut(Ref::THIS) -- which circumvents the - * problem that it is sometimes difficult to know the precise ID, - * especially when hand-writing a diff to populate a data structure. ////////TICKET #996 : `Ref::THIS` is a questionable feature + * \c emu(ID) verb is encountered. * - \c emu bracketing construct and counterpart to \c mut(ID). This verb * must be given precisely at the end of the nested scope (it is * not allowed to "return" from the middle of a scope, for sake @@ -143,7 +144,7 @@ namespace diff{ virtual void skip(GenNode const& n) =0; virtual void after(GenNode const&n) =0; - virtual void set (GenNode const&n) =0; + virtual void set (GenNode const& n) =0; virtual void mut (GenNode const& n) =0; virtual void emu (GenNode const& n) =0; }; diff --git a/tests/library/diff/diff-complex-application-test.cpp b/tests/library/diff/diff-complex-application-test.cpp index dae317bd3..931340645 100644 --- a/tests/library/diff/diff-complex-application-test.cpp +++ b/tests/library/diff/diff-complex-application-test.cpp @@ -290,7 +290,6 @@ namespace test{ * @see DiffTreeApplication_test generic variant of tree diff application * @see TreeMutatorBinding_test coverage of the "building blocks" * @see TreeMutator_test base operations of the adapter - * @see tree-diff-mutator-binding.hpp * @see diff-tree-application.hpp * @see tree-diff.hpp */