/****************************************************************************** * * Copyright (C) 1997-2020 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby * granted. No representations are made about the suitability of this software * for any purpose. It is provided "as is" without express or implied warranty. * See the GNU General Public License for more details. * * Documents produced by Doxygen are derivative works derived from the * input used in their production; they are not affected by this license. * */ #include "xmldocvisitor.h" #include "docparser.h" #include "language.h" #include "doxygen.h" #include "outputgen.h" #include "xmlgen.h" #include "dot.h" #include "message.h" #include "util.h" #include "parserintf.h" #include "filename.h" #include "config.h" #include "htmlentity.h" #include "emoji.h" #include "filedef.h" #include "fileinfo.h" static void startSimpleSect(TextStream &t,const DocSimpleSect &s) { t << ""; } static void endSimpleSect(TextStream &t,const DocSimpleSect &) { t << "\n"; } static void visitCaption(XmlDocVisitor &visitor, const DocNodeList &children) { for (const auto &n : children) { std::visit(visitor,n); } } static void visitPreStart(TextStream &t, const char *cmd, bool doCaption, XmlDocVisitor &visitor, const DocNodeList &children, const QCString &name, bool writeType, DocImage::Type type, const QCString &width, const QCString &height, const QCString engine = QCString(), const QCString &alt = QCString(), bool inlineImage = FALSE) { t << "<" << cmd; if (writeType) { t << " type=\""; switch(type) { case DocImage::Html: t << "html"; break; case DocImage::Latex: t << "latex"; break; case DocImage::Rtf: t << "rtf"; break; case DocImage::DocBook: t << "docbook"; break; case DocImage::Xml: t << "xml"; break; } t << "\""; } if (!name.isEmpty()) { t << " name=\"" << convertToXML(name, TRUE) << "\""; } if (!width.isEmpty()) { t << " width=\"" << convertToXML(width) << "\""; } if (!height.isEmpty()) { t << " height=\"" << convertToXML(height) << "\""; } if (!engine.isEmpty()) { t << " engine=\"" << convertToXML(engine) << "\""; } if (!alt.isEmpty()) { t << " alt=\"" << convertToXML(alt) << "\""; } if (inlineImage) { t << " inline=\"yes\""; } if (doCaption) { t << " caption=\""; visitCaption(visitor, children); t << "\""; } t << ">"; } static void visitPostEnd(TextStream &t, const char *cmd) { t << "\n"; } XmlDocVisitor::XmlDocVisitor(TextStream &t,CodeOutputInterface &ci,const QCString &langExt) : m_t(t), m_ci(ci), m_insidePre(FALSE), m_hide(FALSE), m_langExt(langExt) { } //-------------------------------------- // visitor functions for leaf nodes //-------------------------------------- void XmlDocVisitor::operator()(const DocWord &w) { if (m_hide) return; filter(w.word()); } void XmlDocVisitor::operator()(const DocLinkedWord &w) { if (m_hide) return; startLink(w.ref(),w.file(),w.anchor()); filter(w.word()); endLink(); } void XmlDocVisitor::operator()(const DocWhiteSpace &w) { if (m_hide) return; if (m_insidePre) { m_t << w.chars(); } else { m_t << " "; } } void XmlDocVisitor::operator()(const DocSymbol &s) { if (m_hide) return; const char *res = HtmlEntityMapper::instance()->xml(s.symbol()); if (res) { m_t << res; } else { err("XML: non supported HTML-entity found: %s\n",HtmlEntityMapper::instance()->html(s.symbol(),TRUE)); } } void XmlDocVisitor::operator()(const DocEmoji &s) { if (m_hide) return; const char *res = EmojiEntityMapper::instance()->name(s.index()); if (res) { QCString name=res; name = name.mid(1,name.length()-2); m_t << "unicode(s.index())); m_t << "\"/>"; } else { m_t << s.name(); } } void XmlDocVisitor::operator()(const DocURL &u) { if (m_hide) return; m_t << ""; filter(u.url()); m_t << ""; } void XmlDocVisitor::operator()(const DocLineBreak &) { if (m_hide) return; m_t << "\n"; } void XmlDocVisitor::operator()(const DocHorRuler &) { if (m_hide) return; m_t << "\n"; } void XmlDocVisitor::operator()(const DocStyleChange &s) { if (m_hide) return; switch (s.style()) { case DocStyleChange::Bold: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::S: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Strike: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Del: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Underline: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Ins: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Italic: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Code: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Subscript: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Superscript: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Center: if (s.enable()) m_t << "
"; else m_t << "
"; break; case DocStyleChange::Small: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Cite: if (s.enable()) m_t << ""; else m_t << ""; break; case DocStyleChange::Preformatted: if (s.enable()) { m_t << ""; m_insidePre=TRUE; } else { m_t << ""; m_insidePre=FALSE; } break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; case DocStyleChange::Summary: if (s.enable()) m_t << ""; else m_t << ""; break; } } void XmlDocVisitor::operator()(const DocVerbatim &s) { if (m_hide) return; QCString lang = m_langExt; if (!s.language().isEmpty()) // explicit language setting { lang = s.language(); } SrcLangExt langExt = getLanguageFromCodeLang(lang); switch(s.type()) { case DocVerbatim::Code: m_t << ""; else m_t << ">"; getCodeParser(lang).parseCode(m_ci,s.context(),s.text(),langExt, s.isExample(),s.exampleFile()); m_t << ""; break; case DocVerbatim::JavaDocLiteral: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::JavaDocCode: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::Verbatim: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::HtmlOnly: if (s.isBlock()) { m_t << ""; } else { m_t << ""; } filter(s.text()); m_t << ""; break; case DocVerbatim::RtfOnly: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::ManOnly: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::LatexOnly: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::DocbookOnly: m_t << ""; filter(s.text()); m_t << ""; break; case DocVerbatim::XmlOnly: m_t << s.text(); break; case DocVerbatim::Dot: visitPreStart(m_t, "dot", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height()); filter(s.text()); visitPostEnd(m_t, "dot"); break; case DocVerbatim::Msc: visitPreStart(m_t, "msc", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height()); filter(s.text()); visitPostEnd(m_t, "msc"); break; case DocVerbatim::PlantUML: visitPreStart(m_t, "plantuml", s.hasCaption(), *this, s.children(), QCString(""), FALSE, DocImage::Html, s.width(), s.height(), s.engine()); filter(s.text()); visitPostEnd(m_t, "plantuml"); break; } } void XmlDocVisitor::operator()(const DocAnchor &anc) { if (m_hide) return; m_t << ""; } void XmlDocVisitor::operator()(const DocInclude &inc) { if (m_hide) return; SrcLangExt langExt = getLanguageFromFileName(inc.extension()); switch(inc.type()) { case DocInclude::IncWithLines: { m_t << ""; FileInfo cfi( inc.file().str() ); std::unique_ptr fd { createFileDef( cfi.dirPath(), cfi.fileName() ) }; getCodeParser(inc.extension()).parseCode(m_ci,inc.context(), inc.text(), langExt, inc.isExample(), inc.exampleFile(), fd.get(), // fileDef, -1, // start line -1, // end line FALSE, // inline fragment 0, // memberDef TRUE // show line numbers ); m_t << ""; } break; case DocInclude::Include: m_t << ""; getCodeParser(inc.extension()).parseCode(m_ci,inc.context(), inc.text(), langExt, inc.isExample(), inc.exampleFile(), 0, // fileDef -1, // startLine -1, // endLine TRUE, // inlineFragment 0, // memberDef FALSE // show line numbers ); m_t << ""; break; case DocInclude::DontInclude: case DocInclude::DontIncWithLines: break; case DocInclude::HtmlInclude: if (inc.isBlock()) { m_t << ""; } else { m_t << ""; } filter(inc.text()); m_t << ""; break; case DocInclude::LatexInclude: m_t << ""; filter(inc.text()); m_t << ""; break; case DocInclude::RtfInclude: m_t << ""; filter(inc.text()); m_t << ""; break; case DocInclude::ManInclude: m_t << ""; filter(inc.text()); m_t << ""; break; case DocInclude::XmlInclude: filter(inc.text()); break; case DocInclude::DocbookInclude: m_t << ""; filter(inc.text()); m_t << ""; break; case DocInclude::VerbInclude: m_t << ""; filter(inc.text()); m_t << ""; break; case DocInclude::Snippet: m_t << ""; getCodeParser(inc.extension()).parseCode(m_ci, inc.context(), extractBlock(inc.text(),inc.blockId()), langExt, inc.isExample(), inc.exampleFile() ); m_t << ""; break; case DocInclude::SnipWithLines: { m_t << ""; FileInfo cfi( inc.file().str() ); std::unique_ptr fd { createFileDef( cfi.dirPath(), cfi.fileName() ) }; getCodeParser(inc.extension()).parseCode(m_ci, inc.context(), extractBlock(inc.text(),inc.blockId()), langExt, inc.isExample(), inc.exampleFile(), fd.get(), lineBlock(inc.text(),inc.blockId()), -1, // endLine FALSE, // inlineFragment 0, // memberDef TRUE // show line number ); m_t << ""; } break; case DocInclude::SnippetDoc: case DocInclude::IncludeDoc: err("Internal inconsistency: found switch SnippetDoc / IncludeDoc in file: %s" "Please create a bug report\n",__FILE__); break; } } void XmlDocVisitor::operator()(const DocIncOperator &op) { //printf("DocIncOperator: type=%d first=%d, last=%d text='%s'\n", // op.type(),op.isFirst(),op.isLast(),qPrint(op.text())); if (op.isFirst()) { if (!m_hide) { m_t << ""; } pushHidden(m_hide); m_hide = TRUE; } QCString locLangExt = getFileNameExtension(op.includeFileName()); if (locLangExt.isEmpty()) locLangExt = m_langExt; SrcLangExt langExt = getLanguageFromFileName(locLangExt); if (op.type()!=DocIncOperator::Skip) { m_hide = popHidden(); if (!m_hide) { std::unique_ptr fd; if (!op.includeFileName().isEmpty()) { FileInfo cfi( op.includeFileName().str() ); fd.reset(createFileDef( cfi.dirPath(), cfi.fileName() )); } getCodeParser(locLangExt).parseCode(m_ci,op.context(), op.text(),langExt,op.isExample(), op.exampleFile(), fd.get(), // fileDef op.line(), // startLine -1, // endLine FALSE, // inline fragment 0, // memberDef op.showLineNo() // show line numbers ); } pushHidden(m_hide); m_hide=TRUE; } if (op.isLast()) { m_hide = popHidden(); if (!m_hide) m_t << ""; } else { if (!m_hide) m_t << "\n"; } } void XmlDocVisitor::operator()(const DocFormula &f) { if (m_hide) return; m_t << ""; filter(f.text()); m_t << ""; } void XmlDocVisitor::operator()(const DocIndexEntry &ie) { if (m_hide) return; m_t << "" ""; filter(ie.entry()); m_t << "" "" ""; } void XmlDocVisitor::operator()(const DocSimpleSectSep &sep) { const DocSimpleSect *sect = std::get_if(sep.parent()); if (sect) { endSimpleSect(m_t,*sect); startSimpleSect(m_t,*sect); } } void XmlDocVisitor::operator()(const DocCite &cite) { if (m_hide) return; if (!cite.file().isEmpty()) startLink(cite.ref(),cite.file(),cite.anchor()); filter(cite.text()); if (!cite.file().isEmpty()) endLink(); } //-------------------------------------- // visitor functions for compound nodes //-------------------------------------- void XmlDocVisitor::operator()(const DocAutoList &l) { if (m_hide) return; if (l.isEnumList()) { m_t << "\n"; } else { m_t << "\n"; } visitChildren(l); if (l.isEnumList()) { m_t << "\n"; } else { m_t << "\n"; } } void XmlDocVisitor::operator()(const DocAutoListItem &li) { if (m_hide) return; m_t << ""; visitChildren(li); m_t << ""; } void XmlDocVisitor::operator()(const DocPara &p) { if (m_hide) return; m_t << ""; visitChildren(p); m_t << "\n"; } void XmlDocVisitor::operator()(const DocRoot &r) { visitChildren(r); } void XmlDocVisitor::operator()(const DocSimpleSect &s) { if (m_hide) return; startSimpleSect(m_t,s); if (s.title()) { std::visit(*this,*s.title()); } visitChildren(s); endSimpleSect(m_t,s); } void XmlDocVisitor::operator()(const DocTitle &t) { if (m_hide) return; m_t << ""; visitChildren(t); m_t << ""; } void XmlDocVisitor::operator()(const DocSimpleList &l) { if (m_hide) return; m_t << "\n"; visitChildren(l); m_t << "\n"; } void XmlDocVisitor::operator()(const DocSimpleListItem &li) { if (m_hide) return; m_t << ""; if (li.paragraph()) { std::visit(*this,*li.paragraph()); } m_t << "\n"; } void XmlDocVisitor::operator()(const DocSection &s) { if (m_hide) return; m_t << "\n"; m_t << ""; filter(convertCharEntitiesToUTF8(s.title())); m_t << "\n"; visitChildren(s); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlList &s) { if (m_hide) return; if (s.type()==DocHtmlList::Ordered) { m_t << "\n"; } else { m_t << "\n"; } visitChildren(s); if (s.type()==DocHtmlList::Ordered) { m_t << "\n"; } else { m_t << "\n"; } } void XmlDocVisitor::operator()(const DocHtmlListItem &l) { if (m_hide) return; m_t << "\n"; visitChildren(l); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlDescList &dl) { if (m_hide) return; m_t << "\n"; visitChildren(dl); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlDescTitle &dt) { if (m_hide) return; m_t << ""; visitChildren(dt); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlDescData &dd) { if (m_hide) return; m_t << ""; visitChildren(dd); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlTable &t) { if (m_hide) return; m_t << ""; if (t.caption()) { std::visit(*this,*t.caption()); } visitChildren(t); m_t << "
\n"; } void XmlDocVisitor::operator()(const DocHtmlRow &r) { if (m_hide) return; m_t << "\n"; visitChildren(r); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHtmlCell &c) { if (m_hide) return; if (c.isHeading()) m_t << ""; visitChildren(c); m_t << ""; } void XmlDocVisitor::operator()(const DocHtmlCaption &c) { if (m_hide) return; m_t << ""; visitChildren(c); m_t << "\n"; } void XmlDocVisitor::operator()(const DocInternal &i) { if (m_hide) return; m_t << ""; visitChildren(i); m_t << "\n"; } void XmlDocVisitor::operator()(const DocHRef &href) { if (m_hide) return; m_t << ""; visitChildren(href); m_t << ""; } void XmlDocVisitor::operator()(const DocHtmlDetails &d) { if (m_hide) return; m_t << "
"; visitChildren(d); m_t << "
"; } void XmlDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; m_t << ""; visitChildren(header); m_t << "\n"; } void XmlDocVisitor::operator()(const DocImage &img) { if (m_hide) return; QCString url = img.url(); QCString baseName; if (url.isEmpty()) { baseName = img.relPath()+img.name(); } else { baseName = correctURL(url,img.relPath()); } HtmlAttribList attribs = img.attribs(); auto it = std::find_if(attribs.begin(),attribs.end(), [](const auto &att) { return att.name=="alt"; }); QCString altValue = it!=attribs.end() ? it->value : ""; visitPreStart(m_t, "image", FALSE, *this, img.children(), baseName, TRUE, img.type(), img.width(), img.height(), QCString(), altValue, img.isInlineImage()); // copy the image to the output dir FileDef *fd; bool ambig; if (url.isEmpty() && (fd=findFileDef(Doxygen::imageNameLinkedMap,img.name(),ambig))) { copyFile(fd->absFilePath(),Config_getString(XML_OUTPUT)+"/"+baseName); } visitChildren(img); visitPostEnd(m_t, "image"); } void XmlDocVisitor::operator()(const DocDotFile &df) { if (m_hide) return; copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file())); visitPreStart(m_t, "dotfile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height()); visitChildren(df); visitPostEnd(m_t, "dotfile"); } void XmlDocVisitor::operator()(const DocMscFile &df) { if (m_hide) return; copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file())); visitPreStart(m_t, "mscfile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height()); visitChildren(df); visitPostEnd(m_t, "mscfile"); } void XmlDocVisitor::operator()(const DocDiaFile &df) { if (m_hide) return; copyFile(df.file(),Config_getString(XML_OUTPUT)+"/"+stripPath(df.file())); visitPreStart(m_t, "diafile", FALSE, *this, df.children(), stripPath(df.file()), FALSE, DocImage::Html, df.width(), df.height()); visitChildren(df); visitPostEnd(m_t, "diafile"); } void XmlDocVisitor::operator()(const DocLink &lnk) { if (m_hide) return; startLink(lnk.ref(),lnk.file(),lnk.anchor()); visitChildren(lnk); endLink(); } void XmlDocVisitor::operator()(const DocRef &ref) { if (m_hide) return; if (!ref.file().isEmpty()) { startLink(ref.ref(),ref.file(),ref.isSubPage() ? QCString() : ref.anchor()); } if (!ref.hasLinkText()) filter(ref.targetTitle()); visitChildren(ref); if (!ref.file().isEmpty()) endLink(); } void XmlDocVisitor::operator()(const DocSecRefItem &ref) { if (m_hide) return; m_t << ""; visitChildren(ref); m_t << "\n"; } void XmlDocVisitor::operator()(const DocSecRefList &l) { if (m_hide) return; m_t << "\n"; visitChildren(l); m_t << "\n"; } void XmlDocVisitor::operator()(const DocParamSect &s) { if (m_hide) return; m_t << ""; visitChildren(s); m_t << "\n"; } void XmlDocVisitor::operator()(const DocSeparator &) { m_t << "\n"; m_t << ""; } void XmlDocVisitor::operator()(const DocParamList &pl) { if (m_hide) return; m_t << "\n"; m_t << "\n"; for (const auto ¶m : pl.parameters()) { if (!pl.paramTypes().empty()) { m_t << ""; for (const auto &type : pl.paramTypes()) { std::visit(*this,type); } m_t << "\n"; } m_t << ""; std::visit(*this,param); m_t << "\n"; } m_t << "\n"; m_t << "\n"; for (const auto &par : pl.paragraphs()) { std::visit(*this,par); } m_t << "\n"; m_t << "\n"; } void XmlDocVisitor::operator()(const DocXRefItem &x) { if (m_hide) return; if (x.title().isEmpty()) return; m_t << ""; m_t << ""; filter(x.title()); m_t << ""; m_t << ""; visitChildren(x); if (x.title().isEmpty()) return; m_t << ""; m_t << ""; } void XmlDocVisitor::operator()(const DocInternalRef &ref) { if (m_hide) return; startLink(QCString(),ref.file(),ref.anchor()); visitChildren(ref); endLink(); m_t << " "; } void XmlDocVisitor::operator()(const DocText &t) { visitChildren(t); } void XmlDocVisitor::operator()(const DocHtmlBlockQuote &q) { if (m_hide) return; m_t << "
"; visitChildren(q); m_t << "
"; } void XmlDocVisitor::operator()(const DocVhdlFlow &) { } void XmlDocVisitor::operator()(const DocParBlock &pb) { if (m_hide) return; m_t << ""; visitChildren(pb); m_t << ""; } void XmlDocVisitor::filter(const QCString &str) { m_t << convertToXML(str); } void XmlDocVisitor::startLink(const QCString &ref,const QCString &file,const QCString &anchor) { //printf("XmlDocVisitor: file=%s anchor=%s\n",qPrint(file),qPrint(anchor)); m_t << ""; } void XmlDocVisitor::endLink() { m_t << ""; }