1 /******************************************************************************
3 * Copyright (C) 1997-2020 by Dimitri van Heesch.
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.
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
16 #include "xmldocvisitor.h"
17 #include "docparser.h"
20 #include "outputgen.h"
25 #include "parserintf.h"
28 #include "htmlentity.h"
33 static void startSimpleSect(TextStream &t,const DocSimpleSect &s)
35 t << "<simplesect kind=\"";
38 case DocSimpleSect::See:
40 case DocSimpleSect::Return:
42 case DocSimpleSect::Author:
44 case DocSimpleSect::Authors:
45 t << "authors"; break;
46 case DocSimpleSect::Version:
47 t << "version"; break;
48 case DocSimpleSect::Since:
50 case DocSimpleSect::Date:
52 case DocSimpleSect::Note:
54 case DocSimpleSect::Warning:
55 t << "warning"; break;
56 case DocSimpleSect::Pre:
58 case DocSimpleSect::Post:
60 case DocSimpleSect::Copyright:
61 t << "copyright"; break;
62 case DocSimpleSect::Invar:
63 t << "invariant"; break;
64 case DocSimpleSect::Remark:
66 case DocSimpleSect::Attention:
67 t << "attention"; break;
68 case DocSimpleSect::User:
70 case DocSimpleSect::Rcs:
72 case DocSimpleSect::Unknown: break;
77 static void endSimpleSect(TextStream &t,const DocSimpleSect &)
79 t << "</simplesect>\n";
82 static void visitCaption(XmlDocVisitor &visitor, const DocNodeList &children)
84 for (const auto &n : children)
86 std::visit(visitor,n);
90 static void visitPreStart(TextStream &t, const char *cmd, bool doCaption,
91 XmlDocVisitor &visitor, const DocNodeList &children,
92 const QCString &name, bool writeType, DocImage::Type type, const QCString &width,
93 const QCString &height, const QCString engine = QCString(), const QCString &alt = QCString(), bool inlineImage = FALSE)
101 case DocImage::Html: t << "html"; break;
102 case DocImage::Latex: t << "latex"; break;
103 case DocImage::Rtf: t << "rtf"; break;
104 case DocImage::DocBook: t << "docbook"; break;
105 case DocImage::Xml: t << "xml"; break;
111 t << " name=\"" << convertToXML(name, TRUE) << "\"";
113 if (!width.isEmpty())
115 t << " width=\"" << convertToXML(width) << "\"";
117 if (!height.isEmpty())
119 t << " height=\"" << convertToXML(height) << "\"";
121 if (!engine.isEmpty())
123 t << " engine=\"" << convertToXML(engine) << "\"";
127 t << " alt=\"" << convertToXML(alt) << "\"";
131 t << " inline=\"yes\"";
136 visitCaption(visitor, children);
142 static void visitPostEnd(TextStream &t, const char *cmd)
144 t << "</" << cmd << ">\n";
147 XmlDocVisitor::XmlDocVisitor(TextStream &t,OutputCodeList &ci,const QCString &langExt)
148 : m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE),
153 //--------------------------------------
154 // visitor functions for leaf nodes
155 //--------------------------------------
157 void XmlDocVisitor::operator()(const DocWord &w)
163 void XmlDocVisitor::operator()(const DocLinkedWord &w)
166 startLink(w.ref(),w.file(),w.anchor());
171 void XmlDocVisitor::operator()(const DocWhiteSpace &w)
184 void XmlDocVisitor::operator()(const DocSymbol &s)
187 const char *res = HtmlEntityMapper::instance().xml(s.symbol());
194 err("XML: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance().html(s.symbol(),TRUE));
198 void XmlDocVisitor::operator()(const DocEmoji &s)
201 const char *res = EmojiEntityMapper::instance().name(s.index());
205 name = name.mid(1,name.length()-2);
206 m_t << "<emoji name=\"" << name << "\" unicode=\"";
207 filter(EmojiEntityMapper::instance().unicode(s.index()));
216 void XmlDocVisitor::operator()(const DocURL &u)
219 m_t << "<ulink url=\"";
220 if (u.isEmail()) m_t << "mailto:";
227 void XmlDocVisitor::operator()(const DocLineBreak &)
230 m_t << "<linebreak/>\n";
233 void XmlDocVisitor::operator()(const DocHorRuler &)
236 m_t << "<hruler/>\n";
239 void XmlDocVisitor::operator()(const DocStyleChange &s)
244 case DocStyleChange::Bold:
245 if (s.enable()) m_t << "<bold>"; else m_t << "</bold>";
247 case DocStyleChange::S:
248 if (s.enable()) m_t << "<s>"; else m_t << "</s>";
250 case DocStyleChange::Strike:
251 if (s.enable()) m_t << "<strike>"; else m_t << "</strike>";
253 case DocStyleChange::Del:
254 if (s.enable()) m_t << "<del>"; else m_t << "</del>";
256 case DocStyleChange::Underline:
257 if (s.enable()) m_t << "<underline>"; else m_t << "</underline>";
259 case DocStyleChange::Ins:
260 if (s.enable()) m_t << "<ins>"; else m_t << "</ins>";
262 case DocStyleChange::Italic:
263 if (s.enable()) m_t << "<emphasis>"; else m_t << "</emphasis>";
265 case DocStyleChange::Code:
266 if (s.enable()) m_t << "<computeroutput>"; else m_t << "</computeroutput>";
268 case DocStyleChange::Subscript:
269 if (s.enable()) m_t << "<subscript>"; else m_t << "</subscript>";
271 case DocStyleChange::Superscript:
272 if (s.enable()) m_t << "<superscript>"; else m_t << "</superscript>";
274 case DocStyleChange::Center:
275 if (s.enable()) m_t << "<center>"; else m_t << "</center>";
277 case DocStyleChange::Small:
278 if (s.enable()) m_t << "<small>"; else m_t << "</small>";
280 case DocStyleChange::Cite:
281 if (s.enable()) m_t << "<cite>"; else m_t << "</cite>";
283 case DocStyleChange::Preformatted:
286 m_t << "<preformatted>";
291 m_t << "</preformatted>";
295 case DocStyleChange::Div: /* HTML only */ break;
296 case DocStyleChange::Span: /* HTML only */ break;
300 void XmlDocVisitor::operator()(const DocVerbatim &s)
303 QCString lang = m_langExt;
304 if (!s.language().isEmpty()) // explicit language setting
308 SrcLangExt langExt = getLanguageFromCodeLang(lang);
311 case DocVerbatim::Code:
312 m_t << "<programlisting";
313 if (!s.language().isEmpty())
314 m_t << " filename=\"" << lang << "\">";
317 getCodeParser(lang).parseCode(m_ci,s.context(),s.text(),langExt,
318 s.isExample(),s.exampleFile());
319 m_t << "</programlisting>";
321 case DocVerbatim::JavaDocLiteral:
322 m_t << "<javadocliteral>";
324 m_t << "</javadocliteral>";
326 case DocVerbatim::JavaDocCode:
327 m_t << "<javadoccode>";
329 m_t << "</javadoccode>";
331 case DocVerbatim::Verbatim:
334 m_t << "</verbatim>";
336 case DocVerbatim::HtmlOnly:
339 m_t << "<htmlonly block=\"yes\">";
346 m_t << "</htmlonly>";
348 case DocVerbatim::RtfOnly:
353 case DocVerbatim::ManOnly:
358 case DocVerbatim::LatexOnly:
359 m_t << "<latexonly>";
361 m_t << "</latexonly>";
363 case DocVerbatim::DocbookOnly:
364 m_t << "<docbookonly>";
366 m_t << "</docbookonly>";
368 case DocVerbatim::XmlOnly:
371 case DocVerbatim::Dot:
372 visitPreStart(m_t, "dot", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height());
374 visitPostEnd(m_t, "dot");
376 case DocVerbatim::Msc:
377 visitPreStart(m_t, "msc", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height());
379 visitPostEnd(m_t, "msc");
381 case DocVerbatim::PlantUML:
382 visitPreStart(m_t, "plantuml", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height(), s.engine());
384 visitPostEnd(m_t, "plantuml");
389 void XmlDocVisitor::operator()(const DocAnchor &anc)
392 m_t << "<anchor id=\"" << anc.file() << "_1" << anc.anchor() << "\"/>";
395 void XmlDocVisitor::operator()(const DocInclude &inc)
398 SrcLangExt langExt = getLanguageFromFileName(inc.extension());
401 case DocInclude::IncWithLines:
403 m_t << "<programlisting filename=\"" << inc.file() << "\">";
404 FileInfo cfi( inc.file().str() );
405 auto fd = createFileDef( cfi.dirPath(), cfi.fileName());
406 getCodeParser(inc.extension()).parseCode(m_ci,inc.context(),
411 fd.get(), // fileDef,
414 FALSE, // inline fragment
416 TRUE // show line numbers
418 m_t << "</programlisting>";
421 case DocInclude::Include:
422 m_t << "<programlisting filename=\"" << inc.file() << "\">";
423 getCodeParser(inc.extension()).parseCode(m_ci,inc.context(),
431 TRUE, // inlineFragment
433 FALSE // show line numbers
435 m_t << "</programlisting>";
437 case DocInclude::DontInclude:
438 case DocInclude::DontIncWithLines:
440 case DocInclude::HtmlInclude:
443 m_t << "<htmlonly block=\"yes\">";
450 m_t << "</htmlonly>";
452 case DocInclude::LatexInclude:
453 m_t << "<latexonly>";
455 m_t << "</latexonly>";
457 case DocInclude::RtfInclude:
462 case DocInclude::ManInclude:
467 case DocInclude::XmlInclude:
470 case DocInclude::DocbookInclude:
471 m_t << "<docbookonly>";
473 m_t << "</docbookonly>";
475 case DocInclude::VerbInclude:
478 m_t << "</verbatim>";
480 case DocInclude::Snippet:
481 case DocInclude::SnippetTrimLeft:
482 m_t << "<programlisting filename=\"" << inc.file() << "\">";
483 getCodeParser(inc.extension()).parseCode(m_ci,
485 extractBlock(inc.text(),inc.blockId(),inc.type()==DocInclude::SnippetTrimLeft),
490 m_t << "</programlisting>";
492 case DocInclude::SnipWithLines:
494 m_t << "<programlisting filename=\"" << inc.file() << "\">";
495 FileInfo cfi( inc.file().str() );
496 auto fd = createFileDef( cfi.dirPath(), cfi.fileName());
497 getCodeParser(inc.extension()).parseCode(m_ci,
499 extractBlock(inc.text(),inc.blockId()),
504 lineBlock(inc.text(),inc.blockId()),
506 FALSE, // inlineFragment
508 TRUE // show line number
510 m_t << "</programlisting>";
513 case DocInclude::SnippetDoc:
514 case DocInclude::IncludeDoc:
515 err("Internal inconsistency: found switch SnippetDoc / IncludeDoc in file: %s"
516 "Please create a bug report\n",__FILE__);
521 void XmlDocVisitor::operator()(const DocIncOperator &op)
523 //printf("DocIncOperator: type=%d first=%d, last=%d text='%s'\n",
524 // op.type(),op.isFirst(),op.isLast(),qPrint(op.text()));
529 m_t << "<programlisting filename=\"" << op.includeFileName() << "\">";
534 QCString locLangExt = getFileNameExtension(op.includeFileName());
535 if (locLangExt.isEmpty()) locLangExt = m_langExt;
536 SrcLangExt langExt = getLanguageFromFileName(locLangExt);
537 if (op.type()!=DocIncOperator::Skip)
539 m_hide = popHidden();
542 std::unique_ptr<FileDef> fd;
543 if (!op.includeFileName().isEmpty())
545 FileInfo cfi( op.includeFileName().str() );
546 fd = createFileDef( cfi.dirPath(), cfi.fileName() );
549 getCodeParser(locLangExt).parseCode(m_ci,op.context(),
550 op.text(),langExt,op.isExample(),
553 op.line(), // startLine
555 FALSE, // inline fragment
557 op.showLineNo() // show line numbers
565 m_hide = popHidden();
566 if (!m_hide) m_t << "</programlisting>";
570 if (!m_hide) m_t << "\n";
574 void XmlDocVisitor::operator()(const DocFormula &f)
577 m_t << "<formula id=\"" << f.id() << "\">";
582 void XmlDocVisitor::operator()(const DocIndexEntry &ie)
585 m_t << "<indexentry>"
588 m_t << "</primaryie>"
589 "<secondaryie></secondaryie>"
593 void XmlDocVisitor::operator()(const DocSimpleSectSep &sep)
595 const DocSimpleSect *sect = std::get_if<DocSimpleSect>(sep.parent());
598 endSimpleSect(m_t,*sect);
599 startSimpleSect(m_t,*sect);
603 void XmlDocVisitor::operator()(const DocCite &cite)
606 if (!cite.file().isEmpty()) startLink(cite.ref(),cite.file(),cite.anchor());
608 if (!cite.file().isEmpty()) endLink();
611 //--------------------------------------
612 // visitor functions for compound nodes
613 //--------------------------------------
615 void XmlDocVisitor::operator()(const DocAutoList &l)
620 m_t << "<orderedlist>\n";
624 m_t << "<itemizedlist>\n";
629 m_t << "</orderedlist>\n";
633 m_t << "</itemizedlist>\n";
637 void XmlDocVisitor::operator()(const DocAutoListItem &li)
642 m_t << "</listitem>";
645 void XmlDocVisitor::operator()(const DocPara &p)
653 void XmlDocVisitor::operator()(const DocRoot &r)
658 void XmlDocVisitor::operator()(const DocSimpleSect &s)
661 startSimpleSect(m_t,s);
664 std::visit(*this,*s.title());
667 endSimpleSect(m_t,s);
670 void XmlDocVisitor::operator()(const DocTitle &t)
678 void XmlDocVisitor::operator()(const DocSimpleList &l)
681 m_t << "<itemizedlist>\n";
683 m_t << "</itemizedlist>\n";
686 void XmlDocVisitor::operator()(const DocSimpleListItem &li)
692 std::visit(*this,*li.paragraph());
694 m_t << "</listitem>\n";
697 void XmlDocVisitor::operator()(const DocSection &s)
700 m_t << "<sect" << s.level() << " id=\"" << s.file();
701 if (!s.anchor().isEmpty()) m_t << "_1" << s.anchor();
704 filter(convertCharEntitiesToUTF8(s.title()));
707 m_t << "</sect" << s.level() << ">\n";
710 void XmlDocVisitor::operator()(const DocHtmlList &s)
713 if (s.type()==DocHtmlList::Ordered)
715 m_t << "<orderedlist";
716 for (const auto &opt : s.attribs())
718 m_t << " " << opt.name << "=\"" << opt.value << "\"";
724 m_t << "<itemizedlist>\n";
727 if (s.type()==DocHtmlList::Ordered)
729 m_t << "</orderedlist>\n";
733 m_t << "</itemizedlist>\n";
737 void XmlDocVisitor::operator()(const DocHtmlListItem &l)
741 for (const auto &opt : l.attribs())
743 if (opt.name=="value")
745 m_t << " " << opt.name << "=\"" << opt.value << "\"";
750 m_t << "</listitem>\n";
753 void XmlDocVisitor::operator()(const DocHtmlDescList &dl)
756 m_t << "<variablelist>\n";
758 m_t << "</variablelist>\n";
761 void XmlDocVisitor::operator()(const DocHtmlDescTitle &dt)
764 m_t << "<varlistentry><term>";
766 m_t << "</term></varlistentry>\n";
769 void XmlDocVisitor::operator()(const DocHtmlDescData &dd)
774 m_t << "</listitem>\n";
777 void XmlDocVisitor::operator()(const DocHtmlTable &t)
780 m_t << "<table rows=\"" << t.numRows()
781 << "\" cols=\"" << t.numColumns() << "\"" ;
782 for (const auto &opt : t.attribs())
784 if (opt.name=="width")
786 m_t << " " << opt.name << "=\"" << opt.value << "\"";
792 std::visit(*this,*t.caption());
798 void XmlDocVisitor::operator()(const DocHtmlRow &r)
806 void XmlDocVisitor::operator()(const DocHtmlCell &c)
809 if (c.isHeading()) m_t << "<entry thead=\"yes\""; else m_t << "<entry thead=\"no\"";
810 for (const auto &opt : c.attribs())
812 if (opt.name=="colspan" || opt.name=="rowspan")
814 m_t << " " << opt.name << "=\"" << opt.value.toInt() << "\"";
816 else if (opt.name=="align" &&
817 (opt.value=="right" || opt.value=="left" || opt.value=="center"))
819 m_t << " align=\"" << opt.value << "\"";
821 else if (opt.name=="valign" &&
822 (opt.value == "bottom" || opt.value == "top" || opt.value == "middle"))
824 m_t << " valign=\"" << opt.value << "\"";
826 else if (opt.name=="width")
828 m_t << " width=\"" << opt.value << "\"";
830 else if (opt.name=="class") // handle markdown generated attributes
832 if (opt.value.startsWith("markdownTable")) // handle markdown generated attributes
834 if (opt.value.endsWith("Right"))
836 m_t << " align='right'";
838 else if (opt.value.endsWith("Left"))
840 m_t << " align='left'";
842 else if (opt.value.endsWith("Center"))
844 m_t << " align='center'";
846 // skip 'markdownTable*' value ending with "None"
848 else if (!opt.value.isEmpty())
850 m_t << " class=\"" << convertToXML(opt.value) << "\"";
859 void XmlDocVisitor::operator()(const DocHtmlCaption &c)
863 if (!c.file().isEmpty())
865 m_t << " id=\"" << stripPath(c.file()) << "_1" << c.anchor() << "\"";
869 m_t << "</caption>\n";
872 void XmlDocVisitor::operator()(const DocInternal &i)
877 m_t << "</internal>\n";
880 void XmlDocVisitor::operator()(const DocHRef &href)
883 m_t << "<ulink url=\"" << convertToXML(href.url(), TRUE) << "\">";
888 void XmlDocVisitor::operator()(const DocHtmlSummary &s)
896 void XmlDocVisitor::operator()(const DocHtmlDetails &d)
900 auto summary = d.summary();
903 std::visit(*this,*summary);
909 void XmlDocVisitor::operator()(const DocHtmlHeader &header)
912 m_t << "<heading level=\"" << header.level() << "\">";
913 visitChildren(header);
914 m_t << "</heading>\n";
917 void XmlDocVisitor::operator()(const DocImage &img)
921 QCString url = img.url();
925 baseName = img.relPath()+img.name();
929 baseName = correctURL(url,img.relPath());
931 HtmlAttribList attribs = img.attribs();
932 auto it = std::find_if(attribs.begin(),attribs.end(),
933 [](const auto &att) { return att.name=="alt"; });
934 QCString altValue = it!=attribs.end() ? it->value : "";
935 visitPreStart(m_t, "image", FALSE, *this, img.children(), baseName, TRUE,
936 img.type(), img.width(), img.height(), QCString(),
937 altValue, img.isInlineImage());
939 // copy the image to the output dir
942 if (url.isEmpty() && (fd=findFileDef(Doxygen::imageNameLinkedMap,img.name(),ambig)))
944 copyFile(fd->absFilePath(),Config_getString(XML_OUTPUT)+"/"+baseName);
947 visitPostEnd(m_t, "image");
950 void XmlDocVisitor::operator()(const DocDotFile &df)
953 copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file()));
954 visitPreStart(m_t, "dotfile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height());
956 visitPostEnd(m_t, "dotfile");
959 void XmlDocVisitor::operator()(const DocMscFile &df)
962 copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file()));
963 visitPreStart(m_t, "mscfile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height());
965 visitPostEnd(m_t, "mscfile");
968 void XmlDocVisitor::operator()(const DocDiaFile &df)
971 copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file()));
972 visitPreStart(m_t, "diafile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height());
974 visitPostEnd(m_t, "diafile");
977 void XmlDocVisitor::operator()(const DocLink &lnk)
980 startLink(lnk.ref(),lnk.file(),lnk.anchor());
985 void XmlDocVisitor::operator()(const DocRef &ref)
988 if (!ref.file().isEmpty())
990 startLink(ref.ref(),ref.file(),ref.isSubPage() ? QCString() : ref.anchor());
992 if (!ref.hasLinkText()) filter(ref.targetTitle());
994 if (!ref.file().isEmpty()) endLink();
997 void XmlDocVisitor::operator()(const DocSecRefItem &ref)
1000 m_t << "<tocitem id=\"" << ref.file();
1001 if (!ref.anchor().isEmpty()) m_t << "_1" << ref.anchor();
1005 m_t << "</tocitem>\n";
1008 void XmlDocVisitor::operator()(const DocSecRefList &l)
1011 m_t << "<toclist>\n";
1013 m_t << "</toclist>\n";
1016 void XmlDocVisitor::operator()(const DocParamSect &s)
1019 m_t << "<parameterlist kind=\"";
1022 case DocParamSect::Param:
1023 m_t << "param"; break;
1024 case DocParamSect::RetVal:
1025 m_t << "retval"; break;
1026 case DocParamSect::Exception:
1027 m_t << "exception"; break;
1028 case DocParamSect::TemplateParam:
1029 m_t << "templateparam"; break;
1035 m_t << "</parameterlist>\n";
1038 void XmlDocVisitor::operator()(const DocSeparator &)
1040 m_t << "</parametertype>\n";
1041 m_t << "<parametertype>";
1044 void XmlDocVisitor::operator()(const DocParamList &pl)
1047 m_t << "<parameteritem>\n";
1048 m_t << "<parameternamelist>\n";
1049 for (const auto ¶m : pl.parameters())
1051 if (!pl.paramTypes().empty())
1053 m_t << "<parametertype>";
1054 for (const auto &type : pl.paramTypes())
1056 std::visit(*this,type);
1058 m_t << "</parametertype>\n";
1060 m_t << "<parametername";
1061 if (pl.direction()!=DocParamSect::Unspecified)
1063 m_t << " direction=\"";
1064 if (pl.direction()==DocParamSect::In)
1068 else if (pl.direction()==DocParamSect::Out)
1072 else if (pl.direction()==DocParamSect::InOut)
1079 std::visit(*this,param);
1080 m_t << "</parametername>\n";
1082 m_t << "</parameternamelist>\n";
1083 m_t << "<parameterdescription>\n";
1084 for (const auto &par : pl.paragraphs())
1086 std::visit(*this,par);
1088 m_t << "</parameterdescription>\n";
1089 m_t << "</parameteritem>\n";
1092 void XmlDocVisitor::operator()(const DocXRefItem &x)
1095 if (x.title().isEmpty()) return;
1096 m_t << "<xrefsect id=\"";
1097 m_t << x.file() << "_1" << x.anchor();
1099 m_t << "<xreftitle>";
1101 m_t << "</xreftitle>";
1102 m_t << "<xrefdescription>";
1104 if (x.title().isEmpty()) return;
1105 m_t << "</xrefdescription>";
1106 m_t << "</xrefsect>";
1109 void XmlDocVisitor::operator()(const DocInternalRef &ref)
1112 startLink(QCString(),ref.file(),ref.anchor());
1118 void XmlDocVisitor::operator()(const DocText &t)
1123 void XmlDocVisitor::operator()(const DocHtmlBlockQuote &q)
1126 m_t << "<blockquote>";
1128 m_t << "</blockquote>";
1131 void XmlDocVisitor::operator()(const DocVhdlFlow &)
1135 void XmlDocVisitor::operator()(const DocParBlock &pb)
1138 m_t << "<parblock>";
1140 m_t << "</parblock>";
1144 void XmlDocVisitor::filter(const QCString &str)
1146 m_t << convertToXML(str);
1149 void XmlDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor)
1151 //printf("XmlDocVisitor: file=%s anchor=%s\n",qPrint(file),qPrint(anchor));
1152 m_t << "<ref refid=\"" << file;
1153 if (!anchor.isEmpty()) m_t << "_1" << anchor;
1154 m_t << "\" kindref=\"";
1155 if (!anchor.isEmpty()) m_t << "member"; else m_t << "compound";
1157 if (!ref.isEmpty()) m_t << " external=\"" << ref << "\"";
1161 void XmlDocVisitor::endLink()