Diff-Listener: fill in implementation
...basically just need to intercept three TreeMutator-operations
This commit is contained in:
parent
a33e236630
commit
d8b20ae497
4 changed files with 130 additions and 9 deletions
119
src/lib/diff/tree-mutator-listener-binding.hpp
Normal file
119
src/lib/diff/tree-mutator-listener-binding.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
TREE-MUTATOR-LISTENER-BINDING.hpp - decorator for TreeMutator to attach change listeners
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2019, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 PAR, typename LIS>
|
||||
class Detector4StructuralChanges
|
||||
: public PAR
|
||||
{
|
||||
LIS changeListener_;
|
||||
bool triggered_ = false;
|
||||
|
||||
public:
|
||||
Detector4StructuralChanges (LIS functor, PAR&& chain)
|
||||
: PAR(std::forward<PAR>(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<class PAR>
|
||||
template<typename LIS>
|
||||
inline auto
|
||||
Builder<PAR>::onStructuralChange (LIS changeListener)
|
||||
{
|
||||
ASSERT_VALID_SIGNATURE (LIS, void(void))
|
||||
|
||||
return chainedBuilder<Detector4StructuralChanges<PAR,LIS>> (changeListener);
|
||||
}
|
||||
|
||||
}//(END)Mutator-Builder decorator components...
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_LISTENER_BINDING_H*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<typename LIS>
|
||||
Builder<TreeMutator>///////////////////////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"
|
||||
|
||||
|
|
|
|||
|
|
@ -104,10 +104,9 @@ namespace test{
|
|||
{
|
||||
buff.create (
|
||||
TreeMutator::build()
|
||||
.attach (collection (subject_)
|
||||
)
|
||||
.onStructuralChange ([&](){ ++structChanges_; }
|
||||
));
|
||||
.attach (collection (subject_))
|
||||
.onStructuralChange ([&](){ ++structChanges_; })
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue