summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-16 15:20:49 +0100
committerBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-01-16 15:20:49 +0100
commit9929838e62d9c17647d74be54af5853e8b613c4b (patch)
tree732446f234419a0f1baa5208722e9236affd74bc
parent8cf24170a4998e316c1b9c9bfd2b56e266c544cd (diff)
validate function for Domain::Descriptor.
-rw-r--r--src/core/RangeSet.hpp51
-rw-r--r--src/core/model/Document.cpp107
-rw-r--r--src/core/model/Document.hpp2
-rw-r--r--src/core/model/Domain.cpp11
-rw-r--r--src/core/model/Domain.hpp15
-rw-r--r--test/core/model/DomainTest.cpp68
6 files changed, 249 insertions, 5 deletions
diff --git a/src/core/RangeSet.hpp b/src/core/RangeSet.hpp
index 3b351c3..2c138dc 100644
--- a/src/core/RangeSet.hpp
+++ b/src/core/RangeSet.hpp
@@ -205,7 +205,7 @@ protected:
* end of the list if no such element was found.
*/
typename std::set<Range<T>, RangeComp<T>>::iterator firstOverlapping(
- const Range<T> &r, const bool allowNeighbours)
+ const Range<T> &r, const bool allowNeighbours) const
{
// Find the element with the next larger start value compared to the
// start value given in r.
@@ -265,7 +265,7 @@ public:
* @param r is the range for which the containment should be checked.
* @return true if the above condition is met, false otherwise.
*/
- bool contains(const Range<T> &r)
+ bool contains(const Range<T> &r) const
{
auto it = firstOverlapping(r, false);
if (it != ranges.end()) {
@@ -275,12 +275,29 @@ public:
}
/**
+ * Checks whether this range Set S contains a given value v, which is
+ * the case if at least one contained range R contains v.
+ *
+ * @param v is some value.
+ * @return true if at least one Range r returns true for r.inRange(v)
+ */
+ bool contains(const T &v) const
+ {
+ for (auto &r : ranges) {
+ if (r.inRange(v)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Checks whether this range set S1 contains the given range set S2:
*
* @param s is the range for which the containment should be checked.
* @return true if the above condition is met, false otherwise.
*/
- bool contains(const RangeSet<T> &s)
+ bool contains(const RangeSet<T> &s) const
{
bool res = true;
for (Range<T> &r : s.ranges) {
@@ -290,6 +307,29 @@ public:
}
/**
+ * Returns the minimum value that is still covered by this RangeSet.
+ *
+ * @return the minimum value that is still covered by this RangeSet.
+ */
+ T min() const { return ranges.begin()->start; }
+
+ /**
+ * Returns the maximum value that is still covered by this RangeSet.
+ *
+ * @return the maximum value that is still covered by this RangeSet.
+ */
+ T max() const
+ {
+ T max = ranges.begin()->end;
+ for (Range<T> &r : ranges) {
+ if (r.end > max) {
+ max = r.end;
+ }
+ }
+ return std::move(max);
+ }
+
+ /**
* Empties the set.
*/
void clear() { ranges.clear(); }
@@ -297,7 +337,10 @@ public:
/**
* Returns the current list of ranges as a const reference.
*/
- const std::set<Range<T>, RangeComp<T>> &getRanges() { return this->ranges; }
+ const std::set<Range<T>, RangeComp<T>> &getRanges() const
+ {
+ return this->ranges;
+ }
};
}
diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp
index fb39384..2f12acb 100644
--- a/src/core/model/Document.cpp
+++ b/src/core/model/Document.cpp
@@ -18,6 +18,9 @@
#include "Document.hpp"
+#include <map>
+#include <set>
+
#include <core/common/Exceptions.hpp>
#include <core/common/Rtti.hpp>
@@ -89,6 +92,110 @@ int DocumentEntity::getFieldDescriptorIndex(
}
}
+bool DocumentEntity::validate(Logger &logger) const
+{
+ // TODO: check the validated form of Attributes
+ // iterate over every field
+ for (unsigned int f = 0; f < fields.size(); f++) {
+ // we can do a faster check if this field is empty.
+ if (fields[f].size() == 0) {
+ // if this field is optional, an empty field is valid anyways.
+ if (descriptor->getFieldDescriptors()[f]->optional) {
+ continue;
+ }
+ /*
+ * if it is not optional we have to chack if zero is a valid
+ * cardinality.
+ */
+ for (auto &ac :
+ descriptor->getFieldDescriptors()[f]->getChildren()) {
+ const size_t min = ac->getCardinality().min();
+ if (min > 0) {
+ logger.error(
+ std::string("Field ") +
+ descriptor->getFieldDescriptors()[f]->getName() +
+ " was empty but needs at least " + std::to_string(min) +
+ " elements of class " + ac->getName() +
+ " according to the definition of " +
+ descriptor->getName());
+ return false;
+ }
+ }
+ continue;
+ }
+
+ // create a set of allowed classes identified by their unique id.
+ std::set<ManagedUid> accs;
+ for (auto &ac : descriptor->getFieldDescriptors()[f]->getChildren()) {
+ accs.insert(ac->getUid());
+ }
+ // store the actual numbers of children for each child class in a map
+ std::map<ManagedUid, unsigned int> nums;
+
+ // iterate over every actual child of this DocumentEntity
+ for (auto &rc : fields[f]) {
+ if (!rc->isa(RttiTypes::StructuredEntity)) {
+ continue;
+ }
+ Handle<StructuredEntity> c = rc.cast<StructuredEntity>();
+
+ ManagedUid id = c->getDescriptor()->getUid();
+ // check if its class is allowed.
+ bool allowed = accs.find(id) != accs.end();
+ /*
+ * if it is not allowed directly, we have to check if the class is a
+ * child of a permitted class.
+ */
+ if (!allowed) {
+ for (auto &ac :
+ descriptor->getFieldDescriptors()[f]->getChildren()) {
+ if (c->getDescriptor()
+ .cast<StructuredClass>()
+ ->isSubclassOf(ac)) {
+ allowed = true;
+ id = ac->getUid();
+ }
+ }
+ }
+ if (!allowed) {
+ logger.error(std::string("An instance of ") +
+ c->getDescriptor()->getName() +
+ " is not allowed as child of an instance of " +
+ descriptor->getName() + " in field " +
+ descriptor->getFieldDescriptors()[f]->getName());
+ return false;
+ }
+ // note the number of occurences.
+ const auto &n = nums.find(id);
+ if (n != nums.end()) {
+ n->second++;
+ } else {
+ nums.emplace(id, 1);
+ }
+ }
+
+ // now check if the cardinalities are right.
+ for (auto &ac : descriptor->getFieldDescriptors()[f]->getChildren()) {
+ const auto &n = nums.find(ac->getUid());
+ unsigned int num = 0;
+ if (n != nums.end()) {
+ num = n->second;
+ }
+ if (!ac->getCardinality().contains(num)) {
+ logger.error(
+ std::string("Field ") +
+ descriptor->getFieldDescriptors()[f]->getName() + " had " +
+ std::to_string(num) + " elements of class " +
+ ac->getName() +
+ ", which is invalid according to the definition of " +
+ descriptor->getName());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
/* Class StructureNode */
StructureNode::StructureNode(Manager &mgr, std::string name,
diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp
index be8b5c8..c70a6a3 100644
--- a/src/core/model/Document.hpp
+++ b/src/core/model/Document.hpp
@@ -256,6 +256,8 @@ 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
diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp
index b4fea3c..2ac2d8d 100644
--- a/src/core/model/Domain.cpp
+++ b/src/core/model/Domain.cpp
@@ -36,6 +36,7 @@ static void checkUniqueName(Handle<Node> parent, NodeVector<T> vec,
childNames.insert(c->getName());
}
if (childNames.find(child->getName()) != childNames.end()) {
+ //TODO: Do we really want to have an exception here?
throw OusiaException(std::string("The ") + parentClassName + " " +
parent->getName() + " already has a " +
childClassName + " with name " + child->getName());
@@ -218,6 +219,16 @@ StructuredClass::StructuredClass(Manager &mgr, std::string name,
}
}
+bool StructuredClass::isSubclassOf(Handle<StructuredClass> c) const{
+ if(c == nullptr || superclass == nullptr){
+ return false;
+ }
+ if(c == superclass){
+ return true;
+ }
+ return superclass->isSubclassOf(c);
+}
+
/* Class AnnotationClass */
AnnotationClass::AnnotationClass(
diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp
index 8bc21e9..ac02ec7 100644
--- a/src/core/model/Domain.hpp
+++ b/src/core/model/Domain.hpp
@@ -324,6 +324,10 @@ public:
*/
const NodeVector<StructuredClass> &getChildren() const { return children; }
+ /*
+ *TODO: This should check whether another class is permitted that is a
+ * superclass of this one.
+ */
/**
* Adds a StructuredClass whose instances shall be allowed as children in
* the StructureTree of instances of this field.
@@ -602,6 +606,17 @@ public:
* @return the superclass of this StructuredClass.
*/
Rooted<StructuredClass> getSuperclass() const { return superclass; }
+
+ /**
+ * Returns true if this class is a subclass of the given class. It does not
+ * return true if the other class is equal to the given class.
+ *
+ * @param c is another class that might or might not be a superclass of this
+ * one
+ * @return true if this class is a subclass of the given class.
+ *
+ */
+ bool isSubclassOf(Handle<StructuredClass> c) const;
/**
* Returns the StructuredClasses that are subclasses of this class. This
diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp
index 772130c..5909841 100644
--- a/test/core/model/DomainTest.cpp
+++ b/test/core/model/DomainTest.cpp
@@ -147,7 +147,6 @@ TEST(Descriptor, pathToAdvanced)
*
* So the path start_field , E , E_field should be returned.
*/
- Logger logger;
Manager mgr{1};
Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)};
// Construct the domain
@@ -218,5 +217,72 @@ TEST(Descriptor, pathToAdvanced)
ASSERT_TRUE(path[2]->isa(RttiTypes::FieldDescriptor));
ASSERT_EQ("", path[2]->getName());
}
+
+TEST(StructuredClass, isSubclassOf)
+{
+ // create an inheritance hierarchy.
+ Manager mgr{1};
+ Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)};
+ Rooted<Domain> domain{new Domain(mgr, sys, "inheritance")};
+ Cardinality any;
+ any.merge(Range<size_t>::typeRangeFrom(0));
+ Rooted<StructuredClass> A{new StructuredClass(
+ mgr, "A", domain, any, {nullptr}, {nullptr}, false, true)};
+ // first branch
+ Rooted<StructuredClass> B{
+ new StructuredClass(mgr, "B", domain, any, {nullptr}, A)};
+ Rooted<StructuredClass> C{
+ new StructuredClass(mgr, "C", domain, any, {nullptr}, B)};
+ // second branch
+ Rooted<StructuredClass> D{
+ new StructuredClass(mgr, "D", domain, any, {nullptr}, A)};
+ Rooted<StructuredClass> E{
+ new StructuredClass(mgr, "E", domain, any, {nullptr}, D)};
+ Rooted<StructuredClass> F{
+ new StructuredClass(mgr, "F", domain, any, {nullptr}, D)};
+
+ // check function results
+ ASSERT_FALSE(A->isSubclassOf(A));
+ ASSERT_FALSE(A->isSubclassOf(B));
+ ASSERT_FALSE(A->isSubclassOf(C));
+ ASSERT_FALSE(A->isSubclassOf(D));
+ ASSERT_FALSE(A->isSubclassOf(E));
+ ASSERT_FALSE(A->isSubclassOf(F));
+
+ ASSERT_TRUE(B->isSubclassOf(A));
+ ASSERT_FALSE(B->isSubclassOf(B));
+ ASSERT_FALSE(B->isSubclassOf(C));
+ ASSERT_FALSE(B->isSubclassOf(D));
+ ASSERT_FALSE(B->isSubclassOf(E));
+ ASSERT_FALSE(B->isSubclassOf(F));
+
+ ASSERT_TRUE(C->isSubclassOf(A));
+ ASSERT_TRUE(C->isSubclassOf(B));
+ ASSERT_FALSE(C->isSubclassOf(C));
+ ASSERT_FALSE(C->isSubclassOf(D));
+ ASSERT_FALSE(C->isSubclassOf(E));
+ ASSERT_FALSE(C->isSubclassOf(F));
+
+ ASSERT_TRUE(D->isSubclassOf(A));
+ ASSERT_FALSE(D->isSubclassOf(B));
+ ASSERT_FALSE(D->isSubclassOf(C));
+ ASSERT_FALSE(D->isSubclassOf(D));
+ ASSERT_FALSE(D->isSubclassOf(E));
+ ASSERT_FALSE(D->isSubclassOf(F));
+
+ ASSERT_TRUE(E->isSubclassOf(A));
+ ASSERT_FALSE(E->isSubclassOf(B));
+ ASSERT_FALSE(E->isSubclassOf(C));
+ ASSERT_TRUE(E->isSubclassOf(D));
+ ASSERT_FALSE(E->isSubclassOf(E));
+ ASSERT_FALSE(E->isSubclassOf(F));
+
+ ASSERT_TRUE(F->isSubclassOf(A));
+ ASSERT_FALSE(F->isSubclassOf(B));
+ ASSERT_FALSE(F->isSubclassOf(C));
+ ASSERT_TRUE(F->isSubclassOf(D));
+ ASSERT_FALSE(F->isSubclassOf(E));
+ ASSERT_FALSE(F->isSubclassOf(F));
+}
}
}