data: added Graph data structure for storing code semantics in Storage

This change adds graph data structures Token, Node, Edge and Graph for holding code semantics in the Storage and fills
them with the parsed data. Things to note:
* Token is the base for the classes Edge and Node and holds a unique id.
* Graph is the owner of all Nodes and Edges.
* A Node saves the Edges it is endpoint of.
* An Edge saves the Nodes that are endpoints.
* Nodes and Edges are not subclassed. The different Node and Edge types are distinguished by the enums NodeType and
  EdgeType. Type specific fields are therefore available for all types, a check when assigning them prohibits faulty
  usage.
* Storage uses the Graph::getNodeHierarchy method, which is not strict about knowing type names beforehand. E.g. if A::B
  is passed in and A doesn't exist so far, then node A gets created as NODE_UNDEFINED with B as it's child.
* The Graph methods addNodeAsPlainCopy and addEdgeAsPlainCopy can be used to create a sub-graph without reference to the
  original, but with the same ids.

review id = 19

fortune cookie message = Your cheerful outlook is one of your best assets.
This commit is contained in:
Eberhard Graether
2014-06-30 14:08:01 +02:00
parent 73b74cb618
commit 5ceb7a4b43
28 changed files with 1507 additions and 101 deletions
+146
View File
@@ -0,0 +1,146 @@
#include "data/graph/Edge.h"
#include <sstream>
#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> 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> 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;
}
+71
View File
@@ -0,0 +1,71 @@
#ifndef EDGE_H
#define EDGE_H
#include <memory>
#include <string>
#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<Edge> 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
+341
View File
@@ -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<std::shared_ptr<Node> >::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<std::shared_ptr<Edge> >::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<bool(Node*)> func) const
{
std::vector<std::shared_ptr<Node> >::const_iterator it = find_if(m_nodes.begin(), m_nodes.end(),
[&func](const std::shared_ptr<Node>& n)
{
return func(n.get());
}
);
if (it != m_nodes.end())
{
return it->get();
}
return nullptr;
}
Edge* Graph::findEdge(std::function<bool(Edge*)> func) const
{
std::vector<std::shared_ptr<Edge> >::const_iterator it = find_if(m_edges.begin(), m_edges.end(),
[func](const std::shared_ptr<Edge>& e)
{
return func(e.get());
}
);
if (it != m_edges.end())
{
return it->get();
}
return nullptr;
}
Token* Graph::findToken(std::function<bool(Token*)> 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<void(Node*)> func) const
{
for (const std::shared_ptr<Node>& node : m_nodes)
{
func(node.get());
}
}
void Graph::forEachEdge(std::function<void(Edge*)> func) const
{
for (const std::shared_ptr<Edge>& edge : m_edges)
{
func(edge.get());
}
}
void Graph::forEachToken(std::function<void(Token*)> func) const
{
forEachNode(func);
forEachEdge(func);
}
const std::vector<std::shared_ptr<Node> >& Graph::getNodes() const
{
return m_nodes;
}
const std::vector<std::shared_ptr<Edge> >& Graph::getEdges() const
{
return m_edges;
}
Node* Graph::addNodeAsPlainCopy(Node* node)
{
Node* n = getNodeById(node->getId());
if (n)
{
return n;
}
std::shared_ptr<Node> 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<Edge> 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<std::string> names = utility::split(fullName, DELIMITER);
std::string name;
for (std::vector<std::string>::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<Node> childNodePtr = std::make_shared<Node>(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<Edge> edgePtr = std::make_shared<Edge>(type, from, to);
m_edges.push_back(edgePtr);
return edgePtr.get();
}
void Graph::removeEdgeInternal(Edge* edge)
{
std::vector<std::shared_ptr<Edge> >::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;
}
+62
View File
@@ -0,0 +1,62 @@
#ifndef GRAPH_H
#define GRAPH_H
#include <memory>
#include <vector>
#include <ostream>
#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<bool(Node*)> func) const;
Edge* findEdge(std::function<bool(Edge*)> func) const;
Token* findToken(std::function<bool(Token*)> func) const;
void forEachNode(std::function<void(Node*)> func) const;
void forEachEdge(std::function<void(Edge*)> func) const;
void forEachToken(std::function<void(Token*)> func) const;
Node* addNodeAsPlainCopy(Node* node);
Edge* addEdgeAsPlainCopy(Edge* edge);
protected:
const std::vector<std::shared_ptr<Node> >& getNodes() const;
const std::vector<std::shared_ptr<Edge> >& 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<std::shared_ptr<Node> > m_nodes;
std::vector<std::shared_ptr<Edge> > m_edges;
friend std::ostream& operator<<(std::ostream& ostream, const Graph& graph);
};
std::ostream& operator<<(std::ostream& ostream, const Graph& graph);
#endif // GRAPH_H
+248
View File
@@ -0,0 +1,248 @@
#include "data/graph/Node.h"
#include <sstream>
#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> Node::createPlainCopy() const
{
std::shared_ptr<Node> 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<Edge*>& 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<bool(Edge*)> func) const
{
std::vector<Edge*>::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<bool(Edge*)> func) const
{
std::vector<Edge*>::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<void(Edge*)> func) const
{
for_each(m_edges.begin(), m_edges.end(), func);
}
void Node::forEachEdgeOfType(Edge::EdgeType type, std::function<void(Edge*)> 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;
}
+83
View File
@@ -0,0 +1,83 @@
#ifndef NODE_H
#define NODE_H
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
#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<Node> createPlainCopy() const;
NodeType getType() const;
void setType(NodeType type);
const std::string& getName() const;
const std::vector<Edge*>& getEdges() const;
void addEdge(Edge* edge);
void removeEdge(Edge* edge);
Edge* getMemberEdge() const;
Edge* findEdge(std::function<bool(Edge*)> func) const;
Edge* findEdgeOfType(Edge::EdgeType type, std::function<bool(Edge*)> func) const;
void forEachEdge(std::function<void(Edge*)> func) const;
void forEachEdgeOfType(Edge::EdgeType type, std::function<void(Edge*)> 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<Edge*> 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
+22
View File
@@ -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)
{
}
+30
View File
@@ -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