Doxygen
Loading...
Searching...
No Matches
dotdirdeps.cpp File Reference
#include "dotdirdeps.h"
#include <cmath>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "config.h"
#include "dotnode.h"
#include "doxygen.h"
#include "image.h"
#include "textstream.h"
#include "util.h"
Include dependency graph for dotdirdeps.cpp:

Go to the source code of this file.

Classes

struct  DotDirProperty
 Properties are used to format the directories in the graph distinctively. More...
class  DotDirPropertyBuilder
 Builder helper to create instances of the DotDirProperty struct. More...

Typedefs

using DirDefMap = std::map<std::string,const DirDef *>
typedef std::vector< std::pair< std::unique_ptr< DirRelation >, bool > > DirRelations
 Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees.

Functions

static DString getDirectoryBackgroundColor (int depthIndex)
 Returns a DOT color name according to the directory depth.
static const char * getDirectoryBorderColor (const DotDirProperty &property)
 Returns a DOT color name according to the directory properties.
static std::string getDirectoryBorderStyle (const DotDirProperty &property)
 Returns a DOT node style according to the directory properties.
static TextStreamcommon_attributes (TextStream &t, const DirDef *const dir, const DotDirProperty &prop)
static void drawDirectory (TextStream &t, const DirDef *const directory, const DotDirProperty &property, DirDefMap &directoriesInGraph, int startLevel)
 Puts DOT code for drawing directory to stream and adds it to the list.
static bool isAtMaxDepth (const DirDef *const directory, const int startLevel)
 Checks, if the directory is a the maximum drawn directory level.
static void drawClusterOpening (TextStream &outputStream, const DirDef *const directory, const DotDirProperty &directoryProperty, DirDefMap &directoriesInGraph, const bool isAncestor, int startLevel)
 Writes DOT code for opening a cluster subgraph to stream.
static void drawClusterClosing (TextStream &t)
static void addDependencies (DirRelations &dependencies, const DirDef *const srcDir, bool isLeaf)
 Assembles a list of the directory relations and whether or not they result from "inheritance".
static void drawTree (DirRelations &dependencies, TextStream &t, const DirDef *const directory, int startLevel, DirDefMap &directoriesInGraph, const bool isTreeRoot)
 Recursively draws directory tree.
void writeDotDirDepGraph (TextStream &t, const DirDef *dd, bool linkRelations)
 Write DOT code for directory dependency graph.

Typedef Documentation

◆ DirDefMap

using DirDefMap = std::map<std::string,const DirDef *>

Definition at line 36 of file dotdirdeps.cpp.

◆ DirRelations

typedef std::vector< std::pair< std::unique_ptr<DirRelation>, bool> > DirRelations

Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees.

Definition at line 63 of file dotdirdeps.cpp.

Function Documentation

◆ addDependencies()

void addDependencies ( DirRelations & dependencies,
const DirDef *const srcDir,
bool isLeaf )
static

Assembles a list of the directory relations and whether or not they result from "inheritance".

Parameters
dependenciesArray to add the dependencies to.
srcDiris the source of the dependency.
isLeaftrue, if no children are drawn for this directory.

Definition at line 217 of file dotdirdeps.cpp.

218{
219 for (const auto &usedDirectory : srcDir->usedDirs())
220 {
221 const auto &dstDir = usedDirectory->dir();
222 if (!dstDir->isParentOf(srcDir) && (isLeaf || usedDirectory->hasDirectSrcDeps()))
223 {
224 DString relationName;
225 relationName.sprintf("dir_%06d_%06d", srcDir->dirIndex(), dstDir->dirIndex());
226 bool directRelation = isLeaf ? usedDirectory->hasDirectDstDeps() : usedDirectory->hasDirectDeps();
227 dependencies.emplace_back(
228 std::make_unique<DirRelation>(relationName, srcDir, usedDirectory.get()),
229 directRelation);
230 }
231 }
232}
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
virtual int dirIndex() const =0
virtual const UsedDirLinkedMap & usedDirs() const =0

References DirDef::dirIndex(), DString::sprintf(), and DirDef::usedDirs().

Referenced by drawTree().

◆ common_attributes()

TextStream & common_attributes ( TextStream & t,
const DirDef *const dir,
const DotDirProperty & prop )
static

Definition at line 139 of file dotdirdeps.cpp.

140{
141 DString url = dir->getOutputFileBase();
143 return t <<
144 "style=\"" << getDirectoryBorderStyle(prop) << "\", "
145 "URL=\"" << url << "\","
146 "tooltip=\"" << escapeTooltip(dir->briefDescriptionAsTooltip()) << "\"";
147}
virtual DString briefDescriptionAsTooltip() const =0
virtual DString getOutputFileBase() const =0
static std::string getDirectoryBorderStyle(const DotDirProperty &property)
Returns a DOT node style according to the directory properties.
DString escapeTooltip(const DString &tooltip)
Definition dotnode.cpp:101
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931

References addHtmlExtensionIfMissing(), Definition::briefDescriptionAsTooltip(), escapeTooltip(), getDirectoryBorderStyle(), and Definition::getOutputFileBase().

Referenced by drawClusterOpening(), and drawDirectory().

◆ drawClusterClosing()

void drawClusterClosing ( TextStream & t)
static

Definition at line 206 of file dotdirdeps.cpp.

207{
208 t << " }\n";
209}

Referenced by drawTree().

◆ drawClusterOpening()

void drawClusterOpening ( TextStream & outputStream,
const DirDef *const directory,
const DotDirProperty & directoryProperty,
DirDefMap & directoriesInGraph,
const bool isAncestor,
int startLevel )
static

Writes DOT code for opening a cluster subgraph to stream.

Ancestor clusters directly get a label. Other clusters get a plain text node with a label instead. This is because the plain text node can be used to draw dependency relationships.

Definition at line 181 of file dotdirdeps.cpp.

183{
184 outputStream << " subgraph cluster" << directory->getOutputFileBase() << " {\n"
185 " graph [ "
186 "bgcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", "
187 "pencolor=\"" << getDirectoryBorderColor(directoryProperty) << "\", "
188 "label=\"";
189 if (isAncestor)
190 {
191 outputStream << DotNode::convertLabel(directory->shortName());
192 }
193 outputStream << "\", "
194 << Config_getString(DOT_COMMON_ATTR) << " ";
195 common_attributes(outputStream, directory, directoryProperty)
196 << "]\n";
197 if (!isAncestor)
198 {
199 outputStream << " " << directory->getOutputFileBase() << " [shape=plaintext, "
200 "label=\"" << DotNode::convertLabel(directory->shortName()) << "\""
201 "];\n";
202 directoriesInGraph.emplace(directory->getOutputFileBase().str(), directory);
203 }
204}
const std::string & str() const
Definition dstring.h:645
virtual int level() const =0
virtual const DString shortName() const =0
static DString convertLabel(const DString &, LabelStyle=LabelStyle::Plain)
Definition dotnode.cpp:198
#define Config_getString(name)
Definition config.h:32
static TextStream & common_attributes(TextStream &t, const DirDef *const dir, const DotDirProperty &prop)
static DString getDirectoryBackgroundColor(int depthIndex)
Returns a DOT color name according to the directory depth.
static const char * getDirectoryBorderColor(const DotDirProperty &property)
Returns a DOT color name according to the directory properties.

References common_attributes(), Config_getString, DotNode::convertLabel(), getDirectoryBackgroundColor(), getDirectoryBorderColor(), Definition::getOutputFileBase(), DirDef::level(), DirDef::shortName(), and DString::str().

Referenced by drawTree(), and writeDotDirDepGraph().

◆ drawDirectory()

void drawDirectory ( TextStream & t,
const DirDef *const directory,
const DotDirProperty & property,
DirDefMap & directoriesInGraph,
int startLevel )
static

Puts DOT code for drawing directory to stream and adds it to the list.

Parameters
[in,out]tstream to which the DOT code is written to
[in]directorywill be mapped to a node in DOT code
[in]propertyare evaluated for formatting
[in,out]directoriesInGraphlists the directories which have been written to the output stream
[in]startLevelcurrent level to calculate relative distances from to determine the background color

Definition at line 157 of file dotdirdeps.cpp.

159{
160 t << " " << directory->getOutputFileBase() << " ["
161 "label=\"" << DotNode::convertLabel(directory->shortName()) << "\", "
162 "fillcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", "
163 "color=\"" << getDirectoryBorderColor(property) << "\", ";
164 common_attributes(t, directory, property)
165 << "];\n";
166 directoriesInGraph.emplace(directory->getOutputFileBase().str(), directory);
167}

References common_attributes(), DotNode::convertLabel(), getDirectoryBackgroundColor(), getDirectoryBorderColor(), Definition::getOutputFileBase(), DirDef::level(), DirDef::shortName(), and DString::str().

Referenced by drawTree().

◆ drawTree()

void drawTree ( DirRelations & dependencies,
TextStream & t,
const DirDef *const directory,
int startLevel,
DirDefMap & directoriesInGraph,
const bool isTreeRoot )
static

Recursively draws directory tree.

Definition at line 235 of file dotdirdeps.cpp.

237{
238 if (!directory->hasSubdirs())
239 {
240 const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
241 drawDirectory(t, directory, directoryProperty, directoriesInGraph,startLevel);
242 addDependencies(dependencies, directory, true);
243 }
244 else
245 {
246 if (isAtMaxDepth(directory, startLevel)) // maximum nesting level reached
247 {
248 const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
249 drawDirectory(t, directory, directoryProperty, directoriesInGraph,startLevel);
250 addDependencies(dependencies, directory, true);
251 }
252 else // start a new nesting level
253 {
254 // open cluster
255 {
256 const DotDirProperty directoryProperty = DotDirPropertyBuilder().makeOriginal(isTreeRoot);
257 drawClusterOpening(t, directory, directoryProperty, directoriesInGraph, false, startLevel);
258 addDependencies(dependencies, directory, false);
259 }
260
261 // process all sub directories
262 for (const auto &subDirectory : directory->subDirs())
263 {
264 drawTree(dependencies, t, subDirectory, startLevel, directoriesInGraph, false);
265 }
266
267 // close cluster
268 {
270 }
271 }
272 }
273}
virtual const DirList & subDirs() const =0
virtual bool hasSubdirs() const =0
Builder helper to create instances of the DotDirProperty struct.
DotDirPropertyBuilder & makeOriginal(bool b=true)
static bool isAtMaxDepth(const DirDef *const directory, const int startLevel)
Checks, if the directory is a the maximum drawn directory level.
static void drawClusterClosing(TextStream &t)
static void addDependencies(DirRelations &dependencies, const DirDef *const srcDir, bool isLeaf)
Assembles a list of the directory relations and whether or not they result from "inheritance".
static void drawDirectory(TextStream &t, const DirDef *const directory, const DotDirProperty &property, DirDefMap &directoriesInGraph, int startLevel)
Puts DOT code for drawing directory to stream and adds it to the list.
static void drawClusterOpening(TextStream &outputStream, const DirDef *const directory, const DotDirProperty &directoryProperty, DirDefMap &directoriesInGraph, const bool isAncestor, int startLevel)
Writes DOT code for opening a cluster subgraph to stream.
static void drawTree(DirRelations &dependencies, TextStream &t, const DirDef *const directory, int startLevel, DirDefMap &directoriesInGraph, const bool isTreeRoot)
Recursively draws directory tree.
Properties are used to format the directories in the graph distinctively.

References addDependencies(), drawClusterClosing(), drawClusterOpening(), drawDirectory(), drawTree(), DirDef::hasSubdirs(), isAtMaxDepth(), DotDirPropertyBuilder::makeOriginal(), and DirDef::subDirs().

Referenced by drawTree().

◆ getDirectoryBackgroundColor()

DString getDirectoryBackgroundColor ( int depthIndex)
static

Returns a DOT color name according to the directory depth.

Definition at line 66 of file dotdirdeps.cpp.

67{
68 int hue = Config_getInt(HTML_COLORSTYLE_HUE);
69 int sat = Config_getInt(HTML_COLORSTYLE_SAT);
70 int gamma = Config_getInt(HTML_COLORSTYLE_GAMMA);
71 ASSERT(depthIndex>=0 && depthIndex<=Config_getInt(DIR_GRAPH_MAX_DEPTH));
72 float fraction = static_cast<float>(depthIndex)/static_cast<float>(Config_getInt(DIR_GRAPH_MAX_DEPTH));
73 const char hex[] = "0123456789abcdef";
74 int range = 0x40; // range from darkest color to lightest color
75 int luma = 0xef-static_cast<int>(fraction*static_cast<float>(range)); // interpolation
76 double r=0, g=0, b=0;
77 ColoredImage::hsl2rgb(hue/360.0,sat/255.0,
78 pow(luma/255.0,gamma/100.0),&r,&g,&b);
79 int red = static_cast<int>(r*255.0);
80 int green = static_cast<int>(g*255.0);
81 int blue = static_cast<int>(b*255.0);
82 ASSERT(red>=0 && red<=255);
83 ASSERT(green>=0 && green<=255);
84 ASSERT(blue>=0 && blue<=255);
85 char colStr[8];
86 colStr[0]='#';
87 colStr[1]=hex[red>>4];
88 colStr[2]=hex[red&0xf];
89 colStr[3]=hex[green>>4];
90 colStr[4]=hex[green&0xf];
91 colStr[5]=hex[blue>>4];
92 colStr[6]=hex[blue&0xf];
93 colStr[7]=0;
94 //printf("i=%d max=%d fraction=%f luma=%d %02x %02x %02x -> color=%s\n",
95 // depthIndex,Config_getInt(DIR_GRAPH_MAX_DEPTH),fraction,luma,red,green,blue,colStr);
96 return colStr;
97}
static void hsl2rgb(double h, double s, double l, double *pRed, double *pGreen, double *pBlue)
Definition image.cpp:373
#define Config_getInt(name)
Definition config.h:34
#define ASSERT(x)
Definition message.h:142

References ASSERT, Config_getInt, and ColoredImage::hsl2rgb().

Referenced by drawClusterOpening(), and drawDirectory().

◆ getDirectoryBorderColor()

const char * getDirectoryBorderColor ( const DotDirProperty & property)
static

Returns a DOT color name according to the directory properties.

Definition at line 100 of file dotdirdeps.cpp.

101{
102 if (property.isTruncated && property.isOrphaned)
103 {
104 return "red";
105 }
106 else if (property.isTruncated)
107 {
108 return "red";
109 }
110 else if (property.isOrphaned)
111 {
112 return "grey50";
113 }
114 else
115 {
116 return "grey25";
117 }
118}
bool isTruncated
true has successors, none is drawn
bool isOrphaned
true if parent is not drawn

References DotDirProperty::isOrphaned, and DotDirProperty::isTruncated.

Referenced by drawClusterOpening(), and drawDirectory().

◆ getDirectoryBorderStyle()

std::string getDirectoryBorderStyle ( const DotDirProperty & property)
static

Returns a DOT node style according to the directory properties.

Definition at line 121 of file dotdirdeps.cpp.

122{
123 std::string style = "filled";
124 if (property.isOriginal)
125 {
126 style += ",bold";
127 }
128 if (property.isIncomplete)
129 {
130 style += ",dashed";
131 }
132 else if (property.isTruncated && property.isOrphaned)
133 {
134 style += ",dashed";
135 }
136 return style;
137}
bool isOriginal
true if is the directory for which the graph is drawn
bool isIncomplete
true if not all successors of a cluster are drawn

References DotDirProperty::isIncomplete, DotDirProperty::isOriginal, DotDirProperty::isOrphaned, and DotDirProperty::isTruncated.

Referenced by common_attributes().

◆ isAtMaxDepth()

bool isAtMaxDepth ( const DirDef *const directory,
const int startLevel )
static

Checks, if the directory is a the maximum drawn directory level.

Definition at line 170 of file dotdirdeps.cpp.

171{
172 return (directory->level() - startLevel) >= Config_getInt(DIR_GRAPH_MAX_DEPTH);
173}

References Config_getInt, and DirDef::level().

Referenced by drawTree().

◆ writeDotDirDepGraph()

void writeDotDirDepGraph ( TextStream & t,
const DirDef * dd,
bool linkRelations )

Write DOT code for directory dependency graph.

Code is generated for a directory. Successors (sub-directories) of this directory are recursively drawn. Recursion is limited by DIR_GRAPH_MAX_DEPTH. The dependencies of those directories are drawn.

If a dependee is not part of directory tree above, then the dependency is drawn to the first parent of the dependee, whose parent is an ancestor (sub-directory) of the original directory.

Parameters
tstream where the DOT code is written to
dddirectory for which the graph is generated for
linkRelationsif true, hyperlinks to the list of file dependencies are added

Definition at line 289 of file dotdirdeps.cpp.

290{
291 DirDefMap dirsInGraph;
292
293 dirsInGraph.emplace(dd->getOutputFileBase().str(),dd);
294
295 std::vector<const DirDef *> usedDirsNotDrawn, usedDirsDrawn;
296 for (const auto& usedDir : dd->usedDirs())
297 {
298 usedDirsNotDrawn.push_back(usedDir->dir());
299 }
300
301 auto moveDrawnDirs = [&usedDirsDrawn,&usedDirsNotDrawn](const std::vector<const DirDef *>::iterator &newEnd)
302 {
303 // usedDirsNotDrawn is split into two segments: [begin()....newEnd-1] and [newEnd....end()]
304 // where the second segment starting at newEnd has been drawn, so append this segment to the usedDirsDrawn list and
305 // remove it from the usedDirsNotDrawn list.
306 std::move(newEnd, std::end(usedDirsNotDrawn), std::back_inserter(usedDirsDrawn));
307 usedDirsNotDrawn.erase(newEnd, usedDirsNotDrawn.end());
308 };
309
310 // if dd has a parent draw it as the outer layer
311 const auto &parent = dd->parent();
312 if (parent)
313 {
314 const DotDirProperty parentDirProperty = DotDirPropertyBuilder().
315 makeIncomplete().
316 makeOrphaned(parent->parent()!=nullptr);
317 drawClusterOpening(t, parent, parentDirProperty, dirsInGraph, true, parent->level());
318
319 {
320 // draw all directories which have `dd->parent()` as parent and `dd` as dependent
321 const auto &newEnd = std::stable_partition(usedDirsNotDrawn.begin(), usedDirsNotDrawn.end(),
322 [&](const DirDef *const usedDir)
323 {
324 if (dd!=usedDir && dd->parent()==usedDir->parent()) // usedDir and dd share the same parent
325 {
326 const DotDirProperty usedDirProperty = DotDirPropertyBuilder().makeTruncated(usedDir->hasSubdirs());
327 drawDirectory(t, usedDir, usedDirProperty, dirsInGraph, parent->level());
328 return false; // part of the drawn partition
329 }
330 return true; // part of the not-drawn partition
331 });
332 moveDrawnDirs(newEnd);
333 }
334 }
335
336 // draw the directory tree with dd as root
337 DirRelations dependencies;
338 drawTree(dependencies, t, dd, dd->level(), dirsInGraph, true);
339
340 if (parent)
341 {
343 }
344
345 // add nodes for other used directories (i.e. outside of the cluster of directories directly connected to dd)
346 {
347 const auto &newEnd = std::stable_partition(usedDirsNotDrawn.begin(), usedDirsNotDrawn.end(),
348 [&](const DirDef *const usedDir) // for each used dir (=directly used or a parent of a directly used dir)
349 {
350 const DirDef *dir=dd;
351 while (dir)
352 {
353 if (dir!=usedDir && dir->parent()==usedDir->parent()) // include if both have the same parent (or no parent)
354 {
355 const DotDirProperty usedDirProperty = DotDirPropertyBuilder().
356 makeOrphaned(usedDir->parent()!=nullptr).
357 makeTruncated(usedDir->hasSubdirs()).
358 makePeripheral();
359 drawDirectory(t, usedDir, usedDirProperty, dirsInGraph, dir->level());
360 return false; // part of the drawn partition
361 }
362 dir=dir->parent();
363 }
364 return true; // part of the not-drawn partition
365 });
366 moveDrawnDirs(newEnd);
367 }
368
369 // add relations between all selected directories
370 {
371 for (const auto &relationPair : dependencies)
372 {
373 const auto &relation = relationPair.first;
374 const bool directRelation = relationPair.second;
375 const auto &udir = relation->destination();
376 const auto &usedDir = udir->dir();
377 const bool destIsSibling = std::find(std::begin(usedDirsDrawn), std::end(usedDirsDrawn), usedDir) != std::end(usedDirsDrawn);
378 const bool destIsDrawn = dirsInGraph.find(usedDir->getOutputFileBase().str())!=dirsInGraph.end(); // only point to nodes that are in the graph
379 const bool atMaxDepth = isAtMaxDepth(usedDir, dd->level());
380
381 if (destIsSibling || (destIsDrawn && (directRelation || atMaxDepth)))
382 {
383 const auto &relationName = relation->getOutputFileBase();
384 const auto &dir = relation->source();
385 Doxygen::dirRelations.add(relationName,
386 std::make_unique<DirRelation>(
387 relationName,dir,udir));
388 size_t nrefs = udir->filePairs().size();
389 t << " " << dir->getOutputFileBase() << "->"
390 << usedDir->getOutputFileBase();
391 t << " [headlabel=\"" << nrefs << "\", labeldistance=1.5";
392 if (linkRelations)
393 {
394 DString fn = relationName;
396 t << " headhref=\"" << fn << "\"";
397 t << " href=\"" << fn << "\"";
398 }
399 t << " color=\"steelblue1\" fontcolor=\"steelblue1\"];\n";
400 }
401 }
402 }
403}
A model of a directory symbol.
Definition dirdef.h:108
virtual DirDef * parent() const =0
static DirRelationLinkedMap dirRelations
Definition doxygen.h:121
T * add(const char *k, Args &&... args)
Definition linkedmap.h:90
constexpr DocNodeVariant * parent(DocNodeVariant *n)
returns the parent node of a given node n or nullptr if the node has no parent.
Definition docnode.h:1335
std::vector< std::pair< std::unique_ptr< DirRelation >, bool > > DirRelations
Elements consist of (1) directory relation and (2) whether it is pointing only to inherited dependees...
std::map< std::string, const DirDef * > DirDefMap

References drawClusterOpening(), Definition::getOutputFileBase(), DirDef::parent(), parent(), DString::str(), and DirDef::usedDirs().

Referenced by DotDirDeps::computeTheGraph().