2015-03-21 02:00:55 +01:00
|
|
|
|
/*
|
|
|
|
|
|
GEN-NODE.hpp - 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-03-21 02:00:55 +01: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-03-21 02:00:55 +01:00
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @file gen-node.hpp
|
|
|
|
|
|
** Generic building block for tree shaped (meta)data structures.
|
2015-05-02 01:11:39 +02:00
|
|
|
|
** A representation built from GenNode elements is intended to support
|
2015-06-08 01:58:39 +02:00
|
|
|
|
** (limited) introspection of data structures and exchange of mutations
|
2017-08-13 07:09:06 +02:00
|
|
|
|
** in the form of [diff messages](\ref diff-language.hpp).
|
2015-03-21 02:00:55 +01:00
|
|
|
|
**
|
|
|
|
|
|
** Despite of the name, GenNode is \em not meant to be an universal
|
|
|
|
|
|
** data representation; rather it is limited to embody a fixed hard
|
2015-06-08 01:58:39 +02:00
|
|
|
|
** wired set of data types, able to stand-in for attributes
|
2015-05-02 01:11:39 +02:00
|
|
|
|
** and sub scope contents of the lumiera high-level data model.
|
2015-03-21 02:00:55 +01:00
|
|
|
|
**
|
2017-08-13 07:09:06 +02:00
|
|
|
|
** # Anatomy of a GenNode
|
2015-03-21 02:00:55 +01:00
|
|
|
|
**
|
2015-06-04 19:26:45 +02:00
|
|
|
|
** GenNode is a polymorphic value with well defined identity and type.
|
2015-03-21 02:00:55 +01:00
|
|
|
|
** Each element is conceived to be »unique within context« -- as defined
|
|
|
|
|
|
** by the immediately visible scope within a tree like structure.
|
|
|
|
|
|
** Beyond this identity metadata, each GenNode carries a DataCap, which
|
|
|
|
|
|
** is an inline container and attachment point for payload data. Simple
|
|
|
|
|
|
** attribute values can be carried alongside, while more complex types
|
|
|
|
|
|
** or entities bound to a reference and registration system (e.g. Placement)
|
|
|
|
|
|
** will be referred by a suitable reference representation (PlacementID).
|
|
|
|
|
|
** The DataCap is what creates the polymorphic nature, where the common
|
|
|
|
|
|
** interface is mostly limited to managemental tasks (copying of values,
|
2015-06-08 01:58:39 +02:00
|
|
|
|
** external representation).
|
|
|
|
|
|
**
|
|
|
|
|
|
** To represent object-like structures and for building trees, a special
|
|
|
|
|
|
** kind of data type is placed into the DataCap. This type, Record<GenNode>
|
|
|
|
|
|
** is recursive and has the ability to hold both a a set of attributes
|
|
|
|
|
|
** addressable by-name and an (ordered) collection of elements treated
|
|
|
|
|
|
** as children within the scope of the given record.
|
2015-03-21 02:00:55 +01:00
|
|
|
|
**
|
2018-11-09 20:19:45 +01:00
|
|
|
|
** ## The GenNode ID
|
|
|
|
|
|
**
|
|
|
|
|
|
** Each GenNode holds an ID tag, allowing to establish _identical_ and _distinct_
|
|
|
|
|
|
** elements within a scope. This ID is based on lib::idi::BareEntryID, thereby
|
|
|
|
|
|
** providing a human readable symbolic part, and a hash value. By default, these
|
|
|
|
|
|
** GenNode IDs are fabricated such as to hold a non-reproducible, random hash
|
|
|
|
|
|
** value -- however, there are construction flavours allowing to pass in an
|
|
|
|
|
|
** pre-existing distinct Entry-ID.
|
|
|
|
|
|
**
|
|
|
|
|
|
**
|
2017-08-13 07:09:06 +02:00
|
|
|
|
** # Requirements
|
2015-03-21 19:23:41 +01:00
|
|
|
|
**
|
|
|
|
|
|
** GenNode elements are to be used in the diff detection and implementation.
|
|
|
|
|
|
** This implies some requirements for the (opaque) elements used in diff:
|
|
|
|
|
|
** - they need to support the notion of equality
|
|
|
|
|
|
** - we need to derive a key type for usage in index tables
|
2015-05-02 01:11:39 +02:00
|
|
|
|
** - this implies the necessity to support std::less comparisons for tree-maps
|
|
|
|
|
|
** - and the necessity to support hash code generation for unordered (hash)maps
|
|
|
|
|
|
** - moreover, the elements need to be values, able to be copied and handled at will
|
|
|
|
|
|
** - it will be beneficial for these values to support move semantics explicitly
|
2015-03-21 19:23:41 +01:00
|
|
|
|
** - in addition, the tree diffing suggests a mechanism to re-gain the fully
|
2015-06-04 19:26:45 +02:00
|
|
|
|
** typed context, either based on some kind of embedded type tag, or
|
|
|
|
|
|
** alternatively by visitation and matching
|
2015-03-21 19:23:41 +01:00
|
|
|
|
** - finally, the handling of changes prompts us to support installation
|
2018-11-09 20:19:45 +01:00
|
|
|
|
** of a specifically typed _change handling closure_.
|
2015-03-21 19:23:41 +01:00
|
|
|
|
**
|
2017-08-13 07:09:06 +02:00
|
|
|
|
** ## monadic nature?
|
2015-03-21 19:23:41 +01:00
|
|
|
|
**
|
|
|
|
|
|
** As suggested by the usage for representation of tree shaped data, we acknowledge
|
2015-09-11 18:41:18 +02:00
|
|
|
|
** that GenNode could be a Monad. We support the basic operation \em construction,
|
|
|
|
|
|
** and the operation \em flatMap would be trivial to add. To fit in with this generic
|
|
|
|
|
|
** processing pattern, the one element flavours of GenNode are considered the special case,
|
|
|
|
|
|
** while the collective flavours form the base case -- every GenNode can be iterated.
|
|
|
|
|
|
** The \em construction requirement suggests that GenNode may be created readily, just
|
|
|
|
|
|
** by wrapping any given and suitable element, thereby picking up the element's type.
|
2015-03-21 19:23:41 +01:00
|
|
|
|
**
|
2015-09-11 18:41:18 +02:00
|
|
|
|
** But the purpose and goal of the monadic approach is not clear yet (5/2015).
|
|
|
|
|
|
** To begin with, for the task of diff detection and application, it is sufficient
|
|
|
|
|
|
** to get the children as traversable collection and to offer a depth-first expansion.
|
2015-06-04 19:26:45 +02:00
|
|
|
|
**
|
2019-12-12 23:41:26 +01:00
|
|
|
|
** @see GenNode_test
|
2015-03-21 02:00:55 +01:00
|
|
|
|
** @see diff-list-generation-test.cpp
|
|
|
|
|
|
** @see DiffDetector
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef LIB_DIFF_GEN_NODE_H
|
|
|
|
|
|
#define LIB_DIFF_GEN_NODE_H
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/error.hpp"
|
2015-07-03 04:13:16 +02:00
|
|
|
|
#include "lib/idi/entry-id.hpp"
|
2015-08-28 23:09:10 +02:00
|
|
|
|
#include "lib/time/timevalue.hpp"
|
|
|
|
|
|
#include "lib/diff/record.hpp"
|
2015-05-02 01:11:39 +02:00
|
|
|
|
#include "lib/variant.hpp"
|
|
|
|
|
|
#include "lib/util.hpp"
|
|
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
#include <optional>
|
2015-08-28 23:09:10 +02:00
|
|
|
|
#include <utility>
|
2015-05-02 01:11:39 +02:00
|
|
|
|
#include <string>
|
2015-09-11 03:36:22 +02:00
|
|
|
|
#include <deque>
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace diff{
|
|
|
|
|
|
|
|
|
|
|
|
namespace error = lumiera::error;
|
|
|
|
|
|
|
2015-08-16 00:16:30 +02:00
|
|
|
|
struct GenNode;
|
2015-10-09 03:44:38 +02:00
|
|
|
|
struct Ref;
|
2015-05-02 01:11:39 +02:00
|
|
|
|
|
2015-08-17 02:40:57 +02:00
|
|
|
|
/** Define actual data storage and access types used */
|
|
|
|
|
|
template<>
|
|
|
|
|
|
struct RecordSetup<GenNode>
|
|
|
|
|
|
{
|
|
|
|
|
|
using Storage = std::vector<GenNode>;
|
|
|
|
|
|
using ElmIter = typename Storage::const_iterator;
|
|
|
|
|
|
|
|
|
|
|
|
/** using const reference data access
|
|
|
|
|
|
* relevant for handling large subtrees */
|
|
|
|
|
|
using Access = GenNode const&;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-08 01:58:39 +02:00
|
|
|
|
using Rec = Record<GenNode>;
|
2015-07-04 20:50:18 +02:00
|
|
|
|
using RecRef = RecordRef<GenNode>;
|
2015-07-04 14:55:36 +02:00
|
|
|
|
using MakeRec = Rec::Mutator;
|
2025-06-07 18:04:59 +02:00
|
|
|
|
using DataValues = meta::Types<int
|
2015-05-02 01:11:39 +02:00
|
|
|
|
,int64_t
|
|
|
|
|
|
,short
|
|
|
|
|
|
,char
|
|
|
|
|
|
,bool
|
|
|
|
|
|
,double
|
|
|
|
|
|
,string
|
|
|
|
|
|
,time::Time
|
2015-06-08 01:58:39 +02:00
|
|
|
|
,time::Offset
|
2015-05-02 01:11:39 +02:00
|
|
|
|
,time::Duration
|
|
|
|
|
|
,time::TimeSpan
|
|
|
|
|
|
,hash::LuidH
|
2015-07-04 20:50:18 +02:00
|
|
|
|
,RecRef
|
2015-06-08 01:58:39 +02:00
|
|
|
|
,Rec
|
2015-05-02 01:11:39 +02:00
|
|
|
|
>;
|
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
2015-05-02 01:11:39 +02:00
|
|
|
|
|
|
|
|
|
|
class DataCap
|
|
|
|
|
|
: public Variant<DataValues>
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
template<typename X>
|
2015-07-03 18:16:54 +02:00
|
|
|
|
DataCap(X&& x)
|
2015-05-02 01:11:39 +02:00
|
|
|
|
: Variant<DataValues>(std::forward<X>(x))
|
|
|
|
|
|
{ }
|
2015-07-03 04:13:16 +02:00
|
|
|
|
|
2015-07-03 18:08:28 +02:00
|
|
|
|
////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution??
|
2015-07-03 18:16:54 +02:00
|
|
|
|
DataCap(DataCap const&) =default;
|
|
|
|
|
|
DataCap(DataCap&&) =default;
|
|
|
|
|
|
DataCap(DataCap& o)
|
|
|
|
|
|
: DataCap((DataCap const&)o)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
DataCap& operator= (DataCap const&) =default;
|
|
|
|
|
|
DataCap& operator= (DataCap&&) =default;
|
2015-08-28 16:12:04 +02:00
|
|
|
|
|
2015-08-30 00:00:41 +02:00
|
|
|
|
bool matchData (DataCap const&) const;
|
|
|
|
|
|
bool matchNum (int64_t) const;
|
|
|
|
|
|
bool matchTxt (string const&) const;
|
|
|
|
|
|
bool matchTime (time::TimeValue) const;
|
|
|
|
|
|
bool matchBool (bool) const;
|
|
|
|
|
|
bool matchDbl (double) const;
|
|
|
|
|
|
bool matchLuid (hash::LuidH) const;
|
|
|
|
|
|
bool matchRec (RecRef const&) const;
|
|
|
|
|
|
bool matchRec (Rec const&) const;
|
|
|
|
|
|
|
2015-09-11 18:41:18 +02:00
|
|
|
|
struct Locator;
|
|
|
|
|
|
Locator expand() const;
|
|
|
|
|
|
|
|
|
|
|
|
operator string() const;
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
X& get();
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
X const& get() const;
|
2016-02-05 04:03:11 +01:00
|
|
|
|
|
|
|
|
|
|
/** visit _children_ of a nested `Record<GenNode>` */
|
|
|
|
|
|
Rec::scopeIter
|
|
|
|
|
|
childIter() const
|
|
|
|
|
|
{
|
|
|
|
|
|
const Rec* rec = unConst(this)->maybeGet<Rec>();
|
|
|
|
|
|
if (!rec)
|
|
|
|
|
|
return Rec::scopeIter();
|
|
|
|
|
|
else
|
|
|
|
|
|
return rec->scope();
|
|
|
|
|
|
}
|
2020-03-14 23:52:04 +01:00
|
|
|
|
|
|
|
|
|
|
/** determine if payload constitutes a nested scope ("object") */
|
|
|
|
|
|
bool isNested() const;
|
|
|
|
|
|
|
|
|
|
|
|
/** peek into the type field of a nested `Record<GenNode>` */
|
|
|
|
|
|
string recordType() const;
|
|
|
|
|
|
|
|
|
|
|
|
/** peek into the attributes of a nested Record */
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
std::optional<X>
|
|
|
|
|
|
retrieveAttribute (string key) const;
|
|
|
|
|
|
|
|
|
|
|
|
bool hasAttribute (string key) const;
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
Rec* maybeAccessNestedRec();
|
2015-05-02 01:11:39 +02:00
|
|
|
|
};
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** generic data element node within a tree */
|
2015-07-03 04:13:16 +02:00
|
|
|
|
struct GenNode
|
2015-03-21 02:00:55 +01:00
|
|
|
|
{
|
2015-07-03 04:13:16 +02:00
|
|
|
|
class ID
|
|
|
|
|
|
: public idi::BareEntryID
|
|
|
|
|
|
{
|
2015-08-16 00:16:30 +02:00
|
|
|
|
friend struct GenNode;
|
2015-07-03 04:13:16 +02:00
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
ID (X*, string const& symbolicID)
|
|
|
|
|
|
: idi::BareEntryID (symbolicID,
|
|
|
|
|
|
idi::getTypeHash<X>())
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2018-11-09 22:30:35 +01:00
|
|
|
|
ID (idi::BareEntryID&& rawD)
|
|
|
|
|
|
: idi::BareEntryID{move (rawD)}
|
2018-11-09 20:19:45 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
public:
|
2018-10-12 23:42:56 +02:00
|
|
|
|
explicit
|
2015-07-03 04:13:16 +02:00
|
|
|
|
ID (GenNode const& node)
|
|
|
|
|
|
: ID(node.idi)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
// standard copy operations acceptable
|
2015-07-03 19:18:49 +02:00
|
|
|
|
|
|
|
|
|
|
operator string() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "ID(\""+getSym()+"\")";
|
|
|
|
|
|
}
|
2015-07-03 04:13:16 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
2015-07-03 18:21:46 +02:00
|
|
|
|
|
|
|
|
|
|
//------GenNode Data fields---
|
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
ID idi;
|
|
|
|
|
|
DataCap data;
|
|
|
|
|
|
|
2015-07-03 18:08:28 +02:00
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
template<typename X>
|
2015-07-03 18:16:54 +02:00
|
|
|
|
GenNode(X&& val)
|
2015-07-03 04:13:16 +02:00
|
|
|
|
: idi(&val, buildChildID<X>())
|
|
|
|
|
|
, data(std::forward<X>(val))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
GenNode(string const& symbolicID, X&& val)
|
|
|
|
|
|
: idi(&val, symbolicID)
|
|
|
|
|
|
, data(std::forward<X>(val))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2025-07-02 22:18:39 +02:00
|
|
|
|
GenNode(string const& symbolicID, CStr text)
|
2015-07-03 04:13:16 +02:00
|
|
|
|
: GenNode(symbolicID, string(text))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2025-07-02 22:18:39 +02:00
|
|
|
|
GenNode(CStr text)
|
2015-07-03 04:13:16 +02:00
|
|
|
|
: GenNode(string(text))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2015-07-03 18:16:54 +02:00
|
|
|
|
////////////////////////TICKET #963 Forwarding shadows copy operations -- generic solution??
|
|
|
|
|
|
GenNode(GenNode const&) =default;
|
|
|
|
|
|
GenNode(GenNode&&) =default;
|
2015-10-30 05:41:36 +01:00
|
|
|
|
GenNode(GenNode& o) : GenNode((GenNode const&)o) { }
|
|
|
|
|
|
GenNode(Ref const& r);
|
|
|
|
|
|
GenNode(Ref & r);
|
|
|
|
|
|
GenNode(Ref && r);
|
2015-07-03 18:16:54 +02:00
|
|
|
|
|
2016-02-13 22:14:31 +01:00
|
|
|
|
/** copy assignment
|
|
|
|
|
|
* @remarks we need to define our own version here for sake of sanity.
|
|
|
|
|
|
* The reason is that we use inline storage (embedded within lib::Variant)
|
|
|
|
|
|
* and that we deliberately _erase_ the actual type of data stored inline.
|
|
|
|
|
|
* Because we still do want copy assignment, in case the payload data
|
|
|
|
|
|
* supports this, we use a "virtual copy operator", where in the end
|
|
|
|
|
|
* the storage buffer within lib::Variant has to decide if assignment
|
|
|
|
|
|
* is possible. Only data with the same type may be assigned and we
|
|
|
|
|
|
* prevent change of the (implicit) data type through assignment.
|
|
|
|
|
|
* This check might throw, and for that reason we're better off
|
|
|
|
|
|
* to perform the _data assignment_ first. The probability for
|
|
|
|
|
|
* EntryID assignment to fail is low (but it may happen!).
|
|
|
|
|
|
* @note the use of inline storage turns swapping of data
|
|
|
|
|
|
* into an expensive operation, involving a temporary.
|
|
|
|
|
|
* This rules out the copy-and-swap idiom.
|
|
|
|
|
|
*/
|
|
|
|
|
|
GenNode&
|
|
|
|
|
|
operator= (GenNode const& o)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (&o != this)
|
|
|
|
|
|
{
|
|
|
|
|
|
data = o.data;
|
|
|
|
|
|
idi = o.idi;
|
|
|
|
|
|
}
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GenNode&
|
|
|
|
|
|
operator= (GenNode&& o)
|
|
|
|
|
|
{
|
|
|
|
|
|
ASSERT (&o != this);
|
|
|
|
|
|
data = std::forward<DataCap>(o.data);
|
|
|
|
|
|
idi = std::forward<ID>(o.idi);
|
|
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//note: NOT defining a swap operation, because swapping inline storage is pointless!
|
|
|
|
|
|
|
2015-07-03 18:16:54 +02:00
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
2016-02-05 04:03:11 +01:00
|
|
|
|
/** @internal diagnostics helper */
|
2015-08-28 23:09:10 +02:00
|
|
|
|
operator string() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "GenNode-"+string(idi)+"-"+string(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
bool
|
|
|
|
|
|
isNamed() const
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not util::startsWith (idi.getSym(), "_CHILD_");
|
2015-07-03 04:13:16 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-10-30 05:10:16 +01:00
|
|
|
|
bool
|
|
|
|
|
|
isTypeID() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return "type" == idi.getSym();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-11 20:25:39 +02:00
|
|
|
|
template<typename X>
|
|
|
|
|
|
bool contains (X const& elm) const;
|
2015-07-04 14:55:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
2015-12-26 03:03:46 +01:00
|
|
|
|
bool matches (GenNode const& o) const { return this->matches(o.idi); } ///< @note _not_ comparing payload data. Use equality for that…
|
|
|
|
|
|
bool matches (ID const& id) const { return idi == id; }
|
2015-08-30 00:00:41 +02:00
|
|
|
|
bool matches (int number) const { return data.matchNum(number);}
|
|
|
|
|
|
bool matches (int64_t number) const { return data.matchNum(number);}
|
|
|
|
|
|
bool matches (short number) const { return data.matchNum(number);}
|
|
|
|
|
|
bool matches (char number) const { return data.matchNum(number);}
|
2015-08-30 04:44:20 +02:00
|
|
|
|
bool matches (double number) const { return data.matchDbl(number);}
|
2015-08-30 00:00:41 +02:00
|
|
|
|
bool matches (string text) const { return data.matchTxt(text);}
|
2015-08-30 04:44:20 +02:00
|
|
|
|
bool matches (const char* text) const { return data.matchTxt(text);}
|
2015-08-30 00:00:41 +02:00
|
|
|
|
bool matches (time::TimeValue t) const { return data.matchTime(t); }
|
|
|
|
|
|
bool matches (bool b) const { return data.matchBool(b); }
|
|
|
|
|
|
bool matches (hash::LuidH h) const { return data.matchLuid(h); }
|
|
|
|
|
|
bool matches (RecRef const& ref) const { return data.matchRec(ref); }
|
|
|
|
|
|
bool matches (Rec const& rec) const { return data.matchRec(rec); }
|
|
|
|
|
|
|
2015-09-11 03:36:22 +02:00
|
|
|
|
class ScopeExplorer;
|
2015-07-04 14:55:36 +02:00
|
|
|
|
|
2015-09-11 20:00:36 +02:00
|
|
|
|
struct ScopeExplorerIterator;
|
|
|
|
|
|
using iterator = ScopeExplorerIterator;
|
|
|
|
|
|
|
|
|
|
|
|
iterator begin() ;
|
|
|
|
|
|
iterator begin() const;
|
|
|
|
|
|
iterator end() ;
|
|
|
|
|
|
iterator end() const;
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-02-05 04:03:11 +01:00
|
|
|
|
using ChildDataIter = TransformIter<Rec::scopeIter, DataCap const&>;
|
|
|
|
|
|
|
|
|
|
|
|
/** visit the _data_ of nested child elements
|
|
|
|
|
|
* @return an iterator over the DataCap elements of all children,
|
|
|
|
|
|
* in case this GenNode actually holds a Record.
|
|
|
|
|
|
* Otherwise an empty iterator.
|
|
|
|
|
|
* @note this iterator visits _only_ the children, which are
|
|
|
|
|
|
* by definition unnamed. It does _not_ visit attributes.
|
|
|
|
|
|
*/
|
|
|
|
|
|
friend ChildDataIter
|
|
|
|
|
|
childData (GenNode const& n)
|
|
|
|
|
|
{
|
|
|
|
|
|
return ChildDataIter{ n.data.childIter()
|
|
|
|
|
|
, [](GenNode const& child) ->DataCap const&
|
|
|
|
|
|
{
|
|
|
|
|
|
return child.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-05 15:55:22 +01:00
|
|
|
|
friend ChildDataIter
|
2017-08-11 23:52:13 +02:00
|
|
|
|
childData (Rec::scopeIter&& scopeIter)
|
2016-02-05 15:55:22 +01:00
|
|
|
|
{
|
2017-08-11 23:52:13 +02:00
|
|
|
|
return ChildDataIter{ std::forward<Rec::scopeIter>(scopeIter)
|
2016-02-05 15:55:22 +01:00
|
|
|
|
, [](GenNode const& child) ->DataCap const&
|
|
|
|
|
|
{
|
|
|
|
|
|
return child.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-05 04:03:11 +01:00
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
friend string
|
|
|
|
|
|
name (GenNode const& node)
|
|
|
|
|
|
{
|
|
|
|
|
|
return node.idi.getSym();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-03 18:21:46 +02:00
|
|
|
|
friend bool
|
|
|
|
|
|
operator== (GenNode const& n1, GenNode const& n2)
|
|
|
|
|
|
{
|
2015-08-28 16:12:04 +02:00
|
|
|
|
return n1.idi == n2.idi
|
2025-06-07 23:59:57 +02:00
|
|
|
|
and n1.data.matchData(n2.data);
|
2015-07-03 18:21:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
friend bool
|
|
|
|
|
|
operator!= (GenNode const& n1, GenNode const& n2)
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not (n1 == n2);
|
2015-07-03 18:21:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-02-13 19:48:50 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* allow for storage in ordered containers, ordering
|
|
|
|
|
|
* based on the human-readable ID within the GenNode.
|
|
|
|
|
|
* @warning this constitutes a _weaker equivalence_ than
|
|
|
|
|
|
* given by the equality comparison (`operator==`),
|
|
|
|
|
|
* since GenNode::ID is an EntryID, which also includes
|
|
|
|
|
|
* the type parameter into the identity (hash). This means,
|
|
|
|
|
|
* two GenNodes with different real payload type but same
|
|
|
|
|
|
* ID symbol will not be equal, but be deemed equivalent
|
|
|
|
|
|
* by this IDComparator. This can be dangerous when building
|
|
|
|
|
|
* a set or map based on this comparator.
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct IDComparator
|
|
|
|
|
|
{
|
|
|
|
|
|
bool
|
|
|
|
|
|
operator() (GenNode const& left, GenNode const& right) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return left.idi.getSym() < right.idi.getSym();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2018-11-09 22:30:35 +01:00
|
|
|
|
/** fabricate a GenNode with the literally given ID */
|
2018-11-09 20:19:45 +01:00
|
|
|
|
template<typename X>
|
|
|
|
|
|
static GenNode
|
2018-11-09 22:30:35 +01:00
|
|
|
|
asAttribute (idi::BareEntryID && rawID, X&& payload)
|
2018-11-09 20:19:45 +01:00
|
|
|
|
{
|
2018-11-09 22:30:35 +01:00
|
|
|
|
return GenNode{ID{move (rawID)}, DataCap{forward<X> (payload)}};
|
2018-11-09 20:19:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
/** mismatch tolerant convenience shortcut to peek
|
|
|
|
|
|
* into the attributes of a nested Record */
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
std::optional<X>
|
|
|
|
|
|
retrieveAttribute (string key) const;
|
|
|
|
|
|
|
|
|
|
|
|
bool hasAttribute (string key) const;
|
|
|
|
|
|
bool isNested() const;
|
2023-05-02 04:16:39 +02:00
|
|
|
|
bool hasChildren() const;
|
2023-05-11 22:47:56 +02:00
|
|
|
|
Rec::scopeIter getChildren() const;
|
|
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
|
2018-11-09 20:19:45 +01:00
|
|
|
|
|
2015-07-04 20:50:18 +02:00
|
|
|
|
protected:
|
|
|
|
|
|
/** @internal for dedicated builder subclasses */
|
|
|
|
|
|
GenNode (ID&& id, DataCap&& d)
|
|
|
|
|
|
: idi(std::move(id))
|
|
|
|
|
|
, data(std::move(d))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
static GenNode::ID
|
|
|
|
|
|
fabricateRefID (string const& symbolicID)
|
|
|
|
|
|
{
|
2015-07-08 04:12:10 +02:00
|
|
|
|
X* typeID(0);
|
2015-07-04 20:50:18 +02:00
|
|
|
|
return ID(typeID, symbolicID);
|
|
|
|
|
|
}
|
2015-07-03 18:21:46 +02:00
|
|
|
|
|
2015-07-03 04:13:16 +02:00
|
|
|
|
private:
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
static string
|
|
|
|
|
|
buildChildID()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "_CHILD_" + idi::generateSymbolicID<X>();
|
|
|
|
|
|
}
|
2015-03-21 02:00:55 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-27 22:14:38 +01:00
|
|
|
|
/** compact textual representation of a GenNode tree */
|
|
|
|
|
|
string renderCompact (GenNode const&);
|
|
|
|
|
|
string renderCompact (RecRef const&);
|
|
|
|
|
|
string renderCompact (Rec const&);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-11 18:41:18 +02:00
|
|
|
|
|
2016-03-24 21:32:56 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* metafunction to detect types able to be wrapped into a GenNode.
|
|
|
|
|
|
* Only a limited and fixed set of types may be placed within a GenNode,
|
|
|
|
|
|
* as defined through the typelist `lib::diff::DataValues`. This metafunction
|
|
|
|
|
|
* allows to enable or disable specialisations and definitions based on the
|
|
|
|
|
|
* fact if a type in question can live within a GenNode.
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename ELM>
|
|
|
|
|
|
struct can_wrap_in_GenNode
|
|
|
|
|
|
{
|
|
|
|
|
|
using Yes = lib::meta::Yes_t;
|
|
|
|
|
|
using No = lib::meta::No_t;
|
|
|
|
|
|
|
|
|
|
|
|
template<class X>
|
|
|
|
|
|
static Yes check(typename variant::CanBuildFrom<X, DataValues>::Type*);
|
|
|
|
|
|
template<class X>
|
|
|
|
|
|
static No check(...);
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
static const bool value = (sizeof(Yes)==sizeof(check<ELM>(0)));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-17 19:00:55 +02:00
|
|
|
|
/* === iteration / recursive expansion === */
|
2015-09-11 18:41:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
2015-09-11 03:36:22 +02:00
|
|
|
|
/**
|
2015-09-11 18:41:18 +02:00
|
|
|
|
* @internal Helper to refer to any element position,
|
|
|
|
|
|
* irrespective if on top level or within a nested scope
|
|
|
|
|
|
* @remarks typically used within lib::IterStateWrapper
|
|
|
|
|
|
* @see DataCap#expand()
|
2015-09-11 03:36:22 +02:00
|
|
|
|
*/
|
2015-09-11 18:41:18 +02:00
|
|
|
|
struct DataCap::Locator
|
2015-09-11 03:36:22 +02:00
|
|
|
|
{
|
2015-09-11 18:41:18 +02:00
|
|
|
|
const GenNode* node_;
|
|
|
|
|
|
Rec::iterator scope_;
|
|
|
|
|
|
|
|
|
|
|
|
Locator()
|
|
|
|
|
|
: node_(nullptr)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
Locator(GenNode const& n)
|
|
|
|
|
|
: node_(&n)
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
Locator(Rec const& r)
|
|
|
|
|
|
: node_(nullptr)
|
|
|
|
|
|
, scope_(r.begin())
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
const GenNode *
|
|
|
|
|
|
get() const
|
2015-09-11 03:36:22 +02:00
|
|
|
|
{
|
2015-09-11 18:41:18 +02:00
|
|
|
|
return node_? node_
|
|
|
|
|
|
: scope_? scope_.operator->()
|
|
|
|
|
|
: nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* === Iteration control API for IterStateWrapper == */
|
2015-09-11 03:36:22 +02:00
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
bool
|
|
|
|
|
|
checkPoint() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->get();
|
|
|
|
|
|
}
|
2015-09-11 18:41:18 +02:00
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
GenNode const&
|
|
|
|
|
|
yield() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return *get();
|
|
|
|
|
|
}
|
2015-09-11 18:41:18 +02:00
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
void
|
|
|
|
|
|
iterNext()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (node_)
|
|
|
|
|
|
node_ = nullptr;
|
|
|
|
|
|
else
|
|
|
|
|
|
++scope_;
|
|
|
|
|
|
}
|
2015-09-11 18:41:18 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Building block for monad-like depth-first expansion of a GenNode.
|
2015-09-11 19:23:40 +02:00
|
|
|
|
* When used within lib::IterStateWrapper, the result is an Iterator
|
2023-05-02 04:16:39 +02:00
|
|
|
|
* to visit the contents of a GenNode tree recursively depth-fist.
|
2015-09-11 18:41:18 +02:00
|
|
|
|
*/
|
|
|
|
|
|
class GenNode::ScopeExplorer
|
|
|
|
|
|
{
|
2024-11-26 22:15:33 +01:00
|
|
|
|
using ScopeIter = IterStateWrapper<DataCap::Locator>;
|
2015-09-11 03:36:22 +02:00
|
|
|
|
|
|
|
|
|
|
std::deque<ScopeIter> scopes_;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
ScopeExplorer() { }
|
|
|
|
|
|
ScopeExplorer(GenNode const& n)
|
2015-09-11 18:41:18 +02:00
|
|
|
|
{
|
|
|
|
|
|
scopes_.emplace_back(n);
|
|
|
|
|
|
}
|
2015-09-11 03:36:22 +02:00
|
|
|
|
|
2015-09-24 20:59:04 +02:00
|
|
|
|
size_t
|
|
|
|
|
|
depth() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return scopes_.size();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-11 03:36:22 +02:00
|
|
|
|
/* === Iteration control API for IterStateWrapper == */
|
|
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
bool
|
|
|
|
|
|
checkPoint() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return not scopes_.empty()
|
|
|
|
|
|
and bool(scopes_.back());
|
|
|
|
|
|
}
|
2015-09-11 03:36:22 +02:00
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
GenNode const&
|
|
|
|
|
|
yield() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return *(scopes_.back());
|
|
|
|
|
|
}
|
2015-09-11 03:36:22 +02:00
|
|
|
|
|
2017-12-05 03:28:00 +01:00
|
|
|
|
void
|
|
|
|
|
|
iterNext()
|
|
|
|
|
|
{
|
|
|
|
|
|
ScopeIter& current = scopes_.back();
|
|
|
|
|
|
scopes_.emplace_back (current->data.expand());
|
|
|
|
|
|
++current;
|
|
|
|
|
|
while (not scopes_.empty() and not scopes_.back())
|
|
|
|
|
|
scopes_.pop_back();
|
|
|
|
|
|
}
|
2015-09-11 20:00:36 +02:00
|
|
|
|
|
|
|
|
|
|
friend bool
|
|
|
|
|
|
operator== (ScopeExplorer const& s1, ScopeExplorer const& s2)
|
|
|
|
|
|
{
|
2015-09-25 02:38:59 +02:00
|
|
|
|
return not s1.scopes_.empty()
|
2025-06-07 23:59:57 +02:00
|
|
|
|
and not s2.scopes_.empty()
|
|
|
|
|
|
and s1.scopes_.size() == s2.scopes_.size()
|
|
|
|
|
|
and s1.yield() == s2.yield();
|
2015-09-11 20:00:36 +02:00
|
|
|
|
}
|
2015-09-11 03:36:22 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-11 19:23:40 +02:00
|
|
|
|
/** @internal Core operation to expand nested scopes recursively */
|
2015-09-11 20:00:36 +02:00
|
|
|
|
inline DataCap::Locator
|
2015-09-11 19:23:40 +02:00
|
|
|
|
DataCap::expand() const
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
Rec* val = unConst(this)->maybeGet<Rec>();
|
|
|
|
|
|
if (!val)
|
|
|
|
|
|
return Locator();
|
|
|
|
|
|
else
|
|
|
|
|
|
return Locator(*val);
|
|
|
|
|
|
}
|
2015-09-11 19:23:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
2015-09-11 20:00:36 +02:00
|
|
|
|
struct GenNode::ScopeExplorerIterator
|
2024-11-26 22:15:33 +01:00
|
|
|
|
: IterStateWrapper<ScopeExplorer>
|
2015-09-11 20:00:36 +02:00
|
|
|
|
{
|
2024-11-26 17:35:05 +01:00
|
|
|
|
using IterStateWrapper::IterStateWrapper;
|
2015-09-24 20:59:04 +02:00
|
|
|
|
|
|
|
|
|
|
size_t level() const { return unConst(this)->stateCore().depth(); }
|
2015-09-11 20:00:36 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inline GenNode::iterator GenNode::begin() { return iterator(*this); }
|
|
|
|
|
|
inline GenNode::iterator GenNode::begin() const { return iterator(*this); }
|
|
|
|
|
|
inline GenNode::iterator GenNode::end() { return iterator(); }
|
|
|
|
|
|
inline GenNode::iterator GenNode::end() const { return iterator(); }
|
|
|
|
|
|
|
2015-09-11 20:25:39 +02:00
|
|
|
|
template<typename X>
|
2015-09-11 20:12:26 +02:00
|
|
|
|
inline bool
|
2015-09-11 20:25:39 +02:00
|
|
|
|
GenNode::contains (X const& elm) const
|
2015-09-11 20:12:26 +02:00
|
|
|
|
{
|
|
|
|
|
|
for (auto & n : *this)
|
|
|
|
|
|
if (n.matches(elm))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2015-09-11 20:00:36 +02:00
|
|
|
|
|
2015-09-11 19:23:40 +02:00
|
|
|
|
|
|
|
|
|
|
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
/* === References : special treatment on element access === */
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
inline X&
|
|
|
|
|
|
DataCap::get()
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
return Variant<DataValues>::get<X>();
|
|
|
|
|
|
}
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
inline X const&
|
|
|
|
|
|
DataCap::get() const
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
return Variant<DataValues>::get<X>();
|
|
|
|
|
|
}
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
/** especially when accessing for a Record,
|
|
|
|
|
|
* a payload of type \c RecordRef<Record<GenNode>> (aka RecRef)
|
|
|
|
|
|
* will be automatically dereferenced. Effectively this allows
|
|
|
|
|
|
* a GenNode with a RecRef payload to "stand in" for a node holding
|
|
|
|
|
|
* a full Record inline. And it allows the construction of a special
|
|
|
|
|
|
* \link #Ref Ref-GenNode \endlink, which even shares the \em identity
|
|
|
|
|
|
* (the ID) of the referenced record-GenNode.
|
|
|
|
|
|
* @note effectively this opens an indirect loophole to const correctness,
|
|
|
|
|
|
* since it is possible explicitly to retrieve the RecRef from a
|
|
|
|
|
|
* const GenNode and then to access the referred-to Record
|
|
|
|
|
|
* without const. In case this turns out to be problematic,
|
|
|
|
|
|
* we'd have to alter the semantics of RecRef
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline Rec&
|
|
|
|
|
|
DataCap::get()
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
Rec* rec = maybeGet<Rec>();
|
|
|
|
|
|
if (rec) return *rec;
|
|
|
|
|
|
|
|
|
|
|
|
return Variant<DataValues>::get<RecRef>();
|
|
|
|
|
|
}
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline Rec const&
|
|
|
|
|
|
DataCap::get() const
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
Rec* rec = unConst(this)->maybeGet<Rec>();
|
|
|
|
|
|
if (rec) return *rec;
|
|
|
|
|
|
|
|
|
|
|
|
return Variant<DataValues>::get<RecRef>();
|
|
|
|
|
|
}
|
2016-04-16 00:48:15 +02:00
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
/** @internal helper to possibly peek into a nested record */
|
|
|
|
|
|
inline Rec*
|
|
|
|
|
|
DataCap::maybeAccessNestedRec()
|
|
|
|
|
|
{
|
|
|
|
|
|
Rec* nested = maybeGet<Rec>();
|
|
|
|
|
|
if (!nested)
|
|
|
|
|
|
{ // 2nd try: maybe we hold a reference?
|
|
|
|
|
|
RecRef* ref = maybeGet<RecRef>();
|
|
|
|
|
|
if (ref and not ref->empty())
|
|
|
|
|
|
nested = ref->get();
|
|
|
|
|
|
}
|
|
|
|
|
|
return nested;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-04-16 00:48:15 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* @return either the contents of a nested record's type field
|
2016-08-08 14:20:54 +02:00
|
|
|
|
* or the util::BOTTOM_INDICATOR, when not a record.
|
2016-04-16 00:48:15 +02:00
|
|
|
|
* @remarks this function never raises an error, even if the element
|
|
|
|
|
|
* in fact doesn't constitute a nested scope. Effectively this
|
|
|
|
|
|
* allows to "peek" into the contents to some degree.
|
|
|
|
|
|
*/
|
|
|
|
|
|
inline string
|
|
|
|
|
|
DataCap::recordType() const
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
2020-03-14 23:52:04 +01:00
|
|
|
|
Rec* nested = unConst(this)->maybeAccessNestedRec();
|
2020-03-14 23:04:52 +01:00
|
|
|
|
return nested? nested->getType()
|
|
|
|
|
|
: util::BOTTOM_INDICATOR;
|
|
|
|
|
|
}
|
2016-04-16 00:48:15 +02:00
|
|
|
|
|
2016-09-01 22:58:08 +02:00
|
|
|
|
inline bool
|
|
|
|
|
|
DataCap::isNested() const
|
2020-03-14 23:04:52 +01:00
|
|
|
|
{
|
2020-03-14 23:52:04 +01:00
|
|
|
|
return nullptr != unConst(this)->maybeAccessNestedRec();
|
2020-03-14 23:04:52 +01:00
|
|
|
|
}
|
2016-09-01 22:58:08 +02:00
|
|
|
|
|
2016-04-16 00:48:15 +02:00
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
template<typename X>
|
|
|
|
|
|
inline std::optional<X>
|
|
|
|
|
|
DataCap::retrieveAttribute (string key) const
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert (not std::is_reference_v<X>
|
|
|
|
|
|
,"optional access only possible by value");
|
|
|
|
|
|
|
|
|
|
|
|
Rec* nested = unConst(this)->maybeAccessNestedRec();
|
|
|
|
|
|
if (nested and nested->hasAttribute (key))
|
|
|
|
|
|
{
|
|
|
|
|
|
DataCap const& nestedAttributeData = nested->get(key).data;
|
|
|
|
|
|
X* payload = unConst(nestedAttributeData).maybeGet<X>();
|
|
|
|
|
|
if (payload) return *payload; // Note: payload copied into optional
|
|
|
|
|
|
}
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
|
|
DataCap::hasAttribute (string key) const
|
|
|
|
|
|
{
|
|
|
|
|
|
Rec* nested = unConst(this)->maybeAccessNestedRec();
|
|
|
|
|
|
return nested and nested->hasAttribute (key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
inline std::optional<X>
|
|
|
|
|
|
GenNode::retrieveAttribute (string key) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return data.retrieveAttribute<X> (key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
|
|
GenNode::hasAttribute (string key) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return data.hasAttribute (key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
inline bool
|
|
|
|
|
|
GenNode::isNested() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return data.isNested();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-02 04:16:39 +02:00
|
|
|
|
inline bool
|
|
|
|
|
|
GenNode::hasChildren() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return not isnil (data.childIter());
|
|
|
|
|
|
}
|
2023-05-11 22:47:56 +02:00
|
|
|
|
|
|
|
|
|
|
inline Rec::scopeIter
|
|
|
|
|
|
GenNode::getChildren() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return data.childIter();
|
|
|
|
|
|
}
|
2023-05-02 04:16:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
2020-03-14 23:52:04 +01:00
|
|
|
|
|
|
|
|
|
|
|
2015-09-17 19:00:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
2015-07-04 20:50:18 +02:00
|
|
|
|
/**
|
|
|
|
|
|
* Constructor for a specially crafted 'ref GenNode'.
|
|
|
|
|
|
* The identity record of the generated object will be prepared
|
|
|
|
|
|
* such as to be identical to a regular GenNode with Record payload.
|
|
|
|
|
|
* @note slicing in usage is intentional
|
|
|
|
|
|
*/
|
|
|
|
|
|
struct Ref
|
|
|
|
|
|
: GenNode
|
|
|
|
|
|
{
|
|
|
|
|
|
/** create an empty ID stand-in.
|
|
|
|
|
|
* @note the purpose is to create a symbolic reference by name
|
|
|
|
|
|
*/
|
|
|
|
|
|
explicit
|
|
|
|
|
|
Ref(string const& symbolicID)
|
2015-09-17 19:00:55 +02:00
|
|
|
|
: GenNode(fabricateRefID<Rec> (symbolicID)//note: seeds the type hash with Rec, not RecRef
|
|
|
|
|
|
, DataCap(RecRef())) // note: places NIL into the reference part
|
2015-07-04 20:50:18 +02:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
/** build reference to a Record, using the original ID
|
|
|
|
|
|
* @throw error::Logic when oNode does not hold a Record<GenNode>
|
|
|
|
|
|
*/
|
|
|
|
|
|
Ref(GenNode& oNode)
|
|
|
|
|
|
: GenNode(ID(oNode)
|
|
|
|
|
|
, DataCap(RecRef(oNode.data.get<Rec>())))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2016-02-12 23:46:10 +01:00
|
|
|
|
static const Ref I; ///< symbolic ID ref "_I_"
|
|
|
|
|
|
static const Ref NO; ///< symbolic ID ref "_NO_"
|
2015-10-09 03:03:27 +02:00
|
|
|
|
static const Ref END; ///< symbolic ID ref "_END_"
|
|
|
|
|
|
static const Ref THIS; ///< symbolic ID ref "_THIS_"
|
|
|
|
|
|
static const Ref CHILD; ///< symbolic ID ref "_CHILD_"
|
|
|
|
|
|
static const Ref ATTRIBS; ///< symbolic ID ref "_ATTRIBS_"
|
2015-07-04 20:50:18 +02:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-10-30 05:41:36 +01:00
|
|
|
|
// slice down on copy construction...
|
|
|
|
|
|
inline GenNode::GenNode(Ref const& r) : idi(r.idi), data(r.data) { }
|
|
|
|
|
|
inline GenNode::GenNode(Ref & r) : idi(r.idi), data(r.data) { }
|
|
|
|
|
|
inline GenNode::GenNode(Ref && r) : idi(std::move(r.idi)),
|
|
|
|
|
|
data(std::move(r.data)) { }
|
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
2015-07-04 03:39:53 +02:00
|
|
|
|
/* === Specialisation to add fluent GenNode builder API to Record<GenNode> === */
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
2015-08-29 01:46:24 +02:00
|
|
|
|
inline GenNode
|
2015-07-04 15:22:04 +02:00
|
|
|
|
MakeRec::genNode()
|
2015-07-04 03:39:53 +02:00
|
|
|
|
{
|
2018-11-09 22:30:35 +01:00
|
|
|
|
return GenNode{std::move(record_)};
|
2015-07-04 03:39:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
2015-08-29 01:46:24 +02:00
|
|
|
|
inline GenNode
|
2018-11-09 22:30:35 +01:00
|
|
|
|
MakeRec::genNode (idi::BareEntryID rawID)
|
2015-07-04 03:39:53 +02:00
|
|
|
|
{
|
2018-11-09 22:30:35 +01:00
|
|
|
|
return GenNode::asAttribute (std::move(rawID), std::move(record_));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline GenNode
|
|
|
|
|
|
MakeRec::genNode (string const& symbolicID)
|
|
|
|
|
|
{
|
|
|
|
|
|
return GenNode{symbolicID, std::move(record_)};
|
2015-07-04 03:39:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-05 02:25:07 +02:00
|
|
|
|
/* === Extension point to apply a tree-diff === */
|
|
|
|
|
|
|
|
|
|
|
|
/** implementation is provided by the "diff framework"
|
|
|
|
|
|
* @see tree-mutator-gen-node-binding.hpp
|
|
|
|
|
|
* @see tree-diff.cpp (implementation)
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<>
|
|
|
|
|
|
void MakeRec::buildMutator (BufferHandle buff);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-06-06 02:40:18 +02:00
|
|
|
|
/* === Specialisation for handling of attributes in Record<GenNode> === */
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline bool
|
2015-08-28 18:27:23 +02:00
|
|
|
|
Rec::isAttribute (GenNode const& attrib)
|
2015-06-06 02:40:18 +02:00
|
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
|
return attrib.isNamed();
|
2015-06-06 02:40:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline bool
|
2015-10-30 05:10:16 +01:00
|
|
|
|
Rec::isTypeID (GenNode const& attrib)
|
2015-06-06 02:40:18 +02:00
|
|
|
|
{
|
2015-10-30 05:10:16 +01:00
|
|
|
|
return attrib.isTypeID();
|
2015-06-06 02:40:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline string
|
2015-06-08 01:58:39 +02:00
|
|
|
|
Rec::extractTypeID (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
|
return isTypeID(v)? v.data.get<string>()
|
|
|
|
|
|
: Rec::TYPE_NIL;
|
2015-06-06 02:40:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
|
|
|
|
|
inline string
|
2015-06-08 01:58:39 +02:00
|
|
|
|
Rec::extractKey (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
|
return isAttribute(v)? v.idi.getSym()
|
|
|
|
|
|
: "";
|
2015-06-06 02:40:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<>
|
2015-08-17 02:40:57 +02:00
|
|
|
|
inline GenNode const&
|
2015-06-08 01:58:39 +02:00
|
|
|
|
Rec::extractVal (GenNode const& v)
|
2015-06-06 02:40:18 +02:00
|
|
|
|
{
|
2015-08-28 18:27:23 +02:00
|
|
|
|
return v;
|
2015-06-06 02:40:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-07-05 03:17:39 +02:00
|
|
|
|
template<>
|
|
|
|
|
|
inline string
|
|
|
|
|
|
Rec::renderAttribute (GenNode const& a)
|
|
|
|
|
|
{
|
2015-08-28 23:09:10 +02:00
|
|
|
|
return a.idi.getSym() +" = "+ string(a.data);
|
2015-07-05 03:17:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
Generic Record: finish implementation of Mutator
especially setting (changing) attributes turned out to be tricky,
since in case of a GenNode this would mean to re-bind the hash ID;
we can not possibly do that properly without knowing the type of the payload,
and by design this payload type is opaque (erased).
As resort, I changed the semantics of the assign operation:
now it rather builds a new payload element, with a given initialiser.
In case of the strings, this ends up being the same operation,
while in case of GenNode, this is now something entirely different:
we can now build a new GenNode "in place" of the old one, and both
will have the same symbolic ID (attribute key). Incidentally,
our Variant implementation will reject such a re-building operatinon
when this means to change the (opaque) payload type.
in addition, I created a new API function on the Mutator,
allowing to move-in a complete attribute object. Actually this
new function became the working implementation. This way, it is
still possible to emplace a new attribute efficiently (consider
this to be a whole object graph!). But only, if the key (ID)
embedded in the attribute object is already what is the intended
key for this attribute. This way, we elegantly circumvent the
problem of having to re-bind a hash ID without knowing the type seed
2015-08-17 20:31:07 +02:00
|
|
|
|
template<>
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
inline GenNode
|
|
|
|
|
|
Rec::buildAttribute (string const& key, X&& payload)
|
|
|
|
|
|
{
|
2018-11-09 22:30:35 +01:00
|
|
|
|
return GenNode{key, forward<X>(payload)};
|
Generic Record: finish implementation of Mutator
especially setting (changing) attributes turned out to be tricky,
since in case of a GenNode this would mean to re-bind the hash ID;
we can not possibly do that properly without knowing the type of the payload,
and by design this payload type is opaque (erased).
As resort, I changed the semantics of the assign operation:
now it rather builds a new payload element, with a given initialiser.
In case of the strings, this ends up being the same operation,
while in case of GenNode, this is now something entirely different:
we can now build a new GenNode "in place" of the old one, and both
will have the same symbolic ID (attribute key). Incidentally,
our Variant implementation will reject such a re-building operatinon
when this means to change the (opaque) payload type.
in addition, I created a new API function on the Mutator,
allowing to move-in a complete attribute object. Actually this
new function became the working implementation. This way, it is
still possible to emplace a new attribute efficiently (consider
this to be a whole object graph!). But only, if the key (ID)
embedded in the attribute object is already what is the intended
key for this attribute. This way, we elegantly circumvent the
problem of having to re-bind a hash ID without knowing the type seed
2015-08-17 20:31:07 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-21 02:00:55 +01:00
|
|
|
|
|
2017-10-24 03:51:06 +02:00
|
|
|
|
}// namespace lib::diff
|
|
|
|
|
|
|
|
|
|
|
|
namespace variant {
|
|
|
|
|
|
using diff::Rec;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* specialisation allowing to build a GenNode with a
|
|
|
|
|
|
* nested record from a Record::Mutator (aka `MakeRec`)
|
|
|
|
|
|
*/
|
|
|
|
|
|
template<typename TYPES>
|
|
|
|
|
|
struct CanBuildFrom<diff::MakeRec, Node<Rec, TYPES>>
|
|
|
|
|
|
: std::true_type
|
|
|
|
|
|
{
|
|
|
|
|
|
using Type = Rec;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::variant
|
2015-03-21 02:00:55 +01:00
|
|
|
|
#endif /*LIB_DIFF_GEN_NODE_H*/
|