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