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