diff options
author | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-02-12 19:31:50 +0100 |
---|---|---|
committer | Benjamin Paassen <bpaassen@techfak.uni-bielefeld.de> | 2015-02-12 19:31:50 +0100 |
commit | 89f01a0a49f4fd23034d532b37d54d3f3f612082 (patch) | |
tree | 3d52792489d49b20a25c0332e3b204ab6654e365 | |
parent | 110fb7da850377e39b2879da44339dc936c266dc (diff) |
added a method to retrieve all reachable default fields from a given descriptor.
-rw-r--r-- | src/core/model/Domain.cpp | 99 | ||||
-rw-r--r-- | src/core/model/Domain.hpp | 12 | ||||
-rw-r--r-- | test/core/model/DomainTest.cpp | 80 |
3 files changed, 191 insertions, 0 deletions
diff --git a/src/core/model/Domain.cpp b/src/core/model/Domain.cpp index 619454c..6f33ebd 100644 --- a/src/core/model/Domain.cpp +++ b/src/core/model/Domain.cpp @@ -369,7 +369,106 @@ std::pair<NodeVector<Node>, bool> Descriptor::pathTo( NodeVector<Node> path = ousia::pathTo(this, logger, field, success); return std::make_pair(path, success); } + +template <typename F> +static NodeVector<Node> collect(const Descriptor *start, F match) { + // result + NodeVector<Node> res; + // queue for breadth-first search of graph. + std::queue<Rooted<Node>> q; + { + // initially put every field descriptor on the queue. + NodeVector<FieldDescriptor> fields = start->getFieldDescriptors(); + + for (auto fd : fields) { + // note matches. + if (match(fd)) { + res.push_back(fd); + } + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + q.push(fd); + } + } + } + // set of visited nodes. + std::unordered_set<const Node *> visited; + while (!q.empty()) { + Rooted<Node> n = q.front(); + q.pop(); + // do not proceed if this node was already visited. + if (!visited.insert(n.get()).second) { + continue; + } + + if (n->isa(&RttiTypes::StructuredClass)) { + Rooted<StructuredClass> strct = n.cast<StructuredClass>(); + + // look through all fields. + NodeVector<FieldDescriptor> fields = strct->getFieldDescriptors(); + for (auto fd : fields) { + // note matches. + if (match(fd)) { + res.push_back(fd); + } + // only continue in the TREE field. + if (fd->getFieldType() == FieldDescriptor::FieldType::TREE) { + q.push(fd); + } + } + + /* + * Furthermore we have to consider that all subclasses of this + * StructuredClass are allowed in place of this StructuredClass as + * well, so we continue the search for them as well. + */ + + NodeVector<StructuredClass> subs = strct->getSubclasses(); + for (auto sub : subs) { + // note matches. + if (match(sub)) { + res.push_back(sub); + } + // We only continue our search via transparent classes. + if (sub->isTransparent()) { + q.push(sub); + } + } + } else { + // otherwise this is a FieldDescriptor. + Rooted<FieldDescriptor> field = n.cast<FieldDescriptor>(); + // and we proceed by visiting all permitted children. + for (auto c : field->getChildren()) { + // note matches. + if (match(c)) { + res.push_back(c); + } + // We only continue our search via transparent children. + if (c->isTransparent()) { + q.push(c); + } + } + } + } + return res; +} + +NodeVector<FieldDescriptor> Descriptor::getDefaultFields() const +{ + // TODO: In principle a cast would be nicer here, but for now we copy. + NodeVector<Node> nodes = collect(this, [](Handle<Node> n) { + if (!n->isa(&RttiTypes::FieldDescriptor)) { + return false; + } + Handle<FieldDescriptor> f = n.cast<FieldDescriptor>(); + return f->getFieldType() == FieldDescriptor::FieldType::TREE && + f->isPrimitive(); + }); + NodeVector<FieldDescriptor> res; + for (auto n : nodes) { + res.push_back(n.cast<FieldDescriptor>()); + } + return res; } static ssize_t getFieldDescriptorIndex(const NodeVector<FieldDescriptor> &fds, diff --git a/src/core/model/Domain.hpp b/src/core/model/Domain.hpp index 91d635e..c277812 100644 --- a/src/core/model/Domain.hpp +++ b/src/core/model/Domain.hpp @@ -612,6 +612,18 @@ public: */ std::pair<NodeVector<Node>, bool> pathTo(Handle<FieldDescriptor> field, Logger &logger) const; + + /** + * Returns a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. This also makes use of + * transparency. + * The list is sorted by the number of transparent elements that have to be + * constructed to arrive at the respective FieldDescriptor. + * + * @return a vector of all TREE fields that are allowed as structure tree + * children of an instance of this Descriptor. + */ + NodeVector<FieldDescriptor> getDefaultFields() const; }; /* * TODO: We should discuss Cardinalities one more time. Is it smart to define diff --git a/test/core/model/DomainTest.cpp b/test/core/model/DomainTest.cpp index 672b2d1..83f290f 100644 --- a/test/core/model/DomainTest.cpp +++ b/test/core/model/DomainTest.cpp @@ -221,6 +221,86 @@ TEST(Descriptor, pathToAdvanced) ASSERT_EQ("", path[2]->getName()); } +TEST(Descriptor, getDefaultFields) +{ + // construct a domain with lots of default fields to test. + // start with a single structure class. + Manager mgr{1}; + TerminalLogger logger{std::cout}; + Rooted<SystemTypesystem> sys{new SystemTypesystem(mgr)}; + // Construct the domain + Rooted<Domain> domain{new Domain(mgr, sys, "nasty")}; + + Rooted<StructuredClass> A{new StructuredClass( + mgr, "A", domain, Cardinality::any(), nullptr, false, true)}; + + // in this trivial case no field should be found. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // create a field. + Rooted<FieldDescriptor> A_prim_field = + A->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + // now we should find that. + auto fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // remove that field from A and add it to another class. + + Rooted<StructuredClass> B{new StructuredClass( + mgr, "B", domain, Cardinality::any(), nullptr, false, true)}; + + B->moveFieldDescriptor(A_prim_field, logger); + + // new we shouldn't find the field anymore. + ASSERT_TRUE(A->getDefaultFields().empty()); + + // but we should find it again if we set B as superclass of A. + A->setSuperclass(B, logger); + fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(A_prim_field, fields[0]); + + // and we should not be able to find it if we override the field. + Rooted<FieldDescriptor> A_field = A->createFieldDescriptor(logger); + ASSERT_TRUE(A->getDefaultFields().empty()); + + // add a transparent child class. + + Rooted<StructuredClass> C{new StructuredClass( + mgr, "C", domain, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(C); + + // add a primitive field for it. + Rooted<FieldDescriptor> C_field = + C->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + + // now we should find that. + fields = A->getDefaultFields(); + ASSERT_EQ(1, fields.size()); + ASSERT_EQ(C_field, fields[0]); + + // add another transparent child class to A with a daughter class that has + // in turn a subclass with a primitive field. + Rooted<StructuredClass> D{new StructuredClass( + mgr, "D", domain, Cardinality::any(), nullptr, true, false)}; + A_field->addChild(D); + Rooted<FieldDescriptor> D_field = D->createFieldDescriptor(logger); + Rooted<StructuredClass> E{new StructuredClass( + mgr, "E", domain, Cardinality::any(), nullptr, true, false)}; + D_field->addChild(E); + Rooted<StructuredClass> F{new StructuredClass( + mgr, "E", domain, Cardinality::any(), E, true, false)}; + Rooted<FieldDescriptor> F_field = + F->createPrimitiveFieldDescriptor(sys->getStringType(), logger); + + // now we should find both primitive fields, but the C field first. + fields = A->getDefaultFields(); + ASSERT_EQ(2, fields.size()); + ASSERT_EQ(C_field, fields[0]); + ASSERT_EQ(F_field, fields[1]); +} + TEST(StructuredClass, isSubclassOf) { // create an inheritance hierarchy. |