diff options
-rw-r--r-- | src/core/model/Node.cpp | 98 | ||||
-rw-r--r-- | test/core/model/NodeTest.cpp | 173 |
2 files changed, 215 insertions, 56 deletions
diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index d12e92b..930fbff 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -102,57 +102,52 @@ public: * resolving Node instances by name. */ class ResolutionState { -private: - /** - * Constructor of the ResolutionState class. - * - * @param shared is the shared, path independent state. - * @param resolutionRoot is the current resolution root node. - */ - ResolutionState(SharedResolutionState &shared, int idx, - Node *resolutionRoot) - : shared(shared), idx(idx), resolutionRoot(resolutionRoot) - { - } - public: /** - * Constructor of the ResolutionState class. - * - * @param shared is the shared, path independent state. - * @param resolutionRoot is the current resolution root node. - */ - ResolutionState(SharedResolutionState &shared, - Node *resolutionRoot = nullptr, bool atStartNode = true) - : shared(shared), - idx(0), - resolutionRoot(resolutionRoot), - atStartNode(atStartNode) - { - } - - /** * Reference at the resolution state that is shared between the various * resolution paths. */ SharedResolutionState &shared; /** + * Current resolution root node or nullptr if no resolution root node has + * been set yet. + */ + Node *resolutionRoot; + + /** * Current index within the given path. */ int idx; /** - * Current resolution root node or nullptr if no resolution root node has - * been set yet. + * Set to true if the resolution currently is in the subtree in which the + * node resolution process was started (no reference boundary has been + * passed yet). */ - Node *resolutionRoot; + bool inStartTree; /** - * Set to true if the resolution currently is at the node at which the - * resolution process was started. + * Set to true, once a compositum has been found. */ - bool atStartNode; + bool foundCompositum; + + /** + * Constructor of the ResolutionState class. + * + * @param shared is the shared, path independent state. + * @param resolutionRoot is the current resolution root node. + */ + ResolutionState(SharedResolutionState &shared, + Node *resolutionRoot = nullptr, int idx = 0, + bool inStartTree = true) + : shared(shared), + resolutionRoot(resolutionRoot), + idx(idx), + inStartTree(inStartTree), + foundCompositum(false) + { + } /** * Adds a node to the result. @@ -193,21 +188,31 @@ public: */ bool typeMatches(const RttiBase &type) { return type.isa(shared.type); } + bool canContainType(const RttiBase &type) + { + return type.composedOf(shared.type); + } + const std::string ¤tName() { return shared.path[idx]; } ResolutionState advance() { - return ResolutionState{shared, idx + 1, resolutionRoot}; + return ResolutionState{shared, resolutionRoot, idx + 1, false}; } ResolutionState fork(Node *newResolutionRoot) { - return ResolutionState{shared, newResolutionRoot, false}; + return ResolutionState{shared, newResolutionRoot, 0, false}; } - bool canFollowReferences() { return idx == 0 && atStartNode; } + bool canFollowReferences() + { + return idx == 0 && inStartTree && !foundCompositum; + } bool canFollowComposita() { return idx == 0; } + + size_t resultCount() { return shared.result.size(); } }; /* Class Node */ @@ -249,7 +254,8 @@ bool Node::resolve(ResolutionState &state) { // Try to mark this note as visited, do nothing if already has been visited if (state.markVisited(this)) { - std::cout << "visiting " << name << std::endl; + std::cout << "visiting " << name << " (" << state.idx << ")" + << std::endl; // Add this node to the result if it matches the current description if (state.atEndOfPath()) { @@ -259,7 +265,9 @@ bool Node::resolve(ResolutionState &state) return true; } } else { + size_t resCount = state.resultCount(); continueResolve(state); + return state.resultCount() > resCount; } } return false; @@ -275,7 +283,10 @@ bool Node::continueResolveIndex(const Index &index, ResolutionState &state) Rooted<Node> h = index.resolve(state.currentName()); if (h != nullptr) { ResolutionState advancedState = state.advance(); - return h->resolve(advancedState); + if (h->resolve(advancedState)) { + state.foundCompositum = true; + return true; + } } return false; } @@ -287,6 +298,7 @@ bool Node::continueResolveCompositum(Handle<Node> h, ResolutionState &state) if (h->getName() == state.currentName()) { ResolutionState advancedState = state.advance(); if (h->resolve(advancedState)) { + state.foundCompositum = true; return true; } } @@ -302,8 +314,10 @@ bool Node::continueResolveCompositum(Handle<Node> h, ResolutionState &state) bool Node::continueResolveReference(Handle<Node> h, ResolutionState &state) { // We can only follow references if we currently are at the beginning of the - // path and this node is the root node - if (canFollowReferences(state)) { + // path and this node is the root node. Additionally only follow a reference + // if the node the reference points to is known to contain the type that is + // currently asked for in the resolution process + if (canFollowReferences(state) && state.canContainType(h->type())) { std::cout << "following reference to " << h->getName() << std::endl; ResolutionState forkedState = state.fork(this); return continueResolveCompositum(h, forkedState); @@ -321,7 +335,7 @@ std::vector<ResolutionResult> Node::resolve( // Kickstart the resolution process by treating this very node as compositum std::cout << "------------" << std::endl; std::cout << "resolving: "; - for (auto s: path) { + for (auto s : path) { std::cout << s << " "; } std::cout << " of type " << type.name << std::endl; diff --git a/test/core/model/NodeTest.cpp b/test/core/model/NodeTest.cpp index 334a311..880ce33 100644 --- a/test/core/model/NodeTest.cpp +++ b/test/core/model/NodeTest.cpp @@ -24,23 +24,35 @@ namespace ousia { class TestNode : public Node { -private: - std::vector<Owned<Node>> children; - protected: void continueResolve(ResolutionState &state) override { - continueResolveComposita(children, state); + continueResolveComposita(composita, composita.getIndex(), state); + continueResolveReferences(references, state); } public: - using Node::Node; + NodeVector<TestNode> composita; + NodeVector<TestNode> references; - Rooted<TestNode> addChild(Handle<TestNode> node) + TestNode(Manager &mgr, Handle<Node> parent = nullptr) + : Node(mgr, parent), composita(this), references(this) { - Owned<TestNode> nd = acquire(node); - children.push_back(nd); - return nd; + } + + TestNode(Manager &mgr, std::string name, Handle<Node> parent = nullptr) + : Node(mgr, name, parent), composita(this), references(this) + { + } + + Rooted<TestNode> addCompositum(Handle<TestNode> n) { + composita.push_back(n); + return n; + } + + Rooted<TestNode> addReference(Handle<TestNode> n) { + references.push_back(n); + return n; } }; @@ -51,7 +63,7 @@ const Rtti<ousia::TestNode> TestNode = TEST(Node, isRoot) { - Manager mgr; + Manager mgr{1}; Rooted<TestNode> n1{new TestNode(mgr)}; Rooted<TestNode> n2{new TestNode(mgr)}; Rooted<TestNode> n3{new TestNode(mgr, n2)}; @@ -65,12 +77,37 @@ TEST(Node, isRoot) ASSERT_FALSE(n3->isRoot()); } -TEST(Node, simpleResolve) +TEST(Node, resolveCompositaSimple) +{ + Manager mgr{1}; + Rooted<TestNode> root{new TestNode(mgr, "root")}; + Rooted<TestNode> child1 = root->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child11 = child1->addCompositum(new TestNode(mgr, "child11")); + + std::vector<ResolutionResult> res; + res = root->resolve(std::vector<std::string>{"root", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector<std::string>{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = + root->resolve(std::vector<std::string>{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); +} + +TEST(Node, resolveCompositaDouble) { - Manager mgr; + Manager mgr{1}; Rooted<TestNode> root{new TestNode(mgr, "root")}; - Rooted<TestNode> child1 = root->addChild(new TestNode(mgr, "child1")); - Rooted<TestNode> child11 = child1->addChild(new TestNode(mgr, "child11")); + Rooted<TestNode> root2 = root->addCompositum(new TestNode(mgr, "root")); + Rooted<TestNode> child1 = root2->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child11 = child1->addCompositum(new TestNode(mgr, "child11")); std::vector<ResolutionResult> res; res = root->resolve(std::vector<std::string>{"root", "child1", "child11"}, @@ -89,4 +126,112 @@ TEST(Node, simpleResolve) ASSERT_TRUE(child11 == res[0].node); } +TEST(Node, resolveAmbigousComposita) +{ + Manager mgr{1}; + Rooted<TestNode> root{new TestNode(mgr, "root")}; + Rooted<TestNode> a = root->addCompositum(new TestNode(mgr, "a")); + Rooted<TestNode> b = root->addCompositum(new TestNode(mgr, "b")); + Rooted<TestNode> child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted<TestNode> child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child112 = child12->addCompositum(new TestNode(mgr, "child11")); + + std::vector<ResolutionResult> res; + res = root->resolve(std::vector<std::string>{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector<std::string>{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); +} + +TEST(Node, resolveReferences) +{ + Manager mgr{1}; + Rooted<TestNode> root{new TestNode(mgr, "root")}; + Rooted<TestNode> a = root->addReference(new TestNode(mgr, "a")); + Rooted<TestNode> b = root->addReference(new TestNode(mgr, "b")); + Rooted<TestNode> child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted<TestNode> child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child112 = child12->addCompositum(new TestNode(mgr, "child11")); + + std::vector<ResolutionResult> res; + res = root->resolve(std::vector<std::string>{"a", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector<std::string>{"b", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child112 == res[0].node); + + res = root->resolve(std::vector<std::string>{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector<std::string>{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector<std::string>{"child1"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child1 == res[0].node || child1 == res[1].node); + ASSERT_TRUE(child12 == res[0].node || child12 == res[1].node); +} + +TEST(Node, resolveReferencesAndComposita) +{ + Manager mgr{1}; + Rooted<TestNode> root{new TestNode(mgr, "root")}; + Rooted<TestNode> a = root->addReference(new TestNode(mgr, "a")); + Rooted<TestNode> b = root->addReference(new TestNode(mgr, "b")); + Rooted<TestNode> child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted<TestNode> child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted<TestNode> child112 = child12->addCompositum(new TestNode(mgr, "child11")); + Rooted<TestNode> child13 = root->addCompositum(new TestNode(mgr, "child1")); + + std::vector<ResolutionResult> res; + res = root->resolve(std::vector<std::string>{"a", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector<std::string>{"b", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child112 == res[0].node); + + res = root->resolve(std::vector<std::string>{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector<std::string>{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + // Resolving for "child1" should not descend into the referenced nodes + res = + root->resolve(std::vector<std::string>{"child1"}, RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child13 == res[0].node); +} + } |