summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Stöckel <astoecke@techfak.uni-bielefeld.de>2015-03-03 18:23:15 +0100
committerAndreas Stöckel <andreas@somweyr.de>2015-03-03 18:23:15 +0100
commit4ee3c4042d267c010babb2ab86e15a6b31950849 (patch)
treec4204e0504de047cecf239f10e68e17246f99e8f
parentf6b22312127f29f71aa26ca28142ad3e5f193445 (diff)
added a method to find the matching start anchor for some end anchor.
-rw-r--r--src/core/model/Document.cpp114
-rw-r--r--src/core/model/Document.hpp41
-rw-r--r--test/core/model/DocumentTest.cpp115
3 files changed, 257 insertions, 13 deletions
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index b29767e..62dad11 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -452,6 +452,112 @@ Rooted<Anchor> DocumentEntity::createChildAnchor(const size_t &fieldIdx)
return Rooted<Anchor>{new Anchor(subInst->getManager(), subInst, fieldIdx)};
}
+static bool matchStartAnchor(Handle<AnnotationClass> desc,
+ const std::string &name, Handle<Anchor> a)
+{
+ return (a->getAnnotation() != nullptr) &&
+ (a->getAnnotation()->getEnd() == nullptr) &&
+ (desc == nullptr || a->getAnnotation()->getDescriptor() == desc) &&
+ (name.empty() || a->getAnnotation()->getName() == name);
+}
+
+template <typename Iterator>
+Rooted<Anchor> DocumentEntity::searchStartAnchorInField(
+ Handle<AnnotationClass> desc, const std::string &name, Iterator begin,
+ Iterator end)
+{
+ for (Iterator it = begin; it != end; it++) {
+ Handle<StructureNode> strct = *it;
+ if (strct->isa(&RttiTypes::Anchor)) {
+ // check if this Anchor is the right one.
+ Handle<Anchor> a = strct.cast<Anchor>();
+ if (matchStartAnchor(desc, name, a)) {
+ return a;
+ }
+ continue;
+ } else if (strct->isa(&RttiTypes::StructuredEntity)) {
+ // search downwards.
+ Rooted<Anchor> a =
+ strct.cast<StructuredEntity>()->searchStartAnchorDownwards(
+ desc, name);
+ if (a != nullptr) {
+ return a;
+ }
+ }
+ }
+ return nullptr;
+}
+
+Rooted<Anchor> DocumentEntity::searchStartAnchorDownwards(
+ Handle<AnnotationClass> desc, const std::string &name)
+{
+ if (fields.empty()) {
+ return nullptr;
+ }
+ // get the default field.
+ NodeVector<StructureNode> children = fields[fields.size() - 1];
+ // search it from back to front.
+ return searchStartAnchorInField(desc, name, children.rbegin(),
+ children.rend());
+}
+
+Rooted<Anchor> DocumentEntity::searchStartAnchorUpwards(
+ Handle<AnnotationClass> desc, const std::string &name,
+ const DocumentEntity *child)
+{
+ if (fields.empty()) {
+ return nullptr;
+ }
+ // get the default field.
+ NodeVector<StructureNode> children = fields[fields.size() - 1];
+ // search for the child from back to front.
+ auto it = children.rbegin();
+ while (static_cast<void *>(it->get()) != child->subInst.get() &&
+ it != children.rend()) {
+ it++;
+ }
+ // increment the reverse iterator once more to prevent downwards search
+ // to the child.
+ if (it != children.rend()) {
+ it++;
+ return searchStartAnchorInField(desc, name, it, children.rend());
+ }
+ throw OusiaException("Internal error: Child node not found in parent!");
+}
+
+Rooted<Anchor> DocumentEntity::searchStartAnchor(size_t fieldIdx,
+ Handle<AnnotationClass> desc,
+ const std::string &name)
+{
+ // get the correct field.
+ NodeVector<StructureNode> children = fields[fieldIdx];
+ // search it from back to front.
+ Rooted<Anchor> a = searchStartAnchorInField(desc, name, children.rbegin(),
+ children.rend());
+ // if we found the Anchor, return it.
+ if (a != nullptr) {
+ return a;
+ }
+
+ // If this is either an AnnotationEntity or a SUBTREE field we can not
+ // search upwards.
+ if (subInst->isa(&RttiTypes::AnnotationEntity) ||
+ fieldIdx + 1 < fields.size()) {
+ return nullptr;
+ }
+ // if the children here did not contain the right start Anchor go upwards.
+ if (subInst->getParent()->isa(&RttiTypes::StructuredEntity)) {
+ return subInst->getParent()
+ .cast<StructuredEntity>()
+ ->searchStartAnchorUpwards(desc, name, this);
+ }
+ if (subInst->getParent()->isa(&RttiTypes::AnnotationEntity)) {
+ subInst->getParent().cast<AnnotationEntity>()->searchStartAnchorUpwards(
+ desc, name, this);
+ }
+ return nullptr;
+}
+
/* Class StructureNode */
bool StructureNode::doValidate(Logger &logger) const
@@ -702,7 +808,9 @@ void AnnotationEntity::setStart(Handle<Anchor> s)
}
invalidate();
start = acquire(s);
- s->setAnnotation(this, true);
+ if (s != nullptr) {
+ s->setAnnotation(this, true);
+ }
}
void AnnotationEntity::setEnd(Handle<Anchor> e)
@@ -712,7 +820,9 @@ void AnnotationEntity::setEnd(Handle<Anchor> e)
}
invalidate();
end = acquire(e);
- e->setAnnotation(this, false);
+ if (e != nullptr) {
+ e->setAnnotation(this, false);
+ }
}
/* Class Document */
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index dc0f73f..81e2f41 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -26,8 +26,8 @@
*
* A Document, from top to bottom, consists of "Document" instance,
* which "owns" the structural root node of the in-document graph. This might
- * for example be a "book" node of the "book" ontology. That root node in turn has
- * structure nodes as children, which in turn may have children. This
+ * for example be a "book" node of the "book" ontology. That root node in turn
+ * has structure nodes as children, which in turn may have children. This
* constitutes a Structure Tree. Additionally annotations may be attached to
* Structure Nodes, effectively resulting in a Document Graph instead of a
* Document Tree (other references may introduce cycles as well).
@@ -142,7 +142,7 @@ class Anchor;
*/
class DocumentEntity {
private:
- /*
+ /**
* this is a rather dirty method that should not be used in other cases:
* We store a handle to the Node instance that inherits from
* DocumentEntity. This Handle is not registered and would lead to Segfaults
@@ -156,6 +156,18 @@ private:
void invalidateSubInstance();
+ template <typename Iterator>
+ Rooted<Anchor> searchStartAnchorInField(Handle<AnnotationClass> desc,
+ const std::string &name,
+ Iterator begin, Iterator end);
+
+ Rooted<Anchor> searchStartAnchorDownwards(Handle<AnnotationClass> desc,
+ const std::string &name);
+
+ Rooted<Anchor> searchStartAnchorUpwards(Handle<AnnotationClass> desc,
+ const std::string &name,
+ const DocumentEntity *child);
+
protected:
bool doValidate(Logger &logger) const;
@@ -420,7 +432,7 @@ public:
Rooted<StructuredEntity> createChildStructuredEntity(
Handle<StructuredClass> descriptor, const size_t &fieldIdx,
Variant attributes = Variant::mapType{}, std::string name = "");
- /*
+ /**
* Creates a new DocumentPrimitive as child of this DocumentEntity.
*
* @param content is a Variant containing the content of this
@@ -434,7 +446,7 @@ public:
*/
Rooted<DocumentPrimitive> createChildDocumentPrimitive(
Variant content, const std::string &fieldName = DEFAULT_FIELD_NAME);
- /*
+ /**
* Creates a new DocumentPrimitive as child of this DocumentEntity.
*
* @param fieldIdx is the index of the field, where the newly created
@@ -469,6 +481,23 @@ public:
* @return the newly created Anchor.
*/
Rooted<Anchor> createChildAnchor(const size_t &fieldIdx);
+
+ /**
+ * Does an inverse depth first search starting at this DocumentEntity to
+ * find a child Anchor element that matches the given seach criteria.
+ * The search will not cross SUBTREE to TREE field boundaries and will not
+ * leave AnnotationEntities upwards. If no Anchor is found a nullptr is
+ * returned. AnnotationEntities which already have an end Anchor won't be
+ * returned.
+ *
+ * @param desc is the AnnotationClass of the AnnotationEntity whose
+ * start Anchor you are looking for.
+ * @param name is the AnnotationEntities name.
+ * @return the start Anchor or a nullptr if no Anchor could be found.
+ */
+ Rooted<Anchor> searchStartAnchor(size_t fieldIdx,
+ Handle<AnnotationClass> desc = nullptr,
+ const std::string &name = "");
};
/**
@@ -1061,4 +1090,4 @@ extern const Rtti Anchor;
}
}
-#endif /* _OUSIA_MODEL_DOCUMENT_HPP_ */ \ No newline at end of file
+#endif /* _OUSIA_MODEL_DOCUMENT_HPP_ */
diff --git a/test/core/model/DocumentTest.cpp b/test/core/model/DocumentTest.cpp
index 8ed59f5..9362af8 100644
--- a/test/core/model/DocumentTest.cpp
+++ b/test/core/model/DocumentTest.cpp
@@ -30,6 +30,108 @@
namespace ousia {
+TEST(DocumentEntity, searchStartAnchor)
+{
+ // create a trivial ontology.
+ TerminalLogger logger{std::cerr, true};
+ Manager mgr{1};
+ Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)};
+ Rooted<Ontology> ontology{new Ontology(mgr, sys, "trivial")};
+ // we only have one StructuredClass that may have itself as a child.
+ Rooted<StructuredClass> A = ontology->createStructuredClass(
+ "A", Cardinality::any(), nullptr, false, true);
+ Rooted<FieldDescriptor> A_field = A->createFieldDescriptor(logger).first;
+ A_field->addChild(A);
+ // create two AnnotationClasses.
+ Rooted<AnnotationClass> Alpha = ontology->createAnnotationClass("Alpha");
+ Rooted<AnnotationClass> Beta = ontology->createAnnotationClass("Beta");
+ // validate this ontology.
+ ASSERT_TRUE(ontology->validate(logger));
+
+ // create a trivial document.
+ Rooted<Document> doc{new Document(mgr, "myDoc")};
+ Rooted<StructuredEntity> root = doc->createRootStructuredEntity(A);
+ // add an Anchor.
+ Rooted<Anchor> a = root->createChildAnchor();
+ // create an AnnotationEntity with the Anchor as start.
+ doc->createChildAnnotation(Alpha, a, nullptr, Variant::mapType{}, "myAnno");
+ // We should be able to find the Anchor now if we look for it.
+ ASSERT_EQ(a, root->searchStartAnchor(0));
+ ASSERT_EQ(a, root->searchStartAnchor(0, Alpha));
+ ASSERT_EQ(a, root->searchStartAnchor(0, nullptr, "myAnno"));
+ ASSERT_EQ(a, root->searchStartAnchor(0, Alpha, "myAnno"));
+ // but we should not find it if we look for an Anchor of a different
+ // AnnotationClass.
+ ASSERT_EQ(nullptr, root->searchStartAnchor(0, Beta));
+
+ // now add a child to the root node and place the Anchor there.
+ Rooted<StructuredEntity> child = root->createChildStructuredEntity(A);
+ Rooted<Anchor> b = root->createChildAnchor();
+ doc->createChildAnnotation(Alpha, b, nullptr, Variant::mapType{}, "myAnno");
+ // now b should be returned because its closer.
+ ASSERT_EQ(b, root->searchStartAnchor(0));
+ ASSERT_EQ(b, root->searchStartAnchor(0, Alpha));
+ ASSERT_EQ(b, root->searchStartAnchor(0, nullptr, "myAnno"));
+ ASSERT_EQ(b, root->searchStartAnchor(0, Alpha, "myAnno"));
+}
+
+TEST(DocumentEntity, searchStartAnchorUpwards)
+{
+ // create a trivial ontology.
+ TerminalLogger logger{std::cerr, true};
+ Manager mgr{1};
+ Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)};
+ Rooted<Ontology> ontology{new Ontology(mgr, sys, "trivial")};
+ // we only have one StructuredClass that may have itself as a child in the
+ // default field or a subtree field.
+ Rooted<StructuredClass> A = ontology->createStructuredClass(
+ "A", Cardinality::any(), nullptr, false, true);
+ Rooted<FieldDescriptor> A_field = A->createFieldDescriptor(logger).first;
+ Rooted<FieldDescriptor> A_sub_field =
+ A->createFieldDescriptor(logger, FieldDescriptor::FieldType::SUBTREE,
+ "sub").first;
+ A_field->addChild(A);
+ A_sub_field->addChild(A);
+ // create two AnnotationClasses.
+ Rooted<AnnotationClass> Alpha = ontology->createAnnotationClass("Alpha");
+ Rooted<AnnotationClass> Beta = ontology->createAnnotationClass("Beta");
+ // add a tree field to the annotation class.
+ Rooted<FieldDescriptor> Alpha_field =
+ Alpha->createFieldDescriptor(logger).first;
+ Alpha_field->addChild(A);
+ // validate this ontology.
+ ASSERT_TRUE(ontology->validate(logger));
+
+ // create a document with a root node, and two children, one in the
+ // default and one in the subtree field.
+ Rooted<Document> doc{new Document(mgr, "myDoc")};
+ Rooted<StructuredEntity> root = doc->createRootStructuredEntity(A);
+ // add an Anchor.
+ Rooted<Anchor> a = root->createChildAnchor();
+ // create an AnnotationEntity with the Anchor as start.
+ Rooted<AnnotationEntity> anno = doc->createChildAnnotation(
+ Alpha, a, nullptr, Variant::mapType{}, "myAnno");
+ // add a child.
+ Rooted<StructuredEntity> child = root->createChildStructuredEntity(A);
+ // We should be able to find the Anchor from the child node now. if we look
+ // for it.
+ ASSERT_EQ(a, child->searchStartAnchor(1));
+ ASSERT_EQ(a, child->searchStartAnchor(1, Alpha));
+ ASSERT_EQ(a, child->searchStartAnchor(1, nullptr, "myAnno"));
+ ASSERT_EQ(a, child->searchStartAnchor(1, Alpha, "myAnno"));
+ // we should not be able to find it from the subtree field, however.
+ ASSERT_EQ(nullptr, child->searchStartAnchor(0));
+ // and also we should not be able to find it from the annotation itself.
+ ASSERT_EQ(nullptr, anno->searchStartAnchor(0));
+ // but we can find a new anchor inside the annotation.
+ Rooted<Anchor> b = anno->createChildAnchor();
+ doc->createChildAnnotation(Beta, b, nullptr, Variant::mapType{}, "myAnno");
+ ASSERT_EQ(b, anno->searchStartAnchor(0));
+ ASSERT_EQ(b, anno->searchStartAnchor(0, Beta));
+ ASSERT_EQ(b, anno->searchStartAnchor(0, nullptr, "myAnno"));
+ ASSERT_EQ(b, anno->searchStartAnchor(0, Beta, "myAnno"));
+}
+
TEST(Document, construct)
{
// Construct Manager
@@ -213,8 +315,9 @@ TEST(Document, validate)
* Override the default field in childSubClass with an optional field.
*/
Rooted<FieldDescriptor> childSubField =
- childSubClass->createFieldDescriptor(
- logger, FieldDescriptor::FieldType::TREE, "dummy", true).first;
+ childSubClass->createFieldDescriptor(logger,
+ FieldDescriptor::FieldType::TREE,
+ "dummy", true).first;
// add a child pro forma to make it valid.
childSubField->addChild(childSubClass);
{
@@ -233,8 +336,9 @@ TEST(Document, validate)
// add a primitive field to the subclass with integer content.
Rooted<FieldDescriptor> primitive_field =
childSubClass->createPrimitiveFieldDescriptor(
- sys->getIntType(), logger, FieldDescriptor::FieldType::SUBTREE,
- "int", false).first;
+ sys->getIntType(), logger,
+ FieldDescriptor::FieldType::SUBTREE, "int",
+ false).first;
{
/*
* Now a document with one instance of the Child subclass should be
@@ -260,7 +364,8 @@ TEST(Document, validate)
}
// Now add an Annotation class to the ontology.
- Rooted<AnnotationClass> annoClass{new AnnotationClass(mgr, "anno", ontology)};
+ Rooted<AnnotationClass> annoClass{
+ new AnnotationClass(mgr, "anno", ontology)};
{
/*
* Create a valid document in itself.