Imported Upstream version 1.9.8
[platform/upstream/doxygen.git] / src / xmldocvisitor.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2020 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 "xmldocvisitor.h"
17 #include "docparser.h"
18 #include "language.h"
19 #include "doxygen.h"
20 #include "outputgen.h"
21 #include "xmlgen.h"
22 #include "dot.h"
23 #include "message.h"
24 #include "util.h"
25 #include "parserintf.h"
26 #include "filename.h"
27 #include "config.h"
28 #include "htmlentity.h"
29 #include "emoji.h"
30 #include "filedef.h"
31 #include "fileinfo.h"
32
33 static void startSimpleSect(TextStream &t,const DocSimpleSect &s)
34 {
35   t << "<simplesect kind=\"";
36   switch(s.type())
37   {
38     case DocSimpleSect::See:
39       t << "see"; break;
40     case DocSimpleSect::Return:
41       t << "return"; break;
42     case DocSimpleSect::Author:
43       t << "author"; break;
44     case DocSimpleSect::Authors:
45       t << "authors"; break;
46     case DocSimpleSect::Version:
47       t << "version"; break;
48     case DocSimpleSect::Since:
49       t << "since"; break;
50     case DocSimpleSect::Date:
51       t << "date"; break;
52     case DocSimpleSect::Note:
53       t << "note"; break;
54     case DocSimpleSect::Warning:
55       t << "warning"; break;
56     case DocSimpleSect::Pre:
57       t << "pre"; break;
58     case DocSimpleSect::Post:
59       t << "post"; break;
60     case DocSimpleSect::Copyright:
61       t << "copyright"; break;
62     case DocSimpleSect::Invar:
63       t << "invariant"; break;
64     case DocSimpleSect::Remark:
65       t << "remark"; break;
66     case DocSimpleSect::Attention:
67       t << "attention"; break;
68     case DocSimpleSect::User:
69       t << "par"; break;
70     case DocSimpleSect::Rcs:
71       t << "rcs"; break;
72     case DocSimpleSect::Unknown:  break;
73   }
74   t << "\">";
75 }
76
77 static void endSimpleSect(TextStream &t,const DocSimpleSect &)
78 {
79   t << "</simplesect>\n";
80 }
81
82 static void visitCaption(XmlDocVisitor &visitor, const DocNodeList &children)
83 {
84   for (const auto &n : children)
85   {
86     std::visit(visitor,n);
87   }
88 }
89
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)
94 {
95   t << "<" << cmd;
96   if (writeType)
97   {
98     t << " type=\"";
99     switch(type)
100     {
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;
106     }
107     t << "\"";
108   }
109   if (!name.isEmpty())
110   {
111     t << " name=\"" << convertToXML(name, TRUE) << "\"";
112   }
113   if (!width.isEmpty())
114   {
115     t << " width=\"" << convertToXML(width) << "\"";
116   }
117   if (!height.isEmpty())
118   {
119     t << " height=\"" << convertToXML(height) << "\"";
120   }
121   if (!engine.isEmpty())
122   {
123     t << " engine=\"" << convertToXML(engine) << "\"";
124   }
125   if (!alt.isEmpty())
126   {
127     t << " alt=\"" << convertToXML(alt) << "\"";
128   }
129   if (inlineImage)
130   {
131     t << " inline=\"yes\"";
132   }
133   if (doCaption)
134   {
135     t << " caption=\"";
136     visitCaption(visitor, children);
137     t << "\"";
138   }
139   t << ">";
140 }
141
142 static void visitPostEnd(TextStream &t, const char *cmd)
143 {
144   t << "</" << cmd << ">\n";
145 }
146
147 XmlDocVisitor::XmlDocVisitor(TextStream &t,OutputCodeList &ci,const QCString &langExt)
148   : m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE),
149     m_langExt(langExt)
150 {
151 }
152
153   //--------------------------------------
154   // visitor functions for leaf nodes
155   //--------------------------------------
156
157 void XmlDocVisitor::operator()(const DocWord &w)
158 {
159   if (m_hide) return;
160   filter(w.word());
161 }
162
163 void XmlDocVisitor::operator()(const DocLinkedWord &w)
164 {
165   if (m_hide) return;
166   startLink(w.ref(),w.file(),w.anchor());
167   filter(w.word());
168   endLink();
169 }
170
171 void XmlDocVisitor::operator()(const DocWhiteSpace &w)
172 {
173   if (m_hide) return;
174   if (m_insidePre)
175   {
176     m_t << w.chars();
177   }
178   else
179   {
180     m_t << " ";
181   }
182 }
183
184 void XmlDocVisitor::operator()(const DocSymbol &s)
185 {
186   if (m_hide) return;
187   const char *res = HtmlEntityMapper::instance().xml(s.symbol());
188   if (res)
189   {
190     m_t << res;
191   }
192   else
193   {
194     err("XML: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance().html(s.symbol(),TRUE));
195   }
196 }
197
198 void XmlDocVisitor::operator()(const DocEmoji &s)
199 {
200   if (m_hide) return;
201   const char *res = EmojiEntityMapper::instance().name(s.index());
202   if (res)
203   {
204     QCString name=res;
205     name = name.mid(1,name.length()-2);
206     m_t << "<emoji name=\"" << name << "\" unicode=\"";
207     filter(EmojiEntityMapper::instance().unicode(s.index()));
208     m_t << "\"/>";
209   }
210   else
211   {
212     m_t << s.name();
213   }
214 }
215
216 void XmlDocVisitor::operator()(const DocURL &u)
217 {
218   if (m_hide) return;
219   m_t << "<ulink url=\"";
220   if (u.isEmail()) m_t << "mailto:";
221   filter(u.url());
222   m_t << "\">";
223   filter(u.url());
224   m_t << "</ulink>";
225 }
226
227 void XmlDocVisitor::operator()(const DocLineBreak &)
228 {
229   if (m_hide) return;
230   m_t << "<linebreak/>\n";
231 }
232
233 void XmlDocVisitor::operator()(const DocHorRuler &)
234 {
235   if (m_hide) return;
236   m_t << "<hruler/>\n";
237 }
238
239 void XmlDocVisitor::operator()(const DocStyleChange &s)
240 {
241   if (m_hide) return;
242   switch (s.style())
243   {
244     case DocStyleChange::Bold:
245       if (s.enable()) m_t << "<bold>";      else m_t << "</bold>";
246       break;
247     case DocStyleChange::S:
248       if (s.enable()) m_t << "<s>";      else m_t << "</s>";
249       break;
250     case DocStyleChange::Strike:
251       if (s.enable()) m_t << "<strike>";      else m_t << "</strike>";
252       break;
253     case DocStyleChange::Del:
254       if (s.enable()) m_t << "<del>";      else m_t << "</del>";
255       break;
256     case DocStyleChange::Underline:
257       if (s.enable()) m_t << "<underline>";      else m_t << "</underline>";
258       break;
259     case DocStyleChange::Ins:
260       if (s.enable()) m_t << "<ins>";      else m_t << "</ins>";
261       break;
262     case DocStyleChange::Italic:
263       if (s.enable()) m_t << "<emphasis>";     else m_t << "</emphasis>";
264       break;
265     case DocStyleChange::Code:
266       if (s.enable()) m_t << "<computeroutput>";   else m_t << "</computeroutput>";
267       break;
268     case DocStyleChange::Subscript:
269       if (s.enable()) m_t << "<subscript>";    else m_t << "</subscript>";
270       break;
271     case DocStyleChange::Superscript:
272       if (s.enable()) m_t << "<superscript>";    else m_t << "</superscript>";
273       break;
274     case DocStyleChange::Center:
275       if (s.enable()) m_t << "<center>"; else m_t << "</center>";
276       break;
277     case DocStyleChange::Small:
278       if (s.enable()) m_t << "<small>";  else m_t << "</small>";
279       break;
280     case DocStyleChange::Cite:
281       if (s.enable()) m_t << "<cite>";  else m_t << "</cite>";
282       break;
283     case DocStyleChange::Preformatted:
284       if (s.enable())
285       {
286         m_t << "<preformatted>";
287         m_insidePre=TRUE;
288       }
289       else
290       {
291         m_t << "</preformatted>";
292         m_insidePre=FALSE;
293       }
294       break;
295     case DocStyleChange::Div:  /* HTML only */ break;
296     case DocStyleChange::Span: /* HTML only */ break;
297   }
298 }
299
300 void XmlDocVisitor::operator()(const DocVerbatim &s)
301 {
302   if (m_hide) return;
303   QCString lang = m_langExt;
304   if (!s.language().isEmpty()) // explicit language setting
305   {
306     lang = s.language();
307   }
308   SrcLangExt langExt = getLanguageFromCodeLang(lang);
309   switch(s.type())
310   {
311     case DocVerbatim::Code:
312       m_t << "<programlisting";
313       if (!s.language().isEmpty())
314           m_t << " filename=\"" << lang << "\">";
315       else
316           m_t << ">";
317       getCodeParser(lang).parseCode(m_ci,s.context(),s.text(),langExt,
318                                     s.isExample(),s.exampleFile());
319       m_t << "</programlisting>";
320       break;
321     case DocVerbatim::JavaDocLiteral:
322       m_t << "<javadocliteral>";
323       filter(s.text());
324       m_t << "</javadocliteral>";
325       break;
326     case DocVerbatim::JavaDocCode:
327       m_t << "<javadoccode>";
328       filter(s.text());
329       m_t << "</javadoccode>";
330       break;
331     case DocVerbatim::Verbatim:
332       m_t << "<verbatim>";
333       filter(s.text());
334       m_t << "</verbatim>";
335       break;
336     case DocVerbatim::HtmlOnly:
337       if (s.isBlock())
338       {
339         m_t << "<htmlonly block=\"yes\">";
340       }
341       else
342       {
343         m_t << "<htmlonly>";
344       }
345       filter(s.text());
346       m_t << "</htmlonly>";
347       break;
348     case DocVerbatim::RtfOnly:
349       m_t << "<rtfonly>";
350       filter(s.text());
351       m_t << "</rtfonly>";
352       break;
353     case DocVerbatim::ManOnly:
354       m_t << "<manonly>";
355       filter(s.text());
356       m_t << "</manonly>";
357       break;
358     case DocVerbatim::LatexOnly:
359       m_t << "<latexonly>";
360       filter(s.text());
361       m_t << "</latexonly>";
362       break;
363     case DocVerbatim::DocbookOnly:
364       m_t << "<docbookonly>";
365       filter(s.text());
366       m_t << "</docbookonly>";
367       break;
368     case DocVerbatim::XmlOnly:
369       m_t << s.text();
370       break;
371     case DocVerbatim::Dot:
372       visitPreStart(m_t, "dot", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height());
373       filter(s.text());
374       visitPostEnd(m_t, "dot");
375       break;
376     case DocVerbatim::Msc:
377       visitPreStart(m_t, "msc", s.hasCaption(), *this, s.children(),  QCString(""), FALSE, DocImage::Html, s.width(), s.height());
378       filter(s.text());
379       visitPostEnd(m_t, "msc");
380       break;
381     case DocVerbatim::PlantUML:
382       visitPreStart(m_t, "plantuml", s.hasCaption(), *this, s.children(),  QCString(""), FALSE, DocImage::Html, s.width(), s.height(), s.engine());
383       filter(s.text());
384       visitPostEnd(m_t, "plantuml");
385       break;
386   }
387 }
388
389 void XmlDocVisitor::operator()(const DocAnchor &anc)
390 {
391   if (m_hide) return;
392   m_t << "<anchor id=\"" << anc.file() << "_1" << anc.anchor() << "\"/>";
393 }
394
395 void XmlDocVisitor::operator()(const DocInclude &inc)
396 {
397   if (m_hide) return;
398   SrcLangExt langExt = getLanguageFromFileName(inc.extension());
399   switch(inc.type())
400   {
401     case DocInclude::IncWithLines:
402       {
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(),
407                                            inc.text(),
408                                            langExt,
409                                            inc.isExample(),
410                                            inc.exampleFile(),
411                                            fd.get(), // fileDef,
412                                            -1,    // start line
413                                            -1,    // end line
414                                            FALSE, // inline fragment
415                                            0,     // memberDef
416                                            TRUE   // show line numbers
417                                            );
418          m_t << "</programlisting>";
419       }
420       break;
421     case DocInclude::Include:
422       m_t << "<programlisting filename=\"" << inc.file() << "\">";
423       getCodeParser(inc.extension()).parseCode(m_ci,inc.context(),
424                                         inc.text(),
425                                         langExt,
426                                         inc.isExample(),
427                                         inc.exampleFile(),
428                                         0,     // fileDef
429                                         -1,    // startLine
430                                         -1,    // endLine
431                                         TRUE,  // inlineFragment
432                                         0,     // memberDef
433                                         FALSE  // show line numbers
434                                        );
435       m_t << "</programlisting>";
436       break;
437     case DocInclude::DontInclude:
438     case DocInclude::DontIncWithLines:
439       break;
440     case DocInclude::HtmlInclude:
441       if (inc.isBlock())
442       {
443         m_t << "<htmlonly block=\"yes\">";
444       }
445       else
446       {
447         m_t << "<htmlonly>";
448       }
449       filter(inc.text());
450       m_t << "</htmlonly>";
451       break;
452     case DocInclude::LatexInclude:
453       m_t << "<latexonly>";
454       filter(inc.text());
455       m_t << "</latexonly>";
456       break;
457     case DocInclude::RtfInclude:
458       m_t << "<rtfonly>";
459       filter(inc.text());
460       m_t << "</rtfonly>";
461       break;
462     case DocInclude::ManInclude:
463       m_t << "<manonly>";
464       filter(inc.text());
465       m_t << "</manonly>";
466       break;
467     case DocInclude::XmlInclude:
468       filter(inc.text());
469       break;
470     case DocInclude::DocbookInclude:
471       m_t << "<docbookonly>";
472       filter(inc.text());
473       m_t << "</docbookonly>";
474       break;
475     case DocInclude::VerbInclude:
476       m_t << "<verbatim>";
477       filter(inc.text());
478       m_t << "</verbatim>";
479       break;
480     case DocInclude::Snippet:
481     case DocInclude::SnippetTrimLeft:
482       m_t << "<programlisting filename=\"" << inc.file() << "\">";
483       getCodeParser(inc.extension()).parseCode(m_ci,
484                                         inc.context(),
485                                         extractBlock(inc.text(),inc.blockId(),inc.type()==DocInclude::SnippetTrimLeft),
486                                         langExt,
487                                         inc.isExample(),
488                                         inc.exampleFile()
489                                        );
490       m_t << "</programlisting>";
491       break;
492     case DocInclude::SnipWithLines:
493       {
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,
498                                            inc.context(),
499                                            extractBlock(inc.text(),inc.blockId()),
500                                            langExt,
501                                            inc.isExample(),
502                                            inc.exampleFile(),
503                                            fd.get(),
504                                            lineBlock(inc.text(),inc.blockId()),
505                                            -1,    // endLine
506                                            FALSE, // inlineFragment
507                                            0,     // memberDef
508                                            TRUE   // show line number
509                                           );
510          m_t << "</programlisting>";
511       }
512       break;
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__);
517       break;
518   }
519 }
520
521 void XmlDocVisitor::operator()(const DocIncOperator &op)
522 {
523   //printf("DocIncOperator: type=%d first=%d, last=%d text='%s'\n",
524   //    op.type(),op.isFirst(),op.isLast(),qPrint(op.text()));
525   if (op.isFirst())
526   {
527     if (!m_hide)
528     {
529       m_t << "<programlisting filename=\"" << op.includeFileName() << "\">";
530     }
531     pushHidden(m_hide);
532     m_hide = TRUE;
533   }
534   QCString locLangExt = getFileNameExtension(op.includeFileName());
535   if (locLangExt.isEmpty()) locLangExt = m_langExt;
536   SrcLangExt langExt = getLanguageFromFileName(locLangExt);
537   if (op.type()!=DocIncOperator::Skip)
538   {
539     m_hide = popHidden();
540     if (!m_hide)
541     {
542       std::unique_ptr<FileDef> fd;
543       if (!op.includeFileName().isEmpty())
544       {
545         FileInfo cfi( op.includeFileName().str() );
546         fd = createFileDef( cfi.dirPath(), cfi.fileName() );
547       }
548
549       getCodeParser(locLangExt).parseCode(m_ci,op.context(),
550                                           op.text(),langExt,op.isExample(),
551                                           op.exampleFile(),
552                                           fd.get(),     // fileDef
553                                           op.line(),    // startLine
554                                           -1,    // endLine
555                                           FALSE, // inline fragment
556                                           0,     // memberDef
557                                           op.showLineNo()  // show line numbers
558                                          );
559     }
560     pushHidden(m_hide);
561     m_hide=TRUE;
562   }
563   if (op.isLast())
564   {
565     m_hide = popHidden();
566     if (!m_hide) m_t << "</programlisting>";
567   }
568   else
569   {
570     if (!m_hide) m_t << "\n";
571   }
572 }
573
574 void XmlDocVisitor::operator()(const DocFormula &f)
575 {
576   if (m_hide) return;
577   m_t << "<formula id=\"" << f.id() << "\">";
578   filter(f.text());
579   m_t << "</formula>";
580 }
581
582 void XmlDocVisitor::operator()(const DocIndexEntry &ie)
583 {
584   if (m_hide) return;
585   m_t << "<indexentry>"
586            "<primaryie>";
587   filter(ie.entry());
588   m_t << "</primaryie>"
589            "<secondaryie></secondaryie>"
590          "</indexentry>";
591 }
592
593 void XmlDocVisitor::operator()(const DocSimpleSectSep &sep)
594 {
595   const DocSimpleSect *sect = std::get_if<DocSimpleSect>(sep.parent());
596   if (sect)
597   {
598     endSimpleSect(m_t,*sect);
599     startSimpleSect(m_t,*sect);
600   }
601 }
602
603 void XmlDocVisitor::operator()(const DocCite &cite)
604 {
605   if (m_hide) return;
606   if (!cite.file().isEmpty()) startLink(cite.ref(),cite.file(),cite.anchor());
607   filter(cite.text());
608   if (!cite.file().isEmpty()) endLink();
609 }
610
611 //--------------------------------------
612 // visitor functions for compound nodes
613 //--------------------------------------
614
615 void XmlDocVisitor::operator()(const DocAutoList &l)
616 {
617   if (m_hide) return;
618   if (l.isEnumList())
619   {
620     m_t << "<orderedlist>\n";
621   }
622   else
623   {
624     m_t << "<itemizedlist>\n";
625   }
626   visitChildren(l);
627   if (l.isEnumList())
628   {
629     m_t << "</orderedlist>\n";
630   }
631   else
632   {
633     m_t << "</itemizedlist>\n";
634   }
635 }
636
637 void XmlDocVisitor::operator()(const DocAutoListItem &li)
638 {
639   if (m_hide) return;
640   m_t << "<listitem>";
641   visitChildren(li);
642   m_t << "</listitem>";
643 }
644
645 void XmlDocVisitor::operator()(const DocPara &p)
646 {
647   if (m_hide) return;
648   m_t << "<para>";
649   visitChildren(p);
650   m_t << "</para>\n";
651 }
652
653 void XmlDocVisitor::operator()(const DocRoot &r)
654 {
655   visitChildren(r);
656 }
657
658 void XmlDocVisitor::operator()(const DocSimpleSect &s)
659 {
660   if (m_hide) return;
661   startSimpleSect(m_t,s);
662   if (s.title())
663   {
664     std::visit(*this,*s.title());
665   }
666   visitChildren(s);
667   endSimpleSect(m_t,s);
668 }
669
670 void XmlDocVisitor::operator()(const DocTitle &t)
671 {
672   if (m_hide) return;
673   m_t << "<title>";
674   visitChildren(t);
675   m_t << "</title>";
676 }
677
678 void XmlDocVisitor::operator()(const DocSimpleList &l)
679 {
680   if (m_hide) return;
681   m_t << "<itemizedlist>\n";
682   visitChildren(l);
683   m_t << "</itemizedlist>\n";
684 }
685
686 void XmlDocVisitor::operator()(const DocSimpleListItem &li)
687 {
688   if (m_hide) return;
689   m_t << "<listitem>";
690   if (li.paragraph())
691   {
692     std::visit(*this,*li.paragraph());
693   }
694   m_t << "</listitem>\n";
695 }
696
697 void XmlDocVisitor::operator()(const DocSection &s)
698 {
699   if (m_hide) return;
700   m_t << "<sect" << s.level() << " id=\"" << s.file();
701   if (!s.anchor().isEmpty()) m_t << "_1" << s.anchor();
702   m_t << "\">\n";
703   m_t << "<title>";
704   filter(convertCharEntitiesToUTF8(s.title()));
705   m_t << "</title>\n";
706   visitChildren(s);
707   m_t << "</sect" << s.level() << ">\n";
708 }
709
710 void XmlDocVisitor::operator()(const DocHtmlList &s)
711 {
712   if (m_hide) return;
713   if (s.type()==DocHtmlList::Ordered)
714   {
715     m_t << "<orderedlist";
716     for (const auto &opt : s.attribs())
717     {
718       m_t << " " << opt.name << "=\"" << opt.value << "\"";
719     }
720     m_t << ">\n";
721   }
722   else
723   {
724     m_t << "<itemizedlist>\n";
725   }
726   visitChildren(s);
727   if (s.type()==DocHtmlList::Ordered)
728   {
729     m_t << "</orderedlist>\n";
730   }
731   else
732   {
733     m_t << "</itemizedlist>\n";
734   }
735 }
736
737 void XmlDocVisitor::operator()(const DocHtmlListItem &l)
738 {
739   if (m_hide) return;
740   m_t << "<listitem";
741   for (const auto &opt : l.attribs())
742   {
743     if (opt.name=="value")
744     {
745       m_t << " " << opt.name << "=\"" << opt.value << "\"";
746     }
747   }
748   m_t << ">\n";
749   visitChildren(l);
750   m_t << "</listitem>\n";
751 }
752
753 void XmlDocVisitor::operator()(const DocHtmlDescList &dl)
754 {
755   if (m_hide) return;
756   m_t << "<variablelist>\n";
757   visitChildren(dl);
758   m_t << "</variablelist>\n";
759 }
760
761 void XmlDocVisitor::operator()(const DocHtmlDescTitle &dt)
762 {
763   if (m_hide) return;
764   m_t << "<varlistentry><term>";
765   visitChildren(dt);
766   m_t << "</term></varlistentry>\n";
767 }
768
769 void XmlDocVisitor::operator()(const DocHtmlDescData &dd)
770 {
771   if (m_hide) return;
772   m_t << "<listitem>";
773   visitChildren(dd);
774   m_t << "</listitem>\n";
775 }
776
777 void XmlDocVisitor::operator()(const DocHtmlTable &t)
778 {
779   if (m_hide) return;
780   m_t << "<table rows=\"" << t.numRows()
781       << "\" cols=\"" << t.numColumns() << "\"" ;
782   for (const auto &opt : t.attribs())
783   {
784     if (opt.name=="width")
785     {
786       m_t << " " << opt.name << "=\"" << opt.value << "\"";
787     }
788   }
789   m_t << ">";
790   if (t.caption())
791   {
792     std::visit(*this,*t.caption());
793   }
794   visitChildren(t);
795   m_t << "</table>\n";
796 }
797
798 void XmlDocVisitor::operator()(const DocHtmlRow &r)
799 {
800   if (m_hide) return;
801   m_t << "<row>\n";
802   visitChildren(r);
803   m_t << "</row>\n";
804 }
805
806 void XmlDocVisitor::operator()(const DocHtmlCell &c)
807 {
808   if (m_hide) return;
809   if (c.isHeading()) m_t << "<entry thead=\"yes\""; else m_t << "<entry thead=\"no\"";
810   for (const auto &opt : c.attribs())
811   {
812     if (opt.name=="colspan" || opt.name=="rowspan")
813     {
814       m_t << " " << opt.name << "=\"" << opt.value.toInt() << "\"";
815     }
816     else if (opt.name=="align" &&
817              (opt.value=="right" || opt.value=="left" || opt.value=="center"))
818     {
819       m_t << " align=\"" << opt.value << "\"";
820     }
821     else if (opt.name=="valign" &&
822       (opt.value == "bottom" || opt.value == "top" || opt.value == "middle"))
823     {
824       m_t << " valign=\"" << opt.value << "\"";
825     }
826     else if (opt.name=="width")
827     {
828       m_t << " width=\"" << opt.value << "\"";
829     }
830     else if (opt.name=="class") // handle markdown generated attributes
831     {
832       if (opt.value.startsWith("markdownTable")) // handle markdown generated attributes
833       {
834         if (opt.value.endsWith("Right"))
835         {
836           m_t << " align='right'";
837         }
838         else if (opt.value.endsWith("Left"))
839         {
840           m_t << " align='left'";
841         }
842         else if (opt.value.endsWith("Center"))
843         {
844           m_t << " align='center'";
845         }
846         // skip 'markdownTable*' value ending with "None"
847       }
848       else if (!opt.value.isEmpty())
849       {
850         m_t << " class=\"" << convertToXML(opt.value) << "\"";
851       }
852     }
853   }
854   m_t << ">";
855   visitChildren(c);
856   m_t << "</entry>";
857 }
858
859 void XmlDocVisitor::operator()(const DocHtmlCaption &c)
860 {
861   if (m_hide) return;
862   m_t << "<caption";
863   if (!c.file().isEmpty())
864   {
865     m_t << " id=\""  << stripPath(c.file()) << "_1" << c.anchor() << "\"";
866   }
867   m_t << ">";
868   visitChildren(c);
869   m_t << "</caption>\n";
870 }
871
872 void XmlDocVisitor::operator()(const DocInternal &i)
873 {
874   if (m_hide) return;
875   m_t << "<internal>";
876   visitChildren(i);
877   m_t << "</internal>\n";
878 }
879
880 void XmlDocVisitor::operator()(const DocHRef &href)
881 {
882   if (m_hide) return;
883   m_t << "<ulink url=\"" << convertToXML(href.url(), TRUE) << "\">";
884   visitChildren(href);
885   m_t << "</ulink>";
886 }
887
888 void XmlDocVisitor::operator()(const DocHtmlSummary &s)
889 {
890   if (m_hide) return;
891   m_t << "<summary>";
892   visitChildren(s);
893   m_t << "</summary>";
894 }
895
896 void XmlDocVisitor::operator()(const DocHtmlDetails &d)
897 {
898   if (m_hide) return;
899   m_t << "<details>";
900   auto summary = d.summary();
901   if (summary)
902   {
903     std::visit(*this,*summary);
904   }
905   visitChildren(d);
906   m_t << "</details>";
907 }
908
909 void XmlDocVisitor::operator()(const DocHtmlHeader &header)
910 {
911   if (m_hide) return;
912   m_t << "<heading level=\"" << header.level() << "\">";
913   visitChildren(header);
914   m_t << "</heading>\n";
915 }
916
917 void XmlDocVisitor::operator()(const DocImage &img)
918 {
919   if (m_hide) return;
920
921   QCString url = img.url();
922   QCString baseName;
923   if (url.isEmpty())
924   {
925     baseName = img.relPath()+img.name();
926   }
927   else
928   {
929     baseName = correctURL(url,img.relPath());
930   }
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());
938
939   // copy the image to the output dir
940   FileDef *fd = 0;
941   bool ambig;
942   if (url.isEmpty() && (fd=findFileDef(Doxygen::imageNameLinkedMap,img.name(),ambig)))
943   {
944     copyFile(fd->absFilePath(),Config_getString(XML_OUTPUT)+"/"+baseName);
945   }
946   visitChildren(img);
947   visitPostEnd(m_t, "image");
948 }
949
950 void XmlDocVisitor::operator()(const DocDotFile &df)
951 {
952   if (m_hide) return;
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());
955   visitChildren(df);
956   visitPostEnd(m_t, "dotfile");
957 }
958
959 void XmlDocVisitor::operator()(const DocMscFile &df)
960 {
961   if (m_hide) return;
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());
964   visitChildren(df);
965   visitPostEnd(m_t, "mscfile");
966 }
967
968 void XmlDocVisitor::operator()(const DocDiaFile &df)
969 {
970   if (m_hide) return;
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());
973   visitChildren(df);
974   visitPostEnd(m_t, "diafile");
975 }
976
977 void XmlDocVisitor::operator()(const DocLink &lnk)
978 {
979   if (m_hide) return;
980   startLink(lnk.ref(),lnk.file(),lnk.anchor());
981   visitChildren(lnk);
982   endLink();
983 }
984
985 void XmlDocVisitor::operator()(const DocRef &ref)
986 {
987   if (m_hide) return;
988   if (!ref.file().isEmpty())
989   {
990     startLink(ref.ref(),ref.file(),ref.isSubPage() ? QCString() : ref.anchor());
991   }
992   if (!ref.hasLinkText()) filter(ref.targetTitle());
993   visitChildren(ref);
994   if (!ref.file().isEmpty()) endLink();
995 }
996
997 void XmlDocVisitor::operator()(const DocSecRefItem &ref)
998 {
999   if (m_hide) return;
1000   m_t << "<tocitem id=\"" << ref.file();
1001   if (!ref.anchor().isEmpty()) m_t << "_1" << ref.anchor();
1002   m_t << "\"";
1003   m_t << ">";
1004   visitChildren(ref);
1005   m_t << "</tocitem>\n";
1006 }
1007
1008 void XmlDocVisitor::operator()(const DocSecRefList &l)
1009 {
1010   if (m_hide) return;
1011   m_t << "<toclist>\n";
1012   visitChildren(l);
1013   m_t << "</toclist>\n";
1014 }
1015
1016 void XmlDocVisitor::operator()(const DocParamSect &s)
1017 {
1018   if (m_hide) return;
1019   m_t << "<parameterlist kind=\"";
1020   switch(s.type())
1021   {
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;
1030     default:
1031       ASSERT(0);
1032   }
1033   m_t << "\">";
1034   visitChildren(s);
1035   m_t << "</parameterlist>\n";
1036 }
1037
1038 void XmlDocVisitor::operator()(const DocSeparator &)
1039 {
1040   m_t << "</parametertype>\n";
1041   m_t << "<parametertype>";
1042 }
1043
1044 void XmlDocVisitor::operator()(const DocParamList &pl)
1045 {
1046   if (m_hide) return;
1047   m_t << "<parameteritem>\n";
1048   m_t << "<parameternamelist>\n";
1049   for (const auto &param : pl.parameters())
1050   {
1051     if (!pl.paramTypes().empty())
1052     {
1053       m_t << "<parametertype>";
1054       for (const auto &type : pl.paramTypes())
1055       {
1056         std::visit(*this,type);
1057       }
1058       m_t << "</parametertype>\n";
1059     }
1060     m_t << "<parametername";
1061     if (pl.direction()!=DocParamSect::Unspecified)
1062     {
1063       m_t << " direction=\"";
1064       if (pl.direction()==DocParamSect::In)
1065       {
1066         m_t << "in";
1067       }
1068       else if (pl.direction()==DocParamSect::Out)
1069       {
1070         m_t << "out";
1071       }
1072       else if (pl.direction()==DocParamSect::InOut)
1073       {
1074         m_t << "inout";
1075       }
1076       m_t << "\"";
1077     }
1078     m_t << ">";
1079     std::visit(*this,param);
1080     m_t << "</parametername>\n";
1081   }
1082   m_t << "</parameternamelist>\n";
1083   m_t << "<parameterdescription>\n";
1084   for (const auto &par : pl.paragraphs())
1085   {
1086     std::visit(*this,par);
1087   }
1088   m_t << "</parameterdescription>\n";
1089   m_t << "</parameteritem>\n";
1090 }
1091
1092 void XmlDocVisitor::operator()(const DocXRefItem &x)
1093 {
1094   if (m_hide) return;
1095   if (x.title().isEmpty()) return;
1096   m_t << "<xrefsect id=\"";
1097   m_t << x.file() << "_1" << x.anchor();
1098   m_t << "\">";
1099   m_t << "<xreftitle>";
1100   filter(x.title());
1101   m_t << "</xreftitle>";
1102   m_t << "<xrefdescription>";
1103   visitChildren(x);
1104   if (x.title().isEmpty()) return;
1105   m_t << "</xrefdescription>";
1106   m_t << "</xrefsect>";
1107 }
1108
1109 void XmlDocVisitor::operator()(const DocInternalRef &ref)
1110 {
1111   if (m_hide) return;
1112   startLink(QCString(),ref.file(),ref.anchor());
1113   visitChildren(ref);
1114   endLink();
1115   m_t << " ";
1116 }
1117
1118 void XmlDocVisitor::operator()(const DocText &t)
1119 {
1120   visitChildren(t);
1121 }
1122
1123 void XmlDocVisitor::operator()(const DocHtmlBlockQuote &q)
1124 {
1125   if (m_hide) return;
1126   m_t << "<blockquote>";
1127   visitChildren(q);
1128   m_t << "</blockquote>";
1129 }
1130
1131 void XmlDocVisitor::operator()(const DocVhdlFlow &)
1132 {
1133 }
1134
1135 void XmlDocVisitor::operator()(const DocParBlock &pb)
1136 {
1137   if (m_hide) return;
1138   m_t << "<parblock>";
1139   visitChildren(pb);
1140   m_t << "</parblock>";
1141 }
1142
1143
1144 void XmlDocVisitor::filter(const QCString &str)
1145 {
1146   m_t << convertToXML(str);
1147 }
1148
1149 void XmlDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor)
1150 {
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";
1156   m_t << "\"";
1157   if (!ref.isEmpty()) m_t << " external=\"" << ref << "\"";
1158   m_t << ">";
1159 }
1160
1161 void XmlDocVisitor::endLink()
1162 {
1163   m_t << "</ref>";
1164 }
1165