Doxygen
Loading...
Searching...
No Matches
dotnode.cpp
Go to the documentation of this file.
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// own header
17#include "dotnode.h"
18
19// other includes
20#include "classdef.h"
21#include "config.h"
22#include "language.h"
23#include "membergroup.h"
24#include "memberlist.h"
25#include "textstream.h"
26#include "util.h"
27
28/** Helper struct holding the properties of a edge in a dot graph. */
30{
31 const char * const *edgeColorMap;
32 const char * const *arrowStyleMap;
33 const char * const *edgeStyleMap;
34};
35
36/*! mapping from protection levels to color names */
37static const char *normalEdgeColorMap[] =
38{
39 "steelblue1", // Public
40 "darkgreen", // Protected
41 "firebrick4", // Private
42 "darkorchid3", // "use" relation
43 "grey75", // Undocumented
44 "orange", // template relation
45 "orange" // type constraint
46};
47
48static const char *normalArrowStyleMap[] =
49{
50 "empty", // Public
51 "empty", // Protected
52 "empty", // Private
53 "open", // "use" relation
54 nullptr, // Undocumented
55 nullptr // template relation
56};
57
58static const char *normalEdgeStyleMap[] =
59{
60 "solid", // inheritance
61 "dashed" // usage
62};
63
64static const char *umlEdgeColorMap[] =
65{
66 "steelblue1", // Public
67 "darkgreen", // Protected
68 "firebrick4", // Private
69 "steelblue1", // "use" relation
70 "grey75", // Undocumented
71 "orange", // template relation
72 "orange" // type constraint
73};
74
75static const char *umlArrowStyleMap[] =
76{
77 "onormal", // Public
78 "onormal", // Protected
79 "onormal", // Private
80 "odiamond", // "use" relation
81 nullptr, // Undocumented
82 nullptr // template relation
83};
84
85static const char *umlEdgeStyleMap[] =
86{
87 "solid", // inheritance
88 "solid" // usage
89};
90
95
100
102{
103 if (tooltip.empty()) return tooltip;
104 DString result;
105 const char *p=tooltip.data();
106 char c = 0;
107 while ((c=*p++))
108 {
109 switch(c)
110 {
111 case '"': result+="\\\""; break;
112 case '\\': result+="\\\\"; break;
113 default: result+=c; break;
114 }
115 }
116 return result;
117}
118
120 char prot,const MemberList *ml,const ClassDef *scope,
121 bool &lineWritten,
122 bool isStatic=false,const StringUnorderedSet *skipNames=nullptr)
123{
124 constexpr auto tr_start = "<TR><TD VALIGN=\"top\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
125 constexpr auto tr_mid = "</TD><TD VALIGN=\"top\" ALIGN=\"LEFT\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
126 constexpr auto tr_end = "</TD></TR>\n";
127 constexpr auto br = "<BR ALIGN=\"LEFT\"/>";
128 if (ml)
129 {
130 auto hideUndocMembers = Config_getEnum(HIDE_UNDOC_MEMBERS);
131 int totalCount=0;
132 for (const auto &mma : *ml)
133 {
134 if (mma->getClassDef()==scope &&
135 (skipNames==nullptr || skipNames->find(mma->name().str())==std::end(*skipNames)) &&
136 !(hideUndocMembers && !mma->hasDocumentation())
137 )
138 {
139 totalCount++;
140 }
141 }
142
143 int count=0;
144 auto dotUmlDetails = Config_getEnum(DOT_UML_DETAILS);
145 for (const auto &mma : *ml)
146 {
147 if (mma->getClassDef() == scope &&
148 (skipNames==nullptr || skipNames->find(mma->name().str())==std::end(*skipNames)) &&
149 !(hideUndocMembers && !mma->hasDocumentation())
150 )
151 {
152 int numFields = Config_getInt(UML_LIMIT_NUM_FIELDS);
153 if (numFields>0 && (totalCount>numFields*3/2 && count>=numFields))
154 {
155 t << tr_start << tr_mid << theTranslator->trAndMore(DString().sprintf("%d",totalCount-count)) << tr_end;
156 lineWritten = true;
157 break;
158 }
159 else
160 {
161 t << tr_start << prot << tr_mid;
162 DString label;
163 if (dotUmlDetails==DOT_UML_DETAILS_t::YES)
164 {
165 label+=mma->typeString();
166 label+=" ";
167 }
168 label+=mma->name();
169 if (!mma->isObjCMethod() && (mma->isFunction() || mma->isSlot() || mma->isSignal()))
170 {
171 if (dotUmlDetails==DOT_UML_DETAILS_t::YES)
172 {
173 label+=mma->argsString();
174 }
175 else
176 {
177 label+="()";
178 }
179 }
181 t << br << tr_end;
182 lineWritten = true;
183 count++;
184 }
185 }
186 }
187 // write member groups within the memberlist
188 for (const auto &mg : ml->getMemberGroupList())
189 {
190 if (!mg->members().empty())
191 {
192 writeBoxMemberList(t,prot,&mg->members(),scope,lineWritten,isStatic,skipNames);
193 }
194 }
195 }
196}
197
199{
200 DString bBefore("\\_/<({[: =-+@%#~?$"); // break before character set
201 DString bAfter(">]),:;|"); // break after character set
202 if (l.empty()) return DString();
203 DString result;
204 char pc=0;
205 uint32_t idx = 0;
206 int charsLeft=static_cast<int>(l.length());
207 int sinceLast=0;
208 int foldLen = Config_getInt(DOT_WRAP_THRESHOLD); // ideal text length
209 DString br;
210 DString br1;
211 if (style==LabelStyle::Table)
212 {
213 result += "<<TABLE CELLBORDER=\"0\" BORDER=\"0\"><TR><TD VALIGN=\"top\" ALIGN=\"LEFT\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
214 }
215 if (style==LabelStyle::List)
216 {
217 br = "<BR ALIGN=\"LEFT\"/>";
218 }
219 else if (style==LabelStyle::Table)
220 {
221 br1 = "</TD></TR>\n<TR><TD VALIGN=\"top\" ALIGN=\"LEFT\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
222 br = br1 + "&nbsp;&nbsp;";
223 }
224 else // style==LabelStyle::Plain
225 {
226 br = "\\l";
227 }
228 while (idx < l.length())
229 {
230 char c = l[idx++];
231 char cs[2] = { c, 0 };
232 const char *replacement = cs;
233 if (style!=LabelStyle::Plain)
234 {
235 switch(c)
236 {
237 case '\\': replacement="\\\\"; break;
238 case '\n': replacement="\\n"; break;
239 case '<': replacement="&lt;"; break;
240 case '>': replacement="&gt;"; break;
241 case '"': replacement="&quot;"; break;
242 case '\'': replacement="&apos;"; break;
243 case '&': replacement="&amp;"; break;
244 }
245 }
246 else // style==LabelStyle::Plain
247 {
248 switch(c)
249 {
250 case '\\': replacement="\\\\"; break;
251 case '\n': replacement="\\n"; break;
252 case '<': replacement="\\<"; break;
253 case '>': replacement="\\>"; break;
254 case '"': replacement="\\\""; break;
255 case '|': replacement="\\|"; break;
256 case '{': replacement="\\{"; break;
257 case '}': replacement="\\}"; break;
258 }
259 }
260 // Some heuristics to insert newlines to prevent too long
261 // boxes and at the same time prevent ugly breaks
262 if (c=='\n')
263 {
264 if (style==LabelStyle::Table)
265 {
266 result+=br1;
267 }
268 else
269 {
270 result+=replacement;
271 }
272 foldLen = (3*foldLen+sinceLast+2)/4;
273 sinceLast=1;
274 }
275 else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
276 {
277 result+=br;
278 result+=replacement;
279 foldLen = (foldLen+sinceLast+1)/2;
280 sinceLast=1;
281 }
282 else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
283 !isupper(c) && isupper(l[idx]))
284 {
285 result+=replacement;
286 result+=br;
287 foldLen = (foldLen+sinceLast+1)/2;
288 sinceLast=0;
289 }
290 else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || l[idx]!=':'))
291 {
292 result+=replacement;
293 result+=br;
294 foldLen = (foldLen+sinceLast+1)/2;
295 sinceLast=0;
296 }
297 else
298 {
299 result+=replacement;
300 sinceLast++;
301 }
302 charsLeft--;
303 pc=c;
304 }
305 if (style==LabelStyle::List)
306 {
307 result = result.stripWhiteSpace();
308 }
309 if (style==LabelStyle::Table)
310 {
311 result += "</TD></TR>\n</TABLE>>";
312 }
313 return result;
314}
315
317{
318 if (!s.empty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
319 {
320 return s.mid(1);
321 }
322 else
323 {
324 return s;
325 }
326}
327
328DotNode::DotNode(DotGraph *graph,const DString &lab,const DString &tip, const DString &url,
329 bool isRoot,const ClassDef *cd)
330 : m_graph(graph)
331 , m_number(graph->getNextNodeNumber())
332 , m_label(lab)
333 , m_tooltip(tip)
334 , m_url(url)
335 , m_isRoot(isRoot)
336 , m_classDef(cd)
337{
338}
339
341 EdgeInfo::Colors edgeColor,
342 EdgeInfo::Styles edgeStyle,
343 const DString &edgeLab,
344 const DString &edgeURL,
345 int edgeLabColor
346)
347{
348 m_children.push_back(n);
349 m_edgeInfo.emplace_back(
350 edgeColor,
351 edgeStyle,
352 edgeLab,
353 edgeURL,
354 edgeLabColor==-1 ? edgeColor : edgeLabColor);
355}
356
358{
359 m_parents.push_back(n);
360}
361
363{
364 auto it = std::find(m_children.begin(),m_children.end(),n);
365 if (it!=m_children.end()) m_children.erase(it);
366}
367
369{
370 auto it = std::find(m_parents.begin(),m_parents.end(),n);
371 if (it!=m_parents.end()) m_parents.erase(it);
372}
373
375{
376 if (m_deleted) return; // avoid recursive loops in case the graph has cycles
377 m_deleted=true;
378 // delete all parent nodes of this node
379 for (const auto &pn : m_parents)
380 {
381 pn->deleteNode(deletedList);
382 }
383 // delete all child nodes of this node
384 for (const auto &cn : m_children)
385 {
386 cn->deleteNode(deletedList);
387 }
388 // add this node to the list of deleted nodes.
389 deletedList.push_back(this);
390}
391
396
398{
399 auto it = std::find(m_parents.begin(),m_parents.end(),n);
400 return it!=m_parents.end() ? static_cast<int>(it-m_parents.begin()) : -1;
401}
402
403/*! helper function that deletes all nodes in a connected graph, given
404* one of the graph's nodes
405*/
407{
408 DotNodeRefVector deletedNodes;
409 node->deleteNode(deletedNodes); // collect nodes to be deleted.
410 for (const auto &dotNode : deletedNodes)
411 {
412 delete dotNode;
413 }
414}
415
417{
419 {
420 // Set shape to the plain type.
421 // the UML properties and methods are rendered using dot' HTML like table format
422 t << "shape=plain,label=";
423 // add names shown as relations to a set, so we don't show
424 // them as attributes as well
425 StringUnorderedSet arrowNames;
426 // for each edge
427 for (const auto &ei : m_edgeInfo)
428 {
429 if (!ei.label().empty()) // labels joined by \n
430 {
431 size_t i=0;
432 size_t p=0;
433 DString lab;
434 while ((i=ei.label().find('\n',p))!=DString::npos)
435 {
436 lab = stripProtectionPrefix(ei.label().mid(p,i-p));
437 arrowNames.insert(lab.str());
438 p=i+1;
439 }
440 lab = stripProtectionPrefix(ei.label().right(ei.label().length()-p));
441 arrowNames.insert(lab.str());
442 }
443 }
444
445 constexpr auto hr_start = "<TR><TD COLSPAN=\"2\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
446 constexpr auto hr_end = "</TD></TR>\n";
447 constexpr auto sep = "<HR/>\n";
448 constexpr auto empty_line = "<TR><TD COLSPAN=\"2\" CELLPADDING=\"1\" CELLSPACING=\"0\">&nbsp;</TD></TR>\n";
449 //printf("DotNode::writeBox for %s\n",qPrint(m_classDef->name()));
450 t << "<<TABLE CELLBORDER=\"0\" BORDER=\"1\">";
451 t << hr_start << convertLabel(m_label,LabelStyle::List) << hr_end;
452 auto dotUmlDetails = Config_getEnum(DOT_UML_DETAILS);
453 if (dotUmlDetails!=DOT_UML_DETAILS_t::NONE)
454 {
455 bool lineWritten = false;
456 t << sep;
457 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubAttribs()),m_classDef,lineWritten,false,&arrowNames);
458 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubStaticAttribs()),m_classDef,lineWritten,true,&arrowNames);
459 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::Properties()),m_classDef,lineWritten,false,&arrowNames);
460 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacAttribs()),m_classDef,lineWritten,false,&arrowNames);
461 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacStaticAttribs()),m_classDef,lineWritten,true,&arrowNames);
462 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProAttribs()),m_classDef,lineWritten,false,&arrowNames);
463 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProStaticAttribs()),m_classDef,lineWritten,true,&arrowNames);
464 if (Config_getBool(EXTRACT_PRIVATE))
465 {
466 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriAttribs()),m_classDef,lineWritten,false,&arrowNames);
467 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriStaticAttribs()),m_classDef,lineWritten,true,&arrowNames);
468 }
469 if (!lineWritten) t << empty_line;
470 t << sep;
471 lineWritten = false;
472 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubMethods()),m_classDef,lineWritten);
473 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubStaticMethods()),m_classDef,lineWritten,true);
474 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubSlots()),m_classDef,lineWritten);
475 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacMethods()),m_classDef,lineWritten);
476 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacStaticMethods()),m_classDef,lineWritten,true);
477 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProMethods()),m_classDef,lineWritten);
478 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProStaticMethods()),m_classDef,lineWritten,true);
479 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProSlots()),m_classDef,lineWritten);
480 if (Config_getBool(EXTRACT_PRIVATE))
481 {
482 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriMethods()),m_classDef,lineWritten);
483 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriStaticMethods()),m_classDef,lineWritten,true);
484 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriSlots()),m_classDef,lineWritten);
485 }
486 if (m_classDef->getLanguage()!=SrcLangExt::Fortran)
487 {
488 for (const auto &mg : m_classDef->getMemberGroups())
489 {
490 if (!mg->members().empty())
491 {
492 writeBoxMemberList(t,'*',&mg->members(),m_classDef,lineWritten,false,&arrowNames);
493 }
494 }
495 }
496 if (!lineWritten) t << empty_line;
497 }
498 t << "</TABLE>>\n";
499 }
500 else if (Config_getString(DOT_NODE_ATTR).contains("shape=plain"))
501 {
502 t << "label=";
503 if (m_isRoot)
504 t << "<<b>" << convertToXML(m_label) << "</b>>";
505 else if (m_truncated == Truncated)
506 t << "<<i>" << convertToXML(m_label) << "</i>>";
507 else
508 t << '"' << convertLabel(m_label,LabelStyle::Plain) << '"';
509 }
510 else // standard look
511 {
512 t << "label=" << '"' << convertLabel(m_label,LabelStyle::Plain) << '"';
513 }
514}
515
517{
518 if (m_url.empty() || m_url == DotNode::placeholderUrl) return;
519 size_t tagPos = m_url.rfind('$');
520 t << ",URL=\"";
521 DString noTagURL = m_url;
522 if (tagPos!=DString::npos)
523 {
524 t << m_url.left(tagPos);
525 noTagURL = m_url.mid(tagPos);
526 }
527 size_t anchorPos = noTagURL.rfind('#');
528 if (anchorPos==DString::npos)
529 {
531 t << noTagURL << "\"";
532 }
533 else // insert extensiom before anchor
534 {
535 DString fn = noTagURL.left(anchorPos);
537 t << fn << noTagURL.mid(anchorPos) << "\"";
538 }
539}
540
542 GraphType gt,
543 GraphOutputFormat /*format*/,
544 bool hasNonReachableChildren) const
545{
546 const char *labCol = nullptr;
547 const char *fillCol = "white";
548 if (m_classDef)
549 {
550 if (m_classDef->hasDocumentation() && hasNonReachableChildren)
551 {
552 labCol = "red";
553 fillCol = "#FFF0F0";
554 }
555 else if (m_classDef->hasDocumentation() && !hasNonReachableChildren)
556 {
557 labCol = "gray40";
558 }
559 else if (!m_classDef->hasDocumentation() && hasNonReachableChildren)
560 {
561 labCol = "orangered";
562 }
563 else // (!m_classDef->hasDocumentation() && !hasNonReachableChildren)
564 {
565 labCol = "grey75";
567 {
568 labCol = "gray40";
569 }
570 }
571 }
572 else
573 {
574 labCol = m_url.empty() ? "grey60" : // non link
575 (hasNonReachableChildren ? "red" : "grey40");
576 fillCol = m_url.empty() ? "#E0E0E0" :
577 (hasNonReachableChildren ? "#FFF0F0" : "white");
578 }
579 t << " Node" << m_number << " [";
580 t << "id=\"Node" << DString().sprintf("%06d",m_number) << "\",";
581 writeLabel(t,gt);
582 t << ",height=0.2,width=0.4";
583 if (m_isRoot)
584 {
585 t << ",color=\"gray40\", fillcolor=\"grey60\", style=\"filled\", fontcolor=\"black\"";
586 }
587 else
588 {
589 t << ",color=\"" << labCol << "\"";
590 t << ", fillcolor=\"" << fillCol << "\"";
591 t << ", style=\"filled\"";
592 writeUrl(t);
593 }
594 if (!m_tooltip.empty())
595 {
596 t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
597 }
598 else
599 {
600 t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0' is used
601 }
602 t << "];\n";
603}
604
606 GraphType gt,
607 GraphOutputFormat /* format */,
608 const DotNode *cn,
609 const EdgeInfo *ei,
610 bool topDown,
611 bool pointBack) const
612{
613 t << " Node";
614 if (topDown)
615 t << cn->number();
616 else
617 t << m_number;
618 t << " -> Node";
619 if (topDown)
620 t << m_number;
621 else
622 t << cn->number();
623 t << " [";
624
625 const EdgeProperties *eProps = Config_getBool(UML_LOOK) ? &umlEdgeProps : &normalEdgeProps;
626 DString aStyle = eProps->arrowStyleMap[ei->color()];
627 bool umlUseArrow = aStyle=="odiamond";
628
629 t << "id=\"edge" << m_graph->getNextEdgeNumber() <<
630 "_Node" << DString().sprintf("%06d",m_number) <<
631 "_Node" << DString().sprintf("%06d",cn->number()) << "\",";
632 if (pointBack && !umlUseArrow) t << "dir=\"back\",";
633 t << "color=\"" << eProps->edgeColorMap[ei->color()] << "\",";
634 t << "style=\"" << eProps->edgeStyleMap[ei->style()] << "\"";
635 t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0 -> Node1' is used
636 if (!ei->label().empty())
637 {
638 t << ",label=" << convertLabel(ei->label(),LabelStyle::Table) << " ,fontcolor=\"grey\" ";
639 }
640 if (Config_getBool(UML_LOOK) &&
641 eProps->arrowStyleMap[ei->color()] &&
643 )
644 {
645 bool rev = pointBack;
646 if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
647 if (rev)
648 t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
649 else
650 t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
651 }
652
653 t << "];\n";
654}
655
657 GraphType gt,
658 GraphOutputFormat format,
659 bool topDown,
660 bool toChildren,
661 bool backArrows)
662{
663 //printf("DotNode::write(%d) name=%s this=%p written=%d visible=%d\n",m_distance,qPrint(m_label),this,m_written,m_visible);
664 if (m_written) return; // node already written to the output
665 if (!m_visible) return; // node is not visible
666 writeBox(t,gt,format,m_truncated==Truncated);
667 m_written=true;
668 if (toChildren)
669 {
670 auto it = m_edgeInfo.begin();
671 for (const auto &cn : m_children)
672 {
673 if (cn->isVisible())
674 {
675 //printf("write arrow %s%s%s\n",qPrint(label()),backArrows?"<-":"->",qPrint(cn->label()));
676 writeArrow(t,gt,format,cn,&(*it),topDown,backArrows);
677 }
678 cn->write(t,gt,format,topDown,toChildren,backArrows);
679 ++it;
680 }
681 }
682 else // render parents
683 {
684 for (const auto &pn : m_parents)
685 {
686 if (pn->isVisible())
687 {
688 const auto &children = pn->children();
689 auto child_it = std::find(children.begin(),children.end(),this);
690 size_t index = child_it - children.begin();
691 //printf("write arrow %s%s%s\n",qPrint(label()),backArrows?"<-":"->",qPrint(pn->label()));
692 writeArrow(t,
693 gt,
694 format,
695 pn,
696 &pn->edgeInfo()[index],
697 false,
698 backArrows
699 );
700 }
701 pn->write(t,gt,format,true,false,backArrows);
702 }
703 }
704 //printf("end DotNode::write(%d) name=%s\n",distance,qPrint(m_label));
705}
706
707void DotNode::writeXML(TextStream &t,bool isClassGraph) const
708{
709 t << " <node id=\"" << m_number << "\">\n";
710 t << " <label>" << convertToXML(m_label) << "</label>\n";
711 if (!m_url.empty())
712 {
713 DString url(m_url);
714 size_t dollarPos = url.find('$');
715 if (dollarPos!=DString::npos)
716 {
717 t << " <link refid=\"" << convertToXML(url.mid(dollarPos+1)) << "\"";
718 if (dollarPos>0)
719 {
720 t << " external=\"" << convertToXML(url.left(dollarPos)) << "\"";
721 }
722 t << "/>\n";
723 }
724 }
725 auto it = m_edgeInfo.begin();
726 for (const auto &childNode : m_children)
727 {
728 const EdgeInfo &edgeInfo = *it;
729 t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
730 if (isClassGraph)
731 {
732 switch(edgeInfo.color())
733 {
734 case EdgeInfo::Blue: t << "public-inheritance"; break;
735 case EdgeInfo::Green: t << "protected-inheritance"; break;
736 case EdgeInfo::Red: t << "private-inheritance"; break;
737 case EdgeInfo::Purple: t << "usage"; break;
738 case EdgeInfo::Orange: t << "template-instance"; break;
739 case EdgeInfo::Orange2: t << "type-constraint"; break;
740 case EdgeInfo::Grey: ASSERT(0); break;
741 }
742 }
743 else // include graph
744 {
745 t << "include";
746 }
747 t << "\">\n";
748 if (!edgeInfo.label().empty())
749 {
750 size_t p=0;
751 size_t ni=0;
752 while ((ni=edgeInfo.label().find('\n',p))!=DString::npos)
753 {
754 t << " <edgelabel>"
755 << convertToXML(edgeInfo.label().mid(p,ni-p))
756 << "</edgelabel>\n";
757 p=ni+1;
758 }
759 t << " <edgelabel>"
761 << "</edgelabel>\n";
762 }
763 t << " </childnode>\n";
764 ++it;
765 }
766 t << " </node>\n";
767}
768
769void DotNode::writeDocbook(TextStream &t,bool isClassGraph) const
770{
771 t << " <node id=\"" << m_number << "\">\n";
772 t << " <label>" << convertToXML(m_label) << "</label>\n";
773 if (!m_url.empty())
774 {
775 DString url(m_url);
776 if (size_t dollarPos = url.find('$'); dollarPos!=DString::npos)
777 {
778 t << " <link refid=\"" << convertToXML(url.mid(dollarPos+1)) << "\"";
779 if (dollarPos>0)
780 {
781 t << " external=\"" << convertToXML(url.left(dollarPos)) << "\"";
782 }
783 t << "/>\n";
784 }
785 }
786 auto it = m_edgeInfo.begin();
787 for (const auto &childNode : m_children)
788 {
789 const EdgeInfo &edgeInfo = *it;
790 t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
791 if (isClassGraph)
792 {
793 switch(edgeInfo.color())
794 {
795 case EdgeInfo::Blue: t << "public-inheritance"; break;
796 case EdgeInfo::Green: t << "protected-inheritance"; break;
797 case EdgeInfo::Red: t << "private-inheritance"; break;
798 case EdgeInfo::Purple: t << "usage"; break;
799 case EdgeInfo::Orange: t << "template-instance"; break;
800 case EdgeInfo::Orange2: t << "type-constraint"; break;
801 case EdgeInfo::Grey: ASSERT(0); break;
802 }
803 }
804 else // include graph
805 {
806 t << "include";
807 }
808 t << "\">\n";
809 if (!edgeInfo.label().empty())
810 {
811 size_t p=0;
812 size_t ni=0;
813 while ((ni=edgeInfo.label().find('\n',p))!=DString::npos)
814 {
815 t << " <edgelabel>"
816 << convertToXML(edgeInfo.label().mid(p,ni-p))
817 << "</edgelabel>\n";
818 p=ni+1;
819 }
820 t << " <edgelabel>"
822 << "</edgelabel>\n";
823 }
824 t << " </childnode>\n";
825 ++it;
826 }
827 t << " </node>\n";
828}
829
830
832{
833 const char* nodePrefix = " node-";
834
835 t << " node = {\n";
836 t << nodePrefix << "id = " << m_number << ";\n";
837 t << nodePrefix << "label = '" << m_label << "';\n";
838
839 if (!m_url.empty())
840 {
841 DString url(m_url);
842 if (size_t dollarPos = url.find('$'); dollarPos!=DString::npos)
843 {
844 t << nodePrefix << "link = {\n" << " "
845 << nodePrefix << "link-id = '" << url.mid(dollarPos+1) << "';\n";
846 if (dollarPos>0)
847 {
848 t << " " << nodePrefix << "link-external = '"
849 << url.left(dollarPos) << "';\n";
850 }
851 t << " };\n";
852 }
853 }
854 auto it = m_edgeInfo.begin();
855 for (const auto &childNode : m_children)
856 {
857 const EdgeInfo &edgeInfo = *it;
858 t << " node-child = {\n";
859 t << " child-id = '" << childNode->number() << "';\n";
860 t << " relation = ";
861
862 switch (edgeInfo.color())
863 {
864 case EdgeInfo::Blue: t << "public-inheritance"; break;
865 case EdgeInfo::Green: t << "protected-inheritance"; break;
866 case EdgeInfo::Red: t << "private-inheritance"; break;
867 case EdgeInfo::Purple: t << "usage"; break;
868 case EdgeInfo::Orange: t << "template-instance"; break;
869 case EdgeInfo::Orange2: t << "type-constraint"; break;
870 case EdgeInfo::Grey: ASSERT(0); break;
871 }
872 t << ";\n";
873
874 if (!edgeInfo.label().empty())
875 {
876 t << " edgelabel = <<_EnD_oF_dEf_TeXt_\n"
877 << edgeInfo.label() << "\n"
878 << "_EnD_oF_dEf_TeXt_;\n";
879 }
880 t << " }; /* node-child */\n";
881 ++it;
882 }
883 t << " }; /* node */\n";
884}
885
886
888{
889 m_written=false;
890 for (const auto &pn : m_parents) if (pn->isWritten()) pn->clearWriteFlag();
891 for (const auto &cn : m_children) if (cn->isWritten()) cn->clearWriteFlag();
892}
893
895{
896 for (const auto &cn : m_children)
897 {
898 if (cn->subgraphId()==-1) // uncolored child node
899 {
900 cn->setSubgraphId(curColor);
901 cn->markAsVisible();
902 cn->colorConnectedNodes(curColor);
903 //printf("coloring node %s (%p): %d\n",qPrint(cn->label()),cn,cn->subgraphId());
904 }
905 }
906
907 for (const auto &pn : m_parents)
908 {
909 if (pn->subgraphId()==-1) // uncolored parent node
910 {
911 pn->setSubgraphId(curColor);
912 pn->markAsVisible();
913 pn->colorConnectedNodes(curColor);
914 //printf("coloring node %s (%p): %d\n",qPrint(pn->label()),pn,pn->subgraphId());
915 }
916 }
917}
918
919#define DEBUG_RENUMBERING 0
920
922{
923 if (!isRenumbered())
924 {
925#if DEBUG_RENUMBERING
926 static int level = 0;
927 printf("%3d: ",subgraphId());
928 for (int i = 0; i < level; i++) printf(" ");
929 printf("> %s old = %d new = %d\n",qPrint(m_label),m_number,number);
930 level++;
931#endif
932 m_number = number++;
934 for (const auto &cn : m_children)
935 {
936 cn->renumberNodes(number);
937 }
938 for (const auto &pn : m_parents)
939 {
940 pn->renumberNodes(number);
941 }
942#if DEBUG_RENUMBERING
943 level--;
944 printf("%3d: ",subgraphId());
945 for (int i = 0; i < level; i++) printf(" ");
946 printf("< %s assigned = %d\n",qPrint(m_label),m_number);
947#endif
948 }
949}
950
951
952
953
A abstract class representing of a compound symbol.
Definition classdef.h:100
virtual MemberList * getMemberList(MemberListType lt) const =0
Returns the members in the list identified by lt.
virtual bool isImplicitTemplateInstance() const =0
virtual const MemberGroupList & getMemberGroups() const =0
Returns the member groups defined for this class.
virtual const ClassDef * templateMaster() const =0
Returns the template master of which this class is an instance.
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:244
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:318
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
DString right(size_t len) const
Definition dstring.h:311
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
size_t find(char c, size_t pos=0) const
Definition dstring.h:239
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
DString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition dstring.h:337
DString left(size_t len) const
Definition dstring.h:306
const std::string & str() const
Definition dstring.h:645
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
virtual SrcLangExt getLanguage() const =0
Returns the programming language this definition was written in.
virtual bool hasDocumentation() const =0
A dot graph.
Definition dotgraph.h:35
int getNextEdgeNumber()
returns the edge number.
Definition dotgraph.h:47
void writeDEF(TextStream &t) const
Definition dotnode.cpp:831
int findParent(DotNode *n)
Definition dotnode.cpp:397
int m_number
Definition dotnode.h:129
static DString convertLabel(const DString &, LabelStyle=LabelStyle::Plain)
Definition dotnode.cpp:198
int distance() const
Definition dotnode.h:106
void writeUrl(TextStream &t) const
Definition dotnode.cpp:516
void markRenumbered()
Definition dotnode.h:114
void setDistance(int distance)
Definition dotnode.cpp:392
void writeArrow(TextStream &t, GraphType gt, GraphOutputFormat f, const DotNode *cn, const EdgeInfo *ei, bool topDown, bool pointBack=true) const
Definition dotnode.cpp:605
void write(TextStream &t, GraphType gt, GraphOutputFormat f, bool topDown, bool toChildren, bool backArrows)
Definition dotnode.cpp:656
bool m_isRoot
indicates if this is a root node
Definition dotnode.h:139
void clearWriteFlag()
Definition dotnode.cpp:887
void removeParent(DotNode *n)
Definition dotnode.cpp:368
const EdgeInfoVector & edgeInfo() const
Definition dotnode.h:124
int number() const
Definition dotnode.h:103
void writeBox(TextStream &t, GraphType gt, GraphOutputFormat f, bool hasNonReachableChildren) const
Definition dotnode.cpp:541
TruncState m_truncated
does the node have non-visible children/parents
Definition dotnode.h:142
void writeDocbook(TextStream &t, bool isClassGraph) const
Definition dotnode.cpp:769
void renumberNodes(int &number)
Definition dotnode.cpp:921
bool m_written
used to mark a node as written
Definition dotnode.h:137
void removeChild(DotNode *n)
Definition dotnode.cpp:362
static void deleteNodes(DotNode *node)
Definition dotnode.cpp:406
EdgeInfoVector m_edgeInfo
edge info for each child
Definition dotnode.h:135
bool isRenumbered() const
Definition dotnode.h:108
int m_distance
shortest path to the root node
Definition dotnode.h:143
int subgraphId() const
Definition dotnode.h:107
void addParent(DotNode *n)
Definition dotnode.cpp:357
DotNode(DotGraph *graph, const DString &lab, const DString &tip, const DString &url, bool rootNode=false, const ClassDef *cd=nullptr)
Definition dotnode.cpp:328
DotNodeRefVector m_parents
list of parent nodes (incoming arrows)
Definition dotnode.h:133
bool m_visible
is the node visible in the output
Definition dotnode.h:141
void addChild(DotNode *n, EdgeInfo::Colors edgeColor=EdgeInfo::Purple, EdgeInfo::Styles edgeStyle=EdgeInfo::Solid, const DString &edgeLab=DString(), const DString &edgeURL=DString(), int edgeLabColor=-1)
Definition dotnode.cpp:340
DString m_tooltip
node's tooltip
Definition dotnode.h:131
void colorConnectedNodes(int curColor)
Definition dotnode.cpp:894
DotNodeRefVector m_children
list of child nodes (outgoing arrows)
Definition dotnode.h:134
@ Truncated
Definition dotnode.h:76
DString m_label
label text
Definition dotnode.h:130
const ClassDef * m_classDef
class representing this node (can be 0)
Definition dotnode.h:140
LabelStyle
Definition dotnode.h:69
static constexpr auto placeholderUrl
Definition dotnode.h:70
const DotNodeRefVector & children() const
Definition dotnode.h:122
bool m_deleted
used to mark a node as deleted
Definition dotnode.h:136
void deleteNode(DotNodeRefVector &deletedList)
Definition dotnode.cpp:374
void writeLabel(TextStream &t, GraphType gt) const
Definition dotnode.cpp:416
DotGraph * m_graph
Definition dotnode.h:128
void writeXML(TextStream &t, bool isClassGraph) const
Definition dotnode.cpp:707
DString m_url
url of the node (format: remote$local)
Definition dotnode.h:132
Attributes of an edge of a dot graph.
Definition dotnode.h:32
DString label() const
Definition dotnode.h:40
int style() const
Definition dotnode.h:39
int color() const
Definition dotnode.h:38
@ Green
Definition dotnode.h:34
@ Purple
Definition dotnode.h:34
@ Orange2
Definition dotnode.h:34
@ Orange
Definition dotnode.h:34
A list of MemberDef objects as shown in documentation sections.
Definition memberlist.h:126
Text streaming class that buffers data.
Definition textstream.h:36
virtual DString trAndMore(const DString &number)=0
#define Config_getInt(name)
Definition config.h:34
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
#define Config_getEnum(name)
Definition config.h:35
std::unordered_set< std::string > StringUnorderedSet
Definition containers.h:29
GraphType
Definition dotgraph.h:31
@ Collaboration
Definition dotgraph.h:31
@ Inheritance
Definition dotgraph.h:31
GraphOutputFormat
Definition dotgraph.h:29
static const char * normalEdgeColorMap[]
Definition dotnode.cpp:37
static const char * normalEdgeStyleMap[]
Definition dotnode.cpp:58
static EdgeProperties normalEdgeProps
Definition dotnode.cpp:91
static DString stripProtectionPrefix(const DString &s)
Definition dotnode.cpp:316
static void writeBoxMemberList(TextStream &t, char prot, const MemberList *ml, const ClassDef *scope, bool &lineWritten, bool isStatic=false, const StringUnorderedSet *skipNames=nullptr)
Definition dotnode.cpp:119
static const char * umlEdgeStyleMap[]
Definition dotnode.cpp:85
static const char * normalArrowStyleMap[]
Definition dotnode.cpp:48
static EdgeProperties umlEdgeProps
Definition dotnode.cpp:96
static const char * umlEdgeColorMap[]
Definition dotnode.cpp:64
static const char * umlArrowStyleMap[]
Definition dotnode.cpp:75
DString escapeTooltip(const DString &tooltip)
Definition dotnode.cpp:101
std::vector< DotNode * > DotNodeRefVector
Definition dotnode.h:62
DString escapeTooltip(const DString &tooltip)
Definition dotnode.cpp:101
const char * qPrint(const char *s)
Definition dstring.h:783
Translator * theTranslator
Definition language.cpp:76
#define ASSERT(x)
Definition message.h:142
Helper struct holding the properties of a edge in a dot graph.
Definition dotnode.cpp:30
const char *const * arrowStyleMap
Definition dotnode.cpp:32
const char *const * edgeStyleMap
Definition dotnode.cpp:33
const char *const * edgeColorMap
Definition dotnode.cpp:31
void addHtmlExtensionIfMissing(DString &fName)
Definition util.cpp:3931
DString convertToXML(const DString &s, bool keepEntities, const bool citeEntry)
Definition util.cpp:3232
A bunch of utility functions.