summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/model/Typesystem.cpp34
-rw-r--r--src/core/model/Typesystem.hpp37
-rw-r--r--src/core/parser/ParserScope.cpp135
-rw-r--r--src/core/parser/ParserScope.hpp37
-rw-r--r--src/plugins/xml/XmlParser.cpp45
-rw-r--r--testdata/xmlparser/generic.oxm12
6 files changed, 161 insertions, 139 deletions
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index 591dcbe..dc6df63 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -26,7 +26,10 @@ namespace ousia {
/* Static helper functions */
-static void NullMagicCallback(Variant &, bool, ManagedUid) {}
+static Type::MagicCallbackResult NullMagicCallback(Variant &, const Type *)
+{
+ return Type::MagicCallbackResult::NOT_FOUND;
+}
/* Class Type */
@@ -35,13 +38,18 @@ bool Type::build(Variant &data, Logger &logger,
{
// 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
+ switch (magicCallback(data, this)) {
+ case MagicCallbackResult::NOT_FOUND:
+ break;
+ case MagicCallbackResult::FOUND_INVALID: {
+ // The magic callback has probably already issued an error
+ // message -- do not print more errors
+ Logger nullLogger;
+ return build(data, nullLogger);
+ }
+ case MagicCallbackResult::FOUND_VALID:
+ return true;
+ }
}
try {
@@ -216,6 +224,10 @@ void Attribute::setDefaultValue(const Variant &defaultValue, Logger &logger)
initialize(logger);
}
+const Variant &Attribute::getDefaultValue() const { return defaultValue; }
+
+Variant &Attribute::getDefaultValue() { return defaultValue; }
+
void Attribute::removeDefaultValue()
{
invalidate();
@@ -224,6 +236,8 @@ void Attribute::removeDefaultValue()
optional = false;
}
+bool Attribute::isOptional() const { return optional; }
+
void Attribute::setType(Handle<Type> type, Logger &logger)
{
invalidate();
@@ -232,6 +246,8 @@ void Attribute::setType(Handle<Type> type, Logger &logger)
initialize(logger);
}
+Rooted<Type> Attribute::getType() const { return type; }
+
/* Class StructType */
bool StructType::resolveIndexKey(const std::string &key, size_t &idx) const
@@ -591,6 +607,8 @@ void Constant::setType(Handle<Type> type, Logger &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)
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index d88a7e9..9d85d80 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -57,19 +57,21 @@ class SystemTypesystem;
*/
class Type : public Node {
public:
+ enum class MagicCallbackResult { NOT_FOUND, FOUND_INVALID, FOUND_VALID };
+
/**
* 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.
+ * @param data is the magic value that should be looked up and the variant
+ * to which the value of the looked up constant should be written.
+ * @param type is a const pointer at the type. TODO: Replace this with a
+ * "ConstHandle".
+ * @return true if a constant was found, false otherwise.
*/
- using MagicCallback = std::function<void(Variant &data, bool isValid, ManagedUid Type)>;
+ using MagicCallback =
+ std::function<MagicCallbackResult(Variant &data, const Type *type)>;
protected:
/**
@@ -503,7 +505,14 @@ 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() { return defaultValue; }
+ const Variant &getDefaultValue() const;
+
+ /**
+ * Returns a reference at the default value.
+ *
+ * @return a reference at the default value of the attribute.
+ */
+ Variant &getDefaultValue();
/**
* Removes any default value from the attribute, making this attribute
@@ -517,7 +526,7 @@ public:
*
* @return true if the attribute is optional, false otherwise.
*/
- bool isOptional() const { return optional; }
+ bool isOptional() const;
/**
* Sets the type of the attribute to the specified value. This will
@@ -536,7 +545,7 @@ public:
*
* @return the underlying type of the Rooted object.
*/
- Rooted<Type> getType() const { return type; }
+ Rooted<Type> getType() const;
};
/**
@@ -992,6 +1001,14 @@ public:
*
* @return a const reference to the actual value of the constant.
*/
+ const Variant &getValue() const;
+
+ /**
+ * Returns a reference pointing at the value of the constant. The value must
+ * be interpreted with the help of the type of the constant.
+ *
+ * @return a reference to the actual value of the constant.
+ */
Variant &getValue();
/**
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index c7a9f2a..efd07d1 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/common/VariantWriter.hpp>
#include <core/model/Typesystem.hpp>
#include "ParserScope.hpp"
@@ -319,6 +320,44 @@ bool ParserScope::resolveType(const std::string &name, Handle<Node> owner,
return resolveType(Utils::split(name, '.'), owner, logger, resultCallback);
}
+bool ParserScope::resolveValue(Variant &data, Handle<Type> type,
+ Handle<Node> owner, Logger &logger)
+{
+ return type->build(
+ data, logger,
+ [&](Variant &innerData,
+ const Type *innerType) mutable -> Type::MagicCallbackResult {
+ // Try to resolve the node
+ Rooted<Constant> constant =
+ ParserScopeBase::resolve(RttiTypes::Constant,
+ Utils::split(innerData.asMagic(), '.'),
+ logger).cast<Constant>();
+
+ // Abort if nothing was found
+ if (constant == nullptr) {
+ return Type::MagicCallbackResult::NOT_FOUND;
+ }
+
+ // Set the data to the value of the constant
+ innerData = constant->getValue();
+
+ // Check whether the inner type of the constant is correct
+ // TODO: Use correct "isa" provided by Type
+ Rooted<Type> constantType = constant->getType();
+ if (innerType != constantType) {
+ 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);
+ return Type::MagicCallbackResult::FOUND_INVALID;
+ }
+ return Type::MagicCallbackResult::FOUND_VALID;
+ });
+}
+
bool ParserScope::resolveTypeWithValue(const std::vector<std::string> &path,
Handle<Node> owner, Variant &value,
Logger &logger,
@@ -326,90 +365,20 @@ bool ParserScope::resolveTypeWithValue(const std::vector<std::string> &path,
{
// 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;
+ ParserScope scope = fork();
+ Variant *valuePtr = &value;
+
+ return resolveType(path, owner, logger,
+ [=](Handle<Node> resolved, Handle<Node> owner,
+ Logger &logger) mutable {
+ if (resolved != nullptr) {
+ Rooted<Type> type = resolved.cast<Type>();
+ scope.resolveValue(*valuePtr, type, owner, logger);
+ }
- // Directly call the callback function if there were no magic values
- // involved
- if ((*magicCount) == 0) {
- resultCallback(resolved, owner, logger);
- }
- });
+ // Call the result callback with the type
+ resultCallback(resolved, owner, logger);
+ });
}
bool ParserScope::resolveTypeWithValue(const std::string &name,
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index c49acd3..2ef33d9 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -44,6 +44,7 @@ namespace ousia {
class CharReader;
class Logger;
class ParserScope;
+class Type;
class Variant;
/**
@@ -60,8 +61,8 @@ using ResolutionImposterCallback = std::function<Rooted<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> resolved,
- Handle<Node> owner, Logger &logger)>;
+using ResolutionResultCallback = std::function<
+ void(Handle<Node> resolved, Handle<Node> owner, Logger &logger)>;
/**
* Base class for the
@@ -621,14 +622,25 @@ public:
Logger &logger, ResolutionResultCallback resultCallback);
/**
+ * Build and resolves a (possibly) magic value with the given typesystem
+ * type. This function does not perform any deferred lookups.
+ *
+ * @param data is a reference at a variant that may contain magic values
+ * (even in inner structures). The data will be passed to the "build"
+ * function of the given type.
+ * @param type is the Typesystem type the data should be interpreted with.
+ * @param owner is the node for which the resolution takes place.
+ * @param logger is the logger instance into which resolution problems
+ * should be logged.
+ * @return true if the value was successfully built.
+ */
+ bool resolveValue(Variant &data, Handle<Type> type, Handle<Node> owner,
+ Logger &logger);
+
+ /**
* 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.
@@ -646,17 +658,13 @@ public:
*/
bool resolveTypeWithValue(const std::vector<std::string> &path,
Handle<Node> owner, Variant &value,
- Logger &logger, ResolutionResultCallback resultCallback);
+ 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.
@@ -674,7 +682,8 @@ public:
* later.
*/
bool resolveTypeWithValue(const std::string &name, Handle<Node> owner,
- Variant &value, Logger &logger, ResolutionResultCallback resultCallback);
+ Variant &value, Logger &logger,
+ ResolutionResultCallback resultCallback);
/**
* Tries to resolve all currently deferred resolution steps. The list of
diff --git a/src/plugins/xml/XmlParser.cpp b/src/plugins/xml/XmlParser.cpp
index 70ea48c..88447ed 100644
--- a/src/plugins/xml/XmlParser.cpp
+++ b/src/plugins/xml/XmlParser.cpp
@@ -214,21 +214,23 @@ public:
// 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);
- }
- });
+ 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);
- }
- });
+ scope().resolveType(
+ type, attribute, logger(),
+ [](Handle<Node> type, Handle<Node> attribute, Logger &logger) {
+ if (type != nullptr) {
+ attribute.cast<Attribute>()->setType(type.cast<Type>(),
+ logger);
+ }
+ });
}
}
@@ -256,13 +258,14 @@ public:
constant->setLocation(location());
// Try to resolve the type
- 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);
- }
- });
+ 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 {}
diff --git a/testdata/xmlparser/generic.oxm b/testdata/xmlparser/generic.oxm
index 23b8d66..9d2d860 100644
--- a/testdata/xmlparser/generic.oxm
+++ b/testdata/xmlparser/generic.oxm
@@ -6,14 +6,20 @@
<field name="g" type="int"/>
<field name="b" type="int"/>
</struct>
+ </types>
+ <constants>
+ <constant name="zero" value="0" type="int" />
+ <constant name="zeros" value="[0, 0, 0]" type="int[]" />
+ <constant name="manyZeros" value="[[0, 0], [0, 0], [0, 0]]" type="int[][]" />
+ <constant name="black" value="[zero, zero, zero]" type="color" />
+ </constants>
+ <types>
<struct name="structWithColor">
<field name="color" type="color" default="black" />
</struct>
</types>
<constants>
- <constant name="zero" value="42" type="string" />
- <constant name="black" value="[0, zero, 0]" type="color" />
- <constant name="err" value="0" type="int" />
+ <constant name="blackStructWithColor" value="[color=black]" type="structWithColor" />
</constants>
</typesystem>