From d8b20ae497cb9a665f15930decbb7fd85276afaf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 15 Dec 2019 00:03:50 +0100 Subject: [PATCH] Diff-Listener: fill in implementation ...basically just need to intercept three TreeMutator-operations --- .../diff/tree-mutator-listener-binding.hpp | 119 ++++++++++++++++++ src/lib/diff/tree-mutator-noop-binding.hpp | 2 +- src/lib/diff/tree-mutator.hpp | 11 +- .../diff/diff-tree-mutation-listener-test.cpp | 7 +- 4 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 src/lib/diff/tree-mutator-listener-binding.hpp diff --git a/src/lib/diff/tree-mutator-listener-binding.hpp b/src/lib/diff/tree-mutator-listener-binding.hpp new file mode 100644 index 000000000..f345d6d0a --- /dev/null +++ b/src/lib/diff/tree-mutator-listener-binding.hpp @@ -0,0 +1,119 @@ +/* + TREE-MUTATOR-LISTENER-BINDING.hpp - decorator for TreeMutator to attach change listeners + + Copyright (C) Lumiera.org + 2019, 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-mutator-listener-binding.hpp + ** Special supplement for TreeMutator, to attach listeners for notification + ** on specific changes, especially structural ones. TreeMutator is a + ** customisable intermediary, which enables otherwise opaque implementation + ** data structures to receive and respond to generic structural change messages + ** ("tree diff"). + ** + ** Each concrete TreeMutator instance will be configured differently, and this + ** adaptation is done by combining various building blocks. This header defines + ** a special decorator to be layered on top of such a TreeMutator binding; it will + ** not interfere with the received diff, but detect relevant changes and invoke the + ** bound functor after the triggering diff has been applied completely. + ** + ** @note the header tree-mutator-listener-binding.hpp was split off for sake of readability + ** and is included automatically from bottom of tree-mutator.hpp -- no need to + ** include it explicitly + ** + ** @see tree-mutator-test.cpp + ** @see TreeMutator::build() + ** + */ + + +#ifndef LIB_DIFF_TREE_MUTATOR_LISTENER_BINDING_H +#define LIB_DIFF_TREE_MUTATOR_LISTENER_BINDING_H + + +#include "lib/error.hpp" +#include "lib/diff/gen-node.hpp" +#include "lib/diff/tree-mutator.hpp" + +namespace lib { +namespace diff{ + + namespace { // Mutator-Builder decorator components... + + + /** + * Decorator for TreeMutator bindings, to fire a listener function + * when the applied diff describes a structural change. By convention, + * all changes pertaining the sequence of children count as structural. + * Thus, effectively a structural change incurs usage of the `INS`, `DEL` + * `SKIP` or `FIND` verbs, which in turn are translated into the three + * API operation intercepted here. + * @note TreeMutator is a disposable one-way object; + * the triggering mechanism directly relies on that. + */ + template + class Detector4StructuralChanges + : public PAR + { + LIS changeListener_; + bool triggered_ = false; + + public: + Detector4StructuralChanges (LIS functor, PAR&& chain) + : PAR(std::forward(chain)) + , changeListener_{functor} + { } + + /** once the diff is for this level is completely applied, + * the TreeMutator will be discarded, and we can fire our + * change listener at that point. */ + ~Detector4StructuralChanges() + { + if (triggered_) + changeListener_(); + } + + /* ==== TreeMutator API ==== */ + + using Elm = GenNode const&; + + bool injectNew (Elm elm) override { triggered_ = true; return PAR::injectNew (elm); } + bool findSrc (Elm elm) override { triggered_ = true; return PAR::findSrc (elm); } + void skipSrc (Elm elm) override { triggered_ = true; PAR::skipSrc (elm); } + }; + + + + /** Entry point for DSL builder: attach a functor as listener to be notified after structural changes. + */ + template + template + inline auto + Builder::onStructuralChange (LIS changeListener) + { + ASSERT_VALID_SIGNATURE (LIS, void(void)) + + return chainedBuilder> (changeListener); + } + + }//(END)Mutator-Builder decorator components... + +}} // namespace lib::diff +#endif /*LIB_DIFF_TREE_MUTATOR_LISTENER_BINDING_H*/ diff --git a/src/lib/diff/tree-mutator-noop-binding.hpp b/src/lib/diff/tree-mutator-noop-binding.hpp index 51762e2e7..192ad7c8c 100644 --- a/src/lib/diff/tree-mutator-noop-binding.hpp +++ b/src/lib/diff/tree-mutator-noop-binding.hpp @@ -37,7 +37,7 @@ ** default implementation, which likewise absorbs diff verbs, but in a way to trigger ** a lumiera::error::LUMIERA_ERROR_DIFF_CONFLICT. ** - ** @note the header tree-mutator-attribute-binding.hpp was split off for sake of readability + ** @note the header tree-mutator-noop-binding.hpp was split off for sake of readability ** and is included automatically from bottom of tree-mutator.hpp ** ** @see tree-mutator-test.cpp diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index 6851bd15c..c09c04cc5 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -453,12 +453,14 @@ namespace diff{ /** attach a listener function, to be invoked on _structural changes._ * Here, we define any change as "structural", which alters the _sequence_ of * child elements, as opposed to their content. In practice, this listener will - * be invoked _after_ applying a diff with any `INS`, `DEL`, `FIND`, `SKIP` verb, - * or a specific `AFTER` verb (i.e. _not_ `AFTER(Ref::END)`) + * be invoked _after_ applying a diff with any `INS`, `DEL`, `FIND`, `SKIP` verb. + * @remark in theory, one could also drop contents indirectly, by sending only + * part of the necessary PICK verbs (or using AFTER(...)). However, + * such a diff is formally not prohibited, and will indeed be detected as + * error in many but not all cases, in ChildCollectionMutator::completeScope() */ template - Builder///////////////////////TODO - onStructuralChange (LIS changeListener); + auto onStructuralChange (LIS changeListener); }; }//(END) Mutator-Builder... @@ -480,5 +482,6 @@ namespace diff{ #include "lib/diff/tree-mutator-gen-node-binding.hpp" #include "lib/diff/tree-mutator-attribute-binding.hpp" #include "lib/diff/tree-mutator-collection-binding.hpp" +#include "lib/diff/tree-mutator-listener-binding.hpp" #include "lib/diff/tree-mutator-noop-binding.hpp" diff --git a/tests/library/diff/diff-tree-mutation-listener-test.cpp b/tests/library/diff/diff-tree-mutation-listener-test.cpp index ee3eb5756..18cf8e70d 100644 --- a/tests/library/diff/diff-tree-mutation-listener-test.cpp +++ b/tests/library/diff/diff-tree-mutation-listener-test.cpp @@ -104,10 +104,9 @@ namespace test{ { buff.create ( TreeMutator::build() - .attach (collection (subject_) - ) - .onStructuralChange ([&](){ ++structChanges_; } - )); + .attach (collection (subject_)) + .onStructuralChange ([&](){ ++structChanges_; }) + ); }