diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index c906d4c0..42447cfe 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -44,6 +44,15 @@ add_files( data/access/GraphAccess.cpp data/access/GraphAccess.h + data/graph/Edge.cpp + data/graph/Edge.h + data/graph/Graph.cpp + data/graph/Graph.h + data/graph/Node.cpp + data/graph/Node.h + data/graph/Token.cpp + data/graph/Token.h + data/parser/ParseLocation.cpp data/parser/ParseLocation.h data/parser/Parser.cpp @@ -53,16 +62,8 @@ add_files( data/parser/ParseVariable.cpp data/parser/ParseVariable.h - data/Edge.cpp - data/Edge.h - data/Element.cpp - data/Element.h data/ElementIndex.cpp data/ElementIndex.h - data/Graph.cpp - data/Graph.h - data/Node.cpp - data/Node.h data/SearchIndex.cpp data/SearchIndex.h data/Storage.cpp @@ -124,6 +125,9 @@ add_files( utility/ConfigManager.h utility/FileSystem.cpp utility/FileSystem.h + utility/types.h + utility/utilityString.cpp + utility/utilityString.h utility/Vector.h Application.cpp diff --git a/src/lib/Project.cpp b/src/lib/Project.cpp index fe03438a..df11ae24 100644 --- a/src/lib/Project.cpp +++ b/src/lib/Project.cpp @@ -46,5 +46,6 @@ void Project::parseCode() parser.parseFiles( FileSystem::getSourceFilesFromDirectory(ProjectSettings::getInstance()->getSourcePath(), extension) ); + m_storage->logGraph(); } } diff --git a/src/lib/data/Edge.cpp b/src/lib/data/Edge.cpp deleted file mode 100644 index 8d25aebe..00000000 --- a/src/lib/data/Edge.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "data/Edge.h" - -#include "data/Node.h" - -Edge::Edge(std::weak_ptr from, std::weak_ptr to) -: m_from(from) -, m_to(to) -{ -} - -Edge::~Edge() -{ -} diff --git a/src/lib/data/Edge.h b/src/lib/data/Edge.h deleted file mode 100644 index 7e7c06f2..00000000 --- a/src/lib/data/Edge.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EDGE_H -#define EDGE_H - -#include - -#include "data/Element.h" - -class Node; - -class Edge: public Element -{ -public: - Edge(std::weak_ptr from, std::weak_ptr to); - ~Edge(); - -private: - const std::weak_ptr m_from; - const std::weak_ptr m_to; -}; - - -#endif // EDGE_H diff --git a/src/lib/data/Element.cpp b/src/lib/data/Element.cpp deleted file mode 100644 index b32eb083..00000000 --- a/src/lib/data/Element.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "data/Element.h" diff --git a/src/lib/data/Element.h b/src/lib/data/Element.h deleted file mode 100644 index a80b9b15..00000000 --- a/src/lib/data/Element.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ELEMENT_H -#define ELEMENT_H - -class Element -{ -}; - -#endif // ELEMENT_H diff --git a/src/lib/data/Graph.cpp b/src/lib/data/Graph.cpp deleted file mode 100644 index 83d1235d..00000000 --- a/src/lib/data/Graph.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "data/Graph.h" - -Graph::Graph() -{ -} - -Graph::~Graph() -{ -} diff --git a/src/lib/data/Graph.h b/src/lib/data/Graph.h deleted file mode 100644 index 69b12edc..00000000 --- a/src/lib/data/Graph.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef GRAPH_H -#define GRAPH_H - -#include - -#include "data/Edge.h" -#include "data/Node.h" - -class Graph -{ -public: - Graph(); - ~Graph(); - -private: - std::vector > m_nodes; - std::vector > m_edges; -}; - - -#endif // GRAPH_H diff --git a/src/lib/data/Node.cpp b/src/lib/data/Node.cpp deleted file mode 100644 index 9ce0a575..00000000 --- a/src/lib/data/Node.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "data/Node.h" diff --git a/src/lib/data/Node.h b/src/lib/data/Node.h deleted file mode 100644 index de7365dc..00000000 --- a/src/lib/data/Node.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NODE_H -#define NODE_H - -#include "data/Element.h" - -class Node: public Element -{ -}; - - -#endif // NODE_H diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index afd6fb44..81caba2e 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -25,21 +25,50 @@ void Storage::onTypedefParsed( void Storage::onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { log("class", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_CLASS); + node->setAccess(convertAccessType(access)); } void Storage::onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { log("struct", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_STRUCT); + node->setAccess(convertAccessType(access)); } void Storage::onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) { log("global", variable.fullName, location); + + Node* node = m_graph.createNodeHierarchy(variable.fullName); + node->setType(Node::NODE_GLOBAL); + node->setConst(variable.isConst); + node->setStatic(variable.isStatic); + + m_graph.createEdge(Edge::EDGE_TYPE_OF, node, m_graph.createNodeHierarchy(variable.typeName)); } void Storage::onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) { log("field", variable.fullName, location); + + Node* node = m_graph.createNodeHierarchy(variable.fullName); + node->setType(Node::NODE_FIELD); + node->setConst(variable.isConst); + node->setStatic(variable.isStatic); + + if (access == ACCESS_NONE) + { + LOG_ERROR("Field needs to have access type [public, protected, private] but has none."); + return; + } + node->setAccess(convertAccessType(access)); + + m_graph.createEdge(Edge::EDGE_TYPE_OF, node, m_graph.createNodeHierarchy(variable.typeName)); } void Storage::onFunctionParsed( @@ -48,6 +77,15 @@ void Storage::onFunctionParsed( ) { log("function", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_FUNCTION); + + m_graph.createEdge(Edge::EDGE_RETURN_TYPE_OF, node, m_graph.createNodeHierarchy(returnTypeName)); + for (const ParseVariable& var : parameters) + { + m_graph.createEdge(Edge::EDGE_PARAMETER_OF, node, m_graph.createNodeHierarchy(var.typeName)); + } } void Storage::onMethodParsed( @@ -57,21 +95,56 @@ void Storage::onMethodParsed( ) { log("method", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_METHOD); + node->setConst(isConst); + node->setStatic(isStatic); + + if (access == ACCESS_NONE) + { + LOG_ERROR("Method needs to have access type [public, protected, private] but has none."); + return; + } + node->setAccess(convertAccessType(access)); + + m_graph.createEdge(Edge::EDGE_RETURN_TYPE_OF, node, m_graph.createNodeHierarchy(returnTypeName)); + for (const ParseVariable& parameter : parameters) + { + m_graph.createEdge(Edge::EDGE_PARAMETER_OF, node, m_graph.createNodeHierarchy(parameter.typeName)); + } } void Storage::onNamespaceParsed(const ParseLocation& location, const std::string& fullName) { log("namespace", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_NAMESPACE); } void Storage::onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) { log("enum", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_ENUM); + node->setAccess(convertAccessType(access)); } void Storage::onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) { log("enum field", fullName, location); + + Node* node = m_graph.createNodeHierarchy(fullName); + node->setType(Node::NODE_FIELD); +} + +void Storage::logGraph() const +{ + std::stringstream str; + str << "\n" << m_graph; + LOG_INFO(str.str()); } void Storage::log(std::string type, std::string str, const ParseLocation& location) const @@ -80,3 +153,18 @@ void Storage::log(std::string type, std::string str, const ParseLocation& locati info << type << ": " << str << " <" << location.file << " " << location.line << ":" << location.column << ">"; LOG_INFO(info.str()); } + +Edge::AccessType Storage::convertAccessType(ParserClient::AccessType access) const +{ + switch (access) + { + case ACCESS_PUBLIC: + return Edge::ACCESS_PUBLIC; + case ACCESS_PROTECTED: + return Edge::ACCESS_PROTECTED; + case ACCESS_PRIVATE: + return Edge::ACCESS_PRIVATE; + case ACCESS_NONE: + return Edge::ACCESS_NONE; + } +} diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 26d41f94..bdb6f748 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -4,7 +4,7 @@ #include #include -#include "data/Graph.h" +#include "data/graph/Graph.h" #include "data/parser/ParserClient.h" #include "data/TextLocationFile.h" @@ -37,8 +37,11 @@ public: virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access); virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName); + void logGraph() const; + private: void log(std::string type, std::string str, const ParseLocation& location) const; + Edge::AccessType convertAccessType(ParserClient::AccessType access) const; Graph m_graph; diff --git a/src/lib/data/TextLocation.cpp b/src/lib/data/TextLocation.cpp index 8cb36f50..5c9597b2 100644 --- a/src/lib/data/TextLocation.cpp +++ b/src/lib/data/TextLocation.cpp @@ -1,14 +1,14 @@ #include "data/TextLocation.h" -#include "data/Element.h" +#include "data/graph/Token.h" #include "data/TextLocationLine.h" TextLocation::TextLocation( std::weak_ptr textLocationLine, - std::weak_ptr element, + std::weak_ptr token, unsigned int column ) - : m_element(element) + : m_token(token) , m_textLocationLine(textLocationLine) , m_column(column) { diff --git a/src/lib/data/TextLocation.h b/src/lib/data/TextLocation.h index 90332bd6..940fdc46 100644 --- a/src/lib/data/TextLocation.h +++ b/src/lib/data/TextLocation.h @@ -3,7 +3,7 @@ #include -class Element; +class Token; class TextLocationLine; class TextLocation @@ -11,7 +11,7 @@ class TextLocation public: TextLocation( std::weak_ptr textLocationLine, - std::weak_ptr element, + std::weak_ptr token, unsigned int column ); @@ -20,7 +20,7 @@ public: unsigned int getColumn() const; private: - const std::weak_ptr m_element; + const std::weak_ptr m_token; const std::weak_ptr m_textLocationLine; const unsigned int m_column; }; diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp new file mode 100644 index 00000000..953c9abc --- /dev/null +++ b/src/lib/data/graph/Edge.cpp @@ -0,0 +1,146 @@ +#include "data/graph/Edge.h" + +#include + +#include "data/graph/Node.h" +#include "utility/logging/logging.h" + +Edge::Edge(EdgeType type, Node* from, Node* to) + : m_type(type) + , m_from(from) + , m_to(to) + , m_access(ACCESS_NONE) +{ + m_from->addEdge(this); + m_to->addEdge(this); +} + +Edge::~Edge() +{ + m_from->removeEdge(this); + m_to->removeEdge(this); +} + +std::shared_ptr Edge::createPlainCopy(Node* from, Node* to) const +{ + if (from == m_from || to == m_to || from->getId() != m_from->getId() || to->getId() != m_to->getId()) + { + LOG_ERROR("Nodes are not plain copies."); + return nullptr; + } + + std::shared_ptr edge(new Edge(getId(), m_type, from, to)); + + edge->setAccess(m_access); + + return edge; +} + +Edge::EdgeType Edge::getType() const +{ + return m_type; +} + +Node* Edge::getFrom() const +{ + return m_from; +} + +Node* Edge::getTo() const +{ + return m_to; +} + +bool Edge::isNode() const +{ + return false; +} + +bool Edge::isEdge() const +{ + return true; +} + +Edge::AccessType Edge::getAccess() const +{ + return m_access; +} + +void Edge::setAccess(AccessType access) +{ + if (access != ACCESS_NONE && m_type != EDGE_MEMBER && m_type != EDGE_INHERITANCE) + { + LOG_ERROR("Setting access on wrong edge type."); + return; + } + + if (m_access != ACCESS_NONE && access != m_access) + { + LOG_WARNING("Different AccessType was already set before."); + } + m_access = access; +} + +std::string Edge::getTypeString() const +{ + switch (m_type) + { + case EDGE_MEMBER: + return "has child"; + case EDGE_TYPE_OF: + return "is type of"; + case EDGE_RETURN_TYPE_OF: + return "has return type"; + case EDGE_PARAMETER_OF: + return "has parameter of type"; + default: + LOG_ERROR("TypeString not implemented for edge type."); + } + return ""; +} + +std::string Edge::getAccessString() const +{ + switch (m_access) + { + case ACCESS_PUBLIC: + return "public"; + case ACCESS_PROTECTED: + return "protected"; + case ACCESS_PRIVATE: + return "private"; + case ACCESS_NONE: + return ""; + } + return ""; +} + +std::string Edge::getAsString() const +{ + std::stringstream str; + str << "[" << getId() << "] \"" << m_from->getName() << "\" " << getTypeString() << " \"" + m_to->getName() << "\""; + + if (m_access != ACCESS_NONE) + { + str << " " << getAccessString(); + } + + return str.str(); +} + +Edge::Edge(Id id, EdgeType type, Node* from, Node* to) + : Token(id) + , m_type(type) + , m_from(from) + , m_to(to) + , m_access(ACCESS_NONE) +{ + m_from->addEdge(this); + m_to->addEdge(this); +} + +std::ostream& operator<<(std::ostream& ostream, const Edge& edge) +{ + ostream << edge.getAsString(); + return ostream; +} diff --git a/src/lib/data/graph/Edge.h b/src/lib/data/graph/Edge.h new file mode 100644 index 00000000..25c62e3e --- /dev/null +++ b/src/lib/data/graph/Edge.h @@ -0,0 +1,71 @@ +#ifndef EDGE_H +#define EDGE_H + +#include +#include + +#include "data/graph/Token.h" + +class Node; + +class Edge: public Token +{ +public: + enum EdgeType + { + EDGE_MEMBER, + EDGE_TYPE_OF, + EDGE_RETURN_TYPE_OF, + EDGE_PARAMETER_OF, + EDGE_USAGE, + EDGE_CALL, + EDGE_INHERITANCE + }; + + enum AccessType + { + ACCESS_PUBLIC, + ACCESS_PROTECTED, + ACCESS_PRIVATE, + ACCESS_NONE + }; + + Edge(EdgeType type, Node* from, Node* to); + virtual ~Edge(); + + std::shared_ptr createPlainCopy(Node* from, Node* to) const; + + EdgeType getType() const; + + Node* getFrom() const; + Node* getTo() const; + + // Token implementation + virtual bool isNode() const; + virtual bool isEdge() const; + + // Additional field accessors for different EdgeTypes. + AccessType getAccess() const; + void setAccess(AccessType access); + + // Logging. + std::string getTypeString() const; + std::string getAccessString() const; + std::string getAsString() const; + +private: + // Constructor for plain copies. + Edge(Id id, EdgeType type, Node* from, Node* to); + + const EdgeType m_type; + + Node* const m_from; + Node* const m_to; + + // Additional fields for different EdgeTypes. + AccessType m_access; +}; + +std::ostream& operator<<(std::ostream& ostream, const Edge& edge); + +#endif // EDGE_H diff --git a/src/lib/data/graph/Graph.cpp b/src/lib/data/graph/Graph.cpp new file mode 100644 index 00000000..dc8f6cfc --- /dev/null +++ b/src/lib/data/graph/Graph.cpp @@ -0,0 +1,341 @@ +#include "data/graph/Graph.h" + +#include "utility/logging/logging.h" +#include "utility/utilityString.h" + +Graph::Graph() +{ +} + +Graph::~Graph() +{ + m_edges.clear(); + m_nodes.clear(); +} + +Node* Graph::getNode(const std::string& fullName) const +{ + return findNode( + [fullName](Node* n) + { + return n->getName() == fullName; + } + ); +} + +Edge* Graph::getEdge(Edge::EdgeType type, Node* from, Node* to) const +{ + return from->findEdgeOfType(type, + [to](Edge* e) + { + return e->getTo() == to; + } + ); +} + +Node* Graph::getNodeById(Id id) const +{ + return findNode( + [id](Node* n) + { + return n->getId() == id; + } + ); +} + +Edge* Graph::getEdgeById(Id id) const +{ + return findEdge( + [id](Edge* e) + { + return e->getId() == id; + } + ); +} + +Token* Graph::getTokenById(Id id) const +{ + return findToken( + [id](Token* t) + { + return t->getId() == id; + } + ); +} + +Node* Graph::createNodeHierarchy(const std::string& fullName) +{ + Node* node = getNode(fullName); + if (node) + { + return node; + } + + return insertNodeHierarchy(fullName); +} + +Edge* Graph::createEdge(Edge::EdgeType type, Node* from, Node* to) +{ + Edge* edge = getEdge(type, from, to); + if (edge) + { + return edge; + } + + return insertEdge(type, from, to); +} + +void Graph::removeNode(Node* node) +{ + std::vector >::const_iterator it; + for (it = m_nodes.begin(); it != m_nodes.end(); it++) + { + if (it->get() == node) + { + break; + } + } + + if (it == m_nodes.end()) + { + LOG_WARNING("Node was not found in the graph."); + return; + } + + node->forEachEdgeOfType(Edge::EDGE_MEMBER, [this, node](Edge* e) + { + if (node == e->getFrom()) + { + this->removeNode(e->getTo()); + } + }); + + node->forEachEdge( + [this](Edge* e) + { + this->removeEdgeInternal(e); + } + ); + + if (node->getEdges().size()) + { + LOG_ERROR("Node has still edges."); + } + + m_nodes.erase(it); +} + +void Graph::removeEdge(Edge* edge) +{ + std::vector >::const_iterator it; + for (it = m_edges.begin(); it != m_edges.end(); it++) + { + if (it->get() == edge) + { + break; + } + } + + if (it == m_edges.end()) + { + LOG_WARNING("Edge was not found in the graph."); + } + + if (edge->getType() == Edge::EDGE_MEMBER) + { + LOG_ERROR("Can't remove member edge, without removing the child node."); + return; + } + + m_edges.erase(it); +} + +Node* Graph::findNode(std::function func) const +{ + std::vector >::const_iterator it = find_if(m_nodes.begin(), m_nodes.end(), + [&func](const std::shared_ptr& n) + { + return func(n.get()); + } + ); + + if (it != m_nodes.end()) + { + return it->get(); + } + + return nullptr; +} + +Edge* Graph::findEdge(std::function func) const +{ + std::vector >::const_iterator it = find_if(m_edges.begin(), m_edges.end(), + [func](const std::shared_ptr& e) + { + return func(e.get()); + } + ); + + if (it != m_edges.end()) + { + return it->get(); + } + + return nullptr; +} + +Token* Graph::findToken(std::function func) const +{ + Node* node = findNode(func); + if (node) + { + return node; + } + + Edge* edge = findEdge(func); + if (edge) + { + return edge; + } + + return nullptr; +} + +void Graph::forEachNode(std::function func) const +{ + for (const std::shared_ptr& node : m_nodes) + { + func(node.get()); + } +} + +void Graph::forEachEdge(std::function func) const +{ + for (const std::shared_ptr& edge : m_edges) + { + func(edge.get()); + } +} + +void Graph::forEachToken(std::function func) const +{ + forEachNode(func); + forEachEdge(func); +} + +const std::vector >& Graph::getNodes() const +{ + return m_nodes; +} + +const std::vector >& Graph::getEdges() const +{ + return m_edges; +} + +Node* Graph::addNodeAsPlainCopy(Node* node) +{ + Node* n = getNodeById(node->getId()); + if (n) + { + return n; + } + + std::shared_ptr copy = node->createPlainCopy(); + m_nodes.push_back(copy); + return copy.get(); +} + +Edge* Graph::addEdgeAsPlainCopy(Edge* edge) +{ + Edge* e = getEdgeById(edge->getId()); + if (e) + { + return e; + } + + Node* from = addNodeAsPlainCopy(edge->getFrom()); + Node* to = addNodeAsPlainCopy(edge->getTo()); + + std::shared_ptr copy = edge->createPlainCopy(from, to); + m_edges.push_back(copy); + return copy.get(); +} + +const std::string Graph::DELIMITER = "::"; + +Node* Graph::insertNodeHierarchy(const std::string& fullName) +{ + Node* node = nullptr; + std::vector names = utility::split(fullName, DELIMITER); + std::string name; + + for (std::vector::iterator it = names.begin(); it != names.end(); it++) + { + if (name.size() > 0) + { + name += DELIMITER; + } + name += *it; + + Node* childNode = getNode(name); + + if (!childNode) + { + std::shared_ptr childNodePtr = std::make_shared(Node::NODE_UNDEFINED, name); + m_nodes.push_back(childNodePtr); + childNode = childNodePtr.get(); + + if (node) + { + createEdge(Edge::EDGE_MEMBER, node, childNode); + } + } + + node = childNode; + } + + return node; +} + +Edge* Graph::insertEdge(Edge::EdgeType type, Node* from, Node* to) +{ + std::shared_ptr edgePtr = std::make_shared(type, from, to); + m_edges.push_back(edgePtr); + return edgePtr.get(); +} + +void Graph::removeEdgeInternal(Edge* edge) +{ + std::vector >::const_iterator it; + for (it = m_edges.begin(); it != m_edges.end(); it++) + { + if (it->get() == edge) + { + m_edges.erase(it); + return; + } + } +} + +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'; + } + ); + + return ostream; +} diff --git a/src/lib/data/graph/Graph.h b/src/lib/data/graph/Graph.h new file mode 100644 index 00000000..f178af4e --- /dev/null +++ b/src/lib/data/graph/Graph.h @@ -0,0 +1,62 @@ +#ifndef GRAPH_H +#define GRAPH_H + +#include +#include +#include + +#include "data/graph/Edge.h" +#include "data/graph/Node.h" + +class Graph +{ +public: + Graph(); + virtual ~Graph(); + + Node* getNode(const std::string& fullName) const; + Edge* getEdge(Edge::EdgeType type, Node* from, Node* to) const; + + Node* getNodeById(Id id) const; + Edge* getEdgeById(Id id) const; + Token* getTokenById(Id id) const; + + Node* createNodeHierarchy(const std::string& fullName); + Edge* createEdge(Edge::EdgeType type, Node* from, Node* to); + + void removeNode(Node* node); + void removeEdge(Edge* edge); + + Node* findNode(std::function func) const; + 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); + +protected: + const std::vector >& getNodes() const; + const std::vector >& getEdges() const; + +private: + static const std::string DELIMITER; + + Node* insertNodeHierarchy(const std::string& fullName); + Edge* insertEdge(Edge::EdgeType type, Node* from, Node* to); + void removeEdgeInternal(Edge* edge); + + const std::string m_delimiter; + + std::vector > m_nodes; + std::vector > m_edges; + + friend std::ostream& operator<<(std::ostream& ostream, const Graph& graph); +}; + +std::ostream& operator<<(std::ostream& ostream, const Graph& graph); + +#endif // GRAPH_H diff --git a/src/lib/data/graph/Node.cpp b/src/lib/data/graph/Node.cpp new file mode 100644 index 00000000..57f8f062 --- /dev/null +++ b/src/lib/data/graph/Node.cpp @@ -0,0 +1,248 @@ +#include "data/graph/Node.h" + +#include + +#include "utility/logging/logging.h" + +Node::Node(NodeType type, const std::string& name) + : m_type(type) + , m_name(name) + , m_isConst(false) + , m_isStatic(false) +{ +} + +Node::~Node() +{ +} + +std::shared_ptr Node::createPlainCopy() const +{ + std::shared_ptr node(new Node(getId(), m_type, m_name)); + + node->setConst(m_isConst); + node->setStatic(m_isStatic); + + return node; +} + +Node::NodeType Node::getType() const +{ + return m_type; +} + +void Node::setType(NodeType type) +{ + if (type != m_type && m_type != NODE_NAMESPACE && m_type != NODE_UNDEFINED) + { + LOG_WARNING( + "Changing NodeType after it was already set, from " + getTypeString(m_type) + " to " + getTypeString(type) + ); + } + m_type = type; +} + +const std::string& Node::getName() const +{ + return m_name; +} + +const std::vector& Node::getEdges() const +{ + return m_edges; +} + +void Node::addEdge(Edge* edge) +{ + m_edges.push_back(edge); +} + +void Node::removeEdge(Edge* edge) +{ + m_edges.erase(find(m_edges.begin(), m_edges.end(), edge)); +} + +Edge* Node::getMemberEdge() const +{ + Edge* edge = findEdgeOfType(Edge::EDGE_MEMBER, + [this](Edge* e) + { + return e->getTo() == this; + } + ); + + if (edge) + { + return edge; + } + else + { + LOG_WARNING("Child edge was not found, node " + getName() + " has no parent node."); + return nullptr; + } +} + +Edge* Node::findEdge(std::function func) const +{ + std::vector::const_iterator it = find_if(m_edges.begin(), m_edges.end(), func); + + if (it != m_edges.end()) + { + return *it; + } + + return NULL; +} + +Edge* Node::findEdgeOfType(Edge::EdgeType type, std::function func) const +{ + std::vector::const_iterator it = find_if(m_edges.begin(), m_edges.end(), + [type, func](Edge* e) + { + if (e->getType() == type) + { + return func(e); + } + return false; + } + ); + + if (it != m_edges.end()) + { + return *it; + } + + return NULL; +} + +void Node::forEachEdge(std::function func) const +{ + for_each(m_edges.begin(), m_edges.end(), func); +} + +void Node::forEachEdgeOfType(Edge::EdgeType type, std::function func) const +{ + for_each(m_edges.begin(), m_edges.end(), + [type, func](Edge* e) + { + if (e->getType() == type) + { + func(e); + } + } + ); +} + +bool Node::isNode() const +{ + return true; +} + +bool Node::isEdge() const +{ + return false; +} + +void Node::setAccess(Edge::AccessType access) +{ + if (access != Edge::ACCESS_NONE) + { + if (!getMemberEdge()) + { + LOG_WARNING("Can't set access on node " + getName() + ", because it is not a child."); + return; + } + getMemberEdge()->setAccess(access); + } +} + +bool Node::isConst() const +{ + return m_isConst; +} + +void Node::setConst(bool isConst) +{ + if (isConst && m_type != NODE_GLOBAL && m_type != NODE_FIELD && m_type != NODE_METHOD) + { + LOG_ERROR("Setting const on wrong node of type " + getTypeString(m_type)); + return; + } + + m_isConst = isConst; +} + +bool Node::isStatic() const +{ + return m_isStatic; +} + +void Node::setStatic(bool isStatic) +{ + if (isStatic && m_type != NODE_GLOBAL && m_type != NODE_FIELD && m_type != NODE_METHOD) + { + LOG_ERROR("Setting static on wrong node of type " + getTypeString(m_type)); + return; + } + + m_isStatic = isStatic; +} + +std::string Node::getTypeString(NodeType type) const +{ + switch (type) + { + case NODE_UNDEFINED: + return "type"; + case NODE_CLASS: + return "class"; + case NODE_STRUCT: + return "struct"; + case NODE_GLOBAL: + return "global"; + case NODE_FIELD: + return "field"; + case NODE_FUNCTION: + return "function"; + case NODE_METHOD: + return "method"; + case NODE_NAMESPACE: + return "namespace"; + case NODE_ENUM: + return "enum"; + } + return ""; +} + +std::string Node::getAsString() const +{ + std::stringstream str; + str << "[" << getId() << "] " << getTypeString(m_type) << ": " << "\"" << getName() << "\""; + + if (isStatic()) + { + str << " static"; + } + + if (isConst()) + { + str << " const"; + } + + return str.str(); +} + +Node::Node(Id id, NodeType type, const std::string& name) + : Token(id) + , m_type(type) + , m_name(name) + , m_isConst(false) + , m_isStatic(false) +{ +} + +std::ostream& operator<<(std::ostream& ostream, const Node& node) +{ + ostream << node.getAsString(); + return ostream; +} diff --git a/src/lib/data/graph/Node.h b/src/lib/data/graph/Node.h new file mode 100644 index 00000000..c7e76f39 --- /dev/null +++ b/src/lib/data/graph/Node.h @@ -0,0 +1,83 @@ +#ifndef NODE_H +#define NODE_H + +#include +#include +#include +#include + +#include "data/graph/Edge.h" +#include "data/graph/Token.h" + +class Node: public Token +{ +public: + enum NodeType + { + NODE_UNDEFINED, + NODE_CLASS, + NODE_STRUCT, + NODE_GLOBAL, + NODE_FIELD, + NODE_FUNCTION, + NODE_METHOD, + NODE_NAMESPACE, + NODE_ENUM + }; + + Node(NodeType type, const std::string& name); + virtual ~Node(); + + std::shared_ptr createPlainCopy() const; + + NodeType getType() const; + void setType(NodeType type); + + const std::string& getName() const; + const std::vector& getEdges() const; + + void addEdge(Edge* edge); + void removeEdge(Edge* edge); + + Edge* getMemberEdge() const; + + Edge* findEdge(std::function func) const; + Edge* findEdgeOfType(Edge::EdgeType type, std::function func) const; + + void forEachEdge(std::function func) const; + void forEachEdgeOfType(Edge::EdgeType type, std::function func) const; + + // Token implementation. + virtual bool isNode() const; + virtual bool isEdge() const; + + // Additional field accessors for different NodeTypes. + void setAccess(Edge::AccessType access); + + bool isConst() const; + void setConst(bool isConst); + + bool isStatic() const; + void setStatic(bool isStatic); + + // Logging. + std::string getTypeString(NodeType type) const; + std::string getAsString() const; + +private: + // Constructor for plain copies. + Node(Id id, NodeType type, const std::string& name); + + NodeType m_type; + std::string m_name; + + std::vector m_edges; + + // Additional fields for different NodeTypes. + bool m_isConst; + bool m_isStatic; +}; + +std::ostream& operator<<(std::ostream& ostream, const Node& node); + +#endif // NODE_H diff --git a/src/lib/data/graph/Token.cpp b/src/lib/data/graph/Token.cpp new file mode 100644 index 00000000..b56b851a --- /dev/null +++ b/src/lib/data/graph/Token.cpp @@ -0,0 +1,22 @@ +#include "data/graph/Token.h" + +Token::Token() + : m_id(s_nextId++) +{ +} + +Token::~Token() +{ +} + +Id Token::getId() const +{ + return m_id; +} + +Id Token::s_nextId = 1; + +Token::Token(Id id) + : m_id(id) +{ +} diff --git a/src/lib/data/graph/Token.h b/src/lib/data/graph/Token.h new file mode 100644 index 00000000..f2a2d3b8 --- /dev/null +++ b/src/lib/data/graph/Token.h @@ -0,0 +1,30 @@ +#ifndef TOKEN_H +#define TOKEN_H + +#include "utility/types.h" + +class Token +{ +public: + Token(); + virtual ~Token(); + + Id getId() const; + + virtual bool isNode() const = 0; + virtual bool isEdge() const = 0; + +protected: + // Constructor for plain copies of Node and Edge + Token(Id id); + +private: + static Id s_nextId; + + Token(const Token&); + void operator=(const Token&); + + const Id m_id; +}; + +#endif // TOKEN_H diff --git a/src/lib/utility/types.h b/src/lib/utility/types.h new file mode 100644 index 00000000..c322e8a5 --- /dev/null +++ b/src/lib/utility/types.h @@ -0,0 +1,6 @@ +#ifndef TYPES_H +#define TYPES_H + +typedef unsigned long Id; + +#endif // TYPES_H diff --git a/src/lib/utility/utilityString.cpp b/src/lib/utility/utilityString.cpp new file mode 100644 index 00000000..3f19589d --- /dev/null +++ b/src/lib/utility/utilityString.cpp @@ -0,0 +1,26 @@ +#include "utility/utilityString.h" + +namespace utility +{ + std::vector split(const std::string& str, char delimiter) + { + return split(str, std::string(1, delimiter)); + } + + std::vector split(const std::string& str, const std::string& delimiter) + { + size_t pos = 0; + size_t oldPos = 0; + std::vector vec; + + do + { + pos = str.find(delimiter, oldPos); + vec.push_back(str.substr(oldPos, pos - oldPos)); + oldPos = pos + delimiter.size(); + } + while (pos != std::string::npos); + + return vec; + } +} diff --git a/src/lib/utility/utilityString.h b/src/lib/utility/utilityString.h new file mode 100644 index 00000000..23c31f54 --- /dev/null +++ b/src/lib/utility/utilityString.h @@ -0,0 +1,13 @@ +#ifndef UTILITY_STRING_H +#define UTILITY_STRING_H + +#include +#include + +namespace utility +{ + std::vector split(const std::string& str, char delimiter); + std::vector split(const std::string& str, const std::string& delimiter); +} + +#endif // UTILITY_STRING_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index e46416d9..a7ecc21b 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -4,11 +4,13 @@ add_files( ConfigManagerTestSuite.h CxxParserTestSuite.h FileSystemTestSuite.h + GraphTestSuite.h LogManagerTestSuite.h MessageQueueTestSuite.h ProjectSettingsTestSuite.h TestSuiteFixture.cpp TestSuiteFixture.h TextAccessTestSuite.h + UtilityStringTestSuite.h Vector2TestSuite.h ) diff --git a/src/test/GraphTestSuite.h b/src/test/GraphTestSuite.h new file mode 100644 index 00000000..dffd6816 --- /dev/null +++ b/src/test/GraphTestSuite.h @@ -0,0 +1,293 @@ +#include "cxxtest/TestSuite.h" + +#include "data/graph/Graph.h" + +class GraphTestSuite : public CxxTest::TestSuite +{ +public: + void test_tokens_get_unique_id() + { + Node a(Node::NODE_UNDEFINED, "A"); + Node b(Node::NODE_UNDEFINED, "B"); + Node c(Node::NODE_UNDEFINED, "C"); + + TS_ASSERT_DIFFERS(a.getId(), c.getId()); + TS_ASSERT_DIFFERS(a.getId(), b.getId()); + TS_ASSERT_DIFFERS(b.getId(), c.getId()); + } + + void test_get_and_set_type_of_nodes() + { + Node n(Node::NODE_NAMESPACE, "A"); + TS_ASSERT_EQUALS(Node::NODE_NAMESPACE, n.getType()); + + n.setType(Node::NODE_CLASS); + TS_ASSERT_EQUALS(Node::NODE_CLASS, n.getType()); + } + + void test_get_type_of_edges() + { + Node a(Node::NODE_UNDEFINED, "A"); + Node b(Node::NODE_UNDEFINED, "B"); + Edge e(Edge::EDGE_TYPE_OF, &a, &b); + + TS_ASSERT_EQUALS(Edge::EDGE_TYPE_OF, e.getType()); + } + + void test_get_and_set_const_and_static_of_nodes() + { + Node n(Node::NODE_GLOBAL, "A"); + TS_ASSERT(!n.isConst()); + TS_ASSERT(!n.isStatic()); + + n.setConst(true); + TS_ASSERT(n.isConst()); + TS_ASSERT(!n.isStatic()); + + n.setStatic(true); + TS_ASSERT(n.isConst()); + TS_ASSERT(n.isStatic()); + } + + void test_graph_saves_nodes() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + + TS_ASSERT_EQUALS(2, graph.getNodeCount()); + TS_ASSERT_EQUALS(0, graph.getEdgeCount()); + + TS_ASSERT_EQUALS(a, graph.getNode("A")); + TS_ASSERT_EQUALS("A", graph.getNode("A")->getName()); + + TS_ASSERT_EQUALS(b, graph.getNode("B")); + TS_ASSERT_EQUALS("B", graph.getNode("B")->getName()); + + TS_ASSERT(!graph.getNode("C")); + } + + void test_graph_saves_edges() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + Edge* e = graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + + TS_ASSERT_EQUALS(e, graph.getEdge(Edge::EDGE_TYPE_OF, a, b)); + TS_ASSERT(!graph.getEdge(Edge::EDGE_CALL, a, b)); + } + + void test_graph_finds_nodes_and_edges_by_id() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + Edge* e = graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + + TS_ASSERT(!graph.getEdgeById(a->getId())); + TS_ASSERT_EQUALS(a, graph.getNodeById(a->getId())); + TS_ASSERT_EQUALS(a, graph.getTokenById(a->getId())); + + TS_ASSERT(!graph.getNodeById(e->getId())); + TS_ASSERT_EQUALS(e, graph.getEdgeById(e->getId())); + TS_ASSERT_EQUALS(e, graph.getTokenById(e->getId())); + } + + void test_graph_creates_child_edges() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* ab = graph.createNodeHierarchy("A::B"); + + TS_ASSERT_EQUALS(2, graph.getNodeCount()); + TS_ASSERT_EQUALS(1, graph.getEdgeCount()); + + TS_ASSERT(graph.getEdge(Edge::EDGE_MEMBER, a, ab)); + } + + void test_graph_removes_nodes() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + graph.createNodeHierarchy("A::C"); + graph.createNodeHierarchy("A::C::D"); + graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + + graph.removeNode(a); + + TS_ASSERT_EQUALS(1, graph.getNodeCount()); + TS_ASSERT_EQUALS(0, graph.getEdgeCount()); + + TS_ASSERT(!graph.getNode("A")); + TS_ASSERT(!graph.getNode("A::C")); + TS_ASSERT(graph.getNode("B")); + } + + void test_graph_removes_edge() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + Edge* e = graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + + graph.removeEdge(e); + + TS_ASSERT_EQUALS(2, graph.getNodeCount()); + TS_ASSERT_EQUALS(0, graph.getEdgeCount()); + + TS_ASSERT(graph.getNode("A")); + TS_ASSERT(graph.getNode("B")); + } + + void test_graph_can_not_remove_member_edge() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + Node* c = graph.createNodeHierarchy("A::C"); + graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + + graph.removeEdge(c->getMemberEdge()); + + TS_ASSERT_EQUALS(3, graph.getNodeCount()); + TS_ASSERT_EQUALS(2, graph.getEdgeCount()); + + TS_ASSERT(graph.getNode("A")); + TS_ASSERT(graph.getNode("B")); + TS_ASSERT(graph.getNode("A::C")); + } + + void test_graph_creates_multiple_nodes_as_type_nodes() + { + TestGraph graph; + Node* abc = graph.createNodeHierarchy("A::B::C"); + + TS_ASSERT_EQUALS(3, graph.getNodeCount()); + TS_ASSERT_EQUALS(2, graph.getEdgeCount()); + + TS_ASSERT_EQUALS("A", graph.getNode("A")->getName()); + TS_ASSERT_EQUALS(Node::NODE_UNDEFINED, graph.getNode("A")->getType()); + + TS_ASSERT_EQUALS("A::B", graph.getNode("A::B")->getName()); + TS_ASSERT_EQUALS(Node::NODE_UNDEFINED, graph.getNode("A::B")->getType()); + + TS_ASSERT_EQUALS(abc, graph.getNode("A::B::C")); + TS_ASSERT_EQUALS("A::B::C", graph.getNode("A::B::C")->getName()); + TS_ASSERT_EQUALS(Node::NODE_UNDEFINED, graph.getNode("A::B::C")->getType()); + + Node* abcde = graph.createNodeHierarchy("A::B::C::D::E"); + + TS_ASSERT_EQUALS(5, graph.getNodeCount()); + TS_ASSERT_EQUALS(4, graph.getEdgeCount()); + + TS_ASSERT_EQUALS("A::B::C::D", graph.getNode("A::B::C::D")->getName()); + TS_ASSERT_EQUALS(Node::NODE_UNDEFINED, graph.getNode("A::B::C::D")->getType()); + + TS_ASSERT_EQUALS(abcde, graph.getNode("A::B::C::D::E")); + TS_ASSERT_EQUALS("A::B::C::D::E", graph.getNode("A::B::C::D::E")->getName()); + TS_ASSERT_EQUALS(Node::NODE_UNDEFINED, graph.getNode("A::B::C::D::E")->getType()); + } + + void test_set_access_on_child_edge_via_node() + { + TestGraph graph; + Node* b = graph.createNodeHierarchy("A::B"); + b->getMemberEdge()->setAccess(Edge::ACCESS_PRIVATE); + TS_ASSERT_EQUALS(Edge::ACCESS_PRIVATE, b->getMemberEdge()->getAccess()); + } + + void test_visit_each_token_on_graph() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* b = graph.createNodeHierarchy("B"); + Node* c = graph.createNodeHierarchy("C"); + Edge* e = graph.createEdge(Edge::EDGE_TYPE_OF, a, b); + Edge* f = graph.createEdge(Edge::EDGE_TYPE_OF, b, c); + + unsigned long idSum = a->getId() + b->getId() + c->getId() + e->getId() + f->getId(); + unsigned long checkSum = 0; + + graph.forEachToken( + [&checkSum](Token* t) + { + checkSum += t->getId(); + } + ); + + TS_ASSERT_EQUALS(idSum, checkSum); + } + + void test_visit_each_edge_of_type_on_node() + { + TestGraph graph; + Node* a = graph.createNodeHierarchy("A"); + Node* ab = graph.createNodeHierarchy("A::B"); + Node* ac = graph.createNodeHierarchy("A::C"); + + graph.createEdge(Edge::EDGE_TYPE_OF, a, ab); + graph.createEdge(Edge::EDGE_CALL, a, ac); + + unsigned int sum = 0; + a->forEachEdgeOfType(Edge::EDGE_MEMBER, [&sum](Edge* e) + { + TS_ASSERT_EQUALS(Edge::EDGE_MEMBER, e->getType()); + sum++; + }); + + TS_ASSERT_EQUALS(sum, 2); + } + + void test_creating_plain_copy_of_graph_part() + { + TestGraph graph; + Node* b = graph.createNodeHierarchy("A::B"); + b->getMemberEdge()->setAccess(Edge::ACCESS_PUBLIC); + graph.createNodeHierarchy("A::B::C"); + Node* d = graph.createNodeHierarchy("D"); + Node* e = graph.createNodeHierarchy("E"); + graph.createEdge(Edge::EDGE_TYPE_OF, d, b); + graph.createEdge(Edge::EDGE_TYPE_OF, d, e); + + TestGraph plainGraph; + Node* x = graph.getNode("A::B"); + plainGraph.addNodeAsPlainCopy(x); + + x->forEachEdge( + [&plainGraph](Edge* e) + { + plainGraph.addNodeAsPlainCopy(e->getFrom()); + plainGraph.addNodeAsPlainCopy(e->getTo()); + plainGraph.addEdgeAsPlainCopy(e); + } + ); + + TS_ASSERT_EQUALS(4, plainGraph.getNodeCount()); + TS_ASSERT_EQUALS(3, plainGraph.getEdgeCount()); + + TS_ASSERT(plainGraph.getNode("A")); + TS_ASSERT(plainGraph.getNode("A::B")); + TS_ASSERT(plainGraph.getNode("A::B::C")); + TS_ASSERT(plainGraph.getNode("D")); + TS_ASSERT(!plainGraph.getNode("E")); + TS_ASSERT_EQUALS(Edge::ACCESS_PUBLIC, plainGraph.getNode("A::B")->getMemberEdge()->getAccess()); + } + +private: + class TestGraph: public Graph + { + public: + size_t getNodeCount() const + { + return getNodes().size(); + } + + size_t getEdgeCount() const + { + return getEdges().size(); + } + }; + +}; diff --git a/src/test/UtilityStringTestSuite.h b/src/test/UtilityStringTestSuite.h new file mode 100644 index 00000000..ca502c75 --- /dev/null +++ b/src/test/UtilityStringTestSuite.h @@ -0,0 +1,53 @@ +#include "cxxtest/TestSuite.h" + +#include "utility/utilityString.h" + +class UtilityStringTestSuite : public CxxTest::TestSuite +{ +public: + void test_split_with_char_delimiter() + { + std::vector result = utility::split("A,B,C", ','); + + TS_ASSERT_EQUALS(result.size(), 3); + TS_ASSERT_EQUALS(result[0], "A"); + TS_ASSERT_EQUALS(result[1], "B"); + TS_ASSERT_EQUALS(result[2], "C"); + } + + void test_split_with_string_delimiter() + { + std::vector result = utility::split("A->B>C", "->"); + + TS_ASSERT_EQUALS(result.size(), 2); + TS_ASSERT_EQUALS(result[0], "A"); + TS_ASSERT_EQUALS(result[1], "B>C"); + } + + void test_split_on_empty_string() + { + std::vector result = utility::split("", "->"); + + TS_ASSERT_EQUALS(result.size(), 1); + TS_ASSERT_EQUALS(result[0], ""); + } + + void test_split_with_unused_delimiter() + { + std::vector result = utility::split("A:B:C", ";"); + + TS_ASSERT_EQUALS(result.size(), 1); + TS_ASSERT_EQUALS(result[0], "A:B:C"); + } + + void test_split_with_delimiters_next_to_each() + { + std::vector result = utility::split("A::B:C", ':'); + + TS_ASSERT_EQUALS(result.size(), 4); + TS_ASSERT_EQUALS(result[0], "A"); + TS_ASSERT_EQUALS(result[1], ""); + TS_ASSERT_EQUALS(result[2], "B"); + TS_ASSERT_EQUALS(result[3], "C"); + } +};