NAP
objectgraph.h
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4 
5 #pragma once
6 
7 namespace nap
8 {
19  template<typename ITEM>
20  class ObjectGraph final
21  {
22  public:
23  struct Node;
24 
25  using ItemList = std::vector<typename ITEM::Type>;
26  using ItemCreationFunction = std::function<ITEM(typename ITEM::Type)>;
27 
31  struct Edge
32  {
33  Node* mSource = nullptr; // Link source
34  Node* mDest = nullptr; // Link target
35  };
36 
37  /*
38  * Represents a node in the ObjectGraph, containing an ITEM.
39  */
40  struct Node
41  {
42  int mDepth = -1; // Depth of the node is calculated during build, it represents at what level from the root this node is.
43  ITEM mItem; // User data per Node
44  std::vector<Edge*> mIncomingEdges; // List of incoming edges
45  std::vector<Edge*> mOutgoingEdges; // List of outgoing edges
46  };
47 
48  /*
49  * Rebuilds the object graph. If building fails, return false. If the object graph was previously built, this will also clear the previous state
50  * @param objectList : list of objects to build the graph from.
51  * @param errorState: if false is returned, contains error information.
52  */
53  bool build(const ItemList& objectList, const ItemCreationFunction& creationFunction, utility::ErrorState& errorState)
54  {
55  // Traverse graph and build nodes and edges
56  for (typename ITEM::Type object : objectList)
57  if (getOrCreateItemNode(creationFunction(object)) == nullptr)
58  return false;
59 
60  return rebuild(errorState);
61  }
62 
63  /*
64  * Rebuilds the object graph. If building fails, return false. If the object graph was previously built, this will not clear the previous state.
65  */
66  bool rebuild(utility::ErrorState& errorState)
67  {
68  mEdges.clear();
69  for (auto& kvp : mNodes)
70  {
71  kvp.second->mIncomingEdges.clear();
72  kvp.second->mOutgoingEdges.clear();
73  }
74 
75  for (auto& kvp : mNodes)
76  {
77  std::vector<ITEM> pointees;
78  if (!kvp.second->mItem.getPointees(pointees, errorState))
79  return false;
80 
81  for (ITEM& pointee_node : pointees)
82  {
83  Edge* edge = new Edge();
84  edge->mSource = kvp.second.get();
85  edge->mDest = getOrCreateItemNode(pointee_node);
86  edge->mDest->mIncomingEdges.push_back(edge);
87  edge->mSource->mOutgoingEdges.push_back(edge);
88  mEdges.push_back(std::unique_ptr<Edge>(edge));
89  }
90  }
91 
92  // Assign graph depth
93  std::vector<Node*> root_nodes;
94  for (auto& kvp : mNodes)
95  {
96  Node* node = kvp.second.get();
97  node->mDepth = -1;
98  if (node->mIncomingEdges.empty())
99  root_nodes.push_back(node);
100  }
101 
102  for (Node* root_node : root_nodes)
103  assignDepthRecursive(root_node, 0);
104 
105  return true;
106  }
107 
108  /*
109  * Visit all nodes in the graph and call the visitor function
110  *
111  * @param visitor The visitor to be called for each node in the graph
112  */
113  void visitNodes(const std::function<void(const Node&)>& visitor) const
114  {
115  std::vector<Node*> result;
116  for (auto& kvp : mNodes)
117  visitor(*kvp.second);
118  }
119 
120  /*
121  * Returns object graph node.
122  */
123  Node* findNode(const std::string& ID) const
124  {
125  typename NodeMap::const_iterator iter = mNodes.find(ID);
126  if (iter == mNodes.end())
127  return nullptr;
128 
129  return iter->second.get();
130  }
131 
132  /*
133  * Returns all nodes that are processed during build, sorted on graph depth.
134  */
135  const std::vector<Node*> getSortedNodes() const
136  {
137  std::vector<Node*> sorted_nodes;
138  sorted_nodes.reserve(mNodes.size());
139  for (auto& kvp : mNodes)
140  sorted_nodes.push_back(kvp.second.get());
141 
142  std::sort(sorted_nodes.begin(), sorted_nodes.end(), [](Node* nodeA, Node* nodeB) { return nodeA->mDepth > nodeB->mDepth; });
143 
144  return sorted_nodes;
145  }
146 
147 
153  static void addIncomingObjectsRecursive(Node* node, std::set<Node*>& incomingObjects)
154  {
155  if (incomingObjects.find(node) != incomingObjects.end())
156  return;
157 
158  incomingObjects.insert(node);
159 
160  for (Edge* incoming_edge : node->mIncomingEdges)
161  addIncomingObjectsRecursive(incoming_edge->mSource, incomingObjects);
162  }
163 
164  private:
165 
184  void assignDepthRecursive(Node* node, int depth)
185  {
186  // The following is both a test for 'is visited' and 'should we revisit':
187  if (node->mDepth >= depth)
188  return;
189 
190  for (Edge* outgoing_edge : node->mOutgoingEdges)
191  assignDepthRecursive(outgoing_edge->mDest, depth + 1);
192 
193  node->mDepth = depth;
194  }
195 
196 
197  Node* getOrCreateItemNode(const ITEM& item)
198  {
199  Node* result = nullptr;
200  typename NodeMap::iterator iter = mNodes.find(item.getID());
201  if (iter == mNodes.end())
202  {
203  auto node = std::make_unique<Node>();
204  node->mItem = item;
205  result = node.get();
206  mNodes.insert(std::make_pair(item.getID(), std::move(node)));
207  }
208  else
209  {
210  result = iter->second.get();
211  }
212 
213  return result;
214  }
215 
216  private:
217  using NodeMap = std::map<std::string, std::unique_ptr<Node>>;
218  NodeMap mNodes; // All nodes in the graph, mapped from ID to node
219  std::vector<std::unique_ptr<Edge>> mEdges; // All edges in the graph
220  };
221 }
nap::ObjectGraph::Node::mItem
ITEM mItem
Definition: objectgraph.h:43
nap::ObjectGraph::build
bool build(const ItemList &objectList, const ItemCreationFunction &creationFunction, utility::ErrorState &errorState)
Definition: objectgraph.h:53
nap::ObjectGraph::Node::mIncomingEdges
std::vector< Edge * > mIncomingEdges
Definition: objectgraph.h:44
nap::ObjectGraph::ItemCreationFunction
std::function< ITEM(typename ITEM::Type)> ItemCreationFunction
Definition: objectgraph.h:26
nap::utility::ErrorState
Definition: errorstate.h:19
nap::ObjectGraph
Definition: objectgraph.h:20
nap::ObjectGraph::addIncomingObjectsRecursive
static void addIncomingObjectsRecursive(Node *node, std::set< Node * > &incomingObjects)
Definition: objectgraph.h:153
nap::ObjectGraph::findNode
Node * findNode(const std::string &ID) const
Definition: objectgraph.h:123
nap::ObjectGraph::rebuild
bool rebuild(utility::ErrorState &errorState)
Definition: objectgraph.h:66
nap::ObjectGraph::Node::mDepth
int mDepth
Definition: objectgraph.h:42
nap::ObjectGraph::ItemList
std::vector< typename ITEM::Type > ItemList
Definition: objectgraph.h:25
nap
Definition: templateapp.h:17
nap::ObjectGraph::Node
Definition: objectgraph.h:40
nap::ObjectGraph::visitNodes
void visitNodes(const std::function< void(const Node &)> &visitor) const
Definition: objectgraph.h:113
nap::ObjectGraph::Edge::mDest
Node * mDest
Definition: objectgraph.h:34
nap::ObjectGraph::getSortedNodes
const std::vector< Node * > getSortedNodes() const
Definition: objectgraph.h:135
nap::ObjectGraph::Edge
Definition: objectgraph.h:31
nap::ObjectGraph::Edge::mSource
Node * mSource
Definition: objectgraph.h:33
nap::ObjectGraph::Node::mOutgoingEdges
std::vector< Edge * > mOutgoingEdges
Definition: objectgraph.h:45