/****************************************************************************** * * Copyright (C) 1997-2019 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ #include #include "dotgfxhierarchytable.h" #include "language.h" #include "util.h" #include "message.h" #include "doxygen.h" #include "classlist.h" #include "dir.h" #include "vhdldocgen.h" QCString DotGfxHierarchyTable::getBaseName() const { QCString baseName; if (m_prefix.isEmpty()) baseName.sprintf("inherit_graph_%d", m_graphId); else baseName.sprintf("%sinherit_graph_%d",qPrint(m_prefix), m_graphId); return baseName; } void DotGfxHierarchyTable::computeTheGraph() { TextStream md5stream; writeGraphHeader(md5stream,theTranslator->trGraphicalHierarchy()); md5stream << " rankdir=\"LR\";\n"; for (auto node : m_rootNodes) { if (node->subgraphId()==m_rootSubgraphNode->subgraphId()) { node->clearWriteFlag(); } } for (auto node : m_rootNodes) { if (node->subgraphId()==m_rootSubgraphNode->subgraphId()) { node->write(md5stream,Hierarchy,GOF_BITMAP,FALSE,TRUE,TRUE); } } writeGraphFooter(md5stream); m_theGraph = md5stream.str(); } QCString DotGfxHierarchyTable::getMapLabel() const { return escapeCharsInString(m_rootSubgraphNode->label(),FALSE); } void DotGfxHierarchyTable::createGraph(DotNode *n,TextStream &out, const QCString &path,const QCString &fileName,int id) { m_rootSubgraphNode = n; m_graphId = id; m_noDivTag = TRUE; m_zoomable = FALSE; DotGraph::writeGraph(out, GOF_BITMAP, EOF_Html, path, fileName, "", TRUE, 0); } void DotGfxHierarchyTable::writeGraph(TextStream &out, const QCString &path,const QCString &fileName) { //printf("DotGfxHierarchyTable::writeGraph(%s)\n",name); //printf("m_rootNodes=%p count=%d\n",m_rootNodes,m_rootNodes->count()); if (m_rootSubgraphs.empty()) return; Dir d(path.str()); // store the original directory if (!d.exists()) { term("Output dir %s does not exist!\n",qPrint(path)); } // put each connected subgraph of the hierarchy in a row of the HTML output out << "\n"; int count=0; std::sort(m_rootSubgraphs.begin(),m_rootSubgraphs.end(), [](auto n1,auto n2) { return qstricmp(n1->label(),n2->label())<0; }); for (auto n : m_rootSubgraphs) { out << "\n"; } out << "
"; createGraph(n,out,path,fileName,count++); out << "
\n"; } void DotGfxHierarchyTable::addHierarchy(DotNode *n,const ClassDef *cd,ClassDefSet &visitedClasses) { //printf("addHierarchy '%s' baseClasses=%d\n",qPrint(cd->name()),cd->baseClasses()->count()); for (const auto &bcd : cd->subClasses()) { ClassDef *bClass=bcd.classDef; //printf(" Trying sub class='%s' usedNodes=%d\n",qPrint(bClass->name()),m_usedNodes->count()); if (bClass && bClass->isVisibleInHierarchy() && hasVisibleRoot(bClass->baseClasses())) { auto it = m_usedNodes.find(bClass->name().str()); //printf(" Node '%s' Found visible class='%s'\n",qPrint(n->label()), // qPrint(bClass->name())); DotNode *root = 0; if (it!=m_usedNodes.end()) // node already present { const auto &bn = it->second; root = bn.get(); const auto &children = n->children(); auto child_it = std::find(children.begin(),children.end(),bn.get()); if (child_it==children.end()) // no arrow yet { n->addChild(bn.get(),bcd.prot); bn->addParent(n); //printf(" Adding node %s to existing base node %s (c=%d,p=%d)\n", // qPrint(n->label()), // qPrint(bn->label()), // bn->children() ? bn->children()->count() : 0, // bn->parents() ? bn->parents()->count() : 0 // ); } //else //{ // printf(" Class already has an arrow!\n"); //} } else { QCString tmp_url=""; if (bClass->isLinkable() && !bClass->isHidden()) { tmp_url=bClass->getReference()+"$"+bClass->getOutputFileBase(); if (!bClass->anchor().isEmpty()) { tmp_url+="#"+bClass->anchor(); } } QCString tooltip = bClass->briefDescriptionAsTooltip(); auto bn = std::make_unique(getNextNodeNumber(), bClass->displayName(), tooltip, tmp_url ); n->addChild(bn.get(),bcd.prot); bn->addParent(n); root = bn.get(); //printf(" Adding node %s to new base node %s (c=%d,p=%d)\n", // qPrint(n->label()), // qPrint(bn->label()), // bn->children() ? bn->children()->count() : 0, // bn->parents() ? bn->parents()->count() : 0 // ); //printf(" inserting %s (%p)\n",qPrint(bClass->name()),bn); m_usedNodes.insert(std::make_pair(bClass->name().str(),std::move(bn))); // add node to the used list } if (visitedClasses.find(bClass)==visitedClasses.end() && !bClass->subClasses().empty()) { visitedClasses.insert(bClass); addHierarchy(root,bClass,visitedClasses); } } } //printf("end addHierarchy\n"); } void DotGfxHierarchyTable::addClassList(const ClassLinkedMap &cl,ClassDefSet &visitedClasses) { for (const auto &cd : cl) { //printf("Trying %s subClasses=%d\n",qPrint(cd->name()),cd->subClasses()->count()); if (cd->getLanguage()==SrcLangExt_VHDL && (VhdlDocGen::VhdlClasses)cd->protection()!=VhdlDocGen::ENTITYCLASS ) { continue; } if (Config_getBool(OPTIMIZE_OUTPUT_SLICE) && cd->compoundType() != m_classType) { continue; } if (!hasVisibleRoot(cd->baseClasses()) && cd->isVisibleInHierarchy() ) // root node in the forest { QCString tmp_url=""; if (cd->isLinkable() && !cd->isHidden()) { tmp_url=cd->getReference()+"$"+cd->getOutputFileBase(); if (!cd->anchor().isEmpty()) { tmp_url+="#"+cd->anchor(); } } //printf("Inserting root class %s\n",qPrint(cd->name())); QCString tooltip = cd->briefDescriptionAsTooltip(); auto n = std::make_unique(getNextNodeNumber(), cd->displayName(), tooltip, tmp_url); DotNode *root = n.get(); m_usedNodes.insert(std::make_pair(cd->name().str(),std::move(n))); m_rootNodes.push_back(root); if (visitedClasses.find(cd.get())==visitedClasses.end() && !cd->subClasses().empty()) { addHierarchy(root,cd.get(),visitedClasses); visitedClasses.insert(cd.get()); } } } } DotGfxHierarchyTable::DotGfxHierarchyTable(const QCString &prefix,ClassDef::CompoundType ct) : m_prefix(prefix) , m_classType(ct) { // build a graph with each class as a node and the inheritance relations // as edges ClassDefSet visitedClasses; addClassList(*Doxygen::classLinkedMap,visitedClasses); addClassList(*Doxygen::hiddenClassLinkedMap,visitedClasses); // m_usedNodes now contains all nodes in the graph // color the graph into a set of independent subgraphs bool done=FALSE; int curColor=0; while (!done) // there are still nodes to color { done=TRUE; // we are done unless there are still uncolored nodes for (auto n : m_rootNodes) { if (n->subgraphId()==-1) // not yet colored { //printf("Starting at node %s (%p): %d\n",qPrint(n->label()),n,curColor); done=FALSE; // still uncolored nodes n->setSubgraphId(curColor); n->markAsVisible(); n->colorConnectedNodes(curColor); curColor++; const DotNode *dn=n->findDocNode(); if (dn!=0) m_rootSubgraphs.push_back(const_cast(dn)); else m_rootSubgraphs.push_back(n); } } } //printf("Number of independent subgraphs: %d\n",curColor); for (auto n : m_rootSubgraphs) { //printf("Node %s color=%d (c=%d,p=%d)\n", // qPrint(n->label()),n->m_subgraphId, // n->children()?n->children()->count():0, // n->parents()?n->parents()->count():0); int number=0; n->renumberNodes(number); } }