diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-17 15:26:24 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-17 15:26:24 +0100 |
commit | 8992dc8c4359964168da3e9221a31bfe9e4ffe8f (patch) | |
tree | f2176e0883c4b6ee814a96902d946979326296a6 /src/core | |
parent | 33b5b737313e6d1409c60da77db26f1f00b91b57 (diff) |
Improved validation system
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/model/Document.cpp | 22 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 8 | ||||
-rw-r--r-- | src/core/model/Node.cpp | 65 | ||||
-rw-r--r-- | src/core/model/Node.hpp | 117 |
4 files changed, 128 insertions, 84 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 92791a4..0de750f 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -92,8 +92,7 @@ int DocumentEntity::getFieldDescriptorIndex( } } -bool DocumentEntity::doValidate(Logger &logger, - std::set<ManagedUid> &visited) const +bool DocumentEntity::validate(Logger &logger) const { // TODO: check the validated form of Attributes // iterate over every field @@ -205,14 +204,7 @@ bool DocumentEntity::doValidate(Logger &logger, // go into recursion. for (auto &f : fields) { for (auto &n : f) { - if (!visited.insert(n->getUid()).second) { - logger.error("The given document contains a cycle!"); - return false; - } - if (n->isValidated()) { - continue; - } - if (!n->validate(logger, visited)) { + if (!n->validate(logger)) { return false; } } @@ -247,15 +239,14 @@ StructuredEntity::StructuredEntity(Manager &mgr, Handle<Document> doc, doc->setRoot(this); } -bool StructuredEntity::doValidate(Logger &logger, - std::set<ManagedUid> &visited) const +bool StructuredEntity::doValidate(Logger &logger) const { // check if the parent is set. if (getParent() == nullptr) { return false; } // check the validity as a DocumentEntity. - return DocumentEntity::doValidate(logger, visited); + return DocumentEntity::validate(logger); } /* Class AnnotationEntity */ @@ -272,8 +263,7 @@ AnnotationEntity::AnnotationEntity(Manager &mgr, Handle<Document> parent, parent->annotations.push_back(this); } -bool AnnotationEntity::doValidate(Logger &logger, - std::set<ManagedUid> &visited) const +bool AnnotationEntity::doValidate(Logger &logger) const { // check if this AnnotationEntity is correctly registered at its document. if (getParent() == nullptr || !getParent()->isa(RttiTypes::Document)) { @@ -293,7 +283,7 @@ bool AnnotationEntity::doValidate(Logger &logger, } // check the validity as a DocumentEntity. - if (!DocumentEntity::doValidate(logger, visited)) { + if (!DocumentEntity::validate(logger)) { return false; } // TODO: then check if the anchors are in the correct document. diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index b73e07d..5af3ce2 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -161,7 +161,7 @@ protected: fields[getFieldDescriptorIndex(fieldName, true)].push_back(s); } - bool doValidate(Logger &logger, std::set<ManagedUid> &visited) const; + bool validate(Logger &logger) const; public: /** @@ -369,8 +369,7 @@ public: */ class StructuredEntity : public StructureNode, public DocumentEntity { protected: - bool doValidate(Logger &logger, - std::set<ManagedUid> &visited) const override; + bool doValidate(Logger &logger) const override; public: /** @@ -514,8 +513,7 @@ private: Owned<Anchor> start; Owned<Anchor> end; protected: - bool doValidate(Logger &logger, - std::set<ManagedUid> &visited) const override; + bool doValidate(Logger &logger) const override; public: /** diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index 17ee9b3..da83598 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -20,6 +20,7 @@ #include <unordered_set> #include <core/common/Exceptions.hpp> +#include <core/common/Logger.hpp> #include <core/common/Rtti.hpp> #include <core/common/TypedRttiBuilder.hpp> @@ -352,6 +353,63 @@ std::vector<ResolutionResult> Node::resolve(const std::string &name, return resolve(std::vector<std::string>{name}, type); } +bool Node::doValidate(Logger &logger) const { return true; } + +void Node::invalidate() +{ + // Only perform the invalidation if necessary + if (validationState != ValidationState::UNKNOWN) { + validationState = ValidationState::UNKNOWN; + if (parent != nullptr) { + parent->invalidate(); + } + } +} + +void Node::markInvalid() +{ + // Do not override the validationState if we're currently in the validation + // procedure, try to mark the parent node as invalid + if (validationState != ValidationState::VALIDATING && + validationState != ValidationState::INVALID) { + validationState = ValidationState::INVALID; + if (parent != nullptr) { + parent->markInvalid(); + } + } +} + +bool Node::validate(Logger &logger) const +{ + switch (validationState) { + case ValidationState::UNKNOWN: + validationState = ValidationState::VALIDATING; + try { + if (doValidate(logger)) { + validationState = ValidationState::VALID; + return true; + } + } + catch (OusiaException ex) { + // Make sure the validation state does not stay in the + // "VALIDATING" state + validationState = ValidationState::INVALID; + throw; + } + return false; + case ValidationState::VALID: + return true; + case ValidationState::INVALID: + return false; + case ValidationState::VALIDATING: + // We've run into recursion -- a circular structure cannot be + // properly validated, so return false + logger.error("The given document is cyclic."); + return false; + } + return false; +} + /* RTTI type registrations */ namespace RttiTypes { const Rtti<ousia::Node> Node = @@ -363,9 +421,10 @@ const Rtti<ousia::Node> Node = {[](const Variant &value, ousia::Node *obj) { obj->setName(value.asString()); }}}) - .property("parent", {Node, {[](const ousia::Node *obj) { - return Variant::fromObject(obj->getParent()); - }}}); + .property("parent", {Node, + {[](const ousia::Node *obj) { + return Variant::fromObject(obj->getParent()); + }}}); } } diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp index 3763395..0523c69 100644 --- a/src/core/model/Node.hpp +++ b/src/core/model/Node.hpp @@ -28,6 +28,7 @@ #ifndef _OUSIA_NODE_HPP_ #define _OUSIA_NODE_HPP_ +#include <cstdint> #include <map> #include <set> #include <string> @@ -47,6 +48,36 @@ template <class T> class Rtti; /** + * Describes the validity of a node structure. + */ +enum class ValidationState : uint8_t { + /** + * The validity is set to UNKNOWN if the Node has not yet been validated or + * the validation state has been reset (because the node was changed). + */ + UNKNOWN, + + /** + * The validity is set to validating if the Node has not yet been validated, + * but its validation is currently running. This flag is used to prevent + * recursion. + */ + VALIDATING, + + /** + * The validity is set to VALID if the Node has been validaten and is known + * to be valid. + */ + VALID, + + /** + * The validity is set to INVALID if the Node has been validated and is + * known to be invalid. + */ + INVALID +}; + +/** * Structure describing a single result obtained from the resolution function. */ struct ResolutionResult { @@ -105,11 +136,12 @@ private: * Reference to a parent node instace. */ Owned<Node> parent; + /** - * A "dirty" flag that signifies if this Node has been validated already + * A "dirty" flag that signifies if this Node has been already validated * or not. */ - mutable bool validated = false; + mutable ValidationState validationState; /** * Private version of the "path" function used to construct the path. Calls @@ -272,9 +304,15 @@ protected: /** * This method should be called if the internal state of this Node is - * changed such that a new validation run has to be made. + * changed such that a new validation run has to be made. Also informs the + * parent node about the invalidation. */ - void invalidate() const { validated = false; } + void invalidate(); + + /** + * This method should be called if a Node finds itself in an invalid state. + */ + void markInvalid(); /** * The convention for this function is as follows: @@ -284,44 +322,12 @@ protected: * If some child returns false this method should return false as well. * 4.) If all children could be validated this method should return true. * - * Note that this implementation does not have to touch the visited set. - * You can use it for validation purposes, however (e.g. to detect cycles - * in the graph). - * * The default and trivial behaviour of this function is to return true. * - * @param logger is a logger for error messages if false is returned. - * @param visited is a set containing the ManagedUids of all children - * that already have been visited. - * @return true if this is a valid node and false if it is not. + * @param logger is a logger for error messages if false is returned. + * @return true if this is a valid node and false if it is not. */ - virtual bool doValidate(Logger &logger, std::set<ManagedUid> &visited) const - { - return true; - } - - /** - * A wrapper for doValidate that takes care of not visiting children twice, - * and handling the validated flag. - */ - bool validate(Logger &logger, std::set<ManagedUid> &visited) const { - if (!visited.insert(getUid()).second) { - // if we have visited this Node already within this run, return - // the validated flag. - return validated; - } - if(validated){ - // if this is validated, return true. - return true; - } - if(!doValidate(logger, visited)){ - // if this can not be validated, return false. - return false; - } - // if it could be validated, set the validated flag. - validated = true; - return true; - } + virtual bool doValidate(Logger &logger) const; public: /** @@ -330,7 +336,9 @@ public: * @param mgr is a reference to the Manager instace the node belongs to. */ Node(Manager &mgr, Handle<Node> parent = nullptr) - : Managed(mgr), parent(acquire(parent)) + : Managed(mgr), + parent(acquire(parent)), + validationState(ValidationState::UNKNOWN) { } @@ -342,7 +350,10 @@ public: * @param parent is a handle pointing at the parent node. */ Node(Manager &mgr, std::string name, Handle<Node> parent = nullptr) - : Managed(mgr), name(name), parent(acquire(parent)) + : Managed(mgr), + name(name), + parent(acquire(parent)), + validationState(ValidationState::UNKNOWN) { } @@ -423,29 +434,15 @@ public: const RttiType &type); /** - * Returns true if this node has been validated. Note that a 'false' return - * value does _not_ imply that this Node is invalid. It merely says that - * validity of this node is uncertain. The opposite is true, however: If - * this node is invalid, the validated flag will be false. - */ - bool isValidated() const { return validated; } - - /** * Checks whether this node is valid and returns true if it is and false * if it is not. If the node is invalid further information will be appended - * to the logger. If this is valid this will also set the "validated" flag - * to "true". + * to the logger. * - * @param logger is a logger where errors will be logged if this - * Node is invalid. - * - * @return true if this Node is valid. + * @param logger is a logger where errors will be logged if this Node is + * invalid. + * @return true if this Node is valid. */ - bool validate(Logger &logger) - { - std::set<ManagedUid> visited; - return validate(logger, visited); - } + bool validate(Logger &logger) const; }; /** |