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