From 0b76b4943b53b7b42500f15afc45f7eb38d8734b Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Fri, 16 Jan 2015 11:57:46 +0100 Subject: added getUid() method to Managed. --- src/core/managed/Managed.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp index dd1a23a..48198c2 100644 --- a/src/core/managed/Managed.hpp +++ b/src/core/managed/Managed.hpp @@ -93,6 +93,14 @@ public: */ Manager &getManager() { return mgr; } + /** + * Returns the unique identifier (UID) of this object. Valid UIDs are + * positive non-zero values. + * + * @return the unique id of the object. + */ + ManagedUid getUid() { return mgr.getUid(this); } + /** * Acquires a reference to the object wraped in the given handle -- creates * a new Owned handle instance with this Managed instance as owner and the @@ -151,8 +159,7 @@ public: * object. The event id must be used when unregistering event handlers. */ EventId registerEvent(EventType type, EventHandler handler, - Handle owner, - void *data = nullptr); + Handle owner, void *data = nullptr); /** * Unregisters the event handler with the given signature. -- cgit v1.2.3 From 7bdaa89ccef864d36f1e1adce535179d9e5fadce Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Fri, 16 Jan 2015 12:29:30 +0100 Subject: made getUid work using a rather ugly const cast. Unfortunately this could not be prevented. --- src/core/managed/Managed.hpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/core/managed/Managed.hpp b/src/core/managed/Managed.hpp index 48198c2..e98e95e 100644 --- a/src/core/managed/Managed.hpp +++ b/src/core/managed/Managed.hpp @@ -99,7 +99,17 @@ public: * * @return the unique id of the object. */ - ManagedUid getUid() { return mgr.getUid(this); } + ManagedUid getUid() const + { + /* + * Dear Bjarne Stroustroup, dear gods of C++, please excuse this + * const_cast, for I did try other means but was not able to apply them + * and in my despair turned to this folly, this ugliness, this heresy! + * I pledge my life to better programming and promise that this cast + * will do no harm to anyone. + */ + return mgr.getUid(const_cast(this)); + } /** * Acquires a reference to the object wraped in the given handle -- creates -- cgit v1.2.3 From 8cf24170a4998e316c1b9c9bfd2b56e266c544cd Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Fri, 16 Jan 2015 12:31:06 +0100 Subject: renamed isa to superclass in Domain::Descriptor and id some cosmetic changes. --- src/core/model/Document.cpp | 6 ++--- src/core/model/Domain.cpp | 54 +++++++++++++++------------------------------ src/core/model/Domain.hpp | 15 ++++++------- 3 files changed, 28 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index 299b427..fb39384 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -95,11 +95,11 @@ StructureNode::StructureNode(Manager &mgr, std::string name, Handle parent, const std::string &fieldName) : Node(mgr, std::move(name), parent) { - if(parent->isa(RttiTypes::StructuredEntity)){ + if (parent->isa(RttiTypes::StructuredEntity)) { parent.cast()->addStructureNode(this, fieldName); - } else if(parent->isa(RttiTypes::AnnotationEntity)){ + } else if (parent->isa(RttiTypes::AnnotationEntity)) { parent.cast()->addStructureNode(this, fieldName); - } else{ + } else { throw OusiaException("The proposed parent was no DocumentEntity!"); } } diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index f1b91cc..b4fea3c 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -28,8 +28,7 @@ namespace model { template static void checkUniqueName(Handle parent, NodeVector vec, - Handle child, - const std::string &parentClassName, + Handle child, const std::string &parentClassName, const std::string &childClassName) { std::set childNames; @@ -96,31 +95,15 @@ std::vector> Descriptor::pathTo( return path; } -static bool pathEquals(const Descriptor &a, const Descriptor &b) -{ - // We assume that two Descriptors are equal if their names and domain names - // are equal. - if (a.getName() != b.getName()) { - return false; - } - Handle aDom = a.getParent().cast(); - Handle bDom = b.getParent().cast(); - return aDom->getName() == bDom->getName(); -} - bool Descriptor::continuePath(Handle target, std::vector> ¤tPath, std::set ignoredFields, bool exploreSuperclass, bool exploreSubclasses) const { - // TODO: REMOVE - std::string targetName = target->getName(); - std::string thisName = getName(); - std::string currentPathName; - for (auto &n : currentPath) { - currentPathName += "."; - currentPathName += n->getName(); + // check if we are at the target already + if (this == target) { + return true; } // a variable to determine if we already found a solution bool found = false; @@ -133,7 +116,8 @@ bool Descriptor::continuePath(Handle target, continue; } for (auto &c : fd->getChildren()) { - if (pathEquals(*c, *target)) { + // check if a child is the target node. + if (c == target) { // if we have made the connection, stop the search. currentPath.push_back(fd); return true; @@ -161,11 +145,11 @@ bool Descriptor::continuePath(Handle target, * if this is a StructuredClass, we can also use the super class * (at least for fields that are not overridden) */ - if (exploreSuperclass && !tis->getIsA().isNull()) { + if (exploreSuperclass && tis->getSuperclass() != nullptr) { // copy the path. std::vector> cPath = currentPath; - if (tis->getIsA()->continuePath(target, cPath, ignoredFields, true, - false) && + if (tis->getSuperclass()->continuePath(target, cPath, ignoredFields, + true, false) && (!found || optimum.size() > cPath.size())) { // look if this path is better than the current optimum. optimum = std::move(cPath); @@ -203,13 +187,11 @@ void Descriptor::copyFieldDescriptor(Handle fd) * constructor will add the newly constructed FieldDescriptor to this * Descriptor automatically. */ - new FieldDescriptor(getManager(), this, - fd->getPrimitiveType(), - fd->getName(), fd->optional); + new FieldDescriptor(getManager(), this, fd->getPrimitiveType(), + fd->getName(), fd->optional); } else { - new FieldDescriptor(getManager(), this, - fd->getFieldType(), - fd->getName(), fd->optional); + new FieldDescriptor(getManager(), this, fd->getFieldType(), + fd->getName(), fd->optional); } } @@ -219,17 +201,17 @@ StructuredClass::StructuredClass(Manager &mgr, std::string name, Handle domain, const Cardinality &cardinality, Handle attributesDescriptor, - Handle isa, bool transparent, - bool root) + Handle superclass, + bool transparent, bool root) : Descriptor(mgr, std::move(name), domain, attributesDescriptor), cardinality(cardinality), - isa(acquire(isa)), + superclass(acquire(superclass)), subclasses(this), transparent(transparent), root(root) { - if (!isa.isNull()) { - isa->subclasses.push_back(this); + if (superclass != nullptr) { + superclass->subclasses.push_back(this); } if (!domain.isNull()) { domain->addStructuredClass(this); diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 791d563..8bc21e9 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -546,7 +546,7 @@ typedef RangeSet Cardinality; class StructuredClass : public Descriptor { private: const Cardinality cardinality; - Owned isa; + Owned superclass; NodeVector subclasses; public: @@ -570,7 +570,7 @@ public: * @param attributesDescriptor is a StructType that specifies the attribute * keys as well as value domains for this * Descriptor. - * @param isa references a parent StructuredClass. Please + * @param superclass references a parent StructuredClass. Please * look for more information on inheritance in * the class documentation above. The default is * a null reference, meaning no super class. @@ -585,7 +585,7 @@ public: const Cardinality &cardinality, Handle attributesDescriptor = nullptr, // TODO: What would be a wise default value for isa? - Handle isa = nullptr, + Handle superclass = nullptr, bool transparent = false, bool root = false); /** @@ -596,13 +596,12 @@ public: const Cardinality &getCardinality() const { return cardinality; } /** - * Returns the parent of this StructuredClass in the class inheritance - * hierarchy (!). This is not the same as the parents in the Structure Tree! + * Returns the superclass of this StructuredClass. This is not the same as + * the parents in the Structure Tree! * - * @return the parent of this StructuredClass in the class inheritance - * hierarchy (!). + * @return the superclass of this StructuredClass. */ - Rooted getIsA() const { return isa; } + Rooted getSuperclass() const { return superclass; } /** * Returns the StructuredClasses that are subclasses of this class. This -- cgit v1.2.3 From 9929838e62d9c17647d74be54af5853e8b613c4b Mon Sep 17 00:00:00 2001 From: Benjamin Paassen Date: Fri, 16 Jan 2015 15:20:49 +0100 Subject: validate function for Domain::Descriptor. --- src/core/RangeSet.hpp | 51 ++++++++++++++++++-- src/core/model/Document.cpp | 107 +++++++++++++++++++++++++++++++++++++++++ src/core/model/Document.hpp | 2 + src/core/model/Domain.cpp | 11 +++++ src/core/model/Domain.hpp | 15 ++++++ test/core/model/DomainTest.cpp | 68 +++++++++++++++++++++++++- 6 files changed, 249 insertions(+), 5 deletions(-) (limited to 'src') 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, RangeComp>::iterator firstOverlapping( - const Range &r, const bool allowNeighbours) + const Range &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 &r) + bool contains(const Range &r) const { auto it = firstOverlapping(r, false); if (it != ranges.end()) { @@ -274,13 +274,30 @@ public: return false; } + /** + * 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 &s) + bool contains(const RangeSet &s) const { bool res = true; for (Range &r : s.ranges) { @@ -289,6 +306,29 @@ public: return res; } + /** + * 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 &r : ranges) { + if (r.end > max) { + max = r.end; + } + } + return std::move(max); + } + /** * Empties the set. */ @@ -297,7 +337,10 @@ public: /** * Returns the current list of ranges as a const reference. */ - const std::set, RangeComp> &getRanges() { return this->ranges; } + const std::set, RangeComp> &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 +#include + #include #include @@ -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 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 nums; + + // iterate over every actual child of this DocumentEntity + for (auto &rc : fields[f]) { + if (!rc->isa(RttiTypes::StructuredEntity)) { + continue; + } + Handle c = rc.cast(); + + 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() + ->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 parent, NodeVector 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 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 &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 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 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 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 sys{new SystemTypesystem(mgr)}; + Rooted domain{new Domain(mgr, sys, "inheritance")}; + Cardinality any; + any.merge(Range::typeRangeFrom(0)); + Rooted A{new StructuredClass( + mgr, "A", domain, any, {nullptr}, {nullptr}, false, true)}; + // first branch + Rooted B{ + new StructuredClass(mgr, "B", domain, any, {nullptr}, A)}; + Rooted C{ + new StructuredClass(mgr, "C", domain, any, {nullptr}, B)}; + // second branch + Rooted D{ + new StructuredClass(mgr, "D", domain, any, {nullptr}, A)}; + Rooted E{ + new StructuredClass(mgr, "E", domain, any, {nullptr}, D)}; + Rooted 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)); +} } } -- cgit v1.2.3