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 }
178 t << DotNode::convertLabel(label,true);
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 if (htmlLike)
210 br = "<BR ALIGN=\"LEFT\"/>";
211 else
212 br = "\\l";
213 while (idx < p.length())
214 {
215 char c = p[idx++];
216 char cs[2] = { c, 0 };
217 const char *replacement = cs;
218 if (htmlLike)
219 {
220 switch(c)
221 {
222 case '\\': replacement="\\\\"; break;
223 case '\n': replacement="\\n"; break;
224 case '<': replacement="&lt;"; break;
225 case '>': replacement="&gt;"; break;
226 case '"': replacement="&quot;"; break;
227 case '\'': replacement="&apos;"; break;
228 case '&': replacement="&amp;"; break;
229 }
230 }
231 else
232 {
233 switch(c)
234 {
235 case '\\': replacement="\\\\"; break;
236 case '\n': replacement="\\n"; break;
237 case '<': replacement="\\<"; break;
238 case '>': replacement="\\>"; break;
239 case '"': replacement="\\\""; break;
240 case '|': replacement="\\|"; break;
241 case '{': replacement="\\{"; break;
242 case '}': replacement="\\}"; break;
243 }
244 }
245 // Some heuristics to insert newlines to prevent too long
246 // boxes and at the same time prevent ugly breaks
247 if (c=='\n')
248 {
249 result+=replacement;
250 foldLen = (3*foldLen+sinceLast+2)/4;
251 sinceLast=1;
252 }
253 else if ((pc!=':' || c!=':') && charsLeft>foldLen/3 && sinceLast>foldLen && bBefore.contains(c))
254 {
255 result+=br;
256 result+=replacement;
257 foldLen = (foldLen+sinceLast+1)/2;
258 sinceLast=1;
259 }
260 else if (charsLeft>1+foldLen/4 && sinceLast>foldLen+foldLen/3 &&
261 !isupper(c) && isupper(p[idx]))
262 {
263 result+=replacement;
264 result+=br;
265 foldLen = (foldLen+sinceLast+1)/2;
266 sinceLast=0;
267 }
268 else if (charsLeft>foldLen/3 && sinceLast>foldLen && bAfter.contains(c) && (c!=':' || p[idx]!=':'))
269 {
270 result+=replacement;
271 result+=br;
272 foldLen = (foldLen+sinceLast+1)/2;
273 sinceLast=0;
274 }
275 else
276 {
277 result+=replacement;
278 sinceLast++;
279 }
280 charsLeft--;
281 pc=c;
282 }
283 if (htmlLike)
284 {
285 result = result.stripWhiteSpace();
286 }
287 return result;
288}
289
291{
292 if (!s.isEmpty() && (s[0]=='-' || s[0]=='+' || s[0]=='~' || s[0]=='#'))
293 {
294 return s.mid(1);
295 }
296 else
297 {
298 return s;
299 }
300}
301
302DotNode::DotNode(DotGraph *graph,const QCString &lab,const QCString &tip, const QCString &url,
303 bool isRoot,const ClassDef *cd)
304 : m_graph(graph)
305 , m_number(graph->getNextNodeNumber())
306 , m_label(lab)
307 , m_tooltip(tip)
308 , m_url(url)
309 , m_isRoot(isRoot)
310 , m_classDef(cd)
311{
312}
313
315 EdgeInfo::Colors edgeColor,
316 EdgeInfo::Styles edgeStyle,
317 const QCString &edgeLab,
318 const QCString &edgeURL,
319 int edgeLabCol
320)
321{
322 m_children.push_back(n);
323 m_edgeInfo.emplace_back(
324 edgeColor,
325 edgeStyle,
326 edgeLab,
327 edgeURL,
328 edgeLabCol==-1 ? edgeColor : edgeLabCol);
329}
330
332{
333 m_parents.push_back(n);
334}
335
337{
338 auto it = std::find(m_children.begin(),m_children.end(),n);
339 if (it!=m_children.end()) m_children.erase(it);
340}
341
343{
344 auto it = std::find(m_parents.begin(),m_parents.end(),n);
345 if (it!=m_parents.end()) m_parents.erase(it);
346}
347
349{
350 if (m_deleted) return; // avoid recursive loops in case the graph has cycles
352 // delete all parent nodes of this node
353 for (const auto &pn : m_parents)
354 {
355 pn->deleteNode(deletedList);
356 }
357 // delete all child nodes of this node
358 for (const auto &cn : m_children)
359 {
360 cn->deleteNode(deletedList);
361 }
362 // add this node to the list of deleted nodes.
363 deletedList.push_back(this);
364}
365
370
372{
373 auto it = std::find(m_parents.begin(),m_parents.end(),n);
374 return it!=m_parents.end() ? static_cast<int>(it-m_parents.begin()) : -1;
375}
376
377/*! helper function that deletes all nodes in a connected graph, given
378* one of the graph's nodes
379*/
381{
382 DotNodeRefVector deletedNodes;
383 node->deleteNode(deletedNodes); // collect nodes to be deleted.
384 for (const auto &dotNode : deletedNodes)
385 {
386 delete dotNode;
387 }
388}
389
391{
393 {
394 // Set shape to the plain type.
395 // the UML properties and methods are rendered using dot' HTML like table format
396 t << "shape=plain,label=";
397 // add names shown as relations to a set, so we don't show
398 // them as attributes as well
399 StringUnorderedSet arrowNames;
400 // for each edge
401 for (const auto &ei : m_edgeInfo)
402 {
403 if (!ei.label().isEmpty()) // labels joined by \n
404 {
405 int i=0;
406 int p=0;
407 QCString lab;
408 while ((i=ei.label().find('\n',p))!=-1)
409 {
410 lab = stripProtectionPrefix(ei.label().mid(p,i-p));
411 arrowNames.insert(lab.str());
412 p=i+1;
413 }
414 lab = stripProtectionPrefix(ei.label().right(ei.label().length()-p));
415 arrowNames.insert(lab.str());
416 }
417 }
418
419 constexpr auto hr_start = "<TR><TD COLSPAN=\"2\" CELLPADDING=\"1\" CELLSPACING=\"0\">";
420 constexpr auto hr_end = "</TD></TR>\n";
421 constexpr auto sep = "<HR/>\n";
422 constexpr auto empty_line = "<TR><TD COLSPAN=\"2\" CELLPADDING=\"1\" CELLSPACING=\"0\">&nbsp;</TD></TR>\n";
423 //printf("DotNode::writeBox for %s\n",qPrint(m_classDef->name()));
424 t << "<<TABLE CELLBORDER=\"0\" BORDER=\"1\">";
425 t << hr_start << convertLabel(m_label,true) << hr_end;
426 auto dotUmlDetails = Config_getEnum(DOT_UML_DETAILS);
427 if (dotUmlDetails!=DOT_UML_DETAILS_t::NONE)
428 {
429 bool lineWritten = false;
430 t << sep;
431 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubAttribs()),m_classDef,lineWritten,FALSE,&arrowNames);
432 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubStaticAttribs()),m_classDef,lineWritten,TRUE,&arrowNames);
433 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::Properties()),m_classDef,lineWritten,FALSE,&arrowNames);
434 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacAttribs()),m_classDef,lineWritten,FALSE,&arrowNames);
435 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacStaticAttribs()),m_classDef,lineWritten,TRUE,&arrowNames);
436 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProAttribs()),m_classDef,lineWritten,FALSE,&arrowNames);
437 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProStaticAttribs()),m_classDef,lineWritten,TRUE,&arrowNames);
438 if (Config_getBool(EXTRACT_PRIVATE))
439 {
440 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriAttribs()),m_classDef,lineWritten,FALSE,&arrowNames);
441 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriStaticAttribs()),m_classDef,lineWritten,TRUE,&arrowNames);
442 }
443 if (!lineWritten) t << empty_line;
444 t << sep;
445 lineWritten = false;
446 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubMethods()),m_classDef,lineWritten);
447 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubStaticMethods()),m_classDef,lineWritten,TRUE);
448 writeBoxMemberList(t,'+',m_classDef->getMemberList(MemberListType::PubSlots()),m_classDef,lineWritten);
449 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacMethods()),m_classDef,lineWritten);
450 writeBoxMemberList(t,'~',m_classDef->getMemberList(MemberListType::PacStaticMethods()),m_classDef,lineWritten,TRUE);
451 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProMethods()),m_classDef,lineWritten);
452 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProStaticMethods()),m_classDef,lineWritten,TRUE);
453 writeBoxMemberList(t,'#',m_classDef->getMemberList(MemberListType::ProSlots()),m_classDef,lineWritten);
454 if (Config_getBool(EXTRACT_PRIVATE))
455 {
456 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriMethods()),m_classDef,lineWritten);
457 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriStaticMethods()),m_classDef,lineWritten,TRUE);
458 writeBoxMemberList(t,'-',m_classDef->getMemberList(MemberListType::PriSlots()),m_classDef,lineWritten);
459 }
460 if (m_classDef->getLanguage()!=SrcLangExt::Fortran)
461 {
462 for (const auto &mg : m_classDef->getMemberGroups())
463 {
464 if (!mg->members().empty())
465 {
466 writeBoxMemberList(t,'*',&mg->members(),m_classDef,lineWritten,FALSE,&arrowNames);
467 }
468 }
469 }
470 if (!lineWritten) t << empty_line;
471 }
472 t << "</TABLE>>\n";
473 }
474 else if (Config_getString(DOT_NODE_ATTR).contains("shape=plain"))
475 {
476 t << "label=";
477 if (m_isRoot)
478 t << "<<b>" << convertToXML(m_label) << "</b>>";
479 else if (m_truncated == Truncated)
480 t << "<<i>" << convertToXML(m_label) << "</i>>";
481 else
482 t << '"' << convertLabel(m_label) << '"';
483 }
484 else // standard look
485 {
486 t << "label=" << '"' << convertLabel(m_label) << '"';
487 }
488}
489
491{
492 if (m_url.isEmpty() || m_url == DotNode::placeholderUrl) return;
493 int tagPos = m_url.findRev('$');
494 t << ",URL=\"";
495 QCString noTagURL = m_url;
496 if (tagPos!=-1)
497 {
498 t << m_url.left(tagPos);
499 noTagURL = m_url.mid(tagPos);
500 }
501 int anchorPos = noTagURL.findRev('#');
502 if (anchorPos==-1)
503 {
505 t << noTagURL << "\"";
506 }
507 else // insert extensiom before anchor
508 {
509 QCString fn = noTagURL.left(anchorPos);
511 t << fn << noTagURL.right(noTagURL.length() - anchorPos) << "\"";
512 }
513}
514
516 GraphType gt,
517 GraphOutputFormat /*format*/,
518 bool hasNonReachableChildren) const
519{
520 const char *labCol = nullptr;
521 const char *fillCol = "white";
522 if (m_classDef)
523 {
524 if (m_classDef->hasDocumentation() && hasNonReachableChildren)
525 {
526 labCol = "red";
527 fillCol = "#FFF0F0";
528 }
529 else if (m_classDef->hasDocumentation() && !hasNonReachableChildren)
530 {
531 labCol = "gray40";
532 }
533 else if (!m_classDef->hasDocumentation() && hasNonReachableChildren)
534 {
535 labCol = "orangered";
536 }
537 else // (!m_classDef->hasDocumentation() && !hasNonReachableChildren)
538 {
539 labCol = "grey75";
540 if (m_classDef->templateMaster() && m_classDef->templateMaster()->hasDocumentation())
541 {
542 labCol = "gray40";
543 }
544 }
545 }
546 else
547 {
548 labCol = m_url.isEmpty() ? "grey60" : // non link
549 (hasNonReachableChildren ? "red" : "grey40");
550 fillCol = m_url.isEmpty() ? "#E0E0E0" :
551 (hasNonReachableChildren ? "#FFF0F0" : "white");
552 }
553 t << " Node" << m_number << " [";
554 t << "id=\"Node" << QCString().sprintf("%06d",m_number) << "\",";
555 writeLabel(t,gt);
556 t << ",height=0.2,width=0.4";
557 if (m_isRoot)
558 {
559 t << ",color=\"gray40\", fillcolor=\"grey60\", style=\"filled\", fontcolor=\"black\"";
560 }
561 else
562 {
563 t << ",color=\"" << labCol << "\"";
564 t << ", fillcolor=\"" << fillCol << "\"";
565 t << ", style=\"filled\"";
566 writeUrl(t);
567 }
568 if (!m_tooltip.isEmpty())
569 {
570 t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\"";
571 }
572 else
573 {
574 t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0' is used
575 }
576 t << "];\n";
577}
578
580 GraphType gt,
581 GraphOutputFormat /* format */,
582 const DotNode *cn,
583 const EdgeInfo *ei,
584 bool topDown,
585 bool pointBack) const
586{
587 t << " Node";
588 if (topDown)
589 t << cn->number();
590 else
591 t << m_number;
592 t << " -> Node";
593 if (topDown)
594 t << m_number;
595 else
596 t << cn->number();
597 t << " [";
598
599 const EdgeProperties *eProps = Config_getBool(UML_LOOK) ? &umlEdgeProps : &normalEdgeProps;
600 QCString aStyle = eProps->arrowStyleMap[ei->color()];
601 bool umlUseArrow = aStyle=="odiamond";
602
603 t << "id=\"edge" << m_graph->getNextEdgeNumber() <<
604 "_Node" << QCString().sprintf("%06d",m_number) <<
605 "_Node" << QCString().sprintf("%06d",cn->number()) << "\",";
606 if (pointBack && !umlUseArrow) t << "dir=\"back\",";
607 t << "color=\"" << eProps->edgeColorMap[ei->color()] << "\",";
608 t << "style=\"" << eProps->edgeStyleMap[ei->style()] << "\"";
609 t << ",tooltip=\" \""; // space in tooltip is required otherwise still something like 'Node0 -> Node1' is used
610 if (!ei->label().isEmpty())
611 {
612 t << ",label=\" " << convertLabel(ei->label()) << "\",fontcolor=\"grey\" ";
613 }
614 if (Config_getBool(UML_LOOK) &&
615 eProps->arrowStyleMap[ei->color()] &&
617 )
618 {
619 bool rev = pointBack;
620 if (umlUseArrow) rev=!rev; // UML use relates has arrow on the start side
621 if (rev)
622 t << ",arrowtail=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
623 else
624 t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->color()] << "\"";
625 }
626
627 t << "];\n";
628}
629
631 GraphType gt,
632 GraphOutputFormat format,
633 bool topDown,
634 bool toChildren,
635 bool backArrows)
636{
637 //printf("DotNode::write(%d) name=%s this=%p written=%d visible=%d\n",m_distance,qPrint(m_label),this,m_written,m_visible);
638 if (m_written) return; // node already written to the output
639 if (!m_visible) return; // node is not visible
640 writeBox(t,gt,format,m_truncated==Truncated);
642 if (toChildren)
643 {
644 auto it = m_edgeInfo.begin();
645 for (const auto &cn : m_children)
646 {
647 if (cn->isVisible())
648 {
649 //printf("write arrow %s%s%s\n",qPrint(label()),backArrows?"<-":"->",qPrint(cn->label()));
650 writeArrow(t,gt,format,cn,&(*it),topDown,backArrows);
651 }
652 cn->write(t,gt,format,topDown,toChildren,backArrows);
653 ++it;
654 }
655 }
656 else // render parents
657 {
658 for (const auto &pn : m_parents)
659 {
660 if (pn->isVisible())
661 {
662 const auto &children = pn->children();
663 auto child_it = std::find(children.begin(),children.end(),this);
664 size_t index = child_it - children.begin();
665 //printf("write arrow %s%s%s\n",qPrint(label()),backArrows?"<-":"->",qPrint(pn->label()));
666 writeArrow(t,
667 gt,
668 format,
669 pn,
670 &pn->edgeInfo()[index],
671 FALSE,
672 backArrows
673 );
674 }
675 pn->write(t,gt,format,TRUE,FALSE,backArrows);
676 }
677 }
678 //printf("end DotNode::write(%d) name=%s\n",distance,qPrint(m_label));
679}
680
681void DotNode::writeXML(TextStream &t,bool isClassGraph) const
682{
683 t << " <node id=\"" << m_number << "\">\n";
684 t << " <label>" << convertToXML(m_label) << "</label>\n";
685 if (!m_url.isEmpty())
686 {
687 QCString url(m_url);
688 int dollarPos = url.find('$');
689 if (dollarPos!=-1)
690 {
691 t << " <link refid=\"" << convertToXML(url.mid(dollarPos+1)) << "\"";
692 if (dollarPos>0)
693 {
694 t << " external=\"" << convertToXML(url.left(dollarPos)) << "\"";
695 }
696 t << "/>\n";
697 }
698 }
699 auto it = m_edgeInfo.begin();
700 for (const auto &childNode : m_children)
701 {
702 const EdgeInfo &edgeInfo = *it;
703 t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
704 if (isClassGraph)
705 {
706 switch(edgeInfo.color())
707 {
708 case EdgeInfo::Blue: t << "public-inheritance"; break;
709 case EdgeInfo::Green: t << "protected-inheritance"; break;
710 case EdgeInfo::Red: t << "private-inheritance"; break;
711 case EdgeInfo::Purple: t << "usage"; break;
712 case EdgeInfo::Orange: t << "template-instance"; break;
713 case EdgeInfo::Orange2: t << "type-constraint"; break;
714 case EdgeInfo::Grey: ASSERT(0); break;
715 }
716 }
717 else // include graph
718 {
719 t << "include";
720 }
721 t << "\">\n";
722 if (!edgeInfo.label().isEmpty())
723 {
724 int p=0;
725 int ni=0;
726 while ((ni=edgeInfo.label().find('\n',p))!=-1)
727 {
728 t << " <edgelabel>"
729 << convertToXML(edgeInfo.label().mid(p,ni-p))
730 << "</edgelabel>\n";
731 p=ni+1;
732 }
733 t << " <edgelabel>"
734 << convertToXML(edgeInfo.label().right(edgeInfo.label().length()-p))
735 << "</edgelabel>\n";
736 }
737 t << " </childnode>\n";
738 ++it;
739 }
740 t << " </node>\n";
741}
742
743void DotNode::writeDocbook(TextStream &t,bool isClassGraph) const
744{
745 t << " <node id=\"" << m_number << "\">\n";
746 t << " <label>" << convertToXML(m_label) << "</label>\n";
747 if (!m_url.isEmpty())
748 {
749 QCString url(m_url);
750 int dollarPos = url.find('$');
751 if (dollarPos!=-1)
752 {
753 t << " <link refid=\"" << convertToXML(url.mid(dollarPos+1)) << "\"";
754 if (dollarPos>0)
755 {
756 t << " external=\"" << convertToXML(url.left(dollarPos)) << "\"";
757 }
758 t << "/>\n";
759 }
760 }
761 auto it = m_edgeInfo.begin();
762 for (const auto &childNode : m_children)
763 {
764 const EdgeInfo &edgeInfo = *it;
765 t << " <childnode refid=\"" << childNode->number() << "\" relation=\"";
766 if (isClassGraph)
767 {
768 switch(edgeInfo.color())
769 {
770 case EdgeInfo::Blue: t << "public-inheritance"; break;
771 case EdgeInfo::Green: t << "protected-inheritance"; break;
772 case EdgeInfo::Red: t << "private-inheritance"; break;
773 case EdgeInfo::Purple: t << "usage"; break;
774 case EdgeInfo::Orange: t << "template-instance"; break;
775 case EdgeInfo::Orange2: t << "type-constraint"; break;
776 case EdgeInfo::Grey: ASSERT(0); break;
777 }
778 }
779 else // include graph
780 {
781 t << "include";
782 }
783 t << "\">\n";
784 if (!edgeInfo.label().isEmpty())
785 {
786 int p=0;
787 int ni=0;
788 while ((ni=edgeInfo.label().find('\n',p))!=-1)
789 {
790 t << " <edgelabel>"
791 << convertToXML(edgeInfo.label().mid(p,ni-p))
792 << "</edgelabel>\n";
793 p=ni+1;
794 }
795 t << " <edgelabel>"
796 << convertToXML(edgeInfo.label().right(edgeInfo.label().length()-p))
797 << "</edgelabel>\n";
798 }
799 t << " </childnode>\n";
800 ++it;
801 }
802 t << " </node>\n";
803}
804
805
807{
808 const char* nodePrefix = " node-";
809
810 t << " node = {\n";
811 t << nodePrefix << "id = " << m_number << ";\n";
812 t << nodePrefix << "label = '" << m_label << "';\n";
813
814 if (!m_url.isEmpty())
815 {
816 QCString url(m_url);
817 int dollarPos = url.find('$');
818 if (dollarPos!=-1)
819 {
820 t << nodePrefix << "link = {\n" << " "
821 << nodePrefix << "link-id = '" << url.mid(dollarPos+1) << "';\n";
822 if (dollarPos>0)
823 {
824 t << " " << nodePrefix << "link-external = '"
825 << url.left(dollarPos) << "';\n";
826 }
827 t << " };\n";
828 }
829 }
830 auto it = m_edgeInfo.begin();
831 for (const auto &childNode : m_children)
832 {
833 const EdgeInfo &edgeInfo = *it;
834 t << " node-child = {\n";
835 t << " child-id = '" << childNode->number() << "';\n";
836 t << " relation = ";
837
838 switch (edgeInfo.color())
839 {
840 case EdgeInfo::Blue: t << "public-inheritance"; break;
841 case EdgeInfo::Green: t << "protected-inheritance"; break;
842 case EdgeInfo::Red: t << "private-inheritance"; break;
843 case EdgeInfo::Purple: t << "usage"; break;
844 case EdgeInfo::Orange: t << "template-instance"; break;
845 case EdgeInfo::Orange2: t << "type-constraint"; break;
846 case EdgeInfo::Grey: ASSERT(0); break;
847 }
848 t << ";\n";
849
850 if (!edgeInfo.label().isEmpty())
851 {
852 t << " edgelabel = <<_EnD_oF_dEf_TeXt_\n"
853 << edgeInfo.label() << "\n"
854 << "_EnD_oF_dEf_TeXt_;\n";
855 }
856 t << " }; /* node-child */\n";
857 ++it;
858 }
859 t << " }; /* node */\n";
860}
861
862
864{
866 for (const auto &pn : m_parents) if (pn->isWritten()) pn->clearWriteFlag();
867 for (const auto &cn : m_children) if (cn->isWritten()) cn->clearWriteFlag();
868}
869
871{
872 for (const auto &cn : m_children)
873 {
874 if (cn->subgraphId()==-1) // uncolored child node
875 {
876 cn->setSubgraphId(curColor);
877 cn->markAsVisible();
878 cn->colorConnectedNodes(curColor);
879 //printf("coloring node %s (%p): %d\n",qPrint(cn->label()),cn,cn->subgraphId());
880 }
881 }
882
883 for (const auto &pn : m_parents)
884 {
885 if (pn->subgraphId()==-1) // uncolored parent node
886 {
887 pn->setSubgraphId(curColor);
888 pn->markAsVisible();
889 pn->colorConnectedNodes(curColor);
890 //printf("coloring node %s (%p): %d\n",qPrint(pn->label()),pn,pn->subgraphId());
891 }
892 }
893}
894
895#define DEBUG_RENUMBERING 0
896
898{
899 if (!isRenumbered())
900 {
901#if DEBUG_RENUMBERING
902 static int level = 0;
903 printf("%3d: ",subgraphId());
904 for (int i = 0; i < level; i++) printf(" ");
905 printf("> %s old = %d new = %d\n",qPrint(m_label),m_number,number);
906 level++;
907#endif
908 m_number = number++;
910 for (const auto &cn : m_children)
911 {
912 cn->renumberNodes(number);
913 }
914 for (const auto &pn : m_parents)
915 {
916 pn->renumberNodes(number);
917 }
918#if DEBUG_RENUMBERING
919 level--;
920 printf("%3d: ",subgraphId());
921 for (int i = 0; i < level; i++) printf(" ");
922 printf("< %s assigned = %d\n",qPrint(m_label),m_number);
923#endif
924 }
925}
926
927
928
929
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:806
int findParent(DotNode *n)
Definition dotnode.cpp:371
int m_number
Definition dotnode.h:129
int distance() const
Definition dotnode.h:106
void writeUrl(TextStream &t) const
Definition dotnode.cpp:490
QCString m_tooltip
node's tooltip
Definition dotnode.h:131
void markRenumbered()
Definition dotnode.h:114
void setDistance(int distance)
Definition dotnode.cpp:366
void write(TextStream &t, GraphType gt, GraphOutputFormat f, bool topDown, bool toChildren, bool backArrows)
Definition dotnode.cpp:630
bool m_isRoot
indicates if this is a root node
Definition dotnode.h:139
void clearWriteFlag()
Definition dotnode.cpp:863
void removeParent(DotNode *n)
Definition dotnode.cpp:342
QCString m_label
label text
Definition dotnode.h:130
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:515
TruncState m_truncated
does the node have non-visible children/parents
Definition dotnode.h:142
DotNode(DotGraph *graph, const QCString &lab, const QCString &tip, const QCString &url, bool rootNode=FALSE, const ClassDef *cd=nullptr)
Definition dotnode.cpp:302
void writeDocbook(TextStream &t, bool isClassGraph) const
Definition dotnode.cpp:743
void renumberNodes(int &number)
Definition dotnode.cpp:897
bool m_written
used to mark a node as written
Definition dotnode.h:137
void removeChild(DotNode *n)
Definition dotnode.cpp:336
static void deleteNodes(DotNode *node)
Definition dotnode.cpp:380
static QCString convertLabel(const QCString &, bool htmlLike=false)
Definition dotnode.cpp:196
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:331
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 colorConnectedNodes(int curColor)
Definition dotnode.cpp:870
DotNodeRefVector m_children
list of child nodes (outgoing arrows)
Definition dotnode.h:134
@ Truncated
Definition dotnode.h:76
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:314
const ClassDef * m_classDef
class representing this node (can be 0)
Definition dotnode.h:140
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:348
void writeLabel(TextStream &t, GraphType gt) const
Definition dotnode.cpp:390
void writeArrow(TextStream &t, GraphType gt, GraphOutputFormat f, const DotNode *cn, const EdgeInfo *ei, bool topDown, bool pointBack=TRUE) const
Definition dotnode.cpp:579
DotGraph * m_graph
Definition dotnode.h:128
void writeXML(TextStream &t, bool isClassGraph) const
Definition dotnode.cpp:681
QCString m_url
url of the node (format: remote$local)
Definition dotnode.h:132
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:108
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:526
QCString right(size_t len) const
Definition qcstring.h:219
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:290
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:661
#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:4266
void addHtmlExtensionIfMissing(QCString &fName)
Definition util.cpp:5243
A bunch of utility functions.