* 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.
148 lines
5.1 KiB
C++
148 lines
5.1 KiB
C++
/*
|
||
TEXT-TEMPLATE-GEN-NODE-BINDING.hpp - data binding adapter for ETD
|
||
|
||
Copyright (C)
|
||
2024, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**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.
|
||
|
||
*/
|
||
|
||
|
||
/** @file text-template-gen-node-binding.hpp
|
||
** A complement to allow instantiation of a TextTemplate with ETD data.
|
||
** Instead of requiring a specific data source type, the text template engine relies
|
||
** on an extension point through a data-binding template, which can be (partially) specialised
|
||
** to implement data access for any suitable kind of structured data. The Lumiera [ETD] is
|
||
** a recursive data type comprised of lib::diff::GenNode elements, which in turn can hold
|
||
** a small selection of binary data elements. This scheme for structured data is widely used
|
||
** for internal communication between the components of the Lumiera application; it offers
|
||
** all the structural elements to provide a full featured backing data structure for the
|
||
** text template engine.
|
||
**
|
||
** # Intricacies
|
||
**
|
||
** Since the ETD is based on _binary data,_ we need to invoke a string rendering during
|
||
** data access, in the end relying on util::toString(). This limits the precision of floating-pont
|
||
** and time data (assuming this is adequate for the purpose of instantiating placeholders in a
|
||
** text template). A further challenge arises from the openness of the ETD format, which is
|
||
** intended for loose coupling between subsystems, and deliberately imposes not much structural
|
||
** constraints, while also offering only very limited introspection capabilities (to prevent
|
||
** a »programming by inspection« style). Some further conventions are thus necessary
|
||
** - a _Scope_ is assumed to be a _Record-Node._ (»object structure«)
|
||
** - _Keys_ are translated into _Attribute access._
|
||
** - _Iteration_ is assumed to pick a _loop-control Node_ and descend into this node's child scope.
|
||
** - if such iterated children _happen to be simple values_, then a pseudo-scope is synthesised,
|
||
** containing a single virtual attribute with the KeyID "value" (which implies that TextTemplate
|
||
** can expand a `${value}` placeholder in that scope.
|
||
** - Attributes of enclosing scopes are also visible — unless shadowed.
|
||
** [ETD]: https://lumiera.org/documentation/design/architecture/ETD.html
|
||
** @see TextTemplate_test::verify_ETD_binding
|
||
*/
|
||
|
||
|
||
#ifndef LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H
|
||
#define LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H
|
||
|
||
|
||
#include "lib/diff/gen-node.hpp"
|
||
#include "lib/text-template.hpp"
|
||
|
||
#include <string>
|
||
|
||
|
||
namespace lib {
|
||
|
||
using std::string;
|
||
|
||
|
||
namespace text_template {
|
||
|
||
/* ======= Data binding for GenNode (ETD) ======= */
|
||
|
||
/**
|
||
* Data-binding for a tree of GenNode data (ETD).
|
||
* Attributes are accessible as keys, while iteration descends
|
||
* into the child scope of the attribute indicated in the ${for <key>}` tag.
|
||
* @see TextTemplate_test::verify_ETD_binding()
|
||
*/
|
||
template<>
|
||
struct DataSource<diff::GenNode>
|
||
{
|
||
using Node = diff::GenNode;
|
||
using Rec = diff::Rec;
|
||
|
||
Node const* data_;
|
||
DataSource* parScope_;
|
||
bool isSubScope() { return bool(parScope_); }
|
||
|
||
DataSource(Node const& root)
|
||
: data_{&root}
|
||
, parScope_{nullptr}
|
||
{ }
|
||
|
||
|
||
using Value = std::string;
|
||
using Iter = Rec::scopeIter;
|
||
|
||
Node const*
|
||
findNode (string key)
|
||
{
|
||
if (data_->isNested())
|
||
{// standard case: Attribute lookup
|
||
Rec const& record = data_->data.get<Rec>();
|
||
if (record.hasAttribute (key))
|
||
return & record.get (key);
|
||
}
|
||
else
|
||
if ("value" == key) // special treatment for a »pseudo context«
|
||
return data_; // comprised only of a single value node
|
||
// ask parent scope...
|
||
if (isSubScope())
|
||
return parScope_->findNode (key);
|
||
|
||
return nullptr;
|
||
}
|
||
|
||
bool
|
||
contains (string key)
|
||
{
|
||
return bool(findNode (key));
|
||
}
|
||
|
||
Value
|
||
retrieveContent (string key)
|
||
{
|
||
return renderCompact (*findNode(key));
|
||
}
|
||
|
||
Iter
|
||
getSequence (string key)
|
||
{
|
||
if (not contains(key))
|
||
return Iter{};
|
||
Node const* node = findNode (key);
|
||
if (not node->isNested())
|
||
return Iter{};
|
||
else
|
||
return node->data.get<Rec>().scope();
|
||
}
|
||
|
||
DataSource
|
||
openContext (Iter& iter)
|
||
{
|
||
REQUIRE (iter);
|
||
DataSource nested{*this};
|
||
nested.parScope_ = this;
|
||
nested.data_ = & *iter;
|
||
return nested;
|
||
}
|
||
};
|
||
|
||
}// namespace text_template
|
||
|
||
}// namespace lib
|
||
#endif /*LIB_TEXT_TEMPLATE_GEN_NODE_BINDING_H*/
|