summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/managed/ManagedContainer.hpp12
-rw-r--r--src/core/model/Domain.cpp147
-rw-r--r--src/core/model/Domain.hpp47
-rw-r--r--src/core/model/Node.cpp3
-rw-r--r--src/core/model/Typesystem.hpp16
-rw-r--r--test/core/model/DomainTest.cpp83
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> &current,
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));
+ }
+}
}
}