diff options
author | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-21 01:24:48 +0100 |
---|---|---|
committer | Andreas Stöckel <astoecke@techfak.uni-bielefeld.de> | 2015-01-21 01:24:48 +0100 |
commit | 0c3281b8d737093bc872df236c0156a49ed9028d (patch) | |
tree | 4f5cde7db438142e91dd2ab3f9d8cb80744e8e83 | |
parent | 3c659c2737b26d8ee28c727b277325852df8dd09 (diff) | |
parent | 4ff279b998ce55bc1132e699976c51e7246ff3cc (diff) |
Merge branch 'master' of somweyr.de:ousia
-rw-r--r-- | src/core/managed/ManagedContainer.hpp | 12 | ||||
-rw-r--r-- | src/core/model/Domain.cpp | 147 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 47 | ||||
-rw-r--r-- | src/core/model/Node.cpp | 3 | ||||
-rw-r--r-- | src/core/model/Typesystem.hpp | 16 | ||||
-rw-r--r-- | test/core/model/DomainTest.cpp | 83 |
6 files changed, 264 insertions, 44 deletions
diff --git a/src/core/managed/ManagedContainer.hpp b/src/core/managed/ManagedContainer.hpp index ba9d488..f62ba11 100644 --- a/src/core/managed/ManagedContainer.hpp +++ b/src/core/managed/ManagedContainer.hpp @@ -529,18 +529,6 @@ public: } return c.erase(first, last); } - - iterator find(Handle<Managed> elem) - { - iterator it = begin(); - while (it != end()) { - if (*it == elem) { - break; - } - it++; - } - return it; - } }; /** diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index 0fc078f..50bde9c 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -55,6 +55,72 @@ FieldDescriptor::FieldDescriptor(Manager &mgr, Handle<Descriptor> parent, } } +bool FieldDescriptor::doValidate(Logger &logger) const +{ + bool valid = true; + // check parent type + if(getParent() == nullptr){ + logger.error("This field has no parent!"); + valid = false; + } else if (!getParent()->isa(RttiTypes::Descriptor)) { + logger.error("The parent of this field is not a descriptor!"); + valid = false; + } + // check name + if (!getName().empty()) { + valid = valid & validateName(logger); + } + // check consistency of FieldType with the rest of the FieldDescriptor. + if (fieldType == FieldType::PRIMITIVE) { + if (children.size() > 0) { + logger.error( + "This field is supposed to be primitive but has " + "registered child classes!"); + valid = false; + } + if (primitiveType == nullptr) { + logger.error( + "This field is supposed to be primitive but has " + "no primitive type!"); + valid = false; + } + } else { + if (primitiveType != nullptr) { + logger.error( + "This field is supposed to be non-primitive but has " + "a primitive type!"); + valid = false; + } + } + /* + * we are not allowed to call the validation functions of each child because + * this might lead to cycles. What we should do, however, is to check if + * there are no duplicates. + */ + std::set<std::string> names; + for (Handle<StructuredClass> c : children) { + if (!names.insert(c->getName()).second) { + logger.error(std::string("Field \"") + getName() + + "\" had multiple children with the name \"" + + c->getName() + "\""); + valid = false; + } + } + + return valid; +} + + +bool FieldDescriptor::removeChild(Handle<StructuredClass> c){ + auto it = children.find(c); + if(it != children.end()){ + invalidate(); + children.erase(it); + return true; + } + return false; +} + /* Class Descriptor */ void Descriptor::doResolve(ResolutionState &state) @@ -68,6 +134,40 @@ void Descriptor::doResolve(ResolutionState &state) state); } +bool Descriptor::doValidate(Logger &logger) const +{ + bool valid = true; + // check parent type + if(getParent() == nullptr){ + logger.error("This Descriptor has no parent!"); + valid = false; + } else if (!getParent()->isa(RttiTypes::Domain)) { + logger.error("The parent of this Descriptor is not a Domain!"); + valid = false; + } + // check name + if (getName().empty()) { + logger.error("The name of this Descriptor is empty!"); + valid = false; + } else{ + valid = valid & validateName(logger); + } + // check if all FieldDescriptors have this Descriptor as parent. + for (Handle<FieldDescriptor> fd : fieldDescriptors) { + if (fd->getParent() != this) { + logger.error(std::string("Descriptor \"") + getName() + + "\" has " + "field \"" + + fd->getName() + + "\" as child but the field does not " + "have the Descriptor as parent."); + valid = false; + } + } + // check the FieldDescriptors themselves. + return valid & continueValidationCheckDuplicates(fieldDescriptors, logger); +} + std::vector<Rooted<Node>> Descriptor::pathTo( Handle<StructuredClass> target) const { @@ -184,19 +284,47 @@ StructuredClass::StructuredClass(Manager &mgr, std::string name, } } +bool StructuredClass::doValidate(Logger &logger) const +{ + bool valid = true; + // check if all registered subclasses have this StructuredClass as parent. + for (Handle<StructuredClass> sub : subclasses) { + if (sub->getSuperclass() != this) { + logger.error(std::string("Struct \"") + sub->getName() + + "\" is registered as subclass of \"" + getName() + + "\" but does not have it as superclass!"); + valid = false; + } + } + // check the validity of this superclass. + if (superclass != nullptr) { + valid = valid & superclass->validate(logger); + } + // check the validity as a Descriptor. + /* + * Note that we do not check the validity of all subclasses. This is because + * it will lead to cycles as the subclasses would call validate on their + * superclass, which is this one. + */ + return valid & Descriptor::doValidate(logger); +} + void StructuredClass::setSuperclass(Handle<StructuredClass> sup) { if (superclass == sup) { return; } - invalidate(); - if (sup != nullptr) { - sup->addSubclass(this); - } + // remove this subclass from the old superclass. if (superclass != nullptr) { superclass->removeSubclass(this); } + // set the new superclass superclass = acquire(sup); + invalidate(); + // add this class as new subclass of the new superclass. + if (sup != nullptr) { + sup->addSubclass(this); + } } bool StructuredClass::isSubclassOf(Handle<StructuredClass> c) const @@ -287,6 +415,16 @@ void Domain::doResolve(ResolutionState &state) } } +bool Domain::doValidate(Logger &logger) const +{ + // check validity of name, of StructuredClasses, of AnnotationClasses and + // TypeSystems. + return validateName(logger) & + continueValidationCheckDuplicates(structuredClasses, logger) & + continueValidationCheckDuplicates(annotationClasses, logger) & + continueValidationCheckDuplicates(typesystems, logger); +} + void Domain::addStructuredClass(Handle<StructuredClass> s) { invalidate(); @@ -323,4 +461,3 @@ const Rtti Domain = RttiBuilder<model::Domain>("Domain") .composedOf({&StructuredClass, &AnnotationClass}); } } - diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index e4a6967..e485171 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -271,13 +271,9 @@ private: Owned<Type> primitiveType; bool optional; - /* - * TODO: doValidate with: - * # primitive and primitiveType set and no children XOR other FieldType and - * no primitive type set - * # namecheck - * # parent typecheck - */ +protected: + bool doValidate(Logger &logger) const override; + public: /** * This is the constructor for primitive fields. The type is automatically @@ -348,6 +344,17 @@ public: } /** + * Removes the given StructuredClass from the list of children of this + * FieldDescriptor. + * + * @param c some StructuredClass that shan't be a child of this + * FieldDescriptor anymore. + * @return true if the FieldDescriptor contained this child and false if it + * did not. + */ + bool removeChild(Handle<StructuredClass> c); + + /** * Returns the type of this field (not to be confused with the primitive * type of this field). * @@ -451,14 +458,8 @@ private: protected: void doResolve(ResolutionState &state) override; - /* - * TODO: doValidate with: - * # namecheck - * # FieldDescriptor name uniqueness - * # do all FieldDescriptors have this Descriptor as parent? - * # is the parent a domain? - * # is the attributes descriptor either not set or a StructType? - */ + + bool doValidate(Logger &logger) const override; public: Descriptor(Manager &mgr, std::string name, Handle<Domain> domain, @@ -584,12 +585,12 @@ public: */ typedef RangeSet<size_t> Cardinality; - /** * This is the default cardinality. */ -static Cardinality createAny(){ +static Cardinality createAny() +{ Cardinality any; any.merge(Range<size_t>::typeRangeFrom(0)); return std::move(any); @@ -685,14 +686,8 @@ private: NodeVector<FieldDescriptor> ¤t, std::set<std::string> &overriddenFields) const; - - - /* - * TODO: doValidate with - * # does the subclasses have this class as superclass? - * # are the subclasses and the superclass valid? - * # is this a valid descriptor? - */ +protected: + bool doValidate(Logger &logger) const override; public: /** @@ -883,6 +878,8 @@ protected: * # are all annotationclasses valid and have a unique name? * # are all typesystems valid? */ + bool doValidate(Logger &logger) const override; + public: /** * The constructor for a new domain. Note that this is an empty Domain and diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index 71d59ce..eb0e4a7 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -422,6 +422,7 @@ bool Node::validate(Logger &logger) const validationState = ValidationState::INVALID; throw; } + validationState = ValidationState::INVALID; return false; case ValidationState::VALID: return true; @@ -440,8 +441,8 @@ bool Node::validate(Logger &logger) const void Node::setParent(Handle<Node> p) { - invalidate(); parent = acquire(p); + invalidate(); } /* RTTI type registrations */ diff --git a/src/core/model/Typesystem.hpp b/src/core/model/Typesystem.hpp index 44cf126..f4182fc 100644 --- a/src/core/model/Typesystem.hpp +++ b/src/core/model/Typesystem.hpp @@ -111,12 +111,26 @@ public: * @param data is a variant containing the data that should be checked and * -- if possible and necessary -- converted to a variant adhering to the * internal representation used by the Type class. - * @param logger is the Logger instance into which errors should be written. + * @param logger is the Logger instance into which errors should be + * written. * @return true if the conversion was successful, false otherwise. */ bool build(Variant &data, Logger &logger) const; /** + * Returns true if and only if the given Variant adheres to this Type. In + * essence this just calls the build method on a copy of the input Variant. + * + * @param data is a Variant containing data that shall be validated. + * @param logger is a logger instance to which errors will be written. + * + * @return true if and only if the given Variant adheres to this Type. + */ + bool isValid(Variant data, Logger &logger) const{ + return build(data, logger); + } + + /** * Returns the underlying Typesystem instance. * * @return a Rooted reference pointing at the underlying typesystem diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp index 62a0543..8ece2c9 100644 --- a/test/core/model/DomainTest.cpp +++ b/test/core/model/DomainTest.cpp @@ -284,5 +284,88 @@ TEST(StructuredClass, isSubclassOf) ASSERT_FALSE(F->isSubclassOf(E)); ASSERT_FALSE(F->isSubclassOf(F)); } + +TEST(Domain, validate) +{ + TerminalLogger logger{std::cerr, true}; + Manager mgr{1}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // start with an easy example: Our book domain should be valid. + { + Rooted<Domain> domain = constructBookDomain(mgr, sys, logger); + ASSERT_TRUE(domain->validate(logger)); + } + { + // Even easier: An empty domain should be valid. + Rooted<Domain> domain{new Domain(mgr, sys, "domain")}; + ASSERT_TRUE(domain->validate(logger)); + // if we add a StructureClass it should be valid still. + Rooted<StructuredClass> base{ + new StructuredClass(mgr, "myClass", domain)}; + ASSERT_TRUE(domain->validate(logger)); + // if we tamper with the parent reference, however, it shouldn't be + // valid anymore. + base->setParent(nullptr); + ASSERT_FALSE(base->validate(logger)); + base->setParent(domain); + ASSERT_TRUE(domain->validate(logger)); + // the same goes for the name. + base->setName(""); + ASSERT_FALSE(domain->validate(logger)); + base->setName("my class"); + ASSERT_FALSE(domain->validate(logger)); + base->setName("myClass"); + ASSERT_TRUE(domain->validate(logger)); + // Let's add a primitive field (without a primitive type at first) + Rooted<FieldDescriptor> base_field{ + new FieldDescriptor(mgr, base, nullptr)}; + // this should not be valid. + ASSERT_FALSE(domain->validate(logger)); + // but it should be if we set the type. + base_field->setPrimitiveType(sys->getStringType()); + ASSERT_TRUE(domain->validate(logger)); + // not anymore, however, if we tamper with the FieldType. + base_field->setFieldType(FieldDescriptor::FieldType::TREE); + ASSERT_FALSE(domain->validate(logger)); + base_field->setFieldType(FieldDescriptor::FieldType::PRIMITIVE); + ASSERT_TRUE(domain->validate(logger)); + // add a subclass for our base class. + Rooted<StructuredClass> sub{ + new StructuredClass(mgr, "sub", domain)}; + // this should be valid in itself. + ASSERT_TRUE(domain->validate(logger)); + // and still if we add a superclass. + sub->setSuperclass(base); + ASSERT_TRUE(domain->validate(logger)); + // and still we we remove the subclass from the base class. + base->removeSubclass(sub); + ASSERT_TRUE(domain->validate(logger)); + ASSERT_EQ(nullptr, sub->getSuperclass()); + // and still if we re-add it. + base->addSubclass(sub); + ASSERT_TRUE(domain->validate(logger)); + ASSERT_EQ(base, sub->getSuperclass()); + // add a non-primitive field to the child class. + Rooted<FieldDescriptor> sub_field{ + new FieldDescriptor(mgr, sub)}; + // this should be valid + ASSERT_TRUE(domain->validate(logger)); + // .. until we set a primitive type. + sub_field->setPrimitiveType(sys->getStringType()); + ASSERT_FALSE(domain->validate(logger)); + // and valid again if we unset it. + sub_field->setPrimitiveType(nullptr); + ASSERT_TRUE(domain->validate(logger)); + // we should also be able to add a child and have it still be valid. + sub_field->addChild(base); + ASSERT_TRUE(domain->validate(logger)); + // it should be invalid if we add it twice. + sub_field->addChild(base); + ASSERT_FALSE(domain->validate(logger)); + // and valid again if we remove it once. + sub_field->removeChild(base); + ASSERT_TRUE(domain->validate(logger)); + } +} } } |