summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/model/Document.hpp4
-rw-r--r--src/core/model/Domain.hpp8
-rw-r--r--src/core/model/Node.cpp74
-rw-r--r--src/core/model/Node.hpp49
-rw-r--r--src/core/model/Typesystem.cpp16
-rw-r--r--src/core/model/Typesystem.hpp4
-rw-r--r--src/core/parser/ParserContext.cpp2
-rw-r--r--src/core/parser/ParserContext.hpp12
-rw-r--r--src/core/parser/ParserScope.cpp14
-rw-r--r--src/core/parser/ParserScope.hpp43
-rw-r--r--src/core/resource/ResourceManager.cpp23
-rw-r--r--src/core/resource/ResourceManager.hpp4
12 files changed, 198 insertions, 55 deletions
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index 1f2fb37..dcb8966 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -849,7 +849,7 @@ public:
/**
* Adds a Domain reference to this Document.
*/
- void addDomain(Handle<Domain> d)
+ void referenceDomain(Handle<Domain> d)
{
invalidate();
domains.push_back(d);
@@ -858,7 +858,7 @@ public:
/**
* Adds multiple Domain references to this Document.
*/
- void addDomains(const std::vector<Handle<Domain>> &d)
+ void referenceDomains(const std::vector<Handle<Domain>> &d)
{
invalidate();
domains.insert(domains.end(), d.begin(), d.end());
diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp
index 541a428..cd649d5 100644
--- a/src/core/model/Domain.hpp
+++ b/src/core/model/Domain.hpp
@@ -899,8 +899,6 @@ class Domain : public Node {
private:
NodeVector<StructuredClass> structuredClasses;
NodeVector<AnnotationClass> annotationClasses;
- // TODO: Is it wise to attach the type systems here? If not: What would be
- // a good alternative.
NodeVector<Typesystem> typesystems;
protected:
@@ -937,7 +935,7 @@ public:
Domain(Manager &mgr, Handle<SystemTypesystem> sys, std::string name = "")
: Domain(mgr, std::move(name))
{
- includeTypesystem(sys);
+ referenceTypesystem(sys);
}
/**
@@ -1071,12 +1069,12 @@ public:
/**
* Adds a Typesystem reference to this Domain.
*/
- void includeTypesystem(Handle<Typesystem> t) { typesystems.push_back(t); }
+ void referenceTypesystem(Handle<Typesystem> t) { typesystems.push_back(t); }
/**
* Adds multiple Typesystem references to this Domain.
*/
- void includeTypesystems(const std::vector<Handle<Typesystem>> &ts)
+ void referenceTypesystems(const std::vector<Handle<Typesystem>> &ts)
{
typesystems.insert(typesystems.end(), ts.begin(), ts.end());
}
diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp
index dbc85e2..3b5f38c 100644
--- a/src/core/model/Node.cpp
+++ b/src/core/model/Node.cpp
@@ -361,26 +361,92 @@ bool Node::checkDuplicate(Handle<Node> elem,
const std::string &name = elem->getName();
if (!names.emplace(name).second) {
logger.error(std::string("Element with name \"") + name +
- std::string("\" defined multiple times in parent ") +
- type().name + std::string(" \"") +
- Utils::join(path(), ".") + std::string("\""), *elem);
+ std::string("\" defined multiple times in parent ") +
+ type().name + std::string(" \"") +
+ Utils::join(path(), ".") + std::string("\""),
+ *elem);
return false;
}
return true;
}
+bool Node::checkIsAcyclic(std::vector<const Node *> &path,
+ std::unordered_set<const Node *> &visited,
+ NodeReferenceCallback callback) const
+{
+ // Add this node to the path
+ path.push_back(this);
+
+ // A cycle was found, abort, shorten the path to the actual cycle
+ if (visited.count(this)) {
+ return false;
+ }
+ visited.insert(this);
+
+ // Continue allong the path
+ const Node *node = callback(this);
+ if (node != nullptr) {
+ if (!node->checkIsAcyclic(path, visited, callback)) {
+ return false;
+ }
+ }
+
+ // Remove this node from the path
+ path.pop_back();
+ return true;
+}
+
bool Node::doValidate(Logger &logger) const { return true; }
bool Node::validateName(Logger &logger) const
{
if (!Utils::isIdentifier(name)) {
logger.error(type().name + std::string(" name \"") + name +
- std::string("\" is not a valid identifier"), this);
+ std::string("\" is not a valid identifier"),
+ this);
return false;
}
return true;
}
+bool Node::validateIsAcyclic(const std::string &name,
+ NodeReferenceCallback callback,
+ Logger &logger) const
+{
+ std::vector<const Node *> path;
+ std::unordered_set<const Node *> visited;
+
+ if (!checkIsAcyclic(path, visited, callback)) {
+ logger.error(std::string("Attribute \"") + name + ("\" is cyclic."),
+ this);
+ logger.note("The following nodes are included in the cycle: ",
+ SourceLocation{}, MessageMode::NO_CONTEXT);
+ for (const Node *node : path) {
+ const std::string &name = node->getName();
+ const std::string &typeName = node->type().name;
+ if (name.empty()) {
+ logger.note(std::string("Node of internal type ") + typeName +
+ std::string(" declared here:"),
+ node);
+ } else {
+ logger.note(std::string("Node \"") + name +
+ std::string("\" of internal type ") + typeName +
+ std::string(" declared here:"),
+ node);
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+bool Node::validateParentIsAcyclic(Logger &logger) const
+{
+ return validateIsAcyclic("parent", [](const Node *thisRef) -> const Node *
+ { return thisRef->parent.get(); },
+ logger);
+}
+
void Node::invalidate()
{
// Only perform the invalidation if necessary
diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp
index 60d22e0..036bcae 100644
--- a/src/core/model/Node.hpp
+++ b/src/core/model/Node.hpp
@@ -31,8 +31,8 @@
#include <cstdint>
#include <map>
#include <set>
-#include <unordered_set>
#include <string>
+#include <unordered_set>
#include <vector>
#include <core/common/Location.hpp>
@@ -155,6 +155,7 @@ private:
* vector.
*
* @param p is the list the path should be constructed in.
+ * @param root is a node at which building the path should be aborted.
*/
void path(std::vector<std::string> &p, Handle<Node> root) const;
@@ -215,6 +216,30 @@ private:
std::unordered_set<std::string> &names,
Logger &logger) const;
+ /**
+ * Callback function used to access a Node reference stored inside another
+ * Node.
+ *
+ * @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);
+
+ /**
+ * Checks whether the a certain property is acyclic.
+ *
+ * @param path is a path containing the cycle. If no cycle is found, the
+ * path will be empty.
+ * @param visited a set of visited nodes used for cycle detection.
+ * @param callback is the callback that is used to access the underlying
+ * property that should be checked for acyclicity.
+ * @return true if the node is acyclic regarding this property, false if
+ * a cycle was detected. The cycle is stored in the "path".
+ */
+ bool checkIsAcyclic(std::vector<const Node *> &path,
+ std::unordered_set<const Node *> &visited,
+ NodeReferenceCallback callback) const;
+
protected:
/**
* Sets the parent node.
@@ -365,6 +390,28 @@ protected:
bool validateName(Logger &logger) const;
/**
+ * Makes sure the property accessed by the callback is not cyclic.
+ *
+ * @param name is the name of the property. The passed name is used to build
+ * a nice error message.
+ * @param callback is the callback that is used to access the property.
+ * @param logger is the logger instance to which an error message containing
+ * the cycle is logged.
+ * @return true if the parent reference is acyclic, false otherwise.
+ */
+ bool validateIsAcyclic(const std::string &name,
+ NodeReferenceCallback callback, Logger &logger) const;
+
+ /**
+ * Makes sure the "parent" reference is not cyclic.
+ *
+ * @param logger is the logger instance to which an error message containing
+ * the cycle is logged.
+ * @return true if the parent reference is acyclic, false otherwise.
+ */
+ bool validateParentIsAcyclic(Logger &logger) const;
+
+ /**
* Helper function that can be used to forward the validation process to
* child nodes.
*
diff --git a/src/core/model/Typesystem.cpp b/src/core/model/Typesystem.cpp
index 5f8f613..c10fc02 100644
--- a/src/core/model/Typesystem.cpp
+++ b/src/core/model/Typesystem.cpp
@@ -383,6 +383,11 @@ bool StructType::doBuild(Variant &data, Logger &logger) const
bool StructType::doValidate(Logger &logger) const
{
return validateName(logger) &
+ validateIsAcyclic("parent", [](const Node *thisRef) -> const Node * {
+ return dynamic_cast<const StructType *>(
+ thisRef)->parentStructure.get();
+ },
+ logger) &
continueValidationCheckDuplicates(attributes, logger);
}
@@ -542,7 +547,7 @@ Rooted<StructType> Typesystem::createStructType(const std::string &name)
return structType;
}
-void Typesystem::includeTypesystem(Handle<Typesystem> typesystem)
+void Typesystem::referenceTypesystem(Handle<Typesystem> typesystem)
{
typesystems.push_back(typesystem);
}
@@ -573,8 +578,9 @@ const Rtti DoubleType =
RttiBuilder<ousia::DoubleType>("DoubleType").parent(&Type);
const Rtti BoolType = RttiBuilder<ousia::BoolType>("BoolType").parent(&Type);
const Rtti EnumType = RttiBuilder<ousia::EnumType>("EnumType").parent(&Type);
-const Rtti StructType =
- RttiBuilder<ousia::StructType>("StructType").parent(&Type).composedOf(&Attribute);
+const Rtti StructType = RttiBuilder<ousia::StructType>("StructType")
+ .parent(&Type)
+ .composedOf(&Attribute);
const Rtti ArrayType = RttiBuilder<ousia::ArrayType>("ArrayType").parent(&Type);
const Rtti UnknownType =
RttiBuilder<ousia::UnknownType>("UnknownType").parent(&Type);
@@ -584,8 +590,8 @@ const Rtti Typesystem =
RttiBuilder<ousia::Typesystem>("Typesystem").parent(&Node).composedOf(
{&StringType, &IntType, &DoubleType, &BoolType, &EnumType, &StructType,
&Constant});
-const Rtti SystemTypesystem =
- RttiBuilder<ousia::SystemTypesystem> ("SystemTypesystem").parent(&Typesystem);
+const Rtti SystemTypesystem = RttiBuilder<ousia::SystemTypesystem>(
+ "SystemTypesystem").parent(&Typesystem);
}
}
diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp
index 1405ed6..e06938e 100644
--- a/src/core/model/Typesystem.hpp
+++ b/src/core/model/Typesystem.hpp
@@ -989,7 +989,7 @@ public:
Typesystem(Manager &mgr, Handle<SystemTypesystem> sys, std::string name)
: Typesystem(mgr, std::move(name))
{
- includeTypesystem(sys);
+ referenceTypesystem(sys);
}
/**
@@ -1007,7 +1007,7 @@ public:
* @param typesystem is the typesystem that should be added to the
* referenced typesystems list.
*/
- void includeTypesystem(Handle<Typesystem> typesystem);
+ void referenceTypesystem(Handle<Typesystem> typesystem);
/**
* Adds the given type to the to the type list.
diff --git a/src/core/parser/ParserContext.cpp b/src/core/parser/ParserContext.cpp
index b4e2a9a..14b02df 100644
--- a/src/core/parser/ParserContext.cpp
+++ b/src/core/parser/ParserContext.cpp
@@ -39,7 +39,7 @@ ParserContext::ParserContext(Registry &registry,
{
}
-NodeVector<Node> ParserContext::import(const std::string &path,
+Rooted<Node> ParserContext::import(const std::string &path,
const std::string mimetype,
const std::string rel,
const RttiSet &supportedTypes)
diff --git a/src/core/parser/ParserContext.hpp b/src/core/parser/ParserContext.hpp
index 2787225..1b889b1 100644
--- a/src/core/parser/ParserContext.hpp
+++ b/src/core/parser/ParserContext.hpp
@@ -117,8 +117,8 @@ public:
* checked, not the actual result.
* @return the parsed node or nullptr if something goes wrong.
*/
- NodeVector<Node> import(const std::string &path, const std::string mimetype,
- const std::string rel, const RttiSet &supportedTypes);
+ Rooted<Node> import(const std::string &path, const std::string mimetype,
+ const std::string rel, const RttiSet &supportedTypes);
/**
* Parses a file with ParserContext and the current ParserScope. In contrast
@@ -136,10 +136,12 @@ public:
* @param supportedTypes contains the types of the returned Node the caller
* can deal with. Note that only the types the parser claims to return are
* checked, not the actual result.
- * @return the parsed node or nullptr if something goes wrong.
+ * @return the parsed nodes or an empty list if something goes wrong (or
+ * there were indeed no objects to be parsed).
*/
- NodeVector<Node> include(const std::string &path, const std::string mimetype,
- const std::string rel, const RttiSet &supportedTypes);
+ NodeVector<Node> include(const std::string &path,
+ const std::string mimetype, const std::string rel,
+ const RttiSet &supportedTypes);
/**
* Clones the ParserContext instance but exchanges the ParserScope instance
diff --git a/src/core/parser/ParserScope.cpp b/src/core/parser/ParserScope.cpp
index 0e2350f..3d1ba78 100644
--- a/src/core/parser/ParserScope.cpp
+++ b/src/core/parser/ParserScope.cpp
@@ -168,6 +168,20 @@ Rooted<Node> ParserScope::getRoot() const { return nodes.front(); }
Rooted<Node> ParserScope::getLeaf() const { return nodes.back(); }
+Rooted<Node> ParserScope::select(RttiSet types, int maxDepth)
+{
+ ssize_t minDepth = 0;
+ if (maxDepth >= 0) {
+ minDepth = static_cast<ssize_t>(nodes.size()) - (maxDepth + 1);
+ }
+ for (ssize_t i = nodes.size() - 1; i >= minDepth; i--) {
+ if (nodes[i]->type().isOneOf(types)) {
+ return nodes[i];
+ }
+ }
+ return nullptr;
+}
+
void ParserScope::setFlag(ParserFlag flag, bool value)
{
// Fetch the current stack depth
diff --git a/src/core/parser/ParserScope.hpp b/src/core/parser/ParserScope.hpp
index 2c6093f..1191cbc 100644
--- a/src/core/parser/ParserScope.hpp
+++ b/src/core/parser/ParserScope.hpp
@@ -324,14 +324,24 @@ public:
/**
* Returns the bottom-most Node instance in the ParserScope hirarchy, e.g.
- *the
- * node that was pushed last onto the stack.
+ * the node that was pushed last onto the stack.
*
* @return a reference at the leaf node.
*/
Rooted<Node> getLeaf() const;
/**
+ * Ascends in the stack starting with the leaf node, returns the first node
+ * that matches the type given in the RttiSet or nullptr if none matches.
+ *
+ * @param types is a set of Rtti types for which should be searched in the
+ * stack.
+ * @param maxDepth is the maximum number of stack entries the selection
+ * function may ascend. A negative value indicates no limitation.
+ */
+ Rooted<Node> select(RttiSet types, int maxDepth = -1);
+
+ /**
* Sets a parser flag for the current stack depth.
*
* @param flag is the flag that should be set.
@@ -353,12 +363,10 @@ public:
* Tries to resolve a node for the given type and path for all nodes
* currently on the stack, starting with the topmost node on the stack.
* Calls the "imposterCallback" function for obtaining a temporary
- *result if
- * a node cannot be resolved right now. The "resultCallback" is at most
- * called twice: Once when this method is called (probably with the
+ * result if a node cannot be resolved right now. The "resultCallback" is
+ * at most called twice: Once when this method is called (probably with the
* temporary) and another time if the resolution turned out to be
- *successful
- * at a later point in time.
+ * 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.
@@ -367,24 +375,17 @@ public:
* @param imposterCallback is the callback function that is called if
* the node cannot be resolved at this moment. It gives the caller the
* possibility to create an imposter (a temporary object) that may be
- *used
- * later in the resolution process.
+ * used later in the resolution process.
* @param resultCallback is the callback function to which the result of
* the resolution process is passed. This function is called at least
- *once
- * either with the imposter (if the resolution was not successful) or
- *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.
+ * once either with the imposter (if the resolution was not successful) or
+ * 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 location is the location in the current source file in which
- *the
- * resolution was triggered.
+ * the resolution was triggered.
* @return true if the resolution was immediately successful. This does
- *not
- * mean, that the resolved object does not exist, as it may be resolved
+ * 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,
diff --git a/src/core/resource/ResourceManager.cpp b/src/core/resource/ResourceManager.cpp
index 2484cb2..1d32a4d 100644
--- a/src/core/resource/ResourceManager.cpp
+++ b/src/core/resource/ResourceManager.cpp
@@ -122,8 +122,12 @@ NodeVector<Node> ResourceManager::parse(
innerScope.checkUnwound(logger);
innerScope.performDeferredResolution(logger);
- // Fetch the nodes that were parsed by this parser instance
+ // Fetch the nodes that were parsed by this parser instance and
+ // validate them
parsedNodes = innerScope.getTopLevelNodes();
+ for (auto parsedNode : parsedNodes) {
+ parsedNode->validate(logger);
+ }
// Make sure the number of elements is exactly one -- we can
// only store one element per top-level node.
@@ -184,13 +188,18 @@ NodeVector<Node> ResourceManager::parse(
return parsedNodes;
}
-NodeVector<Node> ResourceManager::import(ParserContext &ctx,
- const std::string &path,
- const std::string &mimetype,
- const std::string &rel,
- const RttiSet &supportedTypes)
+Rooted<Node> ResourceManager::import(ParserContext &ctx,
+ const std::string &path,
+ const std::string &mimetype,
+ const std::string &rel,
+ const RttiSet &supportedTypes)
{
- return parse(ctx, path, mimetype, rel, supportedTypes, ParseMode::IMPORT);
+ NodeVector<Node> res =
+ parse(ctx, path, mimetype, rel, supportedTypes, ParseMode::IMPORT);
+ if (res.size() == 1U) {
+ return res[0];
+ }
+ return nullptr;
}
NodeVector<Node> ResourceManager::include(ParserContext &ctx,
diff --git a/src/core/resource/ResourceManager.hpp b/src/core/resource/ResourceManager.hpp
index 83556aa..1279bee 100644
--- a/src/core/resource/ResourceManager.hpp
+++ b/src/core/resource/ResourceManager.hpp
@@ -158,9 +158,9 @@ public:
* @param supportedTypes contains the types of the returned Node the caller
* can deal with. Note that only the types the parser claims to return are
* checked, not the actual result.
- * @return the parsed nodes or an empty list if something went wrong.
+ * @return the parsed node or nullptr if something went wrong.
*/
- NodeVector<Node> import(ParserContext &ctx, const std::string &path,
+ Rooted<Node> import(ParserContext &ctx, const std::string &path,
const std::string &mimetype, const std::string &rel,
const RttiSet &supportedTypes);