1 /******************************************************************************
2 *
3 * Copyright (C) 1997-2019 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16 #include <sstream>
17
18 #include "dotgfxhierarchytable.h"
19 #include "language.h"
20 #include "util.h"
21 #include "message.h"
22 #include "doxygen.h"
23 #include "classlist.h"
24 #include "dir.h"
25 #include "vhdldocgen.h"
26
getBaseName() const27 QCString DotGfxHierarchyTable::getBaseName() const
28 {
29 QCString baseName;
30 if (m_prefix.isEmpty())
31 baseName.sprintf("inherit_graph_%d", m_graphId);
32 else
33 baseName.sprintf("%sinherit_graph_%d",qPrint(m_prefix), m_graphId);
34 return baseName;
35 }
36
computeTheGraph()37 void DotGfxHierarchyTable::computeTheGraph()
38 {
39 TextStream md5stream;
40 writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy());
41 md5stream << " rankdir=\"LR\";\n";
42 for (auto node : m_rootNodes)
43 {
44 if (node->subgraphId()==m_rootSubgraphNode->subgraphId())
45 {
46 node->clearWriteFlag();
47 }
48 }
49 for (auto node : m_rootNodes)
50 {
51 if (node->subgraphId()==m_rootSubgraphNode->subgraphId())
52 {
53 node->write(md5stream,Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE);
54 }
55 }
56 writeGraphFooter(md5stream);
57 m_theGraph = md5stream.str();
58 }
59
getMapLabel() const60 QCString DotGfxHierarchyTable::getMapLabel() const
61 {
62 return escapeCharsInString(m_rootSubgraphNode->label(),FALSE);
63 }
64
createGraph(DotNode * n,TextStream & out,const QCString & path,const QCString & fileName,int id)65 void DotGfxHierarchyTable::createGraph(DotNode *n,TextStream &out,
66 const QCString &path,const QCString &fileName,int id)
67 {
68 m_rootSubgraphNode = n;
69 m_graphId = id;
70 m_noDivTag = TRUE;
71 m_zoomable = FALSE;
72 DotGraph::writeGraph(out, GOF_BITMAP, EOF_Html, path, fileName, "", TRUE, 0);
73 }
74
writeGraph(TextStream & out,const QCString & path,const QCString & fileName)75 void DotGfxHierarchyTable::writeGraph(TextStream &out,
76 const QCString &path,const QCString &fileName)
77 {
78 //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name);
79 //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count());
80
81 if (m_rootSubgraphs.empty()) return;
82
83 Dir d(path.str());
84 // store the original directory
85 if (!d.exists())
86 {
87 term("Output dir %s does not exist!\n",qPrint(path));
88 }
89
90 // put each connected subgraph of the hierarchy in a row of the HTML output
91 out << "<table border=\"0\" cellspacing=\"10\" cellpadding=\"0\">\n";
92
93 int count=0;
94 std::sort(m_rootSubgraphs.begin(),m_rootSubgraphs.end(),
95 [](auto n1,auto n2) { return qstricmp(n1->label(),n2->label())<0; });
96 for (auto n : m_rootSubgraphs)
97 {
98 out << "<tr><td>";
99 createGraph(n,out,path,fileName,count++);
100 out << "</td></tr>\n";
101 }
102 out << "</table>\n";
103 }
104
addHierarchy(DotNode * n,const ClassDef * cd,ClassDefSet & visitedClasses)105 void DotGfxHierarchyTable::addHierarchy(DotNode *n,const ClassDef *cd,ClassDefSet &visitedClasses)
106 {
107 //printf("addHierarchy '%s' baseClasses=%d\n",qPrint(cd->name()),cd->baseClasses()->count());
108 for (const auto &bcd : cd->subClasses())
109 {
110 ClassDef *bClass=bcd.classDef;
111 //printf(" Trying sub class='%s' usedNodes=%d\n",qPrint(bClass->name()),m_usedNodes->count());
112 if (bClass && bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses()))
113 {
114 auto it = m_usedNodes.find(bClass->name().str());
115 //printf(" Node '%s' Found visible class='%s'\n",qPrint(n->label()),
116 // qPrint(bClass->name()));
117 DotNode *root = 0;
118 if (it!=m_usedNodes.end()) // node already present
119 {
120 const auto &bn = it->second;
121 root = bn.get();
122 const auto &children = n->children();
123 auto child_it = std::find(children.begin(),children.end(),bn.get());
124 if (child_it==children.end()) // no arrow yet
125 {
126 n->addChild(bn.get(),bcd.prot);
127 bn->addParent(n);
128 //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n",
129 // qPrint(n->label()),
130 // qPrint(bn->label()),
131 // bn->children() ? bn->children()->count() : 0,
132 // bn->parents() ? bn->parents()->count() : 0
133 // );
134 }
135 //else
136 //{
137 // printf(" Class already has an arrow!\n");
138 //}
139 }
140 else
141 {
142 QCString tmp_url="";
143 if (bClass->isLinkable() && !bClass->isHidden())
144 {
145 tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase();
146 if (!bClass->anchor().isEmpty())
147 {
148 tmp_url+="#"+bClass->anchor();
149 }
150 }
151 QCString tooltip = bClass->briefDescriptionAsTooltip();
152 auto bn = std::make_unique<DotNode>(getNextNodeNumber(),
153 bClass->displayName(),
154 tooltip,
155 tmp_url
156 );
157 n->addChild(bn.get(),bcd.prot);
158 bn->addParent(n);
159 root = bn.get();
160 //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n",
161 // qPrint(n->label()),
162 // qPrint(bn->label()),
163 // bn->children() ? bn->children()->count() : 0,
164 // bn->parents() ? bn->parents()->count() : 0
165 // );
166 //printf(" inserting %s (%p)\n",qPrint(bClass->name()),bn);
167 m_usedNodes.insert(std::make_pair(bClass->name().str(),std::move(bn))); // add node to the used list
168 }
169 if (visitedClasses.find(bClass)==visitedClasses.end() && !bClass->subClasses().empty())
170 {
171 visitedClasses.insert(bClass);
172 addHierarchy(root,bClass,visitedClasses);
173 }
174 }
175 }
176 //printf("end addHierarchy\n");
177 }
178
addClassList(const ClassLinkedMap & cl,ClassDefSet & visitedClasses)179 void DotGfxHierarchyTable::addClassList(const ClassLinkedMap &cl,ClassDefSet &visitedClasses)
180 {
181 for (const auto &cd : cl)
182 {
183 //printf("Trying %s subClasses=%d\n",qPrint(cd->name()),cd->subClasses()->count());
184 if (cd->getLanguage()==SrcLangExt_VHDL &&
185 (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS
186 )
187 {
188 continue;
189 }
190 if (Config_getBool(OPTIMIZE_OUTPUT_SLICE) && cd->compoundType() != m_classType)
191 {
192 continue;
193 }
194 if (!hasVisibleRoot(cd->baseClasses()) &&
195 cd->isVisibleInHierarchy()
196 ) // root node in the forest
197 {
198 QCString tmp_url="";
199 if (cd->isLinkable() && !cd->isHidden())
200 {
201 tmp_url=cd->getReference()+"$"+cd->getOutputFileBase();
202 if (!cd->anchor().isEmpty())
203 {
204 tmp_url+="#"+cd->anchor();
205 }
206 }
207 //printf("Inserting root class %s\n",qPrint(cd->name()));
208 QCString tooltip = cd->briefDescriptionAsTooltip();
209 auto n = std::make_unique<DotNode>(getNextNodeNumber(),
210 cd->displayName(),
211 tooltip,
212 tmp_url);
213 DotNode *root = n.get();
214
215 m_usedNodes.insert(std::make_pair(cd->name().str(),std::move(n)));
216 m_rootNodes.push_back(root);
217 if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->subClasses().empty())
218 {
219 addHierarchy(root,cd.get(),visitedClasses);
220 visitedClasses.insert(cd.get());
221 }
222 }
223 }
224 }
225
DotGfxHierarchyTable(const QCString & prefix,ClassDef::CompoundType ct)226 DotGfxHierarchyTable::DotGfxHierarchyTable(const QCString &prefix,ClassDef::CompoundType ct)
227 : m_prefix(prefix)
228 , m_classType(ct)
229 {
230 // build a graph with each class as a node and the inheritance relations
231 // as edges
232 ClassDefSet visitedClasses;
233 addClassList(*Doxygen::classLinkedMap,visitedClasses);
234 addClassList(*Doxygen::hiddenClassLinkedMap,visitedClasses);
235 // m_usedNodes now contains all nodes in the graph
236
237 // color the graph into a set of independent subgraphs
238 bool done=FALSE;
239 int curColor=0;
240 while (!done) // there are still nodes to color
241 {
242 done=TRUE; // we are done unless there are still uncolored nodes
243 for (auto n : m_rootNodes)
244 {
245 if (n->subgraphId()==-1) // not yet colored
246 {
247 //printf("Starting at node %s (%p): %d\n",qPrint(n->label()),n,curColor);
248 done=FALSE; // still uncolored nodes
249 n->setSubgraphId(curColor);
250 n->markAsVisible();
251 n->colorConnectedNodes(curColor);
252 curColor++;
253 m_rootSubgraphs.push_back(n);
254 }
255 }
256 }
257
258 //printf("Number of independent subgraphs: %d\n",curColor);
259 for (auto n : m_rootSubgraphs)
260 {
261 //printf("Node %s color=%d (c=%d,p=%d)\n",
262 // qPrint(n->label()),n->m_subgraphId,
263 // n->children()?n->children()->count():0,
264 // n->parents()?n->parents()->count():0);
265 int number=0;
266 n->renumberNodes(number);
267 }
268 }
269
270