summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/model/Node.cpp26
-rw-r--r--src/core/model/Node.hpp17
-rw-r--r--src/core/model/Typesystem.cpp107
-rw-r--r--src/core/model/Typesystem.hpp116
-rw-r--r--src/core/parser/ParserScope.cpp192
-rw-r--r--src/core/parser/ParserScope.hpp196
-rw-r--r--src/plugins/xml/XmlParser.cpp56
7 files changed, 516 insertions, 194 deletions
diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp
index 6c00e89..7cbbbe1 100644
--- a/src/core/model/Node.cpp
+++ b/src/core/model/Node.cpp
@@ -60,14 +60,14 @@ using VisitorSet =
class SharedResolutionState {
public:
/**
- * Actual path (name pattern) that was requested for resolution.
+ * Type of the node that was requested for resolution.
*/
- const std::vector<std::string> &path;
+ const Rtti &type;
/**
- * Type of the node that was requested for resolution.
+ * Actual path (name pattern) that was requested for resolution.
*/
- const Rtti &type;
+ const std::vector<std::string> &path;
/**
* Tracks all nodes that have already been visited.
@@ -82,13 +82,13 @@ public:
/**
* Constructor of the SharedResolutionState class.
*
+ * @param type is the type of the node that should be resolved.
* @param path is a const reference to the actual path that should be
* resolved.
- * @param type is the type of the node that should be resolved.
*/
- SharedResolutionState(const std::vector<std::string> &path,
- const Rtti &type)
- : path(path), type(type)
+ SharedResolutionState(const Rtti &type,
+ const std::vector<std::string> &path)
+ : type(type), path(path)
{
}
};
@@ -329,10 +329,10 @@ bool Node::continueResolveReference(Handle<Node> h, ResolutionState &state)
}
std::vector<ResolutionResult> Node::resolve(
- const std::vector<std::string> &path, const Rtti &type)
+ const Rtti &type, const std::vector<std::string> &path)
{
// Create the state variables
- SharedResolutionState sharedState(path, type);
+ SharedResolutionState sharedState(type, path);
ResolutionState state(sharedState, this);
// Kickstart the resolution process by treating this very node as compositum
@@ -344,11 +344,11 @@ std::vector<ResolutionResult> Node::resolve(
return sharedState.result;
}
-std::vector<ResolutionResult> Node::resolve(const std::string &name,
- const Rtti &type)
+std::vector<ResolutionResult> Node::resolve(const Rtti &type,
+ const std::string &name)
{
// Place the name in a vector and call the corresponding resolve function
- return resolve(std::vector<std::string>{name}, type);
+ return resolve(type, std::vector<std::string>{name});
}
bool Node::checkDuplicate(Handle<Node> elem,
diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp
index 036bcae..61bf418 100644
--- a/src/core/model/Node.hpp
+++ b/src/core/model/Node.hpp
@@ -223,7 +223,7 @@ private:
* @param thisRef is the Node of which the reference should be returned.
* @return the value of the reference.
*/
- using NodeReferenceCallback = const Node* (const Node* thisRef);
+ using NodeReferenceCallback = const Node *(const Node *thisRef);
/**
* Checks whether the a certain property is acyclic.
@@ -400,7 +400,8 @@ protected:
* @return true if the parent reference is acyclic, false otherwise.
*/
bool validateIsAcyclic(const std::string &name,
- NodeReferenceCallback callback, Logger &logger) const;
+ NodeReferenceCallback callback,
+ Logger &logger) const;
/**
* Makes sure the "parent" reference is not cyclic.
@@ -538,26 +539,26 @@ public:
* Function which resolves a name path to a list of possible nodes starting
* from this node.
*
+ * @param type specifies the type of the node that should be located.
* @param path is a list specifying a path of node names meant to specify a
* certain named node.
- * @param type specifies the type of the node that should be located.
* @return a vector containing ResolutionResult structures which describe
* the resolved elements.
*/
- std::vector<ResolutionResult> resolve(const std::vector<std::string> &path,
- const Rtti &type);
+ std::vector<ResolutionResult> resolve(const Rtti &type,
+ const std::vector<std::string> &path);
/**
* Function which resolves a single name to a list of possible nodes
* starting from this node.
*
- * @param name is the name which should be resolved.
* @param type specifies the type of the node that should be located.
+ * @param name is the name which should be resolved.
* @return a vector containing ResolutionResult structures which describe
* the resolved elements.
*/
- std::vector<ResolutionResult> resolve(const std::string &name,
- const Rtti &type);
+ std::vector<ResolutionResult> resolve(const Rtti &type,
+ const std::string &name);
/**
* Checks whether this node is valid and returns true if it is and false
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index 8a50492..591dcbe 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -24,12 +24,28 @@
namespace ousia {
+/* Static helper functions */
+
+static void NullMagicCallback(Variant &, bool, ManagedUid) {}
+
/* Class Type */
-bool Type::build(Variant &data, Logger &logger) const
+bool Type::build(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
+ // If the given variant is marked as "magic", try to resolve the real value
+ if (data.isMagic()) {
+ Variant strData = Variant::fromString(data.asString());
+ Logger nullLogger;
+ bool valid = isValid(strData, nullLogger);
+ magicCallback(data, valid,
+ getUid());
+ build(strData, nullLogger);
+ return true; // Just return true for now
+ }
+
try {
- return doBuild(data, logger);
+ return doBuild(data, logger, magicCallback);
}
catch (LoggableException ex) {
logger.log(ex);
@@ -38,37 +54,47 @@ bool Type::build(Variant &data, Logger &logger) const
}
}
+bool Type::build(Variant &data, Logger &logger) const
+{
+ return build(data, logger, NullMagicCallback);
+}
+
/* Class BoolType */
-bool BoolType::doBuild(Variant &data, Logger &logger) const
+bool BoolType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
return VariantConverter::toBool(data, logger);
}
/* Class IntType */
-bool IntType::doBuild(Variant &data, Logger &logger) const
+bool IntType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
return VariantConverter::toInt(data, logger);
}
/* Class DoubleType */
-bool DoubleType::doBuild(Variant &data, Logger &logger) const
+bool DoubleType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
return VariantConverter::toDouble(data, logger);
}
/* Class StringType */
-bool StringType::doBuild(Variant &data, Logger &logger) const
+bool StringType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
return VariantConverter::toString(data, logger);
}
/* Class EnumType */
-bool EnumType::doBuild(Variant &data, Logger &logger) const
+bool EnumType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
// If the variant is an int, check whether the value is in range
if (data.isInt()) {
@@ -152,7 +178,7 @@ Attribute::Attribute(Manager &mgr, std::string name, Handle<Type> type,
Variant defaultValue, bool optional)
: Node(mgr, std::move(name)),
type(acquire(type)),
- rawDefaultValue(defaultValue),
+ defaultValue(std::move(defaultValue)),
optional(optional)
{
ExceptionLogger logger;
@@ -173,7 +199,6 @@ Attribute::Attribute(Manager &mgr, std::string name, Variant defaultValue,
void Attribute::initialize(Logger &logger)
{
if (optional) {
- defaultValue = rawDefaultValue;
type->build(defaultValue, logger);
}
}
@@ -187,7 +212,6 @@ void Attribute::setDefaultValue(const Variant &defaultValue, Logger &logger)
{
invalidate();
- rawDefaultValue = defaultValue;
optional = true;
initialize(logger);
}
@@ -196,7 +220,6 @@ void Attribute::removeDefaultValue()
{
invalidate();
- rawDefaultValue = nullptr;
defaultValue = nullptr;
optional = false;
}
@@ -264,7 +287,9 @@ bool StructType::insertDefaults(Variant &data, const std::vector<bool> &set,
return ok;
}
-bool StructType::buildFromArray(Variant &data, Logger &logger, bool trim) const
+bool StructType::buildFromArray(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback,
+ bool trim) const
{
bool ok = true;
Variant::arrayType &arr = data.asArray();
@@ -286,14 +311,16 @@ bool StructType::buildFromArray(Variant &data, Logger &logger, bool trim) const
// Make sure the given attributes have to correct type
const size_t len = std::min(n, N);
for (size_t a = 0; a < len; a++) {
- set[a] = attributes[a]->getType()->build(arr[a], logger);
+ set[a] = attributes[a]->getType()->build(arr[a], logger, magicCallback);
ok = ok && set[a];
}
return insertDefaults(data, set, logger) && ok;
}
-bool StructType::buildFromMap(Variant &data, Logger &logger, bool trim) const
+bool StructType::buildFromMap(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback,
+ bool trim) const
{
bool ok = true;
const Variant::mapType &map = data.asMap();
@@ -323,7 +350,8 @@ bool StructType::buildFromMap(Variant &data, Logger &logger, bool trim) const
// Convert the value to the type of the attribute
arr[idx] = value;
- set[idx] = attributes[idx]->getType()->build(arr[idx], logger);
+ set[idx] = attributes[idx]->getType()->build(arr[idx], logger,
+ magicCallback);
} else if (!trim) {
ok = false;
logger.error(std::string("Invalid attribute key \"") + key +
@@ -337,13 +365,14 @@ bool StructType::buildFromMap(Variant &data, Logger &logger, bool trim) const
}
bool StructType::buildFromArrayOrMap(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback,
bool trim) const
{
if (data.isArray()) {
- return buildFromArray(data, logger, trim);
+ return buildFromArray(data, logger, magicCallback, trim);
}
if (data.isMap()) {
- return buildFromMap(data, logger, trim);
+ return buildFromMap(data, logger, magicCallback, trim);
}
throw LoggableException(
std::string(
@@ -375,9 +404,10 @@ void StructType::initialize(Logger &logger)
}
}
-bool StructType::doBuild(Variant &data, Logger &logger) const
+bool StructType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
- return buildFromArrayOrMap(data, logger, false);
+ return buildFromArrayOrMap(data, logger, magicCallback, false);
}
bool StructType::doValidate(Logger &logger) const
@@ -491,7 +521,7 @@ bool StructType::derivedFrom(Handle<StructType> other) const
bool StructType::cast(Variant &data, Logger &logger) const
{
- return buildFromArrayOrMap(data, logger, true);
+ return buildFromArrayOrMap(data, logger, NullMagicCallback, true);
}
ssize_t StructType::indexOf(const std::string &name) const
@@ -510,7 +540,8 @@ bool StructType::hasAttribute(const std::string &name) const
/* Class ArrayType */
-bool ArrayType::doBuild(Variant &data, Logger &logger) const
+bool ArrayType::doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const
{
if (!data.isArray()) {
throw LoggableException(std::string("Expected array, but got ") +
@@ -518,23 +549,32 @@ bool ArrayType::doBuild(Variant &data, Logger &logger) const
}
bool res = true;
for (auto &v : data.asArray()) {
- if (!innerType->build(v, logger)) {
+ if (!innerType->build(v, logger, magicCallback)) {
res = false;
}
}
return res;
}
+/* Class UnknownType */
+
+bool UnknownType::doBuild(Variant &, Logger &, const MagicCallback &) const
+{
+ return true;
+}
+
+UnknownType::UnknownType(Manager &mgr) : Type(mgr, "unknown", nullptr, false) {}
+
+Variant UnknownType::create() const { return Variant{nullptr}; }
+
/* Class Constant */
Constant::Constant(Manager &mgr, std::string name, Handle<Typesystem> system,
Handle<Type> type, Variant value)
- : Node(mgr, std::move(name), system),
- type(acquire(type)),
- rawValue(std::move(value))
+ : Node(mgr, std::move(name), system), type(acquire(type)), value(value)
{
ExceptionLogger logger;
- initialize(logger);
+ this->type->build(this->value, logger);
}
Constant::Constant(Manager &mgr, std::string name, Handle<Typesystem> system,
@@ -543,27 +583,20 @@ Constant::Constant(Manager &mgr, std::string name, Handle<Typesystem> system,
{
}
-void Constant::initialize(Logger &logger)
-{
- value = rawValue;
- type->build(value, logger);
-}
-
Rooted<Type> Constant::getType() const { return type; }
void Constant::setType(Handle<Type> type, Logger &logger)
{
this->type = acquire(type);
- initialize(logger);
+ this->type->build(this->value, logger);
}
-const Variant &Constant::getValue() const { return value; }
+Variant &Constant::getValue() { return value; }
void Constant::setValue(Variant value, Logger &logger)
{
- this->rawValue = value;
this->value = std::move(value);
- initialize(logger);
+ this->type->build(this->value, logger);
}
/* Class Typesystem */
@@ -571,7 +604,7 @@ void Constant::setValue(Variant value, Logger &logger)
void Typesystem::doResolve(ResolutionState &state)
{
continueResolveComposita(constants, constants.getIndex(), state);
- continueResolveComposita(types, constants.getIndex(), state);
+ continueResolveComposita(types, types.getIndex(), state);
continueResolveReferences(typesystems, state);
}
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index 5b1254a..d88a7e9 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -29,6 +29,7 @@
#ifndef _OUSIA_MODEL_TYPESYSTEM_HPP_
#define _OUSIA_MODEL_TYPESYSTEM_HPP_
+#include <functional>
#include <map>
#include <vector>
@@ -55,6 +56,21 @@ class SystemTypesystem;
* definitions.
*/
class Type : public Node {
+public:
+ /**
+ * Callback function called when a variant with "magic" value is reached.
+ * This callback allows to transform these magic values into something else.
+ * This mechanism is used to resolve constants.
+ *
+ * @param data is the magic value that should be looked up.
+ * @param isValid is set to true if the magic value does not necessarily
+ * have to be looked up, in this case no error message has to be generated
+ * if the lookup for a constant fails.
+ * @param type is the managed uid of the underlying Type for which the magic
+ * value should be looked up.
+ */
+ using MagicCallback = std::function<void(Variant &data, bool isValid, ManagedUid Type)>;
+
protected:
/**
* Protected constructor to be called by the classes derived from the Type
@@ -82,9 +98,12 @@ protected:
* -- if possible and necessary -- converted to a variant adhering to the
* internal representation used by the Type class.
* @param logger is the Logger instance into which errors should be written.
+ * @param magicCallback is a callback that should be called to other "build"
+ * functions.
* @return true if the conversion was successful, false otherwise.
*/
- virtual bool doBuild(Variant &data, Logger &logger) const = 0;
+ virtual bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const = 0;
public:
/**
@@ -109,6 +128,22 @@ public:
* internal representation used by the Type class.
* @param logger is the Logger instance into which errors should be
* written.
+ * @param magicCallback is the callback function to be called whenever
+ * a variant with "magic" value is reached.
+ * @return true if the conversion was successful, false otherwise.
+ */
+ bool build(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const;
+
+ /**
+ * Validates and completes the given variant which was read from a
+ * user-supplied source.
+ *
+ * @param data is a variant containing the data that should be checked and
+ * -- if possible and necessary -- converted to a variant adhering to the
+ * internal representation used by the Type class.
+ * @param logger is the Logger instance into which errors should be
+ * written.
* @return true if the conversion was successful, false otherwise.
*/
bool build(Variant &data, Logger &logger) const;
@@ -154,7 +189,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
@@ -192,7 +228,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
@@ -230,7 +267,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
@@ -268,7 +306,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
@@ -332,7 +371,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
@@ -384,12 +424,6 @@ private:
Owned<Type> type;
/**
- * Initial default value passed to the constructor of the Attribute class
- * that has not been passed through the "build" method of the type.
- */
- Variant rawDefaultValue;
-
- /**
* Default value of the attribute.
*/
Variant defaultValue;
@@ -400,11 +434,7 @@ private:
bool optional;
/**
- * Reinitializes the default value from the raw default value with the
- * current type.
- *
- * @param logger is the logger instance to which errors while building the
- * default value should be passed.
+ * Function used to parse the Attribute default value with the current type.
*/
void initialize(Logger &logger);
@@ -473,7 +503,7 @@ public:
* @return the default value of the attribute. If no default value has been
* given a null variant is returned (the opposite does not hold).
*/
- Variant getDefaultValue() const { return defaultValue; }
+ Variant& getDefaultValue() { return defaultValue; }
/**
* Removes any default value from the attribute, making this attribute
@@ -589,7 +619,8 @@ private:
* of attributes (as needed when casting from a derived type).
* @return true if the operation is successful, false otherwise.
*/
- bool buildFromArray(Variant &data, Logger &logger, bool trim) const;
+ bool buildFromArray(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback, bool trim) const;
/**
* Checks a map and its entries for validity and if possible updates its
@@ -601,7 +632,8 @@ private:
* when casting from a derived type.
* @return true if the operation is successful, false otherwise.
*/
- bool buildFromMap(Variant &data, Logger &logger, bool trim) const;
+ bool buildFromMap(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback, bool trim) const;
/**
* Checks a map or an array for validity and if possible updates its content
@@ -613,7 +645,9 @@ private:
* when casting from a derived type.
* @return true if the operation is successful, false otherwise.
*/
- bool buildFromArrayOrMap(Variant &data, Logger &logger, bool trim) const;
+ bool buildFromArrayOrMap(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback,
+ bool trim) const;
/**
* Rebuilds the internal index and attribute list depending on the parent
@@ -645,7 +679,8 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
/**
* Checks the struct descriptor for being valid.
@@ -819,17 +854,18 @@ protected:
* @param logger is the Logger instance into which errors should be written.
* @return true if the conversion was successful, false otherwise.
*/
- bool doBuild(Variant &data, Logger &logger) const override;
+ bool doBuild(Variant &data, Logger &logger,
+ const MagicCallback &magicCallback) const override;
public:
/**
* Constructor of the ArrayType class.
*
- * @param mgr is the Manager instance to be used for the Node.
* @param innerType is the type of the elements stored in the array.
*/
- ArrayType(Manager &mgr, Handle<Type> innerType)
- : Type(mgr, "", innerType->getTypesystem(), false),
+ ArrayType(Handle<Type> innerType)
+ : Type(innerType->getManager(), innerType->getName() + "[]",
+ innerType->getTypesystem(), false),
innerType(acquire(innerType))
{
}
@@ -869,7 +905,7 @@ protected:
*
* @return always true.
*/
- bool doBuild(Variant &, Logger &) const override { return true; }
+ bool doBuild(Variant &, Logger &, const MagicCallback &) const override;
public:
/**
@@ -878,14 +914,14 @@ public:
*
* @param mgr is the Manager instance to be used for the Node.
*/
- UnknownType(Manager &mgr) : Type(mgr, "unknown", nullptr, false) {}
+ UnknownType(Manager &mgr);
/**
* Returns a nullptr variant.
*
* @return a Variant instance with nullptr value.
*/
- Variant create() const override { return Variant{nullptr}; }
+ Variant create() const override;
};
/**
@@ -900,23 +936,10 @@ private:
Owned<Type> type;
/**
- * Value of the value before a proper type was set.
- */
- Variant rawValue;
-
- /**
* Actual value of the constant.
*/
Variant value;
- /**
- * Reinitializes the value from the raw value with the current type.
- *
- * @param logger is the logger instance to which errors while building the
- * value should be passed.
- */
- void initialize(Logger &logger);
-
public:
/**
* Constructor of the Constant node.
@@ -952,9 +975,10 @@ public:
Rooted<Type> getType() const;
/**
- * Sets the type of the constant to the given type. This will cause the raw
- * value of the variant to be reparsed and any error to be logged in the
- * given logger.
+ * Sets the type of the constant to the given type. This will cause the
+ * value of the variant to be built with the given type and any error to be
+ * logged in the given logger. Note: This operation is possibly lossy and
+ * will destroy values if the current variant value doesn't match the type.
*
* @param type is the new type of the constant.
* @param logger is the logger instance to which errors that occur during
@@ -968,7 +992,7 @@ public:
*
* @return a const reference to the actual value of the constant.
*/
- const Variant &getValue() const;
+ Variant &getValue();
/**
* Sets the value of the constant. The value will be passed to the "build"
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index 0de0dbf..c7a9f2a 100644
--- a/src/core/parser/ParserScope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -18,6 +18,7 @@
#include <core/common/Exceptions.hpp>
#include <core/common/Utils.hpp>
+#include <core/model/Typesystem.hpp>
#include "ParserScope.hpp"
@@ -31,12 +32,13 @@ ParserScopeBase::ParserScopeBase(const NodeVector<Node> &nodes) : nodes(nodes)
{
}
-Rooted<Node> ParserScopeBase::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger)
+Rooted<Node> ParserScopeBase::resolve(const Rtti &type,
+ const std::vector<std::string> &path,
+ Logger &logger)
{
// Go up the stack and try to resolve the
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
- std::vector<ResolutionResult> res = (*it)->resolve(path, type);
+ std::vector<ResolutionResult> res = (*it)->resolve(type, path);
// Abort if the object could not be resolved
if (res.empty()) {
@@ -79,14 +81,14 @@ bool DeferredResolution::resolve(
// Fork the logger to prevent error messages from being shown if we actively
// ignore the resolution result
LoggerFork loggerFork = logger.fork();
- Rooted<Node> res = scope.resolve(path, type, loggerFork);
+ Rooted<Node> res = scope.resolve(type, path, loggerFork);
if (res != nullptr) {
if (!ignore.count(res.get())) {
loggerFork.commit();
try {
// Push the location onto the logger default location stack
GuardedLogger loggerGuard(logger, *owner);
- resultCallback(res, logger);
+ resultCallback(res, owner, logger);
}
catch (LoggableException ex) {
logger.log(ex);
@@ -99,6 +101,16 @@ bool DeferredResolution::resolve(
return false;
}
+void DeferredResolution::fail(Logger &logger)
+{
+ try {
+ resultCallback(nullptr, owner, logger);
+ }
+ catch (LoggableException ex) {
+ logger.log(ex);
+ }
+}
+
/* Class ParserScope */
ParserScope::ParserScope(const NodeVector<Node> &nodes,
@@ -227,29 +239,29 @@ bool ParserScope::getFlag(ParserFlag flag)
return false;
}
-bool ParserScope::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger,
+bool ParserScope::resolve(const Rtti &type,
+ const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
ResolutionImposterCallback imposterCallback,
- ResolutionResultCallback resultCallback,
- Handle<Node> owner)
+ ResolutionResultCallback resultCallback)
{
- if (!resolve(path, type, logger, resultCallback, owner)) {
- resultCallback(imposterCallback(), logger);
+ if (!resolve(type, path, owner, logger, resultCallback)) {
+ resultCallback(imposterCallback(), owner, logger);
return false;
}
return true;
}
-bool ParserScope::resolve(const std::vector<std::string> &path,
- const Rtti &type, Logger &logger,
- ResolutionResultCallback resultCallback,
- Handle<Node> owner)
+bool ParserScope::resolve(const Rtti &type,
+ const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback)
{
// Try to directly resolve the node
- Rooted<Node> res = ParserScopeBase::resolve(path, type, logger);
+ Rooted<Node> res = ParserScopeBase::resolve(type, path, logger);
if (res != nullptr && !awaitingResolution.count(res.get())) {
try {
- resultCallback(res, logger);
+ resultCallback(res, owner, logger);
}
catch (LoggableException ex) {
logger.log(ex, *owner);
@@ -266,6 +278,149 @@ bool ParserScope::resolve(const std::vector<std::string> &path,
return false;
}
+bool ParserScope::resolveType(const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ // Check whether the given path denotes an array, if yes recursively resolve
+ // the inner type and wrap it in an array type (this allows multi
+ // dimensional arrays).
+ if (!path.empty()) {
+ const std::string &last = path.back();
+ if (last.size() >= 2 && last.substr(last.size() - 2, 2) == "[]") {
+ // Type ends with "[]", remove this from the last element in the
+ // list
+ std::vector<std::string> p = path;
+ p.back() = p.back().substr(0, last.size() - 2);
+
+ // Resolve the rest of the type
+ return resolveType(p, owner, logger,
+ [resultCallback](Handle<Node> resolved,
+ Handle<Node> owner,
+ Logger &logger) {
+ if (resolved != nullptr) {
+ Rooted<ArrayType> arr{new ArrayType{resolved.cast<Type>()}};
+ resultCallback(arr, owner, logger);
+ } else {
+ resultCallback(nullptr, owner, logger);
+ }
+ });
+ }
+ }
+
+ // Requested type is not an array, call the usual resolve function
+ return resolve(RttiTypes::Type, path, owner, logger, resultCallback);
+}
+
+bool ParserScope::resolveType(const std::string &name, Handle<Node> owner,
+ Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ return resolveType(Utils::split(name, '.'), owner, logger, resultCallback);
+}
+
+bool ParserScope::resolveTypeWithValue(const std::vector<std::string> &path,
+ Handle<Node> owner, Variant &value,
+ Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ // Fork the parser scope -- constants need to be resolved in the same
+ // context as this resolve call
+ std::shared_ptr<ParserScope> scope = std::make_shared<ParserScope>(fork());
+
+ return resolveType(
+ path, owner, logger,
+ [=](Handle<Node> resolved, Handle<Node> owner, Logger &logger) mutable {
+ // Abort if the lookup failed
+ if (resolved == nullptr) {
+ resultCallback(resolved, owner, logger);
+ return;
+ }
+
+ // Fetch the type reference and the manager reference
+ Rooted<Type> type = resolved.cast<Type>();
+ Manager *mgr = &type->getManager();
+
+ // The type has been resolved, try to resolve magic values as
+ // constants and postpone calling the callback function until
+ // all magic values have been resolved
+ std::shared_ptr<bool> isAsync = std::make_shared<bool>(false);
+ std::shared_ptr<int> magicCount = std::make_shared<int>(0);
+ type->build(value, logger, [=](Variant &magicValue, bool isValid,
+ ManagedUid innerTypeUid) mutable {
+ // Fetch the inner type
+ Rooted<Type> innerType =
+ dynamic_cast<Type *>(mgr->getManaged(innerTypeUid));
+ if (innerType == nullptr) {
+ return;
+ }
+
+ // Fetch a pointer at the variant
+ Variant *magicValuePtr = &magicValue;
+
+ // Increment the number of encountered magic values
+ (*magicCount)++;
+
+ // Try to resolve the value as constant
+ std::string constantName = magicValue.asMagic();
+ scope->resolve<Constant>(constantName, owner, logger,
+ [=](Handle<Node> resolved,
+ Handle<Node> owner,
+ Logger &logger) mutable {
+ if (resolved != nullptr) {
+ // Make sure the constant is of the correct inner type
+ Rooted<Constant> constant = resolved.cast<Constant>();
+ Rooted<Type> constantType = constant->getType();
+ if (constantType != innerType) {
+ logger.error(
+ std::string("Expected value of type \"") +
+ innerType->getName() +
+ std::string("\" but found constant \"") +
+ constant->getName() +
+ std::string(" of type \"") +
+ constantType->getName() + "\" instead.",
+ *owner);
+ } else if (!isValid) {
+ logger.error("Identifier \"" + constantName +
+ "\" is not a valid " +
+ innerType->getName(),
+ *owner);
+ }
+
+ // Nevertheless, no matter what happened, set the value
+ // of the original magic variant to the given constant
+ *magicValuePtr = constant->getValue();
+ }
+
+ // Decrement the number of magic values, call the callback
+ // function if all magic values have been resolved
+ (*magicCount)--;
+ if ((*magicCount) == 0 && (*isAsync)) {
+ resultCallback(resolved, owner, logger);
+ }
+ });
+ });
+
+ // Now we are asynchronous
+ (*isAsync) = true;
+
+ // Directly call the callback function if there were no magic values
+ // involved
+ if ((*magicCount) == 0) {
+ resultCallback(resolved, owner, logger);
+ }
+ });
+}
+
+bool ParserScope::resolveTypeWithValue(const std::string &name,
+ Handle<Node> owner, Variant &value,
+ Logger &logger,
+ ResolutionResultCallback resultCallback)
+{
+ return resolveTypeWithValue(Utils::split(name, '.'), owner, value, logger,
+ resultCallback);
+}
+
bool ParserScope::performDeferredResolution(Logger &logger)
{
// Repeat the resolution process as long as something has changed in the
@@ -304,7 +459,8 @@ bool ParserScope::performDeferredResolution(Logger &logger)
// Output error messages for all elements for which resolution did not
// succeed.
- for (const auto &failed : deferred) {
+ for (auto &failed : deferred) {
+ failed.fail(logger);
logger.error(std::string("Could not resolve ") + failed.type.name +
std::string(" \"") + Utils::join(failed.path, ".") +
std::string("\""),
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index 2378967..c49acd3 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -33,8 +33,7 @@
* @file ParserScope.hpp
*
* Contains the ParserScope class used for resolving references based on the
- *current
- * parser state.
+ * current parser state.
*
* @author Andreas Stöckel (astoecke@techfak.uni-bielefeld.de)
*/
@@ -45,6 +44,7 @@ namespace ousia {
class CharReader;
class Logger;
class ParserScope;
+class Variant;
/**
* Callback function type used for creating a dummy object while no correct
@@ -55,9 +55,13 @@ using ResolutionImposterCallback = std::function<Rooted<Node>()>;
/**
* Callback function type called whenever the result of a resolution is
* available.
+ *
+ * @param resolved is the new, resolved node.
+ * @param owner is the node that was passed as "owner".
+ * @param logger is the logger to which errors should be logged.
*/
-using ResolutionResultCallback =
- std::function<void(Handle<Node>, Logger &logger)>;
+using ResolutionResultCallback = std::function<void(Handle<Node> resolved,
+ Handle<Node> owner, Logger &logger)>;
/**
* Base class for the
@@ -90,14 +94,14 @@ public:
* Tries to resolve a node for the given type and path for all nodes that
* are currently in the stack, starting with the topmost node on the stack.
*
- * @param path is the path for which a node should be resolved.
* @param type is the type of the node that should be resolved.
+ * @param path is the path for which a node should be resolved.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @return a reference at a resolved node or nullptr if no node could be
* found.
*/
- Rooted<Node> resolve(const std::vector<std::string> &path, const Rtti &type,
+ Rooted<Node> resolve(const Rtti &type, const std::vector<std::string> &path,
Logger &logger);
};
@@ -167,6 +171,15 @@ public:
*/
bool resolve(const std::unordered_multiset<const Node *> &ignore,
Logger &logger);
+
+ /**
+ * Inform the callee about the failure by calling the callback function with
+ * "nullptr" as resolved element.
+ *
+ * @param logger is the logger instance to which error messages should be
+ * logged.
+ */
+ void fail(Logger &logger);
};
/**
@@ -396,8 +409,9 @@ public:
* temporary) and another time if the resolution turned out to be
* successful at a later point in time.
*
- * @param path is the path for which a node should be resolved.
* @param type is the type of the node that should be resolved.
+ * @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param imposterCallback is the callback function that is called if
@@ -410,15 +424,14 @@ public:
* the resolved object directly when this function is called. If the
* resolution was not successful the first time, it may be called another
* time later in the context of the "performDeferredResolution" function.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does
* not mean, that the resolved object does not exist, as it may be resolved
* later.
*/
- bool resolve(const std::vector<std::string> &path, const Rtti &type,
- Logger &logger, ResolutionImposterCallback imposterCallback,
- ResolutionResultCallback resultCallback,
- Handle<Node> owner = nullptr);
+ bool resolve(const Rtti &type, const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
+ ResolutionImposterCallback imposterCallback,
+ ResolutionResultCallback resultCallback);
/**
* Tries to resolve a node for the given type and path for all nodes
@@ -426,21 +439,21 @@ public:
* The "resultCallback" is called when the resolution was successful, which
* may be at a later point in time.
*
- * @param path is the path for which a node should be resolved.
* @param type is the type of the node that should be resolved.
+ * @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param resultCallback is the callback function to which the result of
* the resolution process is passed. This function is called once the
* resolution was successful.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
- bool resolve(const std::vector<std::string> &path, const Rtti &type,
- Logger &logger, ResolutionResultCallback resultCallback,
- Handle<Node> owner = nullptr);
+ bool resolve(const Rtti &type, const std::vector<std::string> &path,
+ Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback);
/**
* Tries to resolve a node for the given type and path for all nodes
@@ -453,6 +466,7 @@ public:
*
* @tparam T is the type of the node that should be resolved.
* @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param imposterCallback is the callback function that is called if
@@ -465,24 +479,17 @@ public:
* resolved object directly when this function is called. If the resolution
* was not successful the first time, it may be called another time later
* in the context of the "performDeferredResolution" function.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
template <class T>
- bool resolve(const std::vector<std::string> &path, Logger &logger,
- std::function<Rooted<T>()> imposterCallback,
- std::function<void(Handle<T>, Logger &)> resultCallback,
- Handle<Node> owner = nullptr)
+ bool resolve(const std::vector<std::string> &path, Handle<Node> owner,
+ Logger &logger, ResolutionImposterCallback imposterCallback,
+ ResolutionResultCallback resultCallback)
{
- return resolve(
- path, typeOf<T>(), logger,
- [imposterCallback]() -> Rooted<Node> { return imposterCallback(); },
- [resultCallback](Handle<Node> node, Logger &logger) {
- resultCallback(node.cast<T>(), logger);
- },
- owner);
+ return resolve(typeOf<T>(), path, owner, logger, imposterCallback,
+ resultCallback);
}
/**
@@ -493,26 +500,21 @@ public:
*
* @tparam T is the type of the node that should be resolved.
* @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param resultCallback is the callback function to which the result of
* the resolution process is passed. This function is called once the
* resolution was successful.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
template <class T>
- bool resolve(const std::vector<std::string> &path, Logger &logger,
- std::function<void(Handle<T>, Logger &)> resultCallback,
- Handle<Node> owner = nullptr)
+ bool resolve(const std::vector<std::string> &path, Handle<Node> owner,
+ Logger &logger, ResolutionResultCallback resultCallback)
{
- return resolve(path, typeOf<T>(), logger,
- [resultCallback](Handle<Node> node, Logger &logger) {
- resultCallback(node.cast<T>(), logger);
- },
- owner);
+ return resolve(typeOf<T>(), path, owner, logger, resultCallback);
}
/**
@@ -527,6 +529,7 @@ public:
* @tparam T is the type of the node that should be resolved.
* @param name is the path for which a node should be resolved. The name is
* split at '.' to form a path.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param imposterCallback is the callback function that is called if
@@ -539,19 +542,17 @@ public:
* resolved object directly when this function is called. If the resolution
* was not successful the first time, it may be called another time later
* in the context of the "performDeferredResolution" function.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
template <class T>
- bool resolve(const std::string &name, Logger &logger,
- std::function<Rooted<T>()> imposterCallback,
- std::function<void(Handle<T>, Logger &)> resultCallback,
- Handle<Node> owner = nullptr)
+ bool resolve(const std::string &name, Handle<Node> owner, Logger &logger,
+ ResolutionImposterCallback imposterCallback,
+ ResolutionResultCallback resultCallback)
{
- return resolve<T>(Utils::split(name, '.'), logger, imposterCallback,
- resultCallback, owner);
+ return resolve<T>(Utils::split(name, '.'), owner, logger,
+ imposterCallback, resultCallback);
}
/**
@@ -563,26 +564,119 @@ public:
* @tparam T is the type of the node that should be resolved.
* @param name is the path for which a node should be resolved. The name is
* split at '.' to form a path.
+ * @param owner is the node for which the resolution takes place.
* @param logger is the logger instance into which resolution problems
* should be logged.
* @param resultCallback is the callback function to which the result of
* the resolution process is passed. This function is called once the
* resolution was successful.
- * @param owner is the node for which the resolution takes place.
* @return true if the resolution was immediately successful. This does not
* mean, that the resolved object does not exist, as it may be resolved
* later.
*/
template <class T>
- bool resolve(const std::string &name, Logger &logger,
- std::function<void(Handle<T>, Logger &)> resultCallback,
- Handle<Node> owner = nullptr)
+ bool resolve(const std::string &name, Handle<Node> owner, Logger &logger,
+ ResolutionResultCallback resultCallback)
{
- return resolve<T>(Utils::split(name, '.'), logger, resultCallback,
- owner);
+ return resolve<T>(Utils::split(name, '.'), owner, logger,
+ resultCallback);
}
/**
+ * Resolves a typesystem type. Makes sure an array type is returned if an
+ * array type is requested.
+ *
+ * @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveType(const std::vector<std::string> &path, Handle<Node> owner,
+ Logger &logger, ResolutionResultCallback resultCallback);
+
+ /**
+ * Resolves a typesystem type. Makes sure an array type is returned if an
+ * array type is requested.
+ *
+ * @tparam T is the type of the node that should be resolved.
+ * @param name is the path for which a node should be resolved. The name is
+ * split at '.' to form a path.
+ * @param owner is the node for which the resolution takes place.
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveType(const std::string &name, Handle<Node> owner,
+ Logger &logger, ResolutionResultCallback resultCallback);
+
+ /**
+ * Resolves a type and makes sure the corresponding value is of the correct
+ * type.
+ *
+ * <b>Warning:</b> This function is extremely dangerous as you have to make
+ * sure that the "value" reference stays alife as long as the "owner" is
+ * valid. This is especially problematic as internally references at parts
+ * of "value" may be kept. Test usages of this function well!
+ *
+ * @tparam T is the type of the node that should be resolved.
+ * @param path is the path for which a node should be resolved.
+ * @param owner is the node for which the resolution takes place.
+ * @param value is a reference at the Variant that represents the value for
+ * which the type should be looked up. The value must be valid as long as
+ * the owner node is valid (so it should be a part of the owner).
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveTypeWithValue(const std::vector<std::string> &path,
+ Handle<Node> owner, Variant &value,
+ Logger &logger, ResolutionResultCallback resultCallback);
+
+ /**
+ * Resolves a type and makes sure the corresponding value is of the correct
+ * type.
+ *
+ * <b>Warning:</b> This function is extremely dangerous as you have to make
+ * sure that the "value" reference stays alife as long as the "owner" is
+ * valid. This is especially problematic as internally references at parts
+ * of "value" may be kept. Test usages of this function well!
+ *
+ * @tparam T is the type of the node that should be resolved.
+ * @param name is the path for which a node should be resolved. The name is
+ * split at '.' to form a path.
+ * @param owner is the node for which the resolution takes place.
+ * @param value is a reference at the Variant that represents the value for
+ * which the type should be looked up. The value must be valid as long as
+ * the owner node is valid (so it should be a part of the owner).
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @param resultCallback is the callback function to which the result of
+ * the resolution process is passed. This function is called once the
+ * resolution was successful.
+ * @return true if the resolution was immediately successful. This does not
+ * mean, that the resolved object does not exist, as it may be resolved
+ * later.
+ */
+ bool resolveTypeWithValue(const std::string &name, Handle<Node> owner,
+ Variant &value, Logger &logger, ResolutionResultCallback resultCallback);
+
+ /**
* Tries to resolve all currently deferred resolution steps. The list of
* pending deferred resolutions is cleared after this function has run.
*
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp
index 6e0fea0..70ea48c 100644
--- a/src/plugins/xml/XmlParser.cpp
+++ b/src/plugins/xml/XmlParser.cpp
@@ -167,13 +167,15 @@ public:
// Try to resolve the parent type and set it as parent structure
if (!parent.empty()) {
- scope().resolve<StructType>(parent, logger(),
- [structType](Handle<StructType> parent,
- Logger &logger) mutable {
- structType->setParentStructure(
- parent, logger);
- },
- structType);
+ scope().resolve<StructType>(
+ parent, structType, logger(),
+ [](Handle<Node> parent, Handle<Node> structType,
+ Logger &logger) {
+ if (parent != nullptr) {
+ structType.cast<StructType>()->setParentStructure(
+ parent.cast<StructType>(), logger);
+ }
+ });
}
// Descend into the struct type
@@ -201,7 +203,7 @@ public:
// Read the argument values
const std::string &name = args["name"].asString();
const std::string &type = args["type"].asString();
- const Variant &defaultValue = args["default"]; // Build!
+ const Variant &defaultValue = args["default"];
const bool optional =
!(defaultValue.isObject() && defaultValue.asObject() == nullptr);
@@ -210,13 +212,24 @@ public:
structType->createAttribute(name, defaultValue, optional, logger());
attribute->setLocation(location());
- // Try to resolve the type
- scope().resolve<Type>(
- type, logger(),
- [attribute](Handle<Type> type, Logger &logger) mutable {
- attribute->setType(type, logger);
- },
- attribute);
+ // Try to resolve the type and default value
+ if (optional) {
+ scope().resolveTypeWithValue(type, attribute, attribute->getDefaultValue(), logger(),
+ [](Handle<Node> type, Handle<Node> attribute,
+ Logger &logger) {
+ if (type != nullptr) {
+ attribute.cast<Attribute>()->setType(type.cast<Type>(), logger);
+ }
+ });
+ } else {
+ scope().resolveType(type, attribute, logger(),
+ [](Handle<Node> type, Handle<Node> attribute,
+ Logger &logger) {
+ if (type != nullptr) {
+ attribute.cast<Attribute>()->setType(type.cast<Type>(), logger);
+ }
+ });
+ }
}
void end() override {}
@@ -243,12 +256,13 @@ public:
constant->setLocation(location());
// Try to resolve the type
- scope().resolve<Type>(
- type, logger(),
- [constant](Handle<Type> type, Logger &logger) mutable {
- constant->setType(type, logger);
- },
- constant);
+ scope().resolveTypeWithValue(type, constant, constant->getValue(), logger(),
+ [](Handle<Node> type, Handle<Node> constant,
+ Logger &logger) {
+ if (type != nullptr) {
+ constant.cast<Constant>()->setType(type.cast<Type>(), logger);
+ }
+ });
}
void end() override {}