summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/model/Document.cpp76
-rw-r--r--src/core/model/Document.hpp30
-rw-r--r--test/core/model/DocumentTest.cpp88
3 files changed, 160 insertions, 34 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index cf6ded3..b80cb5e 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -92,10 +92,27 @@ int DocumentEntity::getFieldDescriptorIndex(
}
}
-DocumentEntity::DocumentEntity(Handle<Node> owner,
+void DocumentEntity::addStructureNode(Handle<StructureNode> s,
+ const std::string &fieldName)
+{
+ if (subInst->isa(RttiTypes::StructuredEntity)) {
+ const StructuredEntity *s =
+ static_cast<const StructuredEntity *>(subInst);
+ s->invalidate();
+ } else {
+ const AnnotationEntity *a =
+ static_cast<const AnnotationEntity *>(subInst);
+ a->invalidate();
+ }
+ fields[getFieldDescriptorIndex(fieldName, true)].push_back(s);
+}
+
+DocumentEntity::DocumentEntity(Handle<Node> subInst,
Handle<Descriptor> descriptor,
Variant attributes)
- : descriptor(owner->acquire(descriptor)), attributes(std::move(attributes))
+ : subInst(subInst.get()),
+ descriptor(subInst->acquire(descriptor)),
+ attributes(std::move(attributes))
{
// insert empty vectors for each field.
if (!descriptor.isNull()) {
@@ -107,7 +124,7 @@ DocumentEntity::DocumentEntity(Handle<Node> owner,
fieldDescs = descriptor->getFieldDescriptors();
}
for (size_t f = 0; f < fieldDescs.size(); f++) {
- fields.push_back(NodeVector<StructureNode>(owner));
+ fields.push_back(NodeVector<StructureNode>(subInst));
}
}
}
@@ -120,6 +137,7 @@ bool DocumentEntity::doValidate(Logger &logger) const
return false;
}
// TODO: check the validated form of Attributes
+ // TODO: Check if descriptor is registered at the Document?
/*
* generate the set of effective fields. This is trivial for
@@ -150,8 +168,7 @@ bool DocumentEntity::doValidate(Logger &logger) const
const size_t min = ac->getCardinality().min();
if (min > 0) {
logger.error(
- std::string("Field ") +
- descriptor->getFieldDescriptors()[f]->getName() +
+ std::string("Field ") + fieldDescs[f]->getName() +
" was empty but needs at least " + std::to_string(min) +
" elements of class " + ac->getName() +
" according to the definition of " +
@@ -172,11 +189,11 @@ bool DocumentEntity::doValidate(Logger &logger) const
// iterate over every actual child of this DocumentEntity
for (auto &rc : fields[f]) {
- if (!rc->isa(RttiTypes::Anchor)) {
+ if (rc->isa(RttiTypes::Anchor)) {
// Anchors are uninteresting and can be ignored.
continue;
}
- if (!rc->isa(RttiTypes::DocumentPrimitive)) {
+ if (rc->isa(RttiTypes::DocumentPrimitive)) {
// For DocumentPrimitives we have to check the content type.
// TODO: Do that!
continue;
@@ -281,6 +298,12 @@ bool StructuredEntity::doValidate(Logger &logger) const
if (getParent() == nullptr) {
return false;
}
+ // check name
+ if (getName() != "") {
+ if (!validateName(logger)) {
+ return false;
+ }
+ }
// check the validity as a DocumentEntity.
return DocumentEntity::doValidate(logger);
}
@@ -317,6 +340,12 @@ bool AnnotationEntity::doValidate(Logger &logger) const
logger.error("This annotation was not registered at the document.");
return false;
}
+ // check name
+ if (getName() != "") {
+ if (!validateName(logger)) {
+ return false;
+ }
+ }
// check if the Anchors are part of the right document.
if (!doc->hasChild(start)) {
return false;
@@ -344,26 +373,23 @@ void Document::doResolve(ResolutionState &state)
bool Document::doValidate(Logger &logger) const
{
- if (root != nullptr) {
- // check if the root is allowed to be a root.
- if (!root->getDescriptor().cast<StructuredClass>()->root) {
- logger.error(std::string("A node of type ") +
- root->getDescriptor()->getName() +
- " is not allowed to be the Document root!");
- return false;
- }
- // then call validate on the root
- if (!root->validate(logger)) {
- return false;
- }
+ // An empty document is always invalid. TODO: Is this a smart choice?
+ if (root == nullptr) {
+ return false;
}
- // call validate on the AnnotationEntities
- for (auto &a : annotations) {
- if (!a->validate(logger)) {
- return false;
- }
+ // check if the root is allowed to be a root.
+ if (!root->getDescriptor().cast<StructuredClass>()->root) {
+ logger.error(std::string("A node of type ") +
+ root->getDescriptor()->getName() +
+ " is not allowed to be the Document root!");
+ return false;
}
- return true;
+ // then call validate on the root
+ if (!root->validate(logger)) {
+ return false;
+ }
+ // call validate on the AnnotationEntities
+ return continueValidation(annotations, logger);
}
bool Document::hasChild(Handle<StructureNode> s) const
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index b4ee429..15d002c 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -144,6 +144,12 @@ class DocumentEntity {
friend StructureNode;
private:
+ /*
+ * this is a rather dirty method that should not be used in other cases:
+ * We store a pointer to the Node instance that inherits from
+ * DocumentEntity.
+ */
+ const Node *subInst;
Owned<Descriptor> descriptor;
const Variant attributes;
std::vector<NodeVector<StructureNode>> fields;
@@ -156,10 +162,7 @@ private:
protected:
void addStructureNode(Handle<StructureNode> s,
- const std::string &fieldName = "")
- {
- fields[getFieldDescriptorIndex(fieldName, true)].push_back(s);
- }
+ const std::string &fieldName = "");
bool doValidate(Logger &logger) const;
@@ -169,7 +172,7 @@ public:
* from Node. Therefore we need to have a handle to the subclass Node
* instance to create NodeVectors and Owned references.
*
- * @param owner is a handle to the subclass instance
+ * @param subInst is a handle to the subclass instance
* (e.g. StructuredEntity), such that the fields vectors
* and the descriptor reference can be obtained.
* @param descriptor is the Descriptor for this DocumentEntity, which will
@@ -177,7 +180,7 @@ public:
* @param attributes is a Map Variant adhering to the attribute StructType
* in the given descriptor.
*/
- DocumentEntity(Handle<Node> owner, Handle<Descriptor> descriptor,
+ DocumentEntity(Handle<Node> subInst, Handle<Descriptor> descriptor,
Variant attributes = {});
/**
@@ -498,6 +501,8 @@ public:
*
*/
class AnnotationEntity : public Node, public DocumentEntity {
+ friend DocumentEntity;
+
private:
Owned<Anchor> start;
Owned<Anchor> end;
@@ -573,7 +578,11 @@ public:
/**
* Sets the root StructuredEntity of this Document.
*/
- void setRoot(Handle<StructuredEntity> root) { this->root = acquire(root); };
+ void setRoot(Handle<StructuredEntity> root)
+ {
+ invalidate();
+ this->root = acquire(root);
+ };
/**
* Returns the root StructuredEntity of this Document.
@@ -606,13 +615,18 @@ public:
/**
* Adds a Domain reference to this Document.
*/
- void addDomain(Handle<Domain> d) { domains.push_back(d); }
+ void addDomain(Handle<Domain> d)
+ {
+ invalidate();
+ domains.push_back(d);
+ }
/**
* Adds multiple Domain references to this Document.
*/
void addDomains(const std::vector<Handle<Domain>> &d)
{
+ invalidate();
domains.insert(domains.end(), d.begin(), d.end());
}
diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp
index 4b0447d..d8ccef1 100644
--- a/test/core/model/DocumentTest.cpp
+++ b/test/core/model/DocumentTest.cpp
@@ -31,7 +31,7 @@
namespace ousia {
namespace model {
-TEST(Document, testDocumentConstruction)
+TEST(Document, construct)
{
// Construct Manager
TerminalLogger logger{std::cerr, true};
@@ -109,5 +109,91 @@ TEST(Document, testDocumentConstruction)
}
}
}
+
+TEST(Document, validate)
+{
+ // Let's start with a trivial domain and a trivial document.
+ TerminalLogger logger{std::cerr, true};
+ Manager mgr{1};
+ Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)};
+ Rooted<Domain> domain{new Domain(mgr, sys, "trivial")};
+ Cardinality single;
+ single.merge({1});
+ // Set up the "root" StructuredClass.
+ Rooted<StructuredClass> rootClass{new StructuredClass(
+ mgr, "root", domain, single, {nullptr}, {nullptr}, false, true)};
+
+ // set up a document for it.
+ {
+ // first an invalid one, which is empty.
+ Rooted<Document> doc{new Document(mgr, "myDoc.oxd")};
+ doc->addDomain(domain);
+ ASSERT_FALSE(doc->validate(logger));
+ // then add a root, which should make it valid.
+ Rooted<StructuredEntity> root =
+ buildRootStructuredEntity(doc, logger, {"root"});
+ ASSERT_TRUE(doc->validate(logger));
+ }
+
+ // now let's extend the rootClass with a default field.
+ Rooted<FieldDescriptor> rootField{new FieldDescriptor(mgr, rootClass)};
+ // and add a child class for it.
+ Rooted<StructuredClass> childClass{
+ new StructuredClass(mgr, "child", domain, single)};
+ rootField->addChild(childClass);
+ {
+ /*
+ * now check again: Because the child has the cardinality {1} our
+ * document should be invalid again.
+ */
+ Rooted<Document> doc{new Document(mgr, "myDoc.oxd")};
+ doc->addDomain(domain);
+ Rooted<StructuredEntity> root =
+ buildRootStructuredEntity(doc, logger, {"root"});
+ ASSERT_FALSE(doc->validate(logger));
+ // but it should get valid if we add a proper child.
+ buildStructuredEntity(doc, logger, root, {"child"});
+ ASSERT_TRUE(doc->validate(logger));
+ // and it should get invalid again if we add one more child.
+ buildStructuredEntity(doc, logger, root, {"child"});
+ ASSERT_FALSE(doc->validate(logger));
+ }
+ /*
+ * Add a further extension to the domain: We add a subclass to child.
+ */
+ Rooted<StructuredClass> childSubClass{new StructuredClass(
+ mgr, "childSub", domain, single, {nullptr}, childClass)};
+ {
+ /*
+ * A document with one instance of the Child subclass should be valid.
+ */
+ Rooted<Document> doc{new Document(mgr, "myDoc.oxd")};
+ doc->addDomain(domain);
+ Rooted<StructuredEntity> root =
+ buildRootStructuredEntity(doc, logger, {"root"});
+ buildStructuredEntity(doc, logger, root, {"childSub"});
+ ASSERT_TRUE(doc->validate(logger));
+ }
+ /*
+ * Make it even more complicated: child gets a field for further child
+ * instances now.
+ */
+ Rooted<FieldDescriptor> childField{new FieldDescriptor(mgr, childClass)};
+ childField->addChild(childClass);
+ {
+ /*
+ * Now a document with one instance of the Child subclass should be
+ * invalid, because it has no children of itself.
+ */
+ Rooted<Document> doc{new Document(mgr, "myDoc.oxd")};
+ doc->addDomain(domain);
+ Rooted<StructuredEntity> root =
+ buildRootStructuredEntity(doc, logger, {"root"});
+ buildStructuredEntity(doc, logger, root, {"childSub"});
+ ASSERT_FALSE(doc->validate(logger));
+ }
+ // TODO: Override child field in childSub such that an empty childSub is
+ // valid.
+}
}
}