2015-08-30 04:57:32 +02:00
|
|
|
|
/*
|
|
|
|
|
|
GenNode - generic node element for tree like data representation
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2015, Hermann Vosseler <Ichthyostega@web.de>
|
2015-08-30 04:57:32 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** 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. See the file COPYING for further details.
|
2015-08-30 04:57:32 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2015-08-30 04:57:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file gen-node.cpp
|
|
|
|
|
|
** Generic node element (implementation parts).
|
|
|
|
|
|
** Some of the more technical details regarding value access and
|
|
|
|
|
|
** comparisons has been moved down within this compilation unit,
|
|
|
|
|
|
** to cut down compilation time.
|
|
|
|
|
|
**
|
|
|
|
|
|
** \par comparison and match
|
|
|
|
|
|
**
|
|
|
|
|
|
** The DataCap element provides a set of functions to check for \em equivalence
|
|
|
|
|
|
** or match. These are used to build a recursive containment check. To implement
|
|
|
|
|
|
** such predicates, we need to build a one-way off visitor for use with lib::Variant.
|
|
|
|
|
|
** These specifically tailored functors only define \c handle(TY) functions for the
|
|
|
|
|
|
** cases actually of interest. All other cases invoke the default handling, which
|
|
|
|
|
|
** returns \c false.
|
|
|
|
|
|
**
|
2019-12-12 23:41:26 +01:00
|
|
|
|
** @see gen-node-test.cpp
|
2015-08-30 04:57:32 +02:00
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
|
|
|
|
|
#include "lib/diff/diff-language.hpp"
|
|
|
|
|
|
#include "lib/diff/gen-node.hpp"
|
|
|
|
|
|
#include "lib/util-quant.hpp"
|
|
|
|
|
|
#include "lib/variant.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-27 22:14:38 +01:00
|
|
|
|
using util::join;
|
2015-08-30 04:57:32 +02:00
|
|
|
|
using boost::lexical_cast;
|
|
|
|
|
|
using lib::time::TimeValue;
|
2024-03-27 22:14:38 +01:00
|
|
|
|
using lib::transformIterator;
|
2015-08-30 04:57:32 +02:00
|
|
|
|
using util::almostEqual;
|
|
|
|
|
|
using lib::hash::LuidH;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
|
|
/* symbolic marker ID references
|
|
|
|
|
|
* used within the tree diff language
|
2016-02-12 23:46:10 +01:00
|
|
|
|
* to mark specific scopes and situations
|
2015-08-30 04:57:32 +02:00
|
|
|
|
*/
|
2016-02-12 23:46:10 +01:00
|
|
|
|
const Ref Ref::I ("_I_");
|
|
|
|
|
|
const Ref Ref::NO ("_NO_");
|
2015-10-09 03:03:27 +02:00
|
|
|
|
const Ref Ref::END ("_END_");
|
add support for Ref::THIS (questionable, #996)
while simple to add into the implementation, this whole feature
seems rather qestionable to me now, thus I've added a Ticket
to be revisited later.
In a nutshell, right here, when implementing the binding layer
for STL collections, it is easy to enable the framework to treat
Ref::THIS properly, but the *actual implementation* will necessarily
be offloaded onto each and every concrete binding implementation.
Thus client code would have to add support for an rather obscure
shortcut within the Diff language. The only way to avoid this
would be to change the semantics of the "match"-lambda: if this
binding would rather be a back-translation of implementation data
into GenNode::ID values, then we'd be able to implement Ref::THIS
natively. But such an approach looks like a way inferiour deisgn
to me; having delegated the meaning of a "match" to the client
seems like an asset, since it is both natural and opens a lot
of flexibility, without adding complexity.
For that reason I tend to avoid that shortcut now, in the hope
to be able to drop it entirely from the language
2016-04-18 01:21:38 +02:00
|
|
|
|
const Ref Ref::THIS ("_THIS_"); ////////TICKET #996 : Feature of questionable usefulness. Maybe dispensable?
|
2015-10-09 03:03:27 +02:00
|
|
|
|
const Ref Ref::CHILD ("_CHILD_");
|
|
|
|
|
|
const Ref Ref::ATTRIBS("_ATTRIBS_");
|
2015-08-30 04:57:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-19 17:22:41 +01:00
|
|
|
|
/** Implementation of content equality test, delgating to content
|
2015-08-30 04:57:32 +02:00
|
|
|
|
* @throws error::Logic when the given other DataCap
|
|
|
|
|
|
* does not hold a value of the same type than
|
|
|
|
|
|
* this DataCap.
|
|
|
|
|
|
* @remarks since the GenNode::ID is generated including a type hash,
|
|
|
|
|
|
* the equality operator of GenNode ensures this content test
|
|
|
|
|
|
* is only called on a compatible DataCap.
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchData (DataCap const& o) const
|
|
|
|
|
|
{
|
|
|
|
|
|
class EqualityTest
|
|
|
|
|
|
: public Variant<DataValues>::Predicate
|
|
|
|
|
|
{
|
|
|
|
|
|
DataCap const& o_;
|
|
|
|
|
|
|
|
|
|
|
|
#define DERIVE_EQUALITY(_TY_) \
|
|
|
|
|
|
virtual bool handle (_TY_ const& val) override { return (o_.get<_TY_>() == val); }
|
|
|
|
|
|
|
|
|
|
|
|
DERIVE_EQUALITY (int)
|
|
|
|
|
|
DERIVE_EQUALITY (int64_t)
|
|
|
|
|
|
DERIVE_EQUALITY (short)
|
|
|
|
|
|
DERIVE_EQUALITY (char)
|
|
|
|
|
|
DERIVE_EQUALITY (bool)
|
|
|
|
|
|
DERIVE_EQUALITY (double)
|
|
|
|
|
|
DERIVE_EQUALITY (string)
|
|
|
|
|
|
DERIVE_EQUALITY (time::Time)
|
|
|
|
|
|
DERIVE_EQUALITY (time::Offset)
|
|
|
|
|
|
DERIVE_EQUALITY (time::Duration)
|
|
|
|
|
|
DERIVE_EQUALITY (time::TimeSpan)
|
|
|
|
|
|
DERIVE_EQUALITY (hash::LuidH)
|
|
|
|
|
|
DERIVE_EQUALITY (Rec)
|
|
|
|
|
|
|
2015-10-30 22:02:09 +01:00
|
|
|
|
/** special treatment to allow matching a RecRef
|
|
|
|
|
|
* with an Record or RecRef on the other side */
|
|
|
|
|
|
virtual bool
|
|
|
|
|
|
handle (RecRef const& val) override
|
|
|
|
|
|
{
|
|
|
|
|
|
return o_.matchRec(val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-08-30 04:57:32 +02:00
|
|
|
|
public:
|
|
|
|
|
|
EqualityTest(DataCap const& o)
|
|
|
|
|
|
: o_(o)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
EqualityTest visitor(o);
|
|
|
|
|
|
return accept(visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchNum (int64_t num) const
|
|
|
|
|
|
{
|
|
|
|
|
|
class MatchNumber
|
|
|
|
|
|
: public Variant<DataValues>::Predicate
|
|
|
|
|
|
{
|
|
|
|
|
|
int64_t num_;
|
|
|
|
|
|
|
|
|
|
|
|
#define MATCH_NUMBER(_TY_) \
|
|
|
|
|
|
virtual bool handle (_TY_ const& val) override { return val == num_; }
|
|
|
|
|
|
|
|
|
|
|
|
MATCH_NUMBER (int)
|
|
|
|
|
|
MATCH_NUMBER (int64_t)
|
|
|
|
|
|
MATCH_NUMBER (short)
|
|
|
|
|
|
MATCH_NUMBER (char)
|
|
|
|
|
|
MATCH_NUMBER (double)
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
MatchNumber(int64_t num)
|
|
|
|
|
|
: num_(num)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MatchNumber visitor(num);
|
|
|
|
|
|
return accept(visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchDbl (double d) const
|
|
|
|
|
|
{
|
|
|
|
|
|
class MatchDouble
|
2024-03-27 02:07:32 +01:00
|
|
|
|
: public Variant<DataValues>::Predicate //////////////////////////////////TICKET #1360 floating-point precision
|
2015-08-30 04:57:32 +02:00
|
|
|
|
{
|
|
|
|
|
|
double num_;
|
|
|
|
|
|
|
|
|
|
|
|
#define MATCH_DOUBLE(_TY_) \
|
|
|
|
|
|
virtual bool handle (_TY_ const& val) override { return almostEqual (double(val), num_); }
|
|
|
|
|
|
|
|
|
|
|
|
MATCH_DOUBLE (int)
|
|
|
|
|
|
MATCH_DOUBLE (int64_t)
|
|
|
|
|
|
MATCH_DOUBLE (short)
|
|
|
|
|
|
MATCH_DOUBLE (char)
|
|
|
|
|
|
MATCH_DOUBLE (double)
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
MatchDouble(double d)
|
|
|
|
|
|
: num_(d)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MatchDouble visitor(d);
|
|
|
|
|
|
return accept(visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchTxt (string const& text) const
|
|
|
|
|
|
{
|
|
|
|
|
|
class MatchString
|
|
|
|
|
|
: public Variant<DataValues>::Predicate
|
|
|
|
|
|
{
|
|
|
|
|
|
string const& txt_;
|
|
|
|
|
|
|
|
|
|
|
|
#define MATCH_STRING(_TY_) \
|
|
|
|
|
|
virtual bool handle (_TY_ const& val) override { return lexical_cast<string>(val) == txt_; }
|
|
|
|
|
|
|
|
|
|
|
|
MATCH_STRING (int)
|
|
|
|
|
|
MATCH_STRING (int64_t)
|
|
|
|
|
|
MATCH_STRING (short)
|
|
|
|
|
|
MATCH_STRING (double)
|
|
|
|
|
|
MATCH_STRING (bool)
|
|
|
|
|
|
|
|
|
|
|
|
virtual bool handle (string const& str) override { return str == txt_; }
|
|
|
|
|
|
virtual bool handle (char const& c ) override { return 1 == txt_.length() && txt_.front() == c; }
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
MatchString(string const& text)
|
|
|
|
|
|
: txt_(text)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MatchString visitor(text);
|
|
|
|
|
|
return accept(visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchTime (TimeValue time) const
|
|
|
|
|
|
{
|
|
|
|
|
|
class MatchTime
|
|
|
|
|
|
: public Variant<DataValues>::Predicate
|
|
|
|
|
|
{
|
|
|
|
|
|
TimeValue& t_;
|
|
|
|
|
|
|
|
|
|
|
|
#define MATCH_TIME(_TY_) \
|
|
|
|
|
|
virtual bool handle (_TY_ const& val) override { return val == t_; }
|
|
|
|
|
|
|
|
|
|
|
|
MATCH_TIME (time::Time)
|
|
|
|
|
|
MATCH_TIME (time::Offset)
|
|
|
|
|
|
MATCH_TIME (time::Duration)
|
|
|
|
|
|
MATCH_TIME (time::TimeSpan)
|
|
|
|
|
|
MATCH_TIME (hash::LuidH)
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
MatchTime(TimeValue& t)
|
|
|
|
|
|
: t_(t)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
MatchTime visitor(time);
|
|
|
|
|
|
return accept(visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchBool (bool b) const
|
|
|
|
|
|
{
|
|
|
|
|
|
bool* val = unConst(this)->maybeGet<bool>();
|
|
|
|
|
|
return val && (b == *val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchLuid (LuidH hash) const
|
|
|
|
|
|
{
|
|
|
|
|
|
LuidH* val = unConst(this)->maybeGet<LuidH>();
|
|
|
|
|
|
return val && (hash == *val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchRec (RecRef const& ref) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ref)
|
|
|
|
|
|
return matchRec (*ref.get());
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
RecRef* val = unConst(this)->maybeGet<RecRef>();
|
|
|
|
|
|
return val && val->empty();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
DataCap::matchRec (Rec const& rec) const
|
|
|
|
|
|
{
|
|
|
|
|
|
Rec* val = unConst(this)->maybeGet<Rec>();
|
|
|
|
|
|
if (!val)
|
|
|
|
|
|
{
|
|
|
|
|
|
RecRef* r = unConst(this)->maybeGet<RecRef>();
|
|
|
|
|
|
if (r) val = r->get();
|
|
|
|
|
|
}
|
|
|
|
|
|
return val && (rec == *val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DataCap::operator string() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "DataCap|"+string(this->buffer());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-27 22:14:38 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** compact textual representation of a Record<GenNode> (»object«). */
|
|
|
|
|
|
string renderCompact (Rec const& rec)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto renderChild = [](diff::GenNode const& n){ return renderCompact(n); };
|
|
|
|
|
|
auto renderAttrib = [](diff::GenNode const& n){
|
|
|
|
|
|
return (n.isNamed()? n.idi.getSym()+"=" : "")
|
|
|
|
|
|
+ renderCompact(n);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (Rec::TYPE_NIL==rec.getType()? "" : rec.getType())
|
|
|
|
|
|
+ "{"
|
|
|
|
|
|
+ join (transformIterator (rec.attribs(), renderAttrib))
|
|
|
|
|
|
+ (isnil(rec.scope())? "" : "|")
|
|
|
|
|
|
+ join (transformIterator (rec.scope() , renderChild))
|
|
|
|
|
|
+ "}"
|
|
|
|
|
|
;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string
|
|
|
|
|
|
renderCompact (RecRef const& ref)
|
|
|
|
|
|
{
|
|
|
|
|
|
return "Ref->" + (ref.empty()? util::BOTTOM_INDICATOR
|
|
|
|
|
|
: renderCompact (*ref.get()));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** @remark presentation is oriented towards readability
|
|
|
|
|
|
* - numbers are slightly rounded (see \ref util::showDouble() )
|
|
|
|
|
|
* - time values are displayed timecode-like
|
|
|
|
|
|
* - nested scopes are displayed recursively, enclosed in curly brackets
|
|
|
|
|
|
* @see text-template-gen-node-binding.hpp
|
|
|
|
|
|
*/
|
|
|
|
|
|
string
|
|
|
|
|
|
renderCompact (GenNode const& node)
|
|
|
|
|
|
{
|
|
|
|
|
|
class Renderer
|
|
|
|
|
|
: public Variant<diff::DataValues>::Renderer
|
|
|
|
|
|
{
|
|
|
|
|
|
#define RENDER_CONTENT(_TY_) \
|
|
|
|
|
|
virtual string handle (_TY_ const& val) override { return util::toString(val); }
|
|
|
|
|
|
|
|
|
|
|
|
RENDER_CONTENT (int)
|
|
|
|
|
|
RENDER_CONTENT (int64_t)
|
|
|
|
|
|
RENDER_CONTENT (short)
|
|
|
|
|
|
RENDER_CONTENT (char)
|
|
|
|
|
|
RENDER_CONTENT (double)
|
|
|
|
|
|
RENDER_CONTENT (bool)
|
|
|
|
|
|
RENDER_CONTENT (time::Time)
|
|
|
|
|
|
RENDER_CONTENT (time::Offset)
|
|
|
|
|
|
RENDER_CONTENT (time::Duration)
|
|
|
|
|
|
RENDER_CONTENT (time::TimeSpan)
|
|
|
|
|
|
#undef RENDER_CONTENT
|
|
|
|
|
|
|
|
|
|
|
|
virtual string handle (string const& val) override { return val; }
|
|
|
|
|
|
virtual string handle (LuidH const& val) override { return util::showHash(val, 2);}
|
|
|
|
|
|
virtual string handle (RecRef const& ref) override { return renderCompact(ref); }
|
|
|
|
|
|
virtual string handle (Rec const& rec) override { return renderCompact(rec); }
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
Renderer visitor;
|
|
|
|
|
|
return node.data.accept (visitor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-08-30 04:57:32 +02:00
|
|
|
|
}} // namespace lib::diff
|