summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-02-12 19:31:50 +0100
committerBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2015-02-12 19:31:50 +0100
commit89f01a0a49f4fd23034d532b37d54d3f3f612082 (patch)
tree3d52792489d49b20a25c0332e3b204ab6654e365
parent110fb7da850377e39b2879da44339dc936c266dc (diff)
added a method to retrieve all reachable default fields from a given descriptor.
-rw-r--r--src/core/model/Domain.cpp99
-rw-r--r--src/core/model/Domain.hpp12
-rw-r--r--test/core/model/DomainTest.cpp80
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.