summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/model/Document.cpp68
-rw-r--r--src/core/model/Document.hpp15
-rw-r--r--src/core/model/Node.hpp85
3 files changed, 164 insertions, 4 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index 2f12acb..92791a4 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -92,7 +92,8 @@ int DocumentEntity::getFieldDescriptorIndex(
}
}
-bool DocumentEntity::validate(Logger &logger) const
+bool DocumentEntity::doValidate(Logger &logger,
+ std::set<ManagedUid> &visited) const
{
// TODO: check the validated form of Attributes
// iterate over every field
@@ -134,9 +135,16 @@ bool DocumentEntity::validate(Logger &logger) const
// iterate over every actual child of this DocumentEntity
for (auto &rc : fields[f]) {
- if (!rc->isa(RttiTypes::StructuredEntity)) {
+ if (!rc->isa(RttiTypes::Anchor)) {
+ // Anchors are uninteresting and can be ignored.
continue;
}
+ if (!rc->isa(RttiTypes::DocumentPrimitive)) {
+ // For DocumentPrimitives we have to check the content type.
+ // TODO: Do that!
+ continue;
+ }
+ // otherwise this is a StructuredEntity
Handle<StructuredEntity> c = rc.cast<StructuredEntity>();
ManagedUid id = c->getDescriptor()->getUid();
@@ -193,6 +201,23 @@ bool DocumentEntity::validate(Logger &logger) const
}
}
}
+
+ // 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)) {
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -222,6 +247,17 @@ StructuredEntity::StructuredEntity(Manager &mgr, Handle<Document> doc,
doc->setRoot(this);
}
+bool StructuredEntity::doValidate(Logger &logger,
+ std::set<ManagedUid> &visited) const
+{
+ // check if the parent is set.
+ if (getParent() == nullptr) {
+ return false;
+ }
+ // check the validity as a DocumentEntity.
+ return DocumentEntity::doValidate(logger, visited);
+}
+
/* Class AnnotationEntity */
AnnotationEntity::AnnotationEntity(Manager &mgr, Handle<Document> parent,
@@ -236,6 +272,34 @@ AnnotationEntity::AnnotationEntity(Manager &mgr, Handle<Document> parent,
parent->annotations.push_back(this);
}
+bool AnnotationEntity::doValidate(Logger &logger,
+ std::set<ManagedUid> &visited) const
+{
+ // check if this AnnotationEntity is correctly registered at its document.
+ if (getParent() == nullptr || !getParent()->isa(RttiTypes::Document)) {
+ return false;
+ }
+ Handle<Document> doc = getParent().cast<Document>();
+ bool found = false;
+ for (auto &a : doc->getAnnotations()) {
+ if (a == this) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ logger.error("This annotation was not registered at the document.");
+ return false;
+ }
+
+ // check the validity as a DocumentEntity.
+ if (!DocumentEntity::doValidate(logger, visited)) {
+ return false;
+ }
+ // TODO: then check if the anchors are in the correct document.
+ return true;
+}
+
/* Class Document */
void Document::continueResolve(ResolutionState &state)
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index c70a6a3..b73e07d 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -108,6 +108,8 @@
#ifndef _OUSIA_MODEL_DOCUMENT_HPP_
#define _OUSIA_MODEL_DOCUMENT_HPP_
+#include <set>
+
#include <core/managed/ManagedContainer.hpp>
#include <core/common/Variant.hpp>
@@ -159,6 +161,8 @@ protected:
fields[getFieldDescriptorIndex(fieldName, true)].push_back(s);
}
+ bool doValidate(Logger &logger, std::set<ManagedUid> &visited) const;
+
public:
/**
* The constructor for a DocumentEntity. Node that this does not inherit
@@ -256,8 +260,6 @@ public:
return fields[getFieldDescriptorIndex(fieldDescriptor, true)];
}
- bool validate(Logger& logger) const;
-
// TODO: Change this to move methods.
// /**
// * This adds a StructureNode to the field with the given name. If an
@@ -343,6 +345,8 @@ public:
* common superclass for StructuredEntity, Anchor and DocumentPrimitive.
*/
class StructureNode : public Node {
+ friend DocumentEntity;
+
public:
/**
* Constructor for a StructureNode at the root.
@@ -364,6 +368,10 @@ public:
* information please refer to the header documentation above.
*/
class StructuredEntity : public StructureNode, public DocumentEntity {
+protected:
+ bool doValidate(Logger &logger,
+ std::set<ManagedUid> &visited) const override;
+
public:
/**
* Constructor for a StructuredEntity in the Structure Tree.
@@ -505,6 +513,9 @@ class AnnotationEntity : public Node, public DocumentEntity {
private:
Owned<Anchor> start;
Owned<Anchor> end;
+protected:
+ bool doValidate(Logger &logger,
+ std::set<ManagedUid> &visited) const override;
public:
/**
diff --git a/src/core/model/Node.hpp b/src/core/model/Node.hpp
index e8adbaa..3763395 100644
--- a/src/core/model/Node.hpp
+++ b/src/core/model/Node.hpp
@@ -29,6 +29,7 @@
#define _OUSIA_NODE_HPP_
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -40,6 +41,7 @@
namespace ousia {
// Forward declarations
+class Logger;
class RttiType;
template <class T>
class Rtti;
@@ -103,6 +105,11 @@ private:
* Reference to a parent node instace.
*/
Owned<Node> parent;
+ /**
+ * A "dirty" flag that signifies if this Node has been validated already
+ * or not.
+ */
+ mutable bool validated = false;
/**
* Private version of the "path" function used to construct the path. Calls
@@ -263,6 +270,59 @@ protected:
return false;
}
+ /**
+ * This method should be called if the internal state of this Node is
+ * changed such that a new validation run has to be made.
+ */
+ void invalidate() const { validated = false; }
+
+ /**
+ * The convention for this function is as follows:
+ * 1.) The child should validate itself and return false, if constraints are
+ * not met. Errors should be logged if and only if false is returned.
+ * 3.) It should call validate on all children.
+ * 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.
+ */
+ 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;
+ }
+
public:
/**
* Initializes the node with empty name and parent.
@@ -361,6 +421,31 @@ public:
*/
std::vector<ResolutionResult> resolve(const std::string &name,
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".
+ *
+ * @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);
+ }
};
/**