|
| 1 | +#include "stdafx.h" |
| 2 | +#include "ClassDependencyGraph.h" |
| 3 | + |
| 4 | + |
| 5 | +bool DependencyNode::AddInEdge(const DependencyNode* parent, const DependencyType edgeType) { |
| 6 | + DependencyEdge newEdge(parent, edgeType); |
| 7 | + // |
| 8 | + // We do not allow parallel edges, so we check to make sure this edge doesn't already exist. |
| 9 | + // We COULD have used a map for the dependencies container. But, most classes probably won't |
| 10 | + // have a ton of dependencies, so a vector makes sense for our more average case. |
| 11 | + // |
| 12 | + for (auto edge : this->inEdges) { |
| 13 | + if (edge == newEdge) { |
| 14 | + return false; |
| 15 | + } |
| 16 | + } |
| 17 | + this->inEdges.push_back(newEdge); |
| 18 | + return true; |
| 19 | +} |
| 20 | + |
| 21 | +const std::vector<DependencyEdge>* DependencyNode::GetDependencies() const { |
| 22 | + return &this->dependencies; |
| 23 | +} |
| 24 | + |
| 25 | +bool DependencyNode::AddDependency(const DependencyNode* dep, const DependencyType edgeType) { |
| 26 | + DependencyEdge newEdge(dep, edgeType); |
| 27 | + // |
| 28 | + // We do not allow parallel edges, so we check to make sure this edge doesn't already exist. |
| 29 | + // We COULD have used a map for the dependencies container. But, most classes probably won't |
| 30 | + // have a ton of dependencies, so a vector makes sense for our more average case. |
| 31 | + // |
| 32 | + for (auto edge : this->dependencies) { |
| 33 | + if (edge == newEdge) { |
| 34 | + return false; |
| 35 | + } |
| 36 | + } |
| 37 | + this->dependencies.push_back(newEdge); |
| 38 | + return true; |
| 39 | +} |
| 40 | + |
| 41 | + |
| 42 | +std::vector<DependencyNode*> ClassDependencyGraph::GetLeafNodes() { |
| 43 | + std::vector<DependencyNode*> result; |
| 44 | + for (auto it = nodes.begin(); it != nodes.end(); it++) { |
| 45 | + if (it->second.GetInEdges()->size() == 0) { |
| 46 | + result.push_back(&it->second); |
| 47 | + } |
| 48 | + } |
| 49 | + return result; |
| 50 | +} |
| 51 | + |
| 52 | +std::vector<DependencyNode*> ClassDependencyGraph::GetInstanceNodes() { |
| 53 | + std::vector<DependencyNode*> result; |
| 54 | + for (auto it = nodes.begin(); it != nodes.end(); it++) { |
| 55 | + bool hasOutInstanceEdge = false; |
| 56 | + |
| 57 | + for (auto inEdge : *it->second.GetInEdges()) { |
| 58 | + if (inEdge.edgeType == DependencyType::INSTANCE) { |
| 59 | + hasOutInstanceEdge = true; |
| 60 | + break; |
| 61 | + } |
| 62 | + } |
| 63 | + if (hasOutInstanceEdge) { |
| 64 | + result.push_back(&it->second); |
| 65 | + } |
| 66 | + } |
| 67 | + return result; |
| 68 | +} |
| 69 | + |
| 70 | +int ClassDependencyGraph::ProcessDependencies(DependencyEdge* edge, std::map<const DependencyNode*, NodeGenerationStatus>& nodeStatus, std::set<const CNodeClass*>& forwardDeclarations, std::vector<const CNodeClass*> &classDefinitions) { |
| 71 | + int classesAdded = 0; |
| 72 | + NodeGenerationStatus nodeGenStatus; |
| 73 | + const DependencyNode* node = edge->dependency; |
| 74 | + if (nodeStatus.find(node) == nodeStatus.end()) { |
| 75 | + nodeStatus[node] = NodeGenerationStatus::UNPROCESSED; |
| 76 | + } |
| 77 | + nodeGenStatus = nodeStatus[node]; |
| 78 | + // We have to treat instanced nodes differently, since they have 'hard' dependencies |
| 79 | + // on their parent classes. If we come across an instanced node through a pointer, |
| 80 | + // we will forward declare it because if we try to process it like normal we may end |
| 81 | + // up with a back edge to its parent that will break everything. |
| 82 | + // |
| 83 | + // If we come upon an instanced node througn an instance, we process it like normal. |
| 84 | + if (nodeGenStatus == NodeGenerationStatus::INSTANCED) { |
| 85 | + if (edge->edgeType == DependencyType::POINTER) { |
| 86 | + forwardDeclarations.insert(node->GetCClass()); |
| 87 | + return 0; |
| 88 | + } |
| 89 | + else if (edge->edgeType == DependencyType::INSTANCE) { |
| 90 | + // Change nodeGenStatys to trigger the actual processing of the node |
| 91 | + nodeGenStatus = NodeGenerationStatus::UNPROCESSED; |
| 92 | + } |
| 93 | + } |
| 94 | + if (nodeGenStatus == NodeGenerationStatus::PROCESSED) { |
| 95 | + // The node is already written to the output, no further processing needed |
| 96 | + return classesAdded; |
| 97 | + } |
| 98 | + else if (nodeGenStatus == NodeGenerationStatus::PROCESSING) { |
| 99 | + // We've hit a back edge. Since we start from leaf nodes, this dependency cannot |
| 100 | + // possibly be an instance dependency, it must be a pointer dependency. Thus, a |
| 101 | + // forward declaration is enough to handle this case. |
| 102 | + ASSERT(edge->edgeType == DependencyType::POINTER); |
| 103 | + forwardDeclarations.insert(node->GetCClass()); |
| 104 | + // While it isn't actually finished processing, if we leave it as PROCESSING we'll |
| 105 | + // get a new forward declaration every time we hit this node. If it's currently |
| 106 | + // processing, somewhere up the call stack is the actual processing of this node |
| 107 | + // so on return it'll get finished up. |
| 108 | + nodeStatus[node] = NodeGenerationStatus::PROCESSED; |
| 109 | + } |
| 110 | + else { |
| 111 | + // Case where processing has yet to begin. We need to recursively process all dependencies, |
| 112 | + // then add this class to the ordered list of definitions. |
| 113 | + nodeStatus[node] = NodeGenerationStatus::PROCESSING; |
| 114 | + for (auto dep : *node->GetDependencies()) { |
| 115 | + if (dep.edgeType == DependencyType::INSTANCE) |
| 116 | + classesAdded += ProcessDependencies(&dep, nodeStatus, forwardDeclarations, classDefinitions); |
| 117 | + } |
| 118 | + for (auto dep : *node->GetDependencies()) { |
| 119 | + if (dep.edgeType == DependencyType::POINTER) |
| 120 | + classesAdded += ProcessDependencies(&dep, nodeStatus, forwardDeclarations, classDefinitions); |
| 121 | + } |
| 122 | + classDefinitions.push_back(node->GetCClass()); |
| 123 | + nodeStatus[node] = NodeGenerationStatus::PROCESSED; |
| 124 | + classesAdded += 1; |
| 125 | + } |
| 126 | + return classesAdded; |
| 127 | +} |
| 128 | + |
| 129 | +std::string ClassDependencyGraph::ToDot(std::string graphLabel) { |
| 130 | + std::stringstream stream; |
| 131 | + stream << "digraph class_dependency {"; |
| 132 | + for (auto node : this->nodes) { |
| 133 | + for (auto edge : *node.second.GetDependencies()) { |
| 134 | + stream << "\""; |
| 135 | + stream << CT2CA(node.first->GetName()); |
| 136 | + stream << "\""; |
| 137 | + stream << " -> "; |
| 138 | + stream << "\""; |
| 139 | + stream << CT2CA(edge.dependency->GetCClass()->GetName()); |
| 140 | + stream << "\""; |
| 141 | + if (edge.edgeType == DependencyType::POINTER) { |
| 142 | + stream << " [style=dotted]"; |
| 143 | + } |
| 144 | + stream << ";"; |
| 145 | + stream << "\n"; |
| 146 | + } |
| 147 | + } |
| 148 | + stream << "}"; |
| 149 | + return stream.str(); |
| 150 | +} |
| 151 | + |
| 152 | +int ClassDependencyGraph::OrderClassesForGeneration(std::set<const CNodeClass*>& forwardDeclarations, std::vector<const CNodeClass*>& classDefinitions) { |
| 153 | + int classesAdded = 0; |
| 154 | + std::map<const DependencyNode*, NodeGenerationStatus> nodeStatus; |
| 155 | + std::vector<DependencyNode*> leafNodes = this->GetLeafNodes(); |
| 156 | + std::vector<DependencyNode*> instancedNodes = this->GetInstanceNodes(); |
| 157 | + |
| 158 | + for (auto node : instancedNodes) { |
| 159 | + nodeStatus[node] = NodeGenerationStatus::INSTANCED; |
| 160 | + } |
| 161 | + |
| 162 | + for (auto leaf : leafNodes) { |
| 163 | + for (auto dep : *leaf->GetDependencies()) { |
| 164 | + classesAdded += ProcessDependencies(&dep, nodeStatus, forwardDeclarations, classDefinitions); |
| 165 | + } |
| 166 | + classDefinitions.push_back(leaf->GetCClass()); |
| 167 | + classesAdded += 1; |
| 168 | + } |
| 169 | + |
| 170 | + return classesAdded; |
| 171 | + } |
| 172 | + |
0 commit comments