2016-07-24 15:16:06 +02:00
|
|
|
/*
|
2016-09-05 04:36:07 +02:00
|
|
|
TreeDiff - implementation of diff application to opaque data
|
2016-07-24 15:16:06 +02:00
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2016, 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.
|
|
|
|
|
|
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
|
2016-09-05 04:36:07 +02:00
|
|
|
/** @file tree-diff.cpp
|
2016-07-24 15:16:06 +02:00
|
|
|
** 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,
|
|
|
|
|
** as provided by the target data structure. We do not require much
|
|
|
|
|
** additional knowledge regarding the opaque target structure, beyond
|
|
|
|
|
** the ability to construct such a customised TreeMutator. For this reason,
|
|
|
|
|
** the implementation is mostly generic and thus can be emitted here within
|
|
|
|
|
** the library module -- with the exception of the ctor, which indeed picks
|
|
|
|
|
** up some specifics of the concrete usage situation and thus needs to be
|
|
|
|
|
** generated in usage context.
|
|
|
|
|
**
|
2016-09-05 04:36:07 +02:00
|
|
|
** @see tree-diff.hpp
|
|
|
|
|
** @see tree-diff-application.hpp
|
2016-08-29 17:52:35 +02:00
|
|
|
** @see DiffComplexApplication_test
|
2016-07-24 15:16:06 +02:00
|
|
|
**
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
2016-09-05 04:36:07 +02:00
|
|
|
#include "lib/diff/tree-diff-application.hpp"
|
2016-07-24 15:16:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
2016-09-04 23:21:15 +02:00
|
|
|
TreeMutator::~TreeMutator() { } ///< emit VTables here...
|
|
|
|
|
|
|
|
|
|
ScopeManager::~ScopeManager() { };
|
2016-07-29 01:46:11 +02:00
|
|
|
|
|
|
|
|
|
2016-08-29 19:39:19 +02:00
|
|
|
|
2016-09-05 04:36:07 +02:00
|
|
|
|
|
|
|
|
|
2016-07-24 15:16:06 +02:00
|
|
|
/* ======= Implementation of Tree Diff Application via TreeMutator ======= */
|
|
|
|
|
|
|
|
|
|
using util::unConst;
|
|
|
|
|
using util::cStr;
|
|
|
|
|
using util::_Fmt;
|
|
|
|
|
using std::move;
|
|
|
|
|
using std::swap;
|
|
|
|
|
|
|
|
|
|
|
2016-09-05 02:25:07 +02:00
|
|
|
/** @internal possibly recursive invocation to build a TreeMutator binding
|
|
|
|
|
* to an "object" / scope /child node. This function is invoked when creating
|
|
|
|
|
* a DiffApplicator<Rec::Mutator>, and it is then invoked recursively, when
|
|
|
|
|
* the top level TreeMutator enters a nested scope (child node).
|
|
|
|
|
*/
|
|
|
|
|
template<>
|
|
|
|
|
void
|
|
|
|
|
Record<GenNode>::Mutator::buildMutator (BufferHandle buff)
|
|
|
|
|
{
|
|
|
|
|
buff.create (
|
|
|
|
|
TreeMutator::build()
|
|
|
|
|
.attach (*this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-25 15:21:30 +02:00
|
|
|
|
2016-09-05 04:36:07 +02:00
|
|
|
|
|
|
|
|
|
2016-07-25 15:21:30 +02:00
|
|
|
/* == Forwarding: error handling == */
|
|
|
|
|
|
2016-08-10 03:29:32 +02:00
|
|
|
|
2016-07-25 15:21:30 +02:00
|
|
|
void
|
2016-08-26 16:29:50 +02:00
|
|
|
TreeDiffMutatorBinding::__failMismatch (Literal oper, GenNode const& spec)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
throw error::State(_Fmt("Unable to %s element %s. Current shape of target "
|
|
|
|
|
"data does not match expectations") % oper % spec
|
|
|
|
|
, LUMIERA_ERROR_DIFF_CONFLICT);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::__expect_further_elements (GenNode const& elm)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
if (not treeMutator_->hasSrc())
|
2016-08-10 03:29:32 +02:00
|
|
|
throw error::State(_Fmt("Premature end of target sequence, still expecting element %s; "
|
|
|
|
|
"unable to apply diff further.") % elm
|
|
|
|
|
, LUMIERA_ERROR_DIFF_CONFLICT);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::__fail_not_found (GenNode const& elm)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-10 03:29:32 +02:00
|
|
|
throw error::State(_Fmt("Premature end of sequence; unable to locate "
|
|
|
|
|
"element %s in the remainder of the target.") % elm
|
|
|
|
|
, LUMIERA_ERROR_DIFF_CONFLICT);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::__expect_end_of_scope (GenNode::ID const& idi)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-26 02:42:19 +02:00
|
|
|
if (not treeMutator_->completeScope())
|
|
|
|
|
throw error::State(_Fmt("Diff application floundered in nested scope %s; "
|
|
|
|
|
"unexpected extra elements found when diff "
|
|
|
|
|
"should have settled everything.") % idi.getSym()
|
|
|
|
|
, LUMIERA_ERROR_DIFF_CONFLICT);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::__expect_valid_parent_scope (GenNode::ID const& idi)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-25 18:42:51 +02:00
|
|
|
if (0 == scopeManger_->depth())
|
|
|
|
|
throw error::Fatal(_Fmt("Diff application floundered after leaving scope %s; "
|
|
|
|
|
"unbalanced nested scopes, diff attempts to pop root.") % idi.getSym()
|
|
|
|
|
, LUMIERA_ERROR_DIFF_CONFLICT);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-05 04:36:07 +02:00
|
|
|
|
2016-07-25 15:21:30 +02:00
|
|
|
/* == Implementation of the list diff application primitives == */
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::ins (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
investigate and confirm the logic underlying the matchSrc, skipSrc and acceptSrc primitives
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
2016-08-09 23:42:42 +02:00
|
|
|
bool success = treeMutator_->injectNew(n);
|
2016-07-31 00:32:03 +02:00
|
|
|
if (not success)
|
2016-08-26 16:29:50 +02:00
|
|
|
__failMismatch ("insert", n);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::del (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-26 16:29:50 +02:00
|
|
|
__expect_further_elements(n);
|
|
|
|
|
if (not treeMutator_->matchSrc(n))
|
|
|
|
|
__failMismatch("remove", n);
|
|
|
|
|
|
2016-08-10 03:29:32 +02:00
|
|
|
treeMutator_->skipSrc(n);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::pick (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-10 03:29:32 +02:00
|
|
|
bool success = treeMutator_->acceptSrc (n);
|
|
|
|
|
if (not success)
|
2016-08-26 16:29:50 +02:00
|
|
|
__failMismatch ("pick", n);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::skip (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
|
|
|
|
__expect_further_elements (n);
|
2016-08-10 03:29:32 +02:00
|
|
|
treeMutator_->skipSrc(n);
|
2016-07-25 15:21:30 +02:00
|
|
|
} // assume the actual content has been moved away by a previous find()
|
|
|
|
|
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::find (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
|
|
|
|
__expect_further_elements (n);
|
|
|
|
|
// consume and leave waste, expected to be cleaned-up by skip() later
|
2016-08-10 03:29:32 +02:00
|
|
|
if (not treeMutator_->findSrc(n))
|
2016-07-25 15:21:30 +02:00
|
|
|
__fail_not_found (n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-08-26 16:29:50 +02:00
|
|
|
|
|
|
|
|
|
2016-07-25 15:21:30 +02:00
|
|
|
/* == 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 */
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::after (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-25 17:48:40 +02:00
|
|
|
if (not treeMutator_->accept_until(n))
|
2016-07-25 15:21:30 +02:00
|
|
|
__fail_not_found (n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** assignment of changed value in one step */
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::set (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-25 18:42:51 +02:00
|
|
|
if (not treeMutator_->assignElm(n))
|
2016-08-26 16:29:50 +02:00
|
|
|
__failMismatch("assign", n);
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** open nested scope to apply diff to child object */
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::mut (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2018-11-10 02:39:17 +01:00
|
|
|
TreeMutator::Handle buffHandle = scopeManger_->openScope(); // hint: treeMutatorSize(...)
|
2016-08-26 02:42:19 +02:00
|
|
|
if (not treeMutator_->mutateChild(n, buffHandle))
|
2016-08-26 16:29:50 +02:00
|
|
|
__failMismatch("enter nested scope", n);
|
2016-07-25 15:21:30 +02:00
|
|
|
|
2016-08-26 02:42:19 +02:00
|
|
|
TRACE (diff, "tree-diff: ENTER scope %s", cStr(n.idi));
|
|
|
|
|
treeMutator_ = buffHandle.get();
|
2016-07-25 15:21:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** finish and leave child object scope, return to parent */
|
|
|
|
|
void
|
2016-07-28 01:17:50 +02:00
|
|
|
TreeDiffMutatorBinding::emu (GenNode const& n)
|
2016-07-25 15:21:30 +02:00
|
|
|
{
|
2016-08-26 02:42:19 +02:00
|
|
|
TRACE (diff, "tree-diff: LEAVE scope %s", cStr(n.idi));
|
2016-07-25 15:21:30 +02:00
|
|
|
|
|
|
|
|
__expect_end_of_scope (n.idi);
|
2016-08-26 02:42:19 +02:00
|
|
|
treeMutator_ = &scopeManger_->closeScope();
|
2016-07-25 15:21:30 +02:00
|
|
|
__expect_valid_parent_scope (n.idi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-07-24 15:16:06 +02:00
|
|
|
}} // namespace lib::diff
|