LUMIERA.clone/src/lib/text-template-gen-node-binding.hpp
Ichthyostega 806db414dd 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

148 lines
5.1 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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*/