/******************************************************************************
*
* 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 << "" << cmd << ">\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 << "]";
}