From c84972b96ea1281a072ad6b6c3c06bf9d740f842 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Sat, 30 Aug 2014 18:24:00 +0200 Subject: [PATCH] logic: added query syntax and filtering system for search This change allows the user to use a simple query syntax in the search field to filter the results using names, operators and predefined filters. The class QueryTree is capable of parsing a simple query language consiting of QueryNodes, subclassed as QueryOperators, QueryCommands and QueryTokens. QueryToken identifies a Token by name. QueryCommand represents predefined filters. QueryOperator defines the syntactic relationship. QueryNode examples: "A" -> QueryToken that identifies the Token named A class -> QueryCommand that filters all Nodes that are classes . -> QueryOperator that concatenates filters QueryExamples: "A".class -> All Tokens named A that are classes method.(private|protected) -> All methods that are private or protected "A".member -> All members of A "A":field.!const -> All fields of A that are not const (':' can be used instead of '.member') QueryTree checks for correct operator precedence and validity of the query. All QueryCommands can be found in data/query/QueryCommand.cpp All QueryOperators can be found in data/query/QueryOperator.cpp The parsed QueryTree is then passed to the class GraphFilterConductor, which is capable of applying the query on a Graph. Each part of the query gets assigned a GraphFilter that filters the input Graph in order of node precedence. The class SubGraph is used as intermediate container, only holding bare pointers to Nodes and Edges. The output Graph holds all Nodes that match the query. MessageActivateTokens is used to show all of the results in the CodeView. The GraphView currently only shows the first Node of the results. bug id = #6 --- bin/app/data/src/header.h | 19 +- src/lib/CMakeLists.txt | 21 ++ .../component/controller/CodeController.cpp | 23 +- src/lib/component/controller/CodeController.h | 5 +- .../component/controller/GraphController.cpp | 5 + .../component/controller/GraphController.h | 4 + .../component/controller/SearchController.cpp | 9 + src/lib/data/Storage.cpp | 16 + src/lib/data/Storage.h | 2 + src/lib/data/access/GraphAccess.h | 2 + src/lib/data/access/GraphAccessProxy.cpp | 10 + src/lib/data/access/GraphAccessProxy.h | 2 + src/lib/data/graph/FilterableGraph.cpp | 32 ++ src/lib/data/graph/FilterableGraph.h | 35 +++ src/lib/data/graph/Graph.cpp | 129 ++++---- src/lib/data/graph/Graph.h | 34 ++- src/lib/data/graph/SubGraph.cpp | 106 +++++++ src/lib/data/graph/SubGraph.h | 49 +++ src/lib/data/graph/filter/GraphFilter.cpp | 66 +++++ src/lib/data/graph/filter/GraphFilter.h | 27 ++ .../graph/filter/GraphFilterConductor.cpp | 178 +++++++++++ .../data/graph/filter/GraphFilterConductor.h | 26 ++ .../graph/filter/GraphFilterImplementations.h | 280 ++++++++++++++++++ .../TokenComponentDataType.cpp | 5 + .../token_component/TokenComponentDataType.h | 2 + src/lib/data/query/QueryCommand.cpp | 101 +++++++ src/lib/data/query/QueryCommand.h | 67 +++++ src/lib/data/query/QueryNode.cpp | 42 +++ src/lib/data/query/QueryNode.h | 28 ++ src/lib/data/query/QueryOperator.cpp | 135 +++++++++ src/lib/data/query/QueryOperator.h | 61 ++++ src/lib/data/query/QueryToken.cpp | 40 +++ src/lib/data/query/QueryToken.h | 28 ++ src/lib/data/query/QueryTree.cpp | 207 +++++++++++++ src/lib/data/query/QueryTree.h | 38 +++ src/lib/data/type/DataType.cpp | 2 +- .../messaging/type/MessageActivateToken.h | 2 +- .../messaging/type/MessageActivateTokens.h | 23 ++ src/lib/utility/utilityString.h | 66 +++++ src/test/CMakeLists.txt | 5 +- src/test/UtilityStringTestSuite.h | 65 ++++ 41 files changed, 1917 insertions(+), 80 deletions(-) create mode 100644 src/lib/data/graph/FilterableGraph.cpp create mode 100644 src/lib/data/graph/FilterableGraph.h create mode 100644 src/lib/data/graph/SubGraph.cpp create mode 100644 src/lib/data/graph/SubGraph.h create mode 100644 src/lib/data/graph/filter/GraphFilter.cpp create mode 100644 src/lib/data/graph/filter/GraphFilter.h create mode 100644 src/lib/data/graph/filter/GraphFilterConductor.cpp create mode 100644 src/lib/data/graph/filter/GraphFilterConductor.h create mode 100644 src/lib/data/graph/filter/GraphFilterImplementations.h create mode 100644 src/lib/data/query/QueryCommand.cpp create mode 100644 src/lib/data/query/QueryCommand.h create mode 100644 src/lib/data/query/QueryNode.cpp create mode 100644 src/lib/data/query/QueryNode.h create mode 100644 src/lib/data/query/QueryOperator.cpp create mode 100644 src/lib/data/query/QueryOperator.h create mode 100644 src/lib/data/query/QueryToken.cpp create mode 100644 src/lib/data/query/QueryToken.h create mode 100644 src/lib/data/query/QueryTree.cpp create mode 100644 src/lib/data/query/QueryTree.h create mode 100644 src/lib/utility/messaging/type/MessageActivateTokens.h diff --git a/bin/app/data/src/header.h b/bin/app/data/src/header.h index 2f52d575..c3833fed 100644 --- a/bin/app/data/src/header.h +++ b/bin/app/data/src/header.h @@ -40,7 +40,7 @@ public: private: int m_importantValue; char m_importanterValue; - float m_importantestValue; + const float m_importantestValue; }; A globalA(' '); @@ -52,6 +52,13 @@ public: : A('b') { } + + virtual ~B() + { + } + +protected: + virtual float getNumber() = 0; }; class C @@ -61,13 +68,20 @@ public: : m_valuable(0) { globalA.doImportantStuff(); + s_count++; } ~C() { + s_count--; } - void solveAllProblems() + static int getCount() + { + return s_count; + } + + void solveAllProblems() const { A aInstance('a'); aInstance.doImportantStuff(); @@ -75,6 +89,7 @@ public: } private: + static int s_count; int m_valuable; }; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index bceb96cf..c16782cc 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -71,6 +71,12 @@ add_files( data/access/GraphAccessProxy.cpp data/access/GraphAccessProxy.h + data/graph/filter/GraphFilter.cpp + data/graph/filter/GraphFilter.h + data/graph/filter/GraphFilterConductor.cpp + data/graph/filter/GraphFilterConductor.h + data/graph/filter/GraphFilterImplementations.h + data/graph/token_component/TokenComponent.cpp data/graph/token_component/TokenComponent.h data/graph/token_component/TokenComponentAbstraction.cpp @@ -88,10 +94,14 @@ add_files( data/graph/Edge.cpp data/graph/Edge.h + data/graph/FilterableGraph.cpp + data/graph/FilterableGraph.h data/graph/Graph.cpp data/graph/Graph.h data/graph/Node.cpp data/graph/Node.h + data/graph/SubGraph.cpp + data/graph/SubGraph.h data/graph/Token.cpp data/graph/Token.h @@ -117,6 +127,17 @@ add_files( data/parser/ParseVariable.cpp data/parser/ParseVariable.h + data/query/QueryCommand.cpp + data/query/QueryCommand.h + data/query/QueryNode.cpp + data/query/QueryNode.h + data/query/QueryOperator.cpp + data/query/QueryOperator.h + data/query/QueryToken.cpp + data/query/QueryToken.h + data/query/QueryTree.cpp + data/query/QueryTree.h + data/type/modifier/DataTypeModifier.cpp data/type/modifier/DataTypeModifier.h data/type/modifier/DataTypeModifierArray.cpp diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index d7e901e6..6b9a14c8 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -18,14 +18,13 @@ CodeController::~CodeController() { } -void CodeController::setActiveTokenId(Id id) +void CodeController::setActiveTokenIds(const std::vector& ids) { const uint lineRadius = 2; getView()->clearCodeSnippets(); - std::vector activeTokenIds = m_graphAccess->getActiveTokenIdsForId(id); - std::vector locationIds = m_graphAccess->getLocationIdsForTokenIds(activeTokenIds); + std::vector locationIds = m_graphAccess->getLocationIdsForTokenIds(ids); TokenLocationCollection collection = m_locationAccess->getTokenLocationsForLocationIds(locationIds); collection.forEachTokenLocationFile( @@ -49,7 +48,7 @@ void CodeController::setActiveTokenId(Id id) params.startLineNumber = firstLineNumber; params.locationFile = m_locationAccess->getTokenLocationsForLinesInFile(filePath, firstLineNumber, lastLineNumber); - params.activeTokenIds = activeTokenIds; + params.activeTokenIds = ids; getView()->addCodeSnippet(params); } @@ -59,7 +58,21 @@ void CodeController::setActiveTokenId(Id id) void CodeController::handleMessage(MessageActivateToken* message) { - setActiveTokenId(message->tokenId); + std::vector activeTokenIds = m_graphAccess->getActiveTokenIdsForId(message->tokenId); + setActiveTokenIds(activeTokenIds); +} + +void CodeController::handleMessage(MessageActivateTokens* message) +{ + if (message->tokenIds.size() == 1) + { + std::vector activeTokenIds = m_graphAccess->getActiveTokenIdsForId(message->tokenIds[0]); + setActiveTokenIds(activeTokenIds); + } + else + { + setActiveTokenIds(message->tokenIds); + } } void CodeController::handleMessage(MessageRefresh* message) diff --git a/src/lib/component/controller/CodeController.h b/src/lib/component/controller/CodeController.h index 98fbef60..a5838a95 100644 --- a/src/lib/component/controller/CodeController.h +++ b/src/lib/component/controller/CodeController.h @@ -6,6 +6,7 @@ #include "component/controller/Controller.h" #include "utility/messaging/MessageListener.h" #include "utility/messaging/type/MessageActivateToken.h" +#include "utility/messaging/type/MessageActivateTokens.h" #include "utility/messaging/type/MessageRefresh.h" #include "utility/messaging/type/MessageShowFile.h" #include "utility/types.h" @@ -24,6 +25,7 @@ struct AnnotatedText class CodeController : public Controller , public MessageListener + , public MessageListener , public MessageListener , public MessageListener { @@ -31,10 +33,11 @@ public: CodeController(GraphAccess* graphAccess, LocationAccess* locationAccess); ~CodeController(); - void setActiveTokenId(Id id); + void setActiveTokenIds(const std::vector& ids); private: virtual void handleMessage(MessageActivateToken* message); + virtual void handleMessage(MessageActivateTokens* message); virtual void handleMessage(MessageRefresh* message); virtual void handleMessage(MessageShowFile* message); diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 062232f3..76a04873 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -22,6 +22,11 @@ void GraphController::handleMessage(MessageActivateToken* message) createDummyGraph(message->tokenId, &GraphLayouter::layoutSimpleRing); } +void GraphController::handleMessage(MessageActivateTokens* message) +{ + createDummyGraph(message->tokenIds[0], &GraphLayouter::layoutSimpleRing); +} + GraphView* GraphController::getView() { return Controller::getView(); diff --git a/src/lib/component/controller/GraphController.h b/src/lib/component/controller/GraphController.h index 97eed694..911879a1 100644 --- a/src/lib/component/controller/GraphController.h +++ b/src/lib/component/controller/GraphController.h @@ -8,6 +8,7 @@ #include "component/controller/GraphLayouter.h" #include "utility/messaging/MessageListener.h" #include "utility/messaging/type/MessageActivateToken.h" +#include "utility/messaging/type/MessageActivateTokens.h" struct DummyNode; struct DummyEdge; @@ -17,6 +18,7 @@ class GraphAccess; class GraphController : public Controller , public MessageListener + , public MessageListener { public: GraphController(GraphAccess* graphAccess); @@ -24,6 +26,8 @@ public: private: virtual void handleMessage(MessageActivateToken* message); + virtual void handleMessage(MessageActivateTokens* message); + GraphView* getView(); void createDummyGraph(const Id activeId, const LayoutFunction layoutFunction); diff --git a/src/lib/component/controller/SearchController.cpp b/src/lib/component/controller/SearchController.cpp index db271dca..dfe90018 100644 --- a/src/lib/component/controller/SearchController.cpp +++ b/src/lib/component/controller/SearchController.cpp @@ -2,6 +2,7 @@ #include "component/view/SearchView.h" #include "data/access/GraphAccess.h" +#include "utility/messaging/type/MessageActivateTokens.h" SearchController::SearchController(GraphAccess* graphAccess) : m_graphAccess(graphAccess) @@ -15,6 +16,14 @@ SearchController::~SearchController() void SearchController::search(const std::string& s) { LOG_INFO("searching string: \"" + s + "\""); + + std::vector ids = m_graphAccess->getTokenIdsForQuery(s); + if (ids.size()) + { + MessageActivateTokens(ids).dispatch(); + return; + } + Id nodeId = m_graphAccess->getIdForNodeWithName(s); if (nodeId > 0) { diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index c0bd601c..67699429 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -1,8 +1,10 @@ #include "data/Storage.h" +#include "data/graph/filter/GraphFilterConductor.h" #include "data/graph/token_component/TokenComponentConst.h" #include "data/graph/token_component/TokenComponentDataType.h" #include "data/graph/token_component/TokenComponentStatic.h" +#include "data/graph/SubGraph.h" #include "data/location/TokenLocation.h" #include "data/location/TokenLocationFile.h" #include "data/location/TokenLocationLine.h" @@ -10,6 +12,7 @@ #include "data/parser/ParseLocation.h" #include "data/parser/ParseTypeUsage.h" #include "data/parser/ParseVariable.h" +#include "data/query/QueryTree.h" #include "data/type/DataType.h" #include "utility/logging/logging.h" #include "utility/utilityString.h" @@ -525,6 +528,19 @@ std::vector Storage::getLocationIdsForTokenIds(const std::vector& tokenI return ret; } +std::vector Storage::getTokenIdsForQuery(std::string query) const +{ + QueryTree tree(query); + GraphFilterConductor conductor; + SubGraph outGraph; + + conductor.filter(&tree, &m_graph, &outGraph); + + LOG_INFO_STREAM(<< '\n' << tree << '\n' << outGraph); + + return outGraph.getTokenIds(); +} + TokenLocationCollection Storage::getTokenLocationsForLocationIds(const std::vector& locationIds) const { TokenLocationCollection ret; diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 59bd6ccc..ccfba144 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -84,6 +84,8 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId) const; virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const; + virtual std::vector getTokenIdsForQuery(std::string query) const; + // LocationAccess implementation virtual TokenLocationCollection getTokenLocationsForLocationIds(const std::vector& locationIds) const; virtual TokenLocationFile getTokenLocationsForFile(const std::string& filePath) const; diff --git a/src/lib/data/access/GraphAccess.h b/src/lib/data/access/GraphAccess.h index 04f27c01..cea69920 100644 --- a/src/lib/data/access/GraphAccess.h +++ b/src/lib/data/access/GraphAccess.h @@ -33,6 +33,8 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId) const = 0; virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const = 0; + + virtual std::vector getTokenIdsForQuery(std::string query) const = 0; }; #endif // GRAPH_ACCESS_H diff --git a/src/lib/data/access/GraphAccessProxy.cpp b/src/lib/data/access/GraphAccessProxy.cpp index e005c634..102ba168 100644 --- a/src/lib/data/access/GraphAccessProxy.cpp +++ b/src/lib/data/access/GraphAccessProxy.cpp @@ -186,3 +186,13 @@ std::vector GraphAccessProxy::getLocationIdsForTokenIds(const std::vector(); } + +std::vector GraphAccessProxy::getTokenIdsForQuery(std::string query) const +{ + if (hasSubject()) + { + return m_subject->getTokenIdsForQuery(query); + } + + return std::vector(); +} diff --git a/src/lib/data/access/GraphAccessProxy.h b/src/lib/data/access/GraphAccessProxy.h index 48a32d53..fe5e20e5 100644 --- a/src/lib/data/access/GraphAccessProxy.h +++ b/src/lib/data/access/GraphAccessProxy.h @@ -33,6 +33,8 @@ public: virtual std::vector getActiveTokenIdsForId(Id tokenId) const; virtual std::vector getLocationIdsForTokenIds(const std::vector& tokenIds) const; + virtual std::vector getTokenIdsForQuery(std::string query) const; + private: GraphAccess* m_subject; }; diff --git a/src/lib/data/graph/FilterableGraph.cpp b/src/lib/data/graph/FilterableGraph.cpp new file mode 100644 index 00000000..326416f7 --- /dev/null +++ b/src/lib/data/graph/FilterableGraph.cpp @@ -0,0 +1,32 @@ +#include "data/graph/FilterableGraph.h" + +#include "data/graph/Edge.h" +#include "data/graph/Node.h" + +FilterableGraph::FilterableGraph() +{ +} + +FilterableGraph::~FilterableGraph() +{ +} + +void FilterableGraph::print(std::ostream& ostream) const +{ + ostream << "Graph:\n"; + ostream << "nodes (" << getNodeCount() << ")\n"; + forEachNode( + [&ostream](Node* n) + { + ostream << *n << '\n'; + } + ); + + ostream << "edges (" << getEdgeCount() << ")\n"; + forEachEdge( + [&ostream](Edge* e) + { + ostream << *e << '\n'; + } + ); +} diff --git a/src/lib/data/graph/FilterableGraph.h b/src/lib/data/graph/FilterableGraph.h new file mode 100644 index 00000000..473a0984 --- /dev/null +++ b/src/lib/data/graph/FilterableGraph.h @@ -0,0 +1,35 @@ +#ifndef FILTERABLE_GRAPH_H +#define FILTERABLE_GRAPH_H + +#include +#include + +class Edge; +class Node; +class Token; + +class FilterableGraph +{ +public: + FilterableGraph(); + virtual ~FilterableGraph(); + + virtual void copy(const FilterableGraph* other) = 0; + virtual void clear() = 0; + + virtual void add(const FilterableGraph* other) = 0; + + virtual void forEachNode(std::function func) const = 0; + virtual void forEachEdge(std::function func) const = 0; + virtual void forEachToken(std::function func) const = 0; + + virtual void addNode(Node* node) = 0; + virtual void addEdge(Edge* edge) = 0; + + virtual size_t getNodeCount() const = 0; + virtual size_t getEdgeCount() const = 0; + + void print(std::ostream& ostream) const; +}; + +#endif // FILTERABLE_GRAPH_H diff --git a/src/lib/data/graph/Graph.cpp b/src/lib/data/graph/Graph.cpp index 70127b21..4aa9bc1f 100644 --- a/src/lib/data/graph/Graph.cpp +++ b/src/lib/data/graph/Graph.cpp @@ -14,6 +14,79 @@ Graph::~Graph() m_nodes.clear(); } +void Graph::copy(const FilterableGraph* other) +{ + clear(); + add(other); +} + +void Graph::clear() +{ + m_edges.clear(); + m_nodes.clear(); +} + +void Graph::add(const FilterableGraph* other) +{ + other->forEachNode(std::bind(&Graph::addNode, this, std::placeholders::_1)); + other->forEachEdge(std::bind(&Graph::addEdge, this, std::placeholders::_1)); +} + +void Graph::forEachNode(std::function func) const +{ + for (const std::pair>& node : m_nodes) + { + func(node.second.get()); + } +} + +void Graph::forEachEdge(std::function func) const +{ + for (const std::pair>& edge : m_edges) + { + func(edge.second.get()); + } +} + +void Graph::forEachToken(std::function func) const +{ + forEachNode(func); + forEachEdge(func); +} + +void Graph::addNode(Node* node) +{ + addNodeAsPlainCopy(node); +} + +void Graph::addEdge(Edge* edge) +{ + if (getNodeById(edge->getFrom()->getId()) && getNodeById(edge->getTo()->getId())) + { + addEdgeAsPlainCopy(edge); + } +} + +size_t Graph::getNodeCount() const +{ + return m_nodes.size(); +} + +size_t Graph::getEdgeCount() const +{ + return m_edges.size(); +} + +const std::map>& Graph::getNodes() const +{ + return m_nodes; +} + +const std::map>& Graph::getEdges() const +{ + return m_edges; +} + Node* Graph::getNode(const std::string& fullName) const { std::deque names = utility::split>(fullName, DELIMITER); @@ -256,44 +329,6 @@ Token* Graph::findToken(std::function func) const return nullptr; } -void Graph::forEachNode(std::function func) const -{ - for (const std::pair>& node : m_nodes) - { - func(node.second.get()); - } -} - -void Graph::forEachEdge(std::function func) const -{ - for (const std::pair>& edge : m_edges) - { - func(edge.second.get()); - } -} - -void Graph::forEachToken(std::function func) const -{ - forEachNode(func); - forEachEdge(func); -} - -void Graph::clear() -{ - m_edges.clear(); - m_nodes.clear(); -} - -const std::map >& Graph::getNodes() const -{ - return m_nodes; -} - -const std::map >& Graph::getEdges() const -{ - return m_edges; -} - Node* Graph::addNodeAsPlainCopy(Node* node) { Node* n = getNodeById(node->getId()); @@ -410,22 +445,6 @@ void Graph::removeEdgeInternal(Edge* edge) std::ostream& operator<<(std::ostream& ostream, const Graph& graph) { - ostream << "Graph:\n"; - ostream << "nodes (" << graph.getNodes().size() << ")\n"; - graph.forEachNode( - [&ostream](Node* n) - { - ostream << *n << '\n'; - } - ); - - ostream << "edges (" << graph.getEdges().size() << ")\n"; - graph.forEachEdge( - [&ostream](Edge* e) - { - ostream << *e << '\n'; - } - ); - + graph.print(ostream); return ostream; } diff --git a/src/lib/data/graph/Graph.h b/src/lib/data/graph/Graph.h index 2a687e91..79176e8c 100644 --- a/src/lib/data/graph/Graph.h +++ b/src/lib/data/graph/Graph.h @@ -3,18 +3,38 @@ #include #include -#include #include #include "data/graph/Edge.h" +#include "data/graph/FilterableGraph.h" #include "data/graph/Node.h" class Graph + : public FilterableGraph { public: Graph(); virtual ~Graph(); + // FilterableGraph implementation + virtual void copy(const FilterableGraph* other); + virtual void clear(); + + virtual void add(const FilterableGraph* other); + + virtual void forEachNode(std::function func) const; + virtual void forEachEdge(std::function func) const; + virtual void forEachToken(std::function func) const; + + virtual void addNode(Node* node); + virtual void addEdge(Edge* edge); + + virtual size_t getNodeCount() const; + virtual size_t getEdgeCount() const; + + const std::map>& getNodes() const; + const std::map>& getEdges() const; + Node* getNode(const std::string& fullName) const; Edge* getEdge(Edge::EdgeType type, Node* from, Node* to) const; @@ -39,19 +59,9 @@ public: Edge* findEdge(std::function func) const; Token* findToken(std::function func) const; - void forEachNode(std::function func) const; - void forEachEdge(std::function func) const; - void forEachToken(std::function func) const; - Node* addNodeAsPlainCopy(Node* node); Edge* addEdgeAsPlainCopy(Edge* edge); - void clear(); - -protected: - const std::map>& getNodes() const; - const std::map>& getEdges() const; - private: static const std::string DELIMITER; @@ -65,8 +75,6 @@ private: std::map> m_nodes; std::map> m_edges; - - friend std::ostream& operator<<(std::ostream& ostream, const Graph& graph); }; std::ostream& operator<<(std::ostream& ostream, const Graph& graph); diff --git a/src/lib/data/graph/SubGraph.cpp b/src/lib/data/graph/SubGraph.cpp new file mode 100644 index 00000000..c7ee0104 --- /dev/null +++ b/src/lib/data/graph/SubGraph.cpp @@ -0,0 +1,106 @@ +#include "data/graph/SubGraph.h" + +#include "data/graph/Edge.h" +#include "data/graph/Node.h" + +SubGraph::SubGraph() +{ +} + +SubGraph::~SubGraph() +{ +} + +void SubGraph::copy(const FilterableGraph* other) +{ + clear(); + add(other); +} + +void SubGraph::clear() +{ + m_edges.clear(); + m_nodes.clear(); +} + +void SubGraph::add(const FilterableGraph* other) +{ + other->forEachNode(std::bind(&SubGraph::addNode, this, std::placeholders::_1)); + other->forEachEdge(std::bind(&SubGraph::addEdge, this, std::placeholders::_1)); +} + +void SubGraph::forEachNode(std::function func) const +{ + for (const std::pair& node : m_nodes) + { + func(node.second); + } +} + +void SubGraph::forEachEdge(std::function func) const +{ + for (const std::pair& edge : m_edges) + { + func(edge.second); + } +} + +void SubGraph::forEachToken(std::function func) const +{ + forEachNode(func); + forEachEdge(func); +} + +void SubGraph::addNode(Node* node) +{ + m_nodes.emplace(node->getId(), node); +} + +void SubGraph::addEdge(Edge* edge) +{ + m_edges.emplace(edge->getId(), edge); +} + +size_t SubGraph::getNodeCount() const +{ + return m_nodes.size(); +} + +size_t SubGraph::getEdgeCount() const +{ + return m_edges.size(); +} + +std::vector SubGraph::getTokenIds() const +{ + std::vector ids; + for (const std::pair& node : m_nodes) + { + ids.push_back(node.first); + } + + for (const std::pair& edge : m_edges) + { + ids.push_back(edge.first); + } + return ids; +} + +void SubGraph::subtract(const SubGraph& other) +{ + for (const std::pair& node : other.m_nodes) + { + m_nodes.erase(node.first); + } + + for (const std::pair& edge : other.m_edges) + { + m_edges.erase(edge.first); + } +} + +std::ostream& operator<<(std::ostream& ostream, const SubGraph& graph) +{ + graph.print(ostream); + return ostream; +} diff --git a/src/lib/data/graph/SubGraph.h b/src/lib/data/graph/SubGraph.h new file mode 100644 index 00000000..8358f9c2 --- /dev/null +++ b/src/lib/data/graph/SubGraph.h @@ -0,0 +1,49 @@ +#ifndef SUB_GRAPH_H +#define SUB_GRAPH_H + +#include +#include +#include + +#include "data/graph/FilterableGraph.h" +#include "utility/types.h" + +class Edge; +class Node; +class Token; + +class SubGraph + : public FilterableGraph +{ +public: + SubGraph(); + virtual ~SubGraph(); + + // FilterableGraph implementation + virtual void copy(const FilterableGraph* other); + virtual void clear(); + + virtual void add(const FilterableGraph* other); + + virtual void forEachNode(std::function func) const; + virtual void forEachEdge(std::function func) const; + virtual void forEachToken(std::function func) const; + + virtual void addNode(Node* node); + virtual void addEdge(Edge* edge); + + virtual size_t getNodeCount() const; + virtual size_t getEdgeCount() const; + + std::vector getTokenIds() const; + + void subtract(const SubGraph& other); + +private: + std::map m_nodes; + std::map m_edges; +}; + +std::ostream& operator<<(std::ostream& ostream, const SubGraph& graph); + +#endif // SUB_GRAPH_H diff --git a/src/lib/data/graph/filter/GraphFilter.cpp b/src/lib/data/graph/filter/GraphFilter.cpp new file mode 100644 index 00000000..41187d42 --- /dev/null +++ b/src/lib/data/graph/filter/GraphFilter.cpp @@ -0,0 +1,66 @@ +#include "data/graph/filter/GraphFilter.h" + +#include "data/graph/Edge.h" +#include "data/graph/FilterableGraph.h" +#include "utility/logging/logging.h" + +GraphFilter::GraphFilter() + : m_outGraph(nullptr) +{ +} + +GraphFilter::~GraphFilter() +{ +} + +void GraphFilter::apply(const FilterableGraph* in, FilterableGraph* out) +{ + if (!in || !out) + { + LOG_ERROR("GraphFilter not called with correct pointers."); + return; + } + else if (in == out) + { + LOG_ERROR("In and out graphs are the same."); + return; + } + + m_outGraph = out; + + in->forEachNode( + [this](Node* node) + { + visitNode(node); + } + ); + + // in->forEachEdge( + // [this](Edge* edge) + // { + // visitEdge(edge); + // } + // ); + + m_outGraph = nullptr; +} + +void GraphFilter::visitNode(Node* node) +{ +} + +// void GraphFilter::visitEdge(Edge* edge) +// { +// } + +void GraphFilter::addNode(Node* node) +{ + m_outGraph->addNode(node); +} + +void GraphFilter::addEdge(Edge* edge) +{ + m_outGraph->addNode(edge->getFrom()); + m_outGraph->addNode(edge->getTo()); + m_outGraph->addEdge(edge); +} diff --git a/src/lib/data/graph/filter/GraphFilter.h b/src/lib/data/graph/filter/GraphFilter.h new file mode 100644 index 00000000..b0c31072 --- /dev/null +++ b/src/lib/data/graph/filter/GraphFilter.h @@ -0,0 +1,27 @@ +#ifndef GRAPH_FILTER_H +#define GRAPH_FILTER_H + +class Edge; +class FilterableGraph; +class Node; + +class GraphFilter +{ +public: + GraphFilter(); + virtual ~GraphFilter(); + + void apply(const FilterableGraph* in, FilterableGraph* out); + +protected: + virtual void visitNode(Node* node); + // virtual void visitEdge(Edge* edge); + + void addNode(Node* node); + void addEdge(Edge* edge); + +private: + FilterableGraph* m_outGraph; +}; + +#endif // GRAPH_FILTER_H diff --git a/src/lib/data/graph/filter/GraphFilterConductor.cpp b/src/lib/data/graph/filter/GraphFilterConductor.cpp new file mode 100644 index 00000000..e4fe7653 --- /dev/null +++ b/src/lib/data/graph/filter/GraphFilterConductor.cpp @@ -0,0 +1,178 @@ +#include "data/graph/filter/GraphFilterConductor.h" + +#include "data/graph/filter/GraphFilter.h" +#include "data/graph/filter/GraphFilterImplementations.h" +#include "data/graph/SubGraph.h" +#include "data/query/QueryCommand.h" +#include "data/query/QueryNode.h" +#include "data/query/QueryOperator.h" +#include "data/query/QueryToken.h" +#include "data/query/QueryTree.h" +#include "utility/logging/logging.h" + +GraphFilterConductor::GraphFilterConductor() +{ +} + +GraphFilterConductor::~GraphFilterConductor() +{ +} + +void GraphFilterConductor::filter(const QueryTree* tree, const FilterableGraph* in, FilterableGraph* out) const +{ + if (tree->isValid()) + { + filterRecursively(tree->getRoot().get(), in, out); + } +} + +void GraphFilterConductor::filterRecursively(const QueryNode* node, const FilterableGraph* in, FilterableGraph* out) const +{ + if (node->isOperator()) + { + filterOperatorNode(dynamic_cast(node), in, out); + } + else if (node->isCommand()) + { + filterCommandNode(dynamic_cast(node), in, out); + } + else if (node->isToken()) + { + filterTokenNode(dynamic_cast(node), in, out); + } +} + +void GraphFilterConductor::filterOperatorNode(const QueryOperator* node, const FilterableGraph* in, FilterableGraph* out) const +{ + switch (node->getType()) + { + case QueryOperator::OPERATOR_NOT: + { + SubGraph sub, sub2; + filterRecursively(node->getRight().get(), in, &sub); + + sub2.copy(in); + sub2.subtract(sub); + out->add(&sub2); + } + break; + + case QueryOperator::OPERATOR_SUB: + case QueryOperator::OPERATOR_AND: + { + SubGraph sub; + filterRecursively(node->getLeft().get(), in, &sub); + filterRecursively(node->getRight().get(), &sub, out); + } + break; + + case QueryOperator::OPERATOR_HAS: + { + SubGraph sub, sub2; + filterRecursively(node->getLeft().get(), in, &sub); + GraphFilterCommandMember().apply(&sub, &sub2); + filterRecursively(node->getRight().get(), &sub2, out); + } + break; + + case QueryOperator::OPERATOR_OR: + filterRecursively(node->getLeft().get(), in, out); + filterRecursively(node->getRight().get(), in, out); + break; + + default: + break; + } +} + +void GraphFilterConductor::filterCommandNode(const QueryCommand* node, const FilterableGraph* in, FilterableGraph* out) const +{ + switch (node->getType()) + { + case QueryCommand::COMMAND_MEMBER: + GraphFilterCommandMember().apply(in, out); + break; + case QueryCommand::COMMAND_PARENT: + GraphFilterCommandParent().apply(in, out); + break; + case QueryCommand::COMMAND_FUNCTION: + GraphFilterCommandNodeType(Node::NODE_FUNCTION).apply(in, out); + break; + case QueryCommand::COMMAND_GLOBAL_VARIABLE: + GraphFilterCommandNodeType(Node::NODE_GLOBAL_VARIABLE).apply(in, out); + break; + case QueryCommand::COMMAND_CLASS: + GraphFilterCommandNodeType(Node::NODE_CLASS).apply(in, out); + break; + case QueryCommand::COMMAND_METHOD: + GraphFilterCommandNodeType(Node::NODE_METHOD).apply(in, out); + break; + case QueryCommand::COMMAND_FIELD: + GraphFilterCommandNodeType(Node::NODE_FIELD).apply(in, out); + break; + case QueryCommand::COMMAND_NAMESPACE: + GraphFilterCommandNodeType(Node::NODE_NAMESPACE).apply(in, out); + break; + case QueryCommand::COMMAND_STRUCT: + GraphFilterCommandNodeType(Node::NODE_STRUCT).apply(in, out); + break; + case QueryCommand::COMMAND_ENUM: + GraphFilterCommandNodeType(Node::NODE_ENUM).apply(in, out); + break; + case QueryCommand::COMMAND_TYPEDEF: + GraphFilterCommandNodeType(Node::NODE_TYPEDEF).apply(in, out); + break; + + case QueryCommand::COMMAND_CONST: + GraphFilterCommandConst().apply(in, out); + break; + case QueryCommand::COMMAND_STATIC: + GraphFilterCommandStatic().apply(in, out); + break; + + case QueryCommand::COMMAND_VIRTUAL: + GraphFilterCommandAbstractionType(TokenComponentAbstraction::ABSTRACTION_VIRTUAL).apply(in, out); + break; + case QueryCommand::COMMAND_PURE_VIRTUAL: + GraphFilterCommandAbstractionType(TokenComponentAbstraction::ABSTRACTION_PURE_VIRTUAL).apply(in, out); + break; + + case QueryCommand::COMMAND_PUBLIC: + GraphFilterCommandAccessType(TokenComponentAccess::ACCESS_PUBLIC).apply(in, out); + break; + case QueryCommand::COMMAND_PROTECTED: + GraphFilterCommandAccessType(TokenComponentAccess::ACCESS_PROTECTED).apply(in, out); + break; + case QueryCommand::COMMAND_PRIVATE: + GraphFilterCommandAccessType(TokenComponentAccess::ACCESS_PRIVATE).apply(in, out); + break; + + case QueryCommand::COMMAND_CALLER: + GraphFilterCommandCall(true).apply(in, out); + break; + case QueryCommand::COMMAND_CALLEE: + GraphFilterCommandCall(false).apply(in, out); + break; + + case QueryCommand::COMMAND_USAGE: + GraphFilterCommandUsage().apply(in, out); + break; + + case QueryCommand::COMMAND_SUPER_CLASS: + GraphFilterCommandInheritance(true).apply(in, out); + break; + case QueryCommand::COMMAND_SUB_CLASS: + GraphFilterCommandInheritance(false).apply(in, out); + break; + + default: + LOG_ERROR_STREAM(<< "QueryCommand not supported: " << node->getType()); + GraphFilter().apply(in, out); + break; + } +} + +void GraphFilterConductor::filterTokenNode(const QueryToken* node, const FilterableGraph* in, FilterableGraph* out) const +{ + GraphFilterToken(node->getName()).apply(in, out); +} diff --git a/src/lib/data/graph/filter/GraphFilterConductor.h b/src/lib/data/graph/filter/GraphFilterConductor.h new file mode 100644 index 00000000..ee84285e --- /dev/null +++ b/src/lib/data/graph/filter/GraphFilterConductor.h @@ -0,0 +1,26 @@ +#ifndef GRAPH_FILTER_CONDUCTOR_H +#define GRAPH_FILTER_CONDUCTOR_H + +class FilterableGraph; +class QueryCommand; +class QueryNode; +class QueryOperator; +class QueryToken; +class QueryTree; + +class GraphFilterConductor +{ +public: + GraphFilterConductor(); + ~GraphFilterConductor(); + + void filter(const QueryTree* tree, const FilterableGraph* in, FilterableGraph* out) const; + +private: + void filterRecursively(const QueryNode* node, const FilterableGraph* in, FilterableGraph* out) const; + void filterOperatorNode(const QueryOperator* node, const FilterableGraph* in, FilterableGraph* out) const; + void filterCommandNode(const QueryCommand* node, const FilterableGraph* in, FilterableGraph* out) const; + void filterTokenNode(const QueryToken* node, const FilterableGraph* in, FilterableGraph* out) const; +}; + +#endif // GRAPH_FILTER_CONDUCTOR_H diff --git a/src/lib/data/graph/filter/GraphFilterImplementations.h b/src/lib/data/graph/filter/GraphFilterImplementations.h new file mode 100644 index 00000000..8011fed3 --- /dev/null +++ b/src/lib/data/graph/filter/GraphFilterImplementations.h @@ -0,0 +1,280 @@ +#ifndef GRAPH_FILTER_IMPLEMENTATIONS_H +#define GRAPH_FILTER_IMPLEMENTATIONS_H + +#include "data/graph/Edge.h" +#include "data/graph/filter/GraphFilter.h" +#include "data/graph/Node.h" +#include "data/graph/token_component/TokenComponentAbstraction.h" +#include "data/graph/token_component/TokenComponentAccess.h" +#include "data/graph/token_component/TokenComponentConst.h" +#include "data/graph/token_component/TokenComponentDataType.h" +#include "data/graph/token_component/TokenComponentStatic.h" + +/* + * empty GraphFilterImplementation for copy-pasting + * + +class GraphFilter: public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + } +}; +*/ + +class GraphFilterCommandMember + : public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + node->forEachChildNode( + [this](Node* n) + { + addNode(n); + } + ); + } +}; + +class GraphFilterCommandParent + : public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + Node* parent = node->getParentNode(); + if (parent) + { + addNode(parent); + } + } +}; + +class GraphFilterCommandNodeType + : public GraphFilter +{ +public: + GraphFilterCommandNodeType(Node::NodeType type) + : m_type(type) + { + } + +protected: + virtual void visitNode(Node* node) + { + if (node->getType() == m_type) + { + addNode(node); + } + } + +private: + const Node::NodeType m_type; +}; + +class GraphFilterCommandConst + : public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + if (node->getType() == Node::NODE_METHOD && node->getComponent()) + { + addNode(node); + } + + Edge* edge = node->findEdgeOfType(Edge::EDGE_TYPE_OF); + if (!edge) + { + return; + } + + TokenComponentDataType* type = edge->getComponent(); + if (type && type->isConstQualified()) + { + addNode(node); + } + } +}; + +class GraphFilterCommandStatic + : public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + if (node->getComponent()) + { + addNode(node); + } + } +}; + +class GraphFilterCommandAccessType + : public GraphFilter +{ +public: + GraphFilterCommandAccessType(TokenComponentAccess::AccessType type) + : m_type(type) + { + } + +protected: + virtual void visitNode(Node* node) + { + Edge* edge = node->getMemberEdge(); + if (!edge) + { + return; + } + + TokenComponentAccess* access = edge->getComponent(); + if (access && access->getAccess() == m_type) + { + addNode(edge->getTo()); + } + } + +private: + const TokenComponentAccess::AccessType m_type; +}; + +class GraphFilterCommandAbstractionType + : public GraphFilter +{ +public: + GraphFilterCommandAbstractionType(TokenComponentAbstraction::AbstractionType type) + : m_type(type) + { + } + +protected: + virtual void visitNode(Node* node) + { + TokenComponentAbstraction* abstraction = node->getComponent(); + if (abstraction && abstraction->getAbstraction() == m_type) + { + addNode(node); + } + } + +private: + const TokenComponentAbstraction::AbstractionType m_type; +}; + +class GraphFilterCommandCall + : public GraphFilter +{ +public: + GraphFilterCommandCall(bool callers) + : m_callers(callers) + { + } + +protected: + virtual void visitNode(Node* node) + { + node->forEachEdgeOfType(Edge::EDGE_CALL, + [this, node](Edge* edge) + { + Node* from = edge->getFrom(); + Node* to = edge->getTo(); + + if (m_callers && node == to) + { + addNode(from); + } + else if (!m_callers && node == from) + { + addNode(to); + } + } + ); + } + +private: + const bool m_callers; +}; + +class GraphFilterCommandUsage + : public GraphFilter +{ +protected: + virtual void visitNode(Node* node) + { + Edge::EdgeTypeMask mask; + mask = Edge::EDGE_TYPE_OF | Edge::EDGE_RETURN_TYPE_OF | Edge::EDGE_PARAMETER_TYPE_OF; + mask = Edge::EDGE_TYPE_USAGE | Edge::EDGE_USAGE | Edge::EDGE_TYPEDEF_OF | mask; + + node->forEachEdge( + [this, node, mask](Edge* edge) + { + if (node == edge->getTo() && edge->isType(mask)) + { + addNode(edge->getFrom()); + } + } + ); + } +}; + +class GraphFilterCommandInheritance + : public GraphFilter +{ +public: + GraphFilterCommandInheritance(bool super) + : m_super(super) + { + } + +protected: + virtual void visitNode(Node* node) + { + node->forEachEdgeOfType(Edge::EDGE_INHERITANCE, + [this, node](Edge* edge) + { + Node* from = edge->getFrom(); + Node* to = edge->getTo(); + + if (m_super && node == from) + { + addNode(to); + } + else if (!m_super && node == to) + { + addNode(from); + } + } + ); + } + +private: + const bool m_super; +}; + + +class GraphFilterToken + : public GraphFilter +{ +public: + GraphFilterToken(const std::string& name) + : m_name(name) + { + } + +protected: + virtual void visitNode(Node* node) + { + if (node->getName() == m_name) + { + addNode(node); + } + } + +private: + const std::string& m_name; +}; + +#endif // GRAPH_FILTER_IMPLEMENTATIONS_H diff --git a/src/lib/data/graph/token_component/TokenComponentDataType.cpp b/src/lib/data/graph/token_component/TokenComponentDataType.cpp index 7800dc99..addfc457 100644 --- a/src/lib/data/graph/token_component/TokenComponentDataType.cpp +++ b/src/lib/data/graph/token_component/TokenComponentDataType.cpp @@ -28,3 +28,8 @@ std::string TokenComponentDataType::getQualifiedTypeName(const std::string& type { return m_modifierStack.applyTo(m_qualifierList.applyTo(typeName)); } + +bool TokenComponentDataType::isConstQualified() const +{ + return m_qualifierList.hasQualifier(DataTypeQualifierList::QUALIFIER_CONST); +} diff --git a/src/lib/data/graph/token_component/TokenComponentDataType.h b/src/lib/data/graph/token_component/TokenComponentDataType.h index 46c46e36..fc142714 100644 --- a/src/lib/data/graph/token_component/TokenComponentDataType.h +++ b/src/lib/data/graph/token_component/TokenComponentDataType.h @@ -19,6 +19,8 @@ public: DataType getDataType(const std::string& typeName) const; std::string getQualifiedTypeName(const std::string& typeName) const; + bool isConstQualified() const; + private: const DataTypeQualifierList m_qualifierList; const DataTypeModifierStack m_modifierStack; diff --git a/src/lib/data/query/QueryCommand.cpp b/src/lib/data/query/QueryCommand.cpp new file mode 100644 index 00000000..b7a530ad --- /dev/null +++ b/src/lib/data/query/QueryCommand.cpp @@ -0,0 +1,101 @@ +#include "data/query/QueryCommand.h" + +QueryCommand::QueryCommand(const std::string& name) + : m_type(COMMAND_INVALID) + , m_name(name) +{ + std::map commandMap = getCommandTypeMap(); + std::map::iterator it = commandMap.find(name); + + if (it != commandMap.end()) + { + m_type = it->second; + } +} + +QueryCommand::~QueryCommand() +{ +} + +bool QueryCommand::isCommand() const +{ + return true; +} + +bool QueryCommand::isOperator() const +{ + return false; +} + +bool QueryCommand::isToken() const +{ + return false; +} + +bool QueryCommand::isComplete() const +{ + return m_type != COMMAND_INVALID; +} + +void QueryCommand::print(std::ostream& ostream) const +{ + ostream << m_name; + + if (!isComplete()) + { + ostream << " INVALID"; + } +} + +QueryCommand::CommandType QueryCommand::getType() const +{ + return m_type; +} + +std::map QueryCommand::getCommandTypeMap() +{ + static std::map commandMap; + + if (commandMap.size()) + { + return commandMap; + } + + commandMap.emplace("member", COMMAND_MEMBER); + commandMap.emplace("child", COMMAND_MEMBER); + + commandMap.emplace("parent", COMMAND_PARENT); + + commandMap.emplace("function", COMMAND_FUNCTION); + commandMap.emplace("variable", COMMAND_GLOBAL_VARIABLE); + commandMap.emplace("global", COMMAND_GLOBAL_VARIABLE); + commandMap.emplace("class", COMMAND_CLASS); + commandMap.emplace("method", COMMAND_METHOD); + commandMap.emplace("field", COMMAND_FIELD); + commandMap.emplace("namespace", COMMAND_NAMESPACE); + commandMap.emplace("struct", COMMAND_STRUCT); + commandMap.emplace("enum", COMMAND_ENUM); + commandMap.emplace("typedef", COMMAND_TYPEDEF); + + commandMap.emplace("const", COMMAND_CONST); + commandMap.emplace("static", COMMAND_STATIC); + commandMap.emplace("virtual", COMMAND_VIRTUAL); + commandMap.emplace("abstract", COMMAND_PURE_VIRTUAL); + commandMap.emplace("pure-virtual", COMMAND_PURE_VIRTUAL); + + commandMap.emplace("public", COMMAND_PUBLIC); + commandMap.emplace("protected", COMMAND_PROTECTED); + commandMap.emplace("private", COMMAND_PRIVATE); + + commandMap.emplace("caller", COMMAND_CALLER); + commandMap.emplace("callee", COMMAND_CALLEE); + + commandMap.emplace("usage", COMMAND_USAGE); + + commandMap.emplace("base", COMMAND_SUPER_CLASS); + commandMap.emplace("super", COMMAND_SUPER_CLASS); + commandMap.emplace("derived", COMMAND_SUB_CLASS); + commandMap.emplace("subclass", COMMAND_SUB_CLASS); + + return commandMap; +} diff --git a/src/lib/data/query/QueryCommand.h b/src/lib/data/query/QueryCommand.h new file mode 100644 index 00000000..6e069d6a --- /dev/null +++ b/src/lib/data/query/QueryCommand.h @@ -0,0 +1,67 @@ +#ifndef QUERY_COMMAND_H +#define QUERY_COMMAND_H + +#include +#include + +#include "data/query/QueryNode.h" + +class QueryCommand + : public QueryNode +{ +public: + enum CommandType + { + COMMAND_INVALID, + + COMMAND_MEMBER, + COMMAND_PARENT, + COMMAND_FUNCTION, + COMMAND_GLOBAL_VARIABLE, + COMMAND_CLASS, + COMMAND_METHOD, + COMMAND_FIELD, + COMMAND_NAMESPACE, + COMMAND_STRUCT, + COMMAND_ENUM, + COMMAND_TYPEDEF, + + COMMAND_CONST, + COMMAND_STATIC, + COMMAND_VIRTUAL, + COMMAND_PURE_VIRTUAL, + + COMMAND_PUBLIC, + COMMAND_PROTECTED, + COMMAND_PRIVATE, + + COMMAND_CALLER, + COMMAND_CALLEE, + + COMMAND_USAGE, + + COMMAND_SUPER_CLASS, + COMMAND_SUB_CLASS + }; + + QueryCommand(const std::string& name); + ~QueryCommand(); + + virtual bool isCommand() const; + virtual bool isOperator() const; + virtual bool isToken() const; + + virtual bool isComplete() const; + + virtual void print(std::ostream& ostream) const; + + CommandType getType() const; + +private: + static std::map getCommandTypeMap(); + + CommandType m_type; + const std::string m_name; +}; + +#endif // QUERY_COMMAND_H diff --git a/src/lib/data/query/QueryNode.cpp b/src/lib/data/query/QueryNode.cpp new file mode 100644 index 00000000..9546de01 --- /dev/null +++ b/src/lib/data/query/QueryNode.cpp @@ -0,0 +1,42 @@ +#include "data/query/QueryNode.h" + +QueryNode::QueryNode() + : m_isGroup(false) +{ +} + +QueryNode::~QueryNode() +{ +} + +void QueryNode::print(std::ostream& ostream, int n) const +{ + for (int i = 0; i < n; i++) + { + ostream << '\t'; + } + + if (isGroup()) + { + ostream << '('; + } + + print(ostream); + + if (isGroup()) + { + ostream << ')'; + } + + ostream << '\n'; +} + +bool QueryNode::isGroup() const +{ + return m_isGroup; +} + +void QueryNode::setIsGroup(bool isGroup) +{ + m_isGroup = isGroup; +} diff --git a/src/lib/data/query/QueryNode.h b/src/lib/data/query/QueryNode.h new file mode 100644 index 00000000..c0344039 --- /dev/null +++ b/src/lib/data/query/QueryNode.h @@ -0,0 +1,28 @@ +#ifndef QUERY_NODE_H +#define QUERY_NODE_H + +#include + +class QueryNode +{ +public: + QueryNode(); + virtual ~QueryNode(); + + virtual bool isCommand() const = 0; + virtual bool isOperator() const = 0; + virtual bool isToken() const = 0; + + virtual bool isComplete() const = 0; + + virtual void print(std::ostream& ostream) const = 0; + virtual void print(std::ostream& ostream, int n) const; + + bool isGroup() const; + void setIsGroup(bool isGroup); + +private: + bool m_isGroup; +}; + +#endif // QUERY_NODE_H diff --git a/src/lib/data/query/QueryOperator.cpp b/src/lib/data/query/QueryOperator.cpp new file mode 100644 index 00000000..67e74cae --- /dev/null +++ b/src/lib/data/query/QueryOperator.cpp @@ -0,0 +1,135 @@ +#include "data/query/QueryOperator.h" + +const std::map& QueryOperator::getOperatorTypeMap() +{ + static std::map operatorMap; + + if (operatorMap.size()) + { + return operatorMap; + } + + operatorMap.emplace(' ', OPERATOR_NONE); + + operatorMap.emplace('!', OPERATOR_NOT); + operatorMap.emplace('.', OPERATOR_SUB); + operatorMap.emplace(':', OPERATOR_HAS); + operatorMap.emplace('&', OPERATOR_AND); + operatorMap.emplace('|', OPERATOR_OR); + + operatorMap.emplace('"', OPERATOR_NAME); + operatorMap.emplace('(', OPERATOR_GROUP_OPEN); + operatorMap.emplace(')', OPERATOR_GROUP_CLOSE); + + return operatorMap; +} + +QueryOperator::OperatorType QueryOperator::getOperatorType(char c) +{ + const std::map& operatorMap = getOperatorTypeMap(); + std::map::const_iterator it = operatorMap.find(c); + + if (it != operatorMap.end()) + { + return it->second; + } + + return OPERATOR_NONE; +} + +char QueryOperator::getOperator(OperatorType t) +{ + for (const std::pair& p : getOperatorTypeMap()) + { + if (p.second == t) + { + return p.first; + } + } + + return '\0'; +} + +QueryOperator::QueryOperator(OperatorType type) + : m_type(type) +{ +} + +QueryOperator::~QueryOperator() +{ +} + +bool QueryOperator::isCommand() const +{ + return false; +} + +bool QueryOperator::isOperator() const +{ + return true; +} + +bool QueryOperator::isToken() const +{ + return false; +} + +bool QueryOperator::isComplete() const +{ + if (m_type == OPERATOR_NOT) + { + return bool(getRight()); + } + + return getLeft() && getRight(); +} + +void QueryOperator::print(std::ostream& ostream) const +{ + ostream << m_type; +} + +void QueryOperator::print(std::ostream& ostream, int n) const +{ + if (m_left) + { + m_left->print(ostream, n + 1); + } + + QueryNode::print(ostream, n); + + if (m_right) + { + m_right->print(ostream, n + 1); + } +} + +std::shared_ptr QueryOperator::getLeft() const +{ + return m_left; +} + +void QueryOperator::setLeft(std::shared_ptr node) +{ + m_left = node; +} + +std::shared_ptr QueryOperator::getRight() const +{ + return m_right; +} + +void QueryOperator::setRight(std::shared_ptr node) +{ + m_right = node; +} + +QueryOperator::OperatorType QueryOperator::getType() const +{ + return m_type; +} + +bool QueryOperator::lowerPrecedence(const QueryOperator& other) +{ + return m_type < other.m_type; +} diff --git a/src/lib/data/query/QueryOperator.h b/src/lib/data/query/QueryOperator.h new file mode 100644 index 00000000..506971d0 --- /dev/null +++ b/src/lib/data/query/QueryOperator.h @@ -0,0 +1,61 @@ +#ifndef QUERY_OPERATOR_H +#define QUERY_OPERATOR_H + +#include +#include + +#include "data/query/QueryNode.h" + +class QueryOperator + : public QueryNode +{ +public: + enum OperatorType + { + OPERATOR_NONE, + + OPERATOR_NOT, + OPERATOR_SUB, + OPERATOR_HAS, + OPERATOR_AND, + OPERATOR_OR, + + OPERATOR_NAME, + OPERATOR_GROUP_OPEN, + OPERATOR_GROUP_CLOSE + }; + + static const std::map& getOperatorTypeMap(); + static OperatorType getOperatorType(char c); + static char getOperator(OperatorType t); + + QueryOperator(OperatorType type); + ~QueryOperator(); + + virtual bool isCommand() const; + virtual bool isOperator() const; + virtual bool isToken() const; + + virtual bool isComplete() const; + + virtual void print(std::ostream& ostream) const; + virtual void print(std::ostream& ostream, int n) const; + + std::shared_ptr getLeft() const; + void setLeft(std::shared_ptr node); + + std::shared_ptr getRight() const; + void setRight(std::shared_ptr node); + + OperatorType getType() const; + + bool lowerPrecedence(const QueryOperator& other); + +private: + std::shared_ptr m_left; + std::shared_ptr m_right; + + const OperatorType m_type; +}; + +#endif // QUERY_OPERATOR_H diff --git a/src/lib/data/query/QueryToken.cpp b/src/lib/data/query/QueryToken.cpp new file mode 100644 index 00000000..95a7f35e --- /dev/null +++ b/src/lib/data/query/QueryToken.cpp @@ -0,0 +1,40 @@ +#include "data/query/QueryToken.h" + +QueryToken::QueryToken(const std::string& name) + : m_name(name) +{ +} + +QueryToken::~QueryToken() +{ +} + +bool QueryToken::isCommand() const +{ + return false; +} + +bool QueryToken::isOperator() const +{ + return false; +} + +bool QueryToken::isToken() const +{ + return true; +} + +bool QueryToken::isComplete() const +{ + return true; +} + +void QueryToken::print(std::ostream& ostream) const +{ + ostream << '"' << m_name << '"'; +} + +const std::string& QueryToken::getName() const +{ + return m_name; +} diff --git a/src/lib/data/query/QueryToken.h b/src/lib/data/query/QueryToken.h new file mode 100644 index 00000000..206b52e3 --- /dev/null +++ b/src/lib/data/query/QueryToken.h @@ -0,0 +1,28 @@ +#ifndef QUERY_TOKEN_H +#define QUERY_TOKEN_H + +#include + +#include "data/query/QueryNode.h" + +class QueryToken + : public QueryNode +{ +public: + QueryToken(const std::string& name); + ~QueryToken(); + + virtual bool isCommand() const; + virtual bool isOperator() const; + virtual bool isToken() const; + virtual bool isComplete() const; + + virtual void print(std::ostream& ostream) const; + + const std::string& getName() const; + +private: + const std::string m_name; +}; + +#endif // QUERY_TOKEN_H diff --git a/src/lib/data/query/QueryTree.cpp b/src/lib/data/query/QueryTree.cpp new file mode 100644 index 00000000..caafc29f --- /dev/null +++ b/src/lib/data/query/QueryTree.cpp @@ -0,0 +1,207 @@ +#include "data/query/QueryTree.h" + +#include "data/query/QueryCommand.h" +#include "data/query/QueryNode.h" +#include "data/query/QueryOperator.h" +#include "data/query/QueryToken.h" +#include "utility/utilityString.h" + +QueryTree::QueryTree(std::string query) + : m_valid(true) +{ + std::deque tokens = + utility::split>(query, QueryOperator::getOperator(QueryOperator::OPERATOR_NONE)); + + for (const std::pair& p : QueryOperator::getOperatorTypeMap()) + { + tokens = utility::tokenize>(tokens, p.first); + } + + for (std::string str : tokens) + { + m_query += str + ' '; + } + + m_root = buildTree(tokens, nullptr); +} + +QueryTree::~QueryTree() +{ +} + +std::shared_ptr QueryTree::getRoot() const +{ + return m_root; +} + +bool QueryTree::isValid() const +{ + return m_valid; +} + +void QueryTree::print(std::ostream& ostream) const +{ + ostream << m_query; + + if (!m_valid) + { + ostream << " INVALID"; + } + + ostream << '\n'; + + if (m_root) + { + m_root->print(ostream, 0); + } +} + +std::shared_ptr QueryTree::buildTree(std::deque& tokens, std::shared_ptr frontNode) +{ + std::shared_ptr node = getNextNode(tokens); + std::shared_ptr operatorNode = std::dynamic_pointer_cast(node); + + if (frontNode) + { + if (node->isComplete()) + { + std::shared_ptr subNode = std::make_shared(QueryOperator::OPERATOR_SUB); + subNode->setLeft(frontNode); + subNode->setRight(node); + node = subNode; + } + else if (node->isOperator() && operatorNode) + { + operatorNode->setLeft(frontNode); + } + else + { + m_valid = false; + } + } + + if (tokens.size()) + { + if (node->isComplete()) + { + node = buildTree(tokens, node); + } + else if (node->isOperator() && operatorNode) + { + std::shared_ptr rightNode = buildTree(tokens, nullptr); + std::shared_ptr rightOperatorNode = std::dynamic_pointer_cast(rightNode); + + if (rightNode->isOperator() && !rightNode->isGroup() && + operatorNode->lowerPrecedence(*rightOperatorNode.get())) + { + operatorNode->setRight(rightOperatorNode->getLeft()); + rightOperatorNode->setLeft(operatorNode); + node = rightNode; + } + else + { + operatorNode->setRight(rightNode); + } + } + else + { + m_valid = false; + } + } + + if (!node->isComplete()) + { + m_valid = false; + } + + return node; +} + +std::shared_ptr QueryTree::buildGroup(std::deque& tokens, QueryOperator::OperatorType closeType) +{ + std::deque group; + std::string name; + char delimiter = QueryOperator::getOperator(closeType); + while (tokens.front() != std::string(1, delimiter) && tokens.size()) + { + group.push_back(tokens.front()); + name += tokens.front(); + tokens.pop_front(); + } + + if (tokens.size()) + { + tokens.pop_front(); + } + else + { + m_valid = false; + } + + if (!group.size()) + { + return nullptr; + } + + if (closeType == QueryOperator::OPERATOR_NAME) + { + return std::make_shared(name); + } + + std::shared_ptr groupNode = buildTree(group, nullptr); + groupNode->setIsGroup(true); + + return groupNode; +} + +std::shared_ptr QueryTree::getNextNode(std::deque& tokens) +{ + while (tokens.size()) + { + std::string token = tokens.front(); + tokens.pop_front(); + + QueryOperator::OperatorType type = QueryOperator::getOperatorType(token[0]); + + switch (type) + { + case QueryOperator::OPERATOR_NOT: + case QueryOperator::OPERATOR_SUB: + case QueryOperator::OPERATOR_HAS: + case QueryOperator::OPERATOR_AND: + case QueryOperator::OPERATOR_OR: + return std::make_shared(type); + + case QueryOperator::OPERATOR_NAME: + return buildGroup(tokens, QueryOperator::OPERATOR_NAME); + case QueryOperator::OPERATOR_GROUP_OPEN: + return buildGroup(tokens, QueryOperator::OPERATOR_GROUP_CLOSE); + case QueryOperator::OPERATOR_GROUP_CLOSE: + m_valid = false; + break; + + case QueryOperator::OPERATOR_NONE: + return createCommand(token); + } + } + + return nullptr; +} + +std::shared_ptr QueryTree::createCommand(std::string name) +{ + std::shared_ptr node = std::make_shared(name); + + if (node->getType() == QueryCommand::COMMAND_INVALID) + { + m_valid = false; + } + + return node; +} + +std::ostream& operator<<(std::ostream& ostream, const QueryTree& tree) +{ + tree.print(ostream); + return ostream; +} diff --git a/src/lib/data/query/QueryTree.h b/src/lib/data/query/QueryTree.h new file mode 100644 index 00000000..b99dea9c --- /dev/null +++ b/src/lib/data/query/QueryTree.h @@ -0,0 +1,38 @@ +#ifndef QUERY_TREE_H +#define QUERY_TREE_H + +#include +#include +#include +#include + +#include "data/query/QueryOperator.h" + +class QueryNode; + +class QueryTree +{ +public: + QueryTree(std::string query); + ~QueryTree(); + + std::shared_ptr getRoot() const; + + bool isValid() const; + + void print(std::ostream& ostream) const; + +private: + std::shared_ptr buildTree(std::deque& tokens, std::shared_ptr frontNode); + std::shared_ptr buildGroup(std::deque& tokens, QueryOperator::OperatorType closeType); + std::shared_ptr getNextNode(std::deque& tokens); + std::shared_ptr createCommand(std::string name); + + std::shared_ptr m_root; + std::string m_query; + bool m_valid; +}; + +std::ostream& operator<<(std::ostream& ostream, const QueryTree& tree); + +#endif // QUERY_TREE_H diff --git a/src/lib/data/type/DataType.cpp b/src/lib/data/type/DataType.cpp index 66781309..c7109d90 100644 --- a/src/lib/data/type/DataType.cpp +++ b/src/lib/data/type/DataType.cpp @@ -12,7 +12,7 @@ DataType::DataType(const std::string& typeName, const DataTypeQualifierList& qua } DataType::DataType( - const std::string& typeName, const DataTypeQualifierList& qualifierList, const DataTypeModifierStack& modifierStack + const std::string& typeName, const DataTypeQualifierList& qualifierList, const DataTypeModifierStack& modifierStack ) : m_typeName(typeName) , m_qualifierList(qualifierList) diff --git a/src/lib/utility/messaging/type/MessageActivateToken.h b/src/lib/utility/messaging/type/MessageActivateToken.h index 4bf6d18e..5f74068e 100644 --- a/src/lib/utility/messaging/type/MessageActivateToken.h +++ b/src/lib/utility/messaging/type/MessageActivateToken.h @@ -20,4 +20,4 @@ public: const Id tokenId; }; -#endif // MESSAGE_ACTIVATE_TOKEN_LOCATION_H +#endif // MESSAGE_ACTIVATE_TOKEN_H diff --git a/src/lib/utility/messaging/type/MessageActivateTokens.h b/src/lib/utility/messaging/type/MessageActivateTokens.h new file mode 100644 index 00000000..147a7370 --- /dev/null +++ b/src/lib/utility/messaging/type/MessageActivateTokens.h @@ -0,0 +1,23 @@ +#ifndef MESSAGE_ACTIVATE_TOKENS_H +#define MESSAGE_ACTIVATE_TOKENS_H + +#include "utility/messaging/Message.h" +#include "utility/types.h" + +class MessageActivateTokens: public Message +{ +public: + MessageActivateTokens(const std::vector& tokenIds) + : tokenIds(tokenIds) + { + } + + static const std::string getStaticType() + { + return "MessageActivateTokens"; + } + + const std::vector tokenIds; +}; + +#endif // MESSAGE_ACTIVATE_TOKENS_H diff --git a/src/lib/utility/utilityString.h b/src/lib/utility/utilityString.h index 426251c7..ace4c972 100644 --- a/src/lib/utility/utilityString.h +++ b/src/lib/utility/utilityString.h @@ -12,6 +12,18 @@ namespace utility template ContainerType split(const std::string& str, const std::string& delimiter); + template + ContainerType tokenize(const std::string& str, char delimiter); + + template + ContainerType tokenize(const std::string& str, const std::string& delimiter); + + template + ContainerType tokenize(const ContainerType& list, char delimiter); + + template + ContainerType tokenize(const ContainerType& list, const std::string& delimiter); + std::string substrAfter(const std::string& str, char delimiter); bool isPrefix(const std::string& prefix, const std::string& text); @@ -41,4 +53,58 @@ ContainerType utility::split(const std::string& str, const std::string& delimite return c; } +template +ContainerType utility::tokenize(const std::string& str, char delimiter) +{ + return tokenize(str, std::string(1, delimiter)); +} + +template +ContainerType utility::tokenize(const std::string& str, const std::string& delimiter) +{ + size_t pos = 0; + size_t oldPos = 0; + ContainerType c; + + do + { + pos = str.find(delimiter, oldPos); + + if (pos != oldPos) + { + c.push_back(str.substr(oldPos, pos - oldPos)); + } + + if (pos != std::string::npos) + { + c.push_back(str.substr(pos, delimiter.size())); + } + + oldPos = pos + delimiter.size(); + } + while (pos != std::string::npos && oldPos < str.size()); + + return c; +} + +template +ContainerType utility::tokenize(const ContainerType& list, char delimiter) +{ + return tokenize(list, std::string(1, delimiter)); +} + +template +ContainerType utility::tokenize(const ContainerType& list, const std::string& delimiter) +{ + ContainerType c; + + for (std::string str : list) + { + ContainerType c2 = tokenize(str, delimiter); + c.insert(c.end(), c2.begin(), c2.end()); + } + + return c; +} + #endif // UTILITY_STRING_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index c751f0ab..96267776 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,6 +1,9 @@ add_files( TEST_FILES + TestSuiteFixture.cpp + TestSuiteFixture.h + ConfigManagerTestSuite.h CxxParserTestSuite.h DataTypeTestSuite.h @@ -10,8 +13,6 @@ add_files( MessageQueueTestSuite.h SettingsTestSuite.h StorageTestSuite.h - TestSuiteFixture.cpp - TestSuiteFixture.h TextAccessTestSuite.h TokenLocationCollectionTestSuite.h UtilityStringTestSuite.h diff --git a/src/test/UtilityStringTestSuite.h b/src/test/UtilityStringTestSuite.h index 94a048d5..e6123931 100644 --- a/src/test/UtilityStringTestSuite.h +++ b/src/test/UtilityStringTestSuite.h @@ -51,6 +51,71 @@ public: TS_ASSERT_EQUALS(result[3], "C"); } + void test_split_with_delimiter_at_start() + { + std::vector result = utility::split >(":B:C", ':'); + + TS_ASSERT_EQUALS(result.size(), 3); + TS_ASSERT_EQUALS(result[0], ""); + TS_ASSERT_EQUALS(result[1], "B"); + TS_ASSERT_EQUALS(result[2], "C"); + } + + void test_split_with_delimiter_at_end() + { + std::vector result = utility::split >("B:C:", ':'); + + TS_ASSERT_EQUALS(result.size(), 3); + TS_ASSERT_EQUALS(result[0], "B"); + TS_ASSERT_EQUALS(result[1], "C"); + TS_ASSERT_EQUALS(result[2], ""); + } + + void test_tokenize_with_string() + { + std::vector result = utility::tokenize >("A->B->C", "->"); + + TS_ASSERT_EQUALS(result.size(), 5); + TS_ASSERT_EQUALS(result[0], "A"); + TS_ASSERT_EQUALS(result[1], "->"); + TS_ASSERT_EQUALS(result[2], "B"); + TS_ASSERT_EQUALS(result[3], "->"); + TS_ASSERT_EQUALS(result[4], "C"); + } + + void test_tokenize_with_string_and_delimiter_at_start() + { + std::vector result = utility::tokenize >("->B", "->"); + + TS_ASSERT_EQUALS(result.size(), 2); + TS_ASSERT_EQUALS(result[0], "->"); + TS_ASSERT_EQUALS(result[1], "B"); + } + + void test_tokenize_with_string_and_delimiter_at_end() + { + std::vector result = utility::tokenize >("C+", '+'); + + TS_ASSERT_EQUALS(result.size(), 2); + TS_ASSERT_EQUALS(result[0], "C"); + TS_ASSERT_EQUALS(result[1], "+"); + } + + void test_tokenize_with_vector() + { + std::vector result = utility::tokenize >("A->B=C->D", "->"); + result = utility::tokenize>(result, "="); + + TS_ASSERT_EQUALS(result.size(), 7); + TS_ASSERT_EQUALS(result[0], "A"); + TS_ASSERT_EQUALS(result[1], "->"); + TS_ASSERT_EQUALS(result[2], "B"); + TS_ASSERT_EQUALS(result[3], "="); + TS_ASSERT_EQUALS(result[4], "C"); + TS_ASSERT_EQUALS(result[5], "->"); + TS_ASSERT_EQUALS(result[6], "D"); + } + void test_substr_after_with_single_delimiter_occurence() { TS_ASSERT_EQUALS(utility::substrAfter("foo bar", ' '), "bar");