summaryrefslogtreecommitdiffstats
path: root/contrib/tools/swig/Source/Doxygen
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-08-28 14:27:58 +0300
committerrobot-piglet <[email protected]>2025-08-28 14:57:06 +0300
commit81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch)
tree3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Doxygen
parent77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff)
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Doxygen')
-rw-r--r--contrib/tools/swig/Source/Doxygen/csharpdoc.cxx989
-rw-r--r--contrib/tools/swig/Source/Doxygen/csharpdoc.h243
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxycommands.h174
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyentity.cxx69
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyentity.h45
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyparser.cxx1493
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxyparser.h377
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxytranslator.cxx69
-rw-r--r--contrib/tools/swig/Source/Doxygen/doxytranslator.h90
-rw-r--r--contrib/tools/swig/Source/Doxygen/javadoc.cxx849
-rw-r--r--contrib/tools/swig/Source/Doxygen/javadoc.h169
-rw-r--r--contrib/tools/swig/Source/Doxygen/pydoc.cxx993
-rw-r--r--contrib/tools/swig/Source/Doxygen/pydoc.h208
13 files changed, 5768 insertions, 0 deletions
diff --git a/contrib/tools/swig/Source/Doxygen/csharpdoc.cxx b/contrib/tools/swig/Source/Doxygen/csharpdoc.cxx
new file mode 100644
index 00000000000..9b2b6038b94
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/csharpdoc.cxx
@@ -0,0 +1,989 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at http://www.swig.org/legal.html.
+ *
+ * csharpdoc.cxx
+ *
+ * Module to return documentation for nodes formatted for CSharpDoc
+ * ----------------------------------------------------------------------------- */
+
+#include "csharpdoc.h"
+#include "doxyparser.h"
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "swigmod.h"
+
+// define static tables, they are filled in CSharpDocConverter's constructor
+CSharpDocConverter::TagHandlersMap CSharpDocConverter::tagHandlers;
+
+using std::string;
+
+// Helper class increasing the provided indent string in its ctor and decreasing
+// it in its dtor.
+class IndentGuard {
+public:
+ // One indent level.
+ static const char *Level() {
+ return " ";
+ }
+ // Default ctor doesn't do anything and prevents the dtor from doing anything// too and should only be used when the guard needs to be initialized// conditionally as Init() can then be called after checking some condition.// Otherwise, prefer to use the non default ctor below.
+ IndentGuard() {
+ m_initialized = false;
+ }
+
+ // Ctor takes the output to determine the current indent and to remove the
+ // extra indent added to it in the dtor and the variable containing the indent
+ // to use, which must be used after every new line by the code actually
+ // updating the output.
+ IndentGuard(string &output, string &indent) {
+ Init(output, indent);
+ }
+
+ // Really initializes the object created using the default ctor.
+ void Init(string &output, string &indent) {
+ m_output = &output;
+ m_indent = &indent;
+
+ const string::size_type lastNonSpace = m_output->find_last_not_of(' ');
+ if (lastNonSpace == string::npos) {
+ m_firstLineIndent = m_output->length();
+ } else if ((*m_output)[lastNonSpace] == '\n') {
+ m_firstLineIndent = m_output->length() - (lastNonSpace + 1);
+ } else {
+ m_firstLineIndent = 0;
+ }
+
+ // Notice that the indent doesn't include the first line indent because it's
+ // implicit, i.e. it is present in the input and so is copied into the
+ // output anyhow.
+ *m_indent = Level();
+
+ m_initialized = true;
+ }
+
+ // Get the indent for the first line of the paragraph, which is smaller than
+ // the indent for the subsequent lines.
+ string getFirstLineIndent() const {
+ return string(m_firstLineIndent, ' ');
+ }
+
+ ~IndentGuard() {
+ if (!m_initialized)
+ return;
+
+ m_indent->clear();
+
+ // Get rid of possible remaining extra indent, e.g. if there were any trailing
+ // new lines: we shouldn't add the extra indent level to whatever follows
+ // this paragraph.
+ static const size_t lenIndentLevel = strlen(Level());
+ if (m_output->length() > lenIndentLevel) {
+ const size_t start = m_output->length() - lenIndentLevel;
+ if (m_output->compare(start, string::npos, Level()) == 0)
+ m_output->erase(start);
+ }
+ }
+
+private:
+ string *m_output;
+ string *m_indent;
+ string::size_type m_firstLineIndent;
+ bool m_initialized;
+
+ IndentGuard(const IndentGuard &);
+};
+
+static void replaceAll(std::string &src, const std::string &token, const std::string &replace) {
+ std::string::size_type pos = src.find(token);
+
+ while (pos != std::string::npos) {
+ src.replace(pos, token.size(), replace);
+ pos = src.find(token, pos + replace.size());
+ }
+}
+
+static void trimWhitespace(string &s) {
+ const string::size_type lastNonSpace = s.find_last_not_of(' ');
+ if (lastNonSpace == string::npos)
+ s.clear();
+ else
+ s.erase(lastNonSpace + 1);
+}
+
+// Erase the first character in the string if it is a newline
+static void eraseLeadingNewLine(string &s) {
+ if (!s.empty() && s[0] == '\n')
+ s.erase(s.begin());
+}
+
+// Erase the first character in the string if it is a newline
+static void eraseAllNewLine(string &str) {
+ for (size_t i = 0; i < str.size(); i++) {
+ // if the character is a newline character
+ if (str[i] == '\n') {
+ // remove the character
+ str.erase(i, 1);
+ // decrement the index to account for the removed character
+ i--;
+ }
+ }
+}
+
+// Erase last characters in the string if it is a newline or a space
+static void eraseTrailingSpaceNewLines(string &s) {
+ while (!s.empty() && (s[s.size() - 1] == '\n' || s[s.size() - 1] == ' '))
+ s.erase(s.size() - 1);
+}
+
+// escape some characters which cannot appear as it in C# comments
+static void escapeSpecificCharacters(string &str) {
+ for (size_t i = 0; i < str.size(); i++) {
+ if (str[i] == '<') {
+ str.replace(i, 1, "&lt;");
+ } else if (str[i] == '>') {
+ str.replace(i, 1, "&gt;");
+ } else if (str[i] == '&') {
+ str.replace(i, 1, "&amp;");
+ }
+ }
+}
+
+// Check the generated docstring line by line and make sure that any
+// code and verbatim blocks have an empty line preceding them, which
+// is necessary for Sphinx. Additionally, this strips any empty lines
+// appearing at the beginning of the docstring.
+static string padCodeAndVerbatimBlocks(const string &docString) {
+ std::string result;
+
+ std::istringstream iss(docString);
+
+ // Initialize to false because there is no previous line yet
+ bool lastLineWasNonBlank = false;
+
+ for (string line; std::getline(iss, line); result += line) {
+ if (!result.empty()) {
+ // Terminate the previous line
+ result += '\n';
+ }
+
+ const size_t pos = line.find_first_not_of(" \t");
+ if (pos == string::npos) {
+ lastLineWasNonBlank = false;
+ } else {
+ if (lastLineWasNonBlank &&
+ (line.compare(pos, 13, ".. code-block") == 0 ||
+ line.compare(pos, 7, ".. math") == 0 ||
+ line.compare(pos, 3, ">>>") == 0)) {
+ // Must separate code or math blocks from the previous line
+ result += '\n';
+ }
+ lastLineWasNonBlank = true;
+ }
+ }
+ return result;
+}
+
+/* static */
+CSharpDocConverter::TagHandlersMap::mapped_type CSharpDocConverter::make_handler(tagHandler handler) {
+ return make_pair(handler, std::string());
+}
+
+/* static */
+CSharpDocConverter::TagHandlersMap::mapped_type CSharpDocConverter::make_handler(tagHandler handler, const char *arg) {
+ return make_pair(handler, arg);
+}
+
+void CSharpDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+
+ tagHandlers["a"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
+ tagHandlers["b"] = make_handler(&CSharpDocConverter::handleTagWrap, "**");
+ // \c command is translated as single quotes around next word
+ tagHandlers["c"] = make_handler(&CSharpDocConverter::handleTagWrap, "``");
+ tagHandlers["cite"] = make_handler(&CSharpDocConverter::handleTagWrap, "'");
+ tagHandlers["e"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["@"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["\\"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["<"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&lt;");
+ tagHandlers[">"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&gt;");
+ tagHandlers["&"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&amp;");
+ tagHandlers["#"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["%"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["~"] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["\""] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["."] = make_handler(&CSharpDocConverter::handleTagChar);
+ tagHandlers["::"] = make_handler(&CSharpDocConverter::handleTagChar);
+ // these commands are stripped out, and only their content is printed
+ tagHandlers["attention"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
+ tagHandlers["author"] = make_handler(&CSharpDocConverter::handleTagWord, "Author");
+ tagHandlers["authors"] = make_handler(&CSharpDocConverter::handleTagWord, "Author");
+ tagHandlers["brief"] = make_handler(&CSharpDocConverter::handleSummary);
+ tagHandlers["bug"] = make_handler(&CSharpDocConverter::handleTagWord, "Bug:");
+ tagHandlers["code"] = make_handler(&CSharpDocConverter::handleCode);
+ tagHandlers["copyright"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
+ tagHandlers["date"] = make_handler(&CSharpDocConverter::handleTagWord, "Date");
+ tagHandlers["deprecated"] = make_handler(&CSharpDocConverter::handleTagWord, "Deprecated");
+ tagHandlers["details"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
+ tagHandlers["em"] = make_handler(&CSharpDocConverter::handleTagWrap, "*");
+ tagHandlers["example"] = make_handler(&CSharpDocConverter::handleTagWord, "Example");
+ tagHandlers["exception"] = tagHandlers["throw"] = tagHandlers["throws"] = make_handler(&CSharpDocConverter::handleTagException);
+ tagHandlers["htmlonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["invariant"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["latexonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["link"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["manonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["note"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["p"] = make_handler(&CSharpDocConverter::handleTagWrap, "``");
+ tagHandlers["partofdescription"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["rtfonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["remark"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
+ tagHandlers["remarks"] = make_handler(&CSharpDocConverter::handleParagraph, "remarks");
+ tagHandlers["sa"] = make_handler(&CSharpDocConverter::handleTagSee);
+ tagHandlers["see"] = make_handler(&CSharpDocConverter::handleTagSee);
+ tagHandlers["since"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["short"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ tagHandlers["todo"] = make_handler(&CSharpDocConverter::handleTagWord, "TODO");
+ tagHandlers["version"] = make_handler(&CSharpDocConverter::handleTagWord, "Version");
+ tagHandlers["verbatim"] = make_handler(&CSharpDocConverter::handleVerbatimBlock);
+ tagHandlers["warning"] = make_handler(&CSharpDocConverter::handleLine, "remarks");
+ tagHandlers["xmlonly"] = make_handler(&CSharpDocConverter::handleNotHandled);
+ // these commands have special handlers
+ tagHandlers["arg"] = make_handler(&CSharpDocConverter::handleAddList);
+ tagHandlers["cond"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["else"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["elseif"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["endcond"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["if"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["ifnot"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["image"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["li"] = make_handler(&CSharpDocConverter::handleIgnore);
+ tagHandlers["overload"] = make_handler(&CSharpDocConverter::handleIgnore);
+
+ tagHandlers["par"] = make_handler(&CSharpDocConverter::handleTagWord, "Title");
+ tagHandlers["param"] = tagHandlers["tparam"] = make_handler(&CSharpDocConverter::handleTagParam);
+ tagHandlers["ref"] = make_handler(&CSharpDocConverter::handleTagRef);
+ tagHandlers["result"] = tagHandlers["return"] = tagHandlers["returns"] = make_handler(&CSharpDocConverter::handleTagReturn);
+
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_handler(&CSharpDocConverter::handlePlainString);
+ tagHandlers["plainstd::endl"] = make_handler(&CSharpDocConverter::handleNewLine);
+ tagHandlers["n"] = make_handler(&CSharpDocConverter::handleNewLine);
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = tagHandlers["f["] = tagHandlers["f{"] = make_handler(&CSharpDocConverter::handleMath);
+
+ // HTML tags
+ tagHandlers["<a"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_A);
+ tagHandlers["<b"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<blockquote"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_A, "Quote: ");
+ tagHandlers["<body"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<br"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "\n");
+
+ // there is no formatting for this tag as it was deprecated in HTML 4.01 and
+ // not used in HTML 5
+ tagHandlers["<center"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<caption"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<code"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "``");
+
+ tagHandlers["<dl"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<dd"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<dt"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+
+ tagHandlers["<dfn"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<div"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<em"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<form"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<hr"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "--------------------------------------------------------------------\n");
+ tagHandlers["<h1"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "# ");
+ tagHandlers["<h2"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "## ");
+ tagHandlers["<h3"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "### ");
+ tagHandlers["<i"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "*");
+ tagHandlers["<input"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<img"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "Image:");
+ tagHandlers["<li"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "* ");
+ tagHandlers["<meta"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<multicol"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ol"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<p"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, "\n");
+ tagHandlers["<pre"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<small"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<span"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "'");
+ tagHandlers["<strong"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "**");
+
+ // make a space between text and super/sub script.
+ tagHandlers["<sub"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<sup"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag, " ");
+
+ tagHandlers["<table"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTagNoParam);
+ tagHandlers["<td"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_td);
+ tagHandlers["<th"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_th);
+ tagHandlers["<tr"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag_tr);
+ tagHandlers["<tt"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<kbd"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ul"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<var"] = make_handler(&CSharpDocConverter::handleDoxyHtmlTag2, "*");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(C)");
+ tagHandlers["&trade"] = make_handler(&CSharpDocConverter::handleHtmlEntity, " TM");
+ tagHandlers["&reg"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(R)");
+ tagHandlers["&lt"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<");
+ tagHandlers["&gt"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ">");
+ tagHandlers["&amp"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "&");
+ tagHandlers["&apos"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&quot"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&lsquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "`");
+ tagHandlers["&rsquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&ldquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&rdquo"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&ndash"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&mdash"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "--");
+ tagHandlers["&nbsp"] = make_handler(&CSharpDocConverter::handleHtmlEntity, " ");
+ tagHandlers["&times"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "x");
+ tagHandlers["&minus"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&sdot"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ".");
+ tagHandlers["&sim"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "~");
+ tagHandlers["&le"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<=");
+ tagHandlers["&ge"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ">=");
+ tagHandlers["&larr"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<--");
+ tagHandlers["&rarr"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "-->");
+}
+
+CSharpDocConverter::CSharpDocConverter(int flags):
+DoxygenTranslator(flags), m_tableLineLen(0), m_prevRowIsTH(false) {
+ fillStaticTables();
+}
+
+// Return the type as it should appear in the output documentation.
+static std::string getCSharpDocType(Node *n, const_String_or_char_ptr lname = "") {
+ std::string type;
+
+ String *s = Swig_typemap_lookup("doctype", n, lname, 0);
+ if (!s) {
+ if (String *t = Getattr(n, "type"))
+ s = SwigType_str(t, "");
+ }
+ /////////////////
+
+ if (!s)
+ return type;
+
+ type = Char(s);
+
+ Delete(s);
+
+ return type;
+}
+
+std::string CSharpDocConverter::getParamType(std::string param) {
+ std::string type;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ type = getCSharpDocType(p, pname);
+ break;
+ }
+ }
+ Delete(plist);
+ return type;
+}
+
+std::string CSharpDocConverter::getParamValue(std::string param) {
+ std::string value;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ String *pval = Getattr(p, "value");
+ if (pval)
+ value = Char(pval);
+ break;
+ }
+ }
+ Delete(plist);
+ return value;
+}
+
+/**
+ * Returns true, if the given parameter exists in the current node
+ * (for example param is a name of function parameter). If feature
+ * 'doxygen:nostripparams' is set, then this method always returns
+ * true - parameters are copied to output regardless of presence in
+ * function params list.
+ */
+bool CSharpDocConverter::paramExists(std::string param) {
+
+ if (GetFlag(currentNode, "feature:doxygen:nostripparams")) {
+ return true;
+ }
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+
+ for (Parm *p = plist; p;) {
+
+ if (Getattr(p, "name") && Char(Getattr(p, "name")) == param) {
+ return true;
+ }
+ /* doesn't seem to work always: in some cases (especially for 'self' parameters)
+ * tmap:in is present, but tmap:in:next is not and so this code skips all the parameters
+ */
+ //p = Getattr(p, "tmap:in") ? Getattr(p, "tmap:in:next") : nextSibling(p);
+ p = nextSibling(p);
+ }
+
+ Delete(plist);
+
+ return false;
+}
+
+std::string CSharpDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
+ std::string translatedComment;
+
+ if (doxygenEntity.isLeaf)
+ return translatedComment;
+
+ std::string currentSection;
+ std::list<DoxygenEntity>::iterator p = doxygenEntity.entityList.begin();
+ while (p != doxygenEntity.entityList.end()) {
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ p++;
+ }
+
+ return translatedComment;
+}
+
+void CSharpDocConverter::translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment) {
+ // check if we have needed handler and call it
+ std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
+ it = tagHandlers.find(getBaseCommand(doxyEntity.typeOfEntity));
+ if (it != tagHandlers.end())
+ (this->*(it->second.first)) (doxyEntity, translatedComment, it->second.second);
+}
+
+void CSharpDocConverter::handleIgnore(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size()) {
+ tag.entityList.pop_front();
+ }
+
+ translatedComment += translateSubtree(tag);
+}
+
+void CSharpDocConverter::handleSummary(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+
+ translatedComment += "<summary>";
+ std::string summary = translateSubtree(tag);
+
+ eraseAllNewLine(summary);
+ trimWhitespace(summary);
+ // remove final newlines
+ eraseTrailingSpaceNewLines(summary);
+ escapeSpecificCharacters(summary);
+
+ translatedComment += summary;
+
+
+ translatedComment += "</summary>";
+ translatedComment += "\n";
+}
+
+void CSharpDocConverter::handleLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &tagName) {
+
+ translatedComment += "<" + tagName + ">";
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ }
+ translatedComment += "</" + tagName + ">";
+}
+
+
+void CSharpDocConverter::handleNotHandled(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+
+ std::string paragraph = translateSubtree(tag);
+
+ eraseLeadingNewLine(paragraph);
+ eraseTrailingSpaceNewLines(paragraph);
+ trimWhitespace(paragraph);
+ escapeSpecificCharacters(paragraph);
+ translatedComment += paragraph;
+ translatedComment += "\n";
+}
+
+void CSharpDocConverter::handleAddList(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string listItem = translateSubtree(tag);
+ eraseAllNewLine(listItem);
+
+ translatedComment += "* ";
+ translatedComment += listItem;
+ translatedComment += "\n";
+}
+
+
+void CSharpDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &tagName) {
+ translatedComment += "<";
+ translatedComment += tagName;
+ translatedComment += ">";
+
+ std::string paragraph = translateSubtree(tag);
+
+ eraseAllNewLine(paragraph);
+ trimWhitespace(paragraph);
+ eraseTrailingSpaceNewLines(paragraph);
+ escapeSpecificCharacters(paragraph);
+
+ translatedComment += paragraph;
+
+ translatedComment += "</";
+ translatedComment += tagName;
+ translatedComment += ">\n";
+}
+
+void CSharpDocConverter::handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ string verb = translateSubtree(tag);
+
+ eraseLeadingNewLine(verb);
+
+ // Remove the last newline to prevent doubling the newline already present after \endverbatim
+ trimWhitespace(verb); // Needed to catch trailing newline below
+ eraseTrailingSpaceNewLines(verb);
+ escapeSpecificCharacters(verb);
+
+ translatedComment += verb;
+}
+
+void CSharpDocConverter::handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent;
+
+ // Only \f$ is translated to inline formulae, \f[ and \f{ are for the block ones.
+ const bool inlineFormula = tag.typeOfEntity == "f$";
+
+ string formulaNL;
+
+ if (inlineFormula) {
+ translatedComment += ":math:`";
+ } else {
+ indent.Init(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ const string formulaIndent = indent.getFirstLineIndent();
+ translatedComment += formulaIndent;
+ translatedComment += ".. math::\n";
+
+ formulaNL = '\n';
+ formulaNL += formulaIndent;
+ formulaNL += m_indent;
+ translatedComment += formulaNL;
+ }
+
+ std::string formula;
+ handleTagVerbatim(tag, formula, arg);
+
+ // It is important to ensure that we have no spaces around the inline math
+ // contents, so strip them.
+ const size_t start = formula.find_first_not_of(" \t\n");
+ const size_t end = formula.find_last_not_of(" \t\n");
+ if (start != std::string::npos) {
+ for (size_t n = start; n <= end; n++) {
+ if (formula[n] == '\n') {
+ // New lines must be suppressed in inline maths and indented in the block ones.
+ if (!inlineFormula)
+ translatedComment += formulaNL;
+ } else {
+ // Just copy everything else.
+ translatedComment += formula[n];
+ }
+ }
+ }
+
+ if (inlineFormula) {
+ translatedComment += "`";
+ }
+}
+
+void CSharpDocConverter::handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ translatedComment += "<code>";
+
+ std::string code;
+ handleTagVerbatim(tag, code, arg);
+
+ // Try and remove leading newline, which is present for block \code
+ // command:
+ escapeSpecificCharacters(code);
+ eraseLeadingNewLine(code);
+ trimWhitespace(code);
+
+ // Check for python doctest blocks, and treat them specially:
+ bool isDocTestBlock = false;
+ size_t startPos;
+ // ">>>" would normally appear at the beginning, but doxygen comment
+ // style may have space in front, so skip leading whitespace
+ if ((startPos = code.find_first_not_of(" \t")) != string::npos && code.substr(startPos, 3) == ">>>")
+ isDocTestBlock = true;
+
+ string codeIndent;
+ if (!isDocTestBlock) {
+ // Use the current indent for the code-block line itself.
+ translatedComment += indent.getFirstLineIndent();
+
+ // Specify the level of extra indentation that will be used for
+ // subsequent lines within the code block. Note that the correct
+ // "starting indentation" is already present in the input, so we
+ // only need to add the desired code block indentation.
+ codeIndent = m_indent;
+ }
+
+ translatedComment += codeIndent;
+ for (size_t n = 0; n < code.length(); n++) {
+ if (code[n] == '\n') {
+ // Don't leave trailing white space, this results in PEP8 validation
+ // errors in Python code (which are performed by our own unit tests).
+ trimWhitespace(translatedComment);
+ translatedComment += '\n';
+
+ // Ensure that we indent all the lines by the code indent.
+ translatedComment += codeIndent;
+ } else {
+ // Just copy everything else.
+ translatedComment += code[n];
+ }
+ }
+
+ trimWhitespace(translatedComment);
+
+ // For block commands, the translator adds the newline after
+ // \endcode, so try and compensate by removing the last newline from
+ // the code text:
+ eraseTrailingSpaceNewLines(translatedComment);
+
+ translatedComment += "</code>";
+ translatedComment += "\n";
+}
+
+void CSharpDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.data;
+}
+
+void CSharpDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ translatedComment += it->data;
+ }
+}
+
+void CSharpDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ handleParagraph(tag, translatedComment);
+ translatedComment += "\">\n";
+}
+
+void CSharpDocConverter::handleTagSee(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += "<seealso cref=\"";
+ std::string seeAlso = translateSubtree(tag);
+ escapeSpecificCharacters(seeAlso);
+
+ // Remove parameter list
+ // Alternative would be to try and convert them into C# types similar to Java implementation
+ std::string::size_type lbrace = seeAlso.find('(');
+ if (lbrace != std::string::npos)
+ seeAlso.erase(lbrace);
+
+ replaceAll(seeAlso, "::", ".");
+ eraseTrailingSpaceNewLines(seeAlso);
+
+ translatedComment += seeAlso;
+ translatedComment += "\"/>\n";
+}
+
+void CSharpDocConverter::handleTagCharReplace(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+}
+
+void CSharpDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.typeOfEntity;
+}
+
+void CSharpDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += " {" + translateSubtree(tag) + "}";
+ }
+}
+
+void CSharpDocConverter::handleTagWord(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg + ": ";
+ if (tag.entityList.size())
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += translateSubtree(tag);
+ translatedComment += "\n";
+}
+
+void CSharpDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+ tag.entityList.pop_front();
+ translatedComment += "Image: ";
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ if (tag.entityList.size())
+ translatedComment += "(" + tag.entityList.begin()->data + ")";
+}
+
+void CSharpDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+
+ if (tag.entityList.size() < 2)
+ return;
+
+ if (!paramExists(tag.entityList.begin()->data))
+ return;
+
+ IndentGuard indent(translatedComment, m_indent);
+
+ DoxygenEntity paramNameEntity = *tag.entityList.begin();
+ tag.entityList.pop_front();
+
+ const std::string &paramName = paramNameEntity.data;
+
+ const std::string paramValue = getParamValue(paramName);
+
+ translatedComment += "<param name=\"" + paramName + "\">";
+
+ translatedComment += translateSubtree(tag);
+ eraseTrailingSpaceNewLines(translatedComment);
+
+ translatedComment += "</param> \n";
+}
+
+
+void CSharpDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ translatedComment += "<returns>";
+ translatedComment += translateSubtree(tag);
+ eraseTrailingSpaceNewLines(translatedComment);
+ translatedComment += "</returns> \n";
+}
+
+
+void CSharpDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ DoxygenEntity paramNameEntity = *tag.entityList.begin();
+ tag.entityList.pop_front();
+
+ const std::string &paramName = paramNameEntity.data;
+
+ const std::string paramType = getParamType(paramName);
+ const std::string paramValue = getParamValue(paramName);
+
+ translatedComment += "<exception cref=\"" + paramName + "\">";
+
+ translatedComment += translateSubtree(tag);
+ eraseTrailingSpaceNewLines(translatedComment);
+
+ translatedComment += "</exception> \n";
+}
+
+
+void CSharpDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+
+ if (!tag.entityList.size())
+ return;
+
+ string anchor = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ string anchorText = anchor;
+
+ size_t pos = anchorText.find('#');
+ if (pos != string::npos) {
+ anchorText = anchorText.substr(pos + 1);
+ }
+
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "\\ref " + anchorText;
+}
+
+
+void CSharpDocConverter::handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ if (tag.entityList.size()) { // do not include empty tags
+ std::string tagData = translateSubtree(tag);
+ // wrap the thing, ignoring whitespace
+ size_t wsPos = tagData.find_last_not_of("\n\t ");
+ if (wsPos != std::string::npos && wsPos != tagData.size() - 1)
+ translatedComment += arg + tagData.substr(0, wsPos + 1) + arg + tagData.substr(wsPos + 1);
+ else
+ translatedComment += arg + tagData + arg;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ // translatedComment += "</" + arg.substr(1) + ">";
+ } else {
+ translatedComment += arg + htmlTagArgs;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, "</a>
+ translatedComment += " (" + m_url + ')';
+ m_url.clear();
+ } else {
+ m_url.clear();
+ size_t pos = htmlTagArgs.find('=');
+ if (pos != string::npos) {
+ m_url = htmlTagArgs.substr(pos + 1);
+ }
+ translatedComment += arg;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</em>
+ translatedComment += arg;
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ size_t nlPos = translatedComment.rfind('\n');
+ if (htmlTagArgs == "/") {
+ // end tag, </tr> appends vertical table line '|'
+ translatedComment += '|';
+ if (nlPos != string::npos) {
+ size_t startOfTableLinePos = translatedComment.find_first_not_of(" \t", nlPos + 1);
+ if (startOfTableLinePos != string::npos) {
+ m_tableLineLen = translatedComment.size() - startOfTableLinePos;
+ }
+ }
+ } else {
+ if (m_prevRowIsTH) {
+ // if previous row contained <th> tag, add horizontal separator
+ // but first get leading spaces, because they'll be needed for the next row
+ size_t numLeadingSpaces = translatedComment.size() - nlPos - 1;
+
+ translatedComment += string(m_tableLineLen, '-') + '\n';
+
+ if (nlPos != string::npos) {
+ translatedComment += string (numLeadingSpaces, ' ');
+ }
+ m_prevRowIsTH = false;
+ }
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </th> is ignored
+ } else {
+ translatedComment += '|';
+ m_prevRowIsTH = true;
+ }
+}
+
+void CSharpDocConverter::handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </td> is ignored
+ } else {
+ translatedComment += '|';
+ }
+}
+
+void CSharpDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
+ // html entities
+ translatedComment += arg;
+}
+
+void CSharpDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, const std::string &) {
+ trimWhitespace(translatedComment);
+
+ translatedComment += "\n";
+
+ if (!m_indent.empty())
+ translatedComment += m_indent;
+}
+
+String *CSharpDocConverter::makeDocumentation(Node *n) {
+ String *documentation;
+ std::string csharpDocString;
+
+ // store the node, we may need it later
+ currentNode = n;
+
+ documentation = getDoxygenComment(n);
+ if (documentation != NULL) {
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ csharpDocString = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+ csharpDocString = translateSubtree(root);
+ }
+ }
+
+ // if we got something log the result
+ if (!csharpDocString.empty()) {
+
+ // remove the last spaces and '\n' since additional one is added during writing to file
+ eraseTrailingSpaceNewLines(csharpDocString);
+
+ // ensure that a blank line occurs before code or math blocks
+ csharpDocString = padCodeAndVerbatimBlocks(csharpDocString);
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN CSHARPDOC---" << std::endl;
+ std::cout << csharpDocString;
+ std::cout << std::endl;
+ }
+ }
+
+ return NewString(csharpDocString.c_str());
+}
diff --git a/contrib/tools/swig/Source/Doxygen/csharpdoc.h b/contrib/tools/swig/Source/Doxygen/csharpdoc.h
new file mode 100644
index 00000000000..4f47f6a76a9
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/csharpdoc.h
@@ -0,0 +1,243 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at http://www.swig.org/legal.html.
+ *
+ * csharpdoc.h
+ *
+ * Module to return documentation for nodes formatted for CSharp
+ * ----------------------------------------------------------------------------- */
+
+#ifndef CSHARPDOCCONVERTER_H_
+#define CSHARPDOCCONVERTER_H_
+
+#include <list>
+#include <string>
+#include "swig.h"
+#include "doxyentity.h"
+#include "doxytranslator.h"
+
+#define DOC_STRING_LENGTH 64 // characters per line allowed
+#define DOC_PARAM_STRING_LENGTH 30 // characters reserved for param name / type
+
+class CSharpDocConverter : public DoxygenTranslator {
+public:
+ CSharpDocConverter(int flags = 0);
+
+ String *makeDocumentation(Node *node);
+
+protected:
+
+ size_t m_tableLineLen;
+ bool m_prevRowIsTH;
+ std::string m_url;
+
+ /*
+ * Translate every entity in a tree, also manages sections
+ * display. Prints title for every group of tags that have
+ * a section title associated with them
+ */
+ std::string translateSubtree(DoxygenEntity &doxygenEntity);
+
+ /*
+ * Translate one entity with the appropriate handler, according
+ * to the tagHandlers
+ */
+ void translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment);
+
+ /*
+ * Typedef for the function that handles one tag
+ * arg - some string argument to easily pass it through lookup table
+ */
+ typedef void (CSharpDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Wrap the command data with the some string
+ * arg - string to wrap with, like '_' or '*'
+ */
+ void handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Just prints new line
+ */
+ void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Replace char by the one ine argument
+ */
+ void handleTagCharReplace(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the name of tag to the output, used for escape-commands
+ */
+ void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \exception tag or its synonyms.
+ */
+ void handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /**
+ * Print the content without any tag
+ */
+ void handleNotHandled(DoxygenEntity &tag, std::string &translatedComment, const std::string &tagName);
+
+ /**
+ * Print the content as an item of a list
+ */
+ void handleAddList(DoxygenEntity &tag, std::string &translatedComment, const std::string &tagName);
+
+ /*
+ * Print only the content and strip original tag
+ */
+ void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+/*
+ * Ignore the tag
+ */
+ void handleIgnore(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Print only the line content and strip original tag
+ */
+ void handleLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Print summary
+ */
+ void handleSummary(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle Doxygen verbatim tag
+ */
+ void handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle one of the Doxygen formula-related tags.
+ */
+ void handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handle a code snippet.
+ */
+ void handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print only data part of code
+ */
+ void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /**
+ * Copies verbatim args of the tag to output, used for commands like \f$, ...
+ */
+ void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the if-elseif-else-endif section
+ */
+ void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Prints the specified message, than the contents of the tag
+ * arg - message
+ */
+ void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Prints the seealso tag
+ */
+ void handleTagSee(DoxygenEntity &tag, std::string &translatedComment, const std::string &);
+
+ /*
+ * Insert 'Word: ...'
+ */
+ void handleTagWord(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Insert 'Image: ...'
+ */
+ void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format nice param description with type information
+ */
+ void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \return tag or its synonyms.
+ */
+ void handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Writes text for \ref tag.
+ */
+ void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
+
+ void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Does not output params of HTML tag, for example in <table border='1'>
+ * 'border=1' is not written to output.
+ */
+ void handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Translates tag <a href = "url">text</a> to: text ("url"). */
+ void handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handles HTML tags, which are translated to markdown-like syntax, for example
+ * <i>text</i> --> _text_. Appends arg for start HTML tag and end HTML tag.
+ */
+ void handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <tr> */
+ void handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <th> */
+ void handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <td> */
+ void handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
+ void handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg);
+
+
+ /*
+ * Simple helper function that calculates correct parameter type
+ * of the node stored in 'currentNode'
+ * If param with specified name is not found, empty string is returned
+ */
+ std::string getParamType(std::string name);
+
+ /*
+ * Simple helper function to retrieve the parameter value
+ */
+ std::string getParamValue(std::string name);
+
+private:
+ // temporary thing, should be refactored somehow
+ Node *currentNode;
+
+ // Extra indent for the current paragraph, must be output after each new line.
+ std::string m_indent;
+
+ // this contains the handler pointer and one string argument
+ typedef std::map<std::string, std::pair<tagHandler, std::string> >TagHandlersMap;
+ static TagHandlersMap tagHandlers;
+
+
+ // Helper functions for fillStaticTables(): make a new tag handler object.
+ TagHandlersMap::mapped_type make_handler(tagHandler handler);
+ TagHandlersMap::mapped_type make_handler(tagHandler handler, const char *arg);
+
+ void fillStaticTables();
+
+ bool paramExists(std::string param);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxycommands.h b/contrib/tools/swig/Source/Doxygen/doxycommands.h
new file mode 100644
index 00000000000..782b6ab9441
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxycommands.h
@@ -0,0 +1,174 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxycommands.h
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef DOXYGENCOMMANDS_H
+#define DOXYGENCOMMANDS_H
+
+// doxy commands are not processed inside this block
+const char *CMD_HTML_ONLY = "htmlonly";
+// doxy commands are not processed inside this block
+const char *CMD_VERBATIM = "verbatim";
+const char *CMD_CODE = "code";
+const char *CMD_LATEX_1 = "f$";
+const char *CMD_LATEX_2 = "f{";
+const char *CMD_LATEX_3 = "f[";
+const char *CMD_END_HTML_ONLY = "endhtmlonly";
+const char *CMD_END_VERBATIM = "endverbatim";
+const char *CMD_END_CODE = "endcode";
+const char *CMD_END_LATEX_1 = "f$";
+const char *CMD_END_LATEX_2 = "f}";
+const char *CMD_END_LATEX_3 = "f]";
+
+const char *sectionIndicators[] = {
+ "attention", "author", "authors", "brief", "bug", "cond", "date",
+ "deprecated", "details", "else", "elseif", "endcond", "endif",
+ "exception", "if", "ifnot", "invariant", "note", "par", "param",
+ "tparam", "post", "pre", "remarks", "remark", "result", "return",
+ "returns", "retval", "sa", "see", "since", "test", "throw", "throws",
+ "todo", "version", "warning", "xrefitem"
+};
+
+const int sectionIndicatorsSize = sizeof(sectionIndicators) / sizeof(*sectionIndicators);
+
+/* All of the doxygen commands divided up by how they are parsed */
+const char *simpleCommands[] = {
+ // the first line are escaped chars, except \~, which is a language ID command.
+ "n", "$", "@", "\\", "&", "~", "<", ">", "#", "%", "\"", ".", "::",
+ // Member groups, which we currently ignore.
+ "{", "}",
+ "endcond",
+ "callgraph", "callergraph", "showinitializer", "hideinitializer", "internal",
+ "nosubgrouping", "public", "publicsection", "private", "privatesection",
+ "protected", "protectedsection", "tableofcontents"
+};
+
+const int simpleCommandsSize = sizeof(simpleCommands) / sizeof(*simpleCommands);
+
+const char *commandWords[] = {
+ "a", "b", "c", "e", "em", "p", "def", "enum", "package", "relates",
+ "namespace", "relatesalso", "anchor", "dontinclude", "include",
+ "includelineno", "copydoc", "copybrief", "copydetails", "verbinclude",
+ "htmlinclude", "extends", "implements", "memberof", "related", "relatedalso",
+ "cite"
+};
+
+const int commandWordsSize = sizeof(commandWords) / sizeof(*commandWords);
+
+const char *commandLines[] = {
+ "addindex", "fn", "name", "line", "var", "skipline", "typedef", "skip",
+ "until", "property"
+};
+
+const int commandLinesSize = sizeof(commandLines) / sizeof(*commandLines);
+
+const char *commandParagraph[] = {
+ "partofdescription", "result", "return", "returns", "remarks", "remark",
+ "since", "test", "sa", "see", "pre", "post", "details", "invariant",
+ "deprecated", "date", "note", "warning", "version", "todo", "bug",
+ "attention", "brief", "author", "authors", "copyright", "short"
+};
+
+const int commandParagraphSize = sizeof(commandParagraph) / sizeof(*commandParagraph);
+
+const char *commandEndCommands[] = {
+ CMD_HTML_ONLY, "latexonly", "manonly", "xmlonly", "link", "rtfonly"
+};
+
+const int commandEndCommandsSize = sizeof(commandEndCommands) / sizeof(*commandEndCommands);
+
+const char *commandWordParagraphs[] = {
+ "param", "tparam", "throw", "throws", "retval", "exception", "example"
+};
+
+const int commandWordParagraphsSize = sizeof(commandWordParagraphs) / sizeof(*commandWordParagraphs);
+
+const char *commandWordLines[] = {
+ "page", "subsection", "subsubsection", "section", "paragraph", "defgroup",
+ "snippet", "mainpage"
+};
+
+const int commandWordLinesSize = sizeof(commandWordLines) / sizeof(*commandWordLines);
+
+const char *commandWordOWordOWords[] = {
+ "category", "class", "protocol", "interface", "struct", "union"
+};
+
+const int commandWordOWordOWordsSize = sizeof(commandWordOWordOWords) / sizeof(*commandWordOWordOWords);
+
+const char *commandOWords[] = {
+ "dir", "file", "cond"
+};
+
+const int commandOWordsSize = sizeof(commandOWords) / sizeof(*commandOWords);
+
+const char *commandErrorThrowings[] = {
+ "annotatedclassstd::list", "classhierarchy", "define", "functionindex", "header",
+ "headerfilestd::list", "inherit", "l", "postheader", "endcode", "enddot", "endmsc", "endhtmlonly",
+ "endlatexonly", "endmanonly", "endlink", "endverbatim", "endxmlonly", "f]", "f}", "endif", "else",
+ "endrtfonly"
+};
+
+const int commandErrorThrowingsSize = sizeof(commandErrorThrowings) / sizeof(*commandErrorThrowings);
+
+const char *commandUniques[] = {
+ "xrefitem", "arg", "ingroup", "par", "headerfile", "overload", "weakgroup", "ref", "subpage", "dotfile", "image", "addtogroup", "li",
+ "if", "ifnot", "elseif", "else", "mscfile", "code", CMD_VERBATIM, "f{", "f[", "f$", "dot", "msc"
+};
+
+const int commandUniquesSize = sizeof(commandUniques) / sizeof(*commandUniques);
+
+// These HTML commands are transformed when producing output in other formats.
+// Other commands are left intact, but '<' and '> are replaced with entities in HTML
+// output. So <varName> appears as &lt;varName&gt; in HTML output. The same
+// behavior must be repeated by SWIG. See Doxygen doc for the list of commands.
+// '<' is prepended to distinguish HTML tags from Doxygen commands.
+const char *commandHtml[] = {
+ "<a", "<b", "<blockquote", "<body", "<br", "<center", "<caption", "<code", "<dd", "<dfn",
+ "<div", "<dl", "<dt", "<em", "<form", "<hr", "<h1", "<h2", "<h3", "<i", "<input", "<img",
+ "<li", "<meta", "<multicol", "<ol", "<p", "<pre", "<small", "<span", "<strong",
+ "<sub", "<sup", "<table", "<td", "<th", "<tr", "<tt", "<kbd", "<ul", "<var"
+};
+
+const int commandHtmlSize = sizeof(commandHtml) / sizeof(*commandHtml);
+
+// Only entities which are translatable to plain text are used here. Others
+// are copied unchanged to output.
+const char *commandHtmlEntities[] = {
+ "&copy", // (C)
+ "&trade", // (TM)
+ "&reg", // (R)
+ "&lt", // less-than symbol
+ "&gt", // greater-than symbol
+ "&amp", // ampersand
+ "&apos", // single quotation mark (straight)
+ "&quot", // double quotation mark (straight)
+ "&lsquo", // left single quotation mark
+ "&rsquo", // right single quotation mark
+ "&ldquo", // left double quotation mark
+ "&rdquo", // right double quotation mark
+ "&ndash", // n-dash (for numeric ranges, e.g. 2–8)
+ "&mdash", // --
+ "&nbsp", //
+ "&times", // x
+ "&minus", // -
+ "&sdot", // .
+ "&sim", // ~
+ "&le", // <=
+ "&ge", // >=
+ "&larr", // <--
+ "&rarr" // -->
+};
+
+const int commandHtmlEntitiesSize = sizeof(commandHtmlEntities) / sizeof(*commandHtmlEntities);
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxyentity.cxx b/contrib/tools/swig/Source/Doxygen/doxyentity.cxx
new file mode 100644
index 00000000000..29bbbe6c125
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyentity.cxx
@@ -0,0 +1,69 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyentity.cxx
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#include "doxyentity.h"
+#include <iostream>
+
+using std::cout;
+
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt):typeOfEntity(typeEnt), isLeaf(true) {
+}
+
+
+/* Basic node for commands that have
+ * only 1 item after them
+ * example: \b word
+ * OR holding a std::string
+ */
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt, const std::string &param1) : typeOfEntity(typeEnt), data(param1), isLeaf(true) {
+}
+
+
+/* Nonterminal node
+ * contains
+ */
+DoxygenEntity::DoxygenEntity(const std::string &typeEnt, const DoxygenEntityList &entList) : typeOfEntity(typeEnt), isLeaf(false), entityList(entList) {
+}
+
+
+void DoxygenEntity::printEntity(int level) const {
+
+ int thisLevel = level;
+
+ if (isLeaf) {
+ for (int i = 0; i < thisLevel; i++) {
+ cout << '\t';
+ }
+
+ cout << "Node Leaf Command: '" << typeOfEntity << "', ";
+
+ if (!data.empty()) {
+ cout << "Node Data: '" << data << "'";
+ }
+ cout << std::endl;
+
+ } else {
+
+ for (int i = 0; i < thisLevel; i++) {
+ cout << '\t';
+ }
+
+ cout << "Node Command: '" << typeOfEntity << "'" << std::endl;
+
+ thisLevel++;
+
+ for (DoxygenEntityListCIt p = entityList.begin(); p != entityList.end(); p++) {
+ p->printEntity(thisLevel);
+ }
+ }
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxyentity.h b/contrib/tools/swig/Source/Doxygen/doxyentity.h
new file mode 100644
index 00000000000..e475141a3a8
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyentity.h
@@ -0,0 +1,45 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyentity.h
+ *
+ * Part of the Doxygen comment translation module of SWIG.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYENTITY_H
+#define SWIG_DOXYENTITY_H
+
+#include <string>
+#include <list>
+
+
+class DoxygenEntity;
+
+typedef std::list<DoxygenEntity> DoxygenEntityList;
+typedef DoxygenEntityList::iterator DoxygenEntityListIt;
+typedef DoxygenEntityList::const_iterator DoxygenEntityListCIt;
+
+
+/*
+ * Structure to represent a doxygen comment entry
+ */
+class DoxygenEntity {
+public:
+ std::string typeOfEntity;
+ std::string data;
+ bool isLeaf;
+ DoxygenEntityList entityList;
+
+ DoxygenEntity(const std::string &typeEnt);
+ DoxygenEntity(const std::string &typeEnt, const std::string &param1);
+ DoxygenEntity(const std::string &typeEnt, const DoxygenEntityList &entList);
+
+ void printEntity(int level) const;
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxyparser.cxx b/contrib/tools/swig/Source/Doxygen/doxyparser.cxx
new file mode 100644
index 00000000000..451c7035988
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyparser.cxx
@@ -0,0 +1,1493 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyparser.cxx
+ * ----------------------------------------------------------------------------- */
+
+#include "doxyparser.h"
+#include "doxycommands.h"
+#include "swig.h"
+#include "swigwarn.h"
+
+#include <iostream>
+#include <vector>
+
+using std::string;
+using std::cout;
+using std::endl;
+
+// This constant defines the (only) characters valid inside a Doxygen "word".
+// It includes some unusual ones because of the commands such as \f[, \f{, \f],
+// \f} and \f$.
+static const char *DOXYGEN_WORD_CHARS = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "$[]{}";
+
+// Define static class members
+DoxygenParser::DoxyCommandsMap DoxygenParser::doxygenCommands;
+std::set<std::string> DoxygenParser::doxygenSectionIndicators;
+
+const int TOKENSPERLINE = 8; //change this to change the printing behaviour of the token list
+const std::string END_HTML_TAG_MARK("/");
+
+std::string getBaseCommand(const std::string &cmd) {
+ if (cmd.substr(0,5) == "param")
+ return "param";
+ else if (cmd.substr(0,4) == "code")
+ return "code";
+ else
+ return cmd;
+}
+
+// Find the first position beyond the word command. Extra logic is
+// used to avoid putting the characters "," and "." in
+// DOXYGEN_WORD_CHARS.
+static size_t getEndOfWordCommand(const std::string &line, size_t pos) {
+ size_t endOfWordPos = line.find_first_not_of(DOXYGEN_WORD_CHARS, pos);
+ if (line.substr(pos, 6) == "param[")
+ // include ",", which can appear in param[in,out]
+ endOfWordPos = line.find_first_not_of(string(DOXYGEN_WORD_CHARS)+ ",", pos);
+ else if (line.substr(pos, 5) == "code{")
+ // include ".", which can appear in e.g. code{.py}
+ endOfWordPos = line.find_first_not_of(string(DOXYGEN_WORD_CHARS)+ ".", pos);
+ return endOfWordPos;
+}
+
+
+DoxygenParser::DoxygenParser(bool noisy) : noisy(noisy) {
+ fillTables();
+}
+
+DoxygenParser::~DoxygenParser() {
+}
+
+void DoxygenParser::fillTables() {
+ // run it only once
+ if (doxygenCommands.size())
+ return;
+
+ // fill in tables with data from doxycommands.h
+ for (int i = 0; i < simpleCommandsSize; i++)
+ doxygenCommands[simpleCommands[i]] = SIMPLECOMMAND;
+
+ for (int i = 0; i < commandWordsSize; i++)
+ doxygenCommands[commandWords[i]] = COMMANDWORD;
+
+ for (int i = 0; i < commandLinesSize; i++)
+ doxygenCommands[commandLines[i]] = COMMANDLINE;
+
+ for (int i = 0; i < commandParagraphSize; i++)
+ doxygenCommands[commandParagraph[i]] = COMMANDPARAGRAPH;
+
+ for (int i = 0; i < commandEndCommandsSize; i++)
+ doxygenCommands[commandEndCommands[i]] = COMMANDENDCOMMAND;
+
+ for (int i = 0; i < commandWordParagraphsSize; i++)
+ doxygenCommands[commandWordParagraphs[i]] = COMMANDWORDPARAGRAPH;
+
+ for (int i = 0; i < commandWordLinesSize; i++)
+ doxygenCommands[commandWordLines[i]] = COMMANDWORDLINE;
+
+ for (int i = 0; i < commandWordOWordOWordsSize; i++)
+ doxygenCommands[commandWordOWordOWords[i]] = COMMANDWORDOWORDWORD;
+
+ for (int i = 0; i < commandOWordsSize; i++)
+ doxygenCommands[commandOWords[i]] = COMMANDOWORD;
+
+ for (int i = 0; i < commandErrorThrowingsSize; i++)
+ doxygenCommands[commandErrorThrowings[i]] = COMMANDERRORTHROW;
+
+ for (int i = 0; i < commandUniquesSize; i++)
+ doxygenCommands[commandUniques[i]] = COMMANDUNIQUE;
+
+ for (int i = 0; i < commandHtmlSize; i++)
+ doxygenCommands[commandHtml[i]] = COMMAND_HTML;
+
+ for (int i = 0; i < commandHtmlEntitiesSize; i++)
+ doxygenCommands[commandHtmlEntities[i]] = COMMAND_HTML_ENTITY;
+
+ // fill section indicators command set
+ for (int i = 0; i < sectionIndicatorsSize; i++)
+ doxygenSectionIndicators.insert(sectionIndicators[i]);
+}
+
+std::string DoxygenParser::stringToLower(const std::string &stringToConvert) {
+
+ string result(stringToConvert.size(), ' ');
+
+ for (size_t i = 0; i < result.size(); i++) {
+ result[i] = tolower(stringToConvert[i]);
+ }
+
+ return result;
+}
+
+bool DoxygenParser::isSectionIndicator(const std::string &smallString) {
+
+ std::set<std::string>::iterator it = doxygenSectionIndicators.find(stringToLower(smallString));
+
+ return it != doxygenSectionIndicators.end();
+}
+
+void DoxygenParser::printTree(const DoxygenEntityList &rootList) {
+ DoxygenEntityList::const_iterator p = rootList.begin();
+ while (p != rootList.end()) {
+ (*p).printEntity(0);
+ p++;
+ }
+}
+
+DoxygenParser::DoxyCommandEnum DoxygenParser::commandBelongs(const std::string &theCommand) {
+ DoxyCommandsMapIt it = doxygenCommands.find(stringToLower(getBaseCommand(theCommand)));
+
+ if (it != doxygenCommands.end()) {
+ return it->second;
+ }
+ // Check if this command is defined as an alias.
+ if (Getattr(m_node, ("feature:doxygen:alias:" + theCommand).c_str())) {
+ return COMMAND_ALIAS;
+ }
+ // Check if this command should be ignored.
+ if (String *const ignore = getIgnoreFeature(theCommand)) {
+ // Check that no value is specified for this feature ("1" is the implicit
+ // one given to it by SWIG itself), we may use the value in the future, but
+ // for now we only use the attributes.
+ if (Strcmp(ignore, "1") != 0) {
+ Swig_warning(WARN_PP_UNEXPECTED_TOKENS, m_fileName.c_str(), m_fileLineNo,
+ "Feature \"doxygen:ignore\" value ignored for Doxygen command \"%s\".\n", theCommand.c_str());
+ }
+ // Also ensure that the matching end command, if any, will be recognized.
+ const string endCommand = getIgnoreFeatureEndCommand(theCommand);
+ if (!endCommand.empty()) {
+ Setattr(m_node, ("feature:doxygen:ignore:" + endCommand).c_str(), NewString("1"));
+ }
+
+ return COMMAND_IGNORE;
+ }
+
+ return NONE;
+}
+
+std::string DoxygenParser::trim(const std::string &text) {
+ size_t start = text.find_first_not_of(" \t");
+ size_t end = text.find_last_not_of(" \t");
+
+ if (start == string::npos || start > end) {
+ return "";
+ }
+ return text.substr(start, end - start + 1);
+}
+
+bool DoxygenParser::isEndOfLine() {
+ if (m_tokenListIt == m_tokenList.end()) {
+ return false;
+ }
+ Token nextToken = *m_tokenListIt;
+ return nextToken.m_tokenType == END_LINE;
+}
+
+void DoxygenParser::skipWhitespaceTokens() {
+ if (m_tokenListIt == m_tokenList.end()) {
+ return;
+ }
+
+ while (m_tokenListIt != m_tokenList.end()
+ && (m_tokenListIt->m_tokenType == END_LINE || trim(m_tokenListIt->m_tokenString).empty())) {
+
+ m_tokenListIt++;
+ }
+}
+
+std::string DoxygenParser::getNextToken() {
+
+ if (m_tokenListIt == m_tokenList.end()) {
+ return "";
+ }
+
+ if (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ return (m_tokenListIt++)->m_tokenString;
+ }
+
+ return "";
+}
+
+std::string DoxygenParser::getNextWord() {
+
+ /* if (m_tokenListIt == m_tokenList.end()) {
+ return "";
+ }
+ */
+ while (m_tokenListIt != m_tokenList.end()
+ && (m_tokenListIt->m_tokenType == PLAINSTRING)) {
+ // handle quoted strings as words
+ string token = m_tokenListIt->m_tokenString;
+ if (token == "\"") {
+
+ string word = m_tokenListIt->m_tokenString;
+ m_tokenListIt++;
+ while (true) {
+ string nextWord = getNextToken();
+ if (nextWord.empty()) { // maybe report unterminated string error
+ return word;
+ }
+ word += nextWord;
+ if (nextWord == "\"") {
+ return word;
+ }
+ }
+ }
+
+ string tokenStr = trim(m_tokenListIt->m_tokenString);
+ m_tokenListIt++;
+ if (!tokenStr.empty()) {
+ return tokenStr;
+ }
+ }
+
+ return "";
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getOneLine(const TokenList &tokList) {
+
+ TokenListCIt endOfLineIt = m_tokenListIt;
+
+ while (endOfLineIt != tokList.end()) {
+ if (endOfLineIt->m_tokenType == END_LINE) {
+ return endOfLineIt;
+ }
+ endOfLineIt++;
+ }
+
+ return tokList.end();
+}
+
+std::string DoxygenParser::getStringTilCommand(const TokenList &tokList) {
+
+ if (m_tokenListIt == tokList.end()) {
+ return "";
+ }
+
+ string description;
+
+ while (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ const Token &currentToken = *m_tokenListIt++;
+ if (currentToken.m_tokenType == PLAINSTRING) {
+ description = description + currentToken.m_tokenString; // + " ";
+ }
+ }
+ return description;
+}
+
+std::string DoxygenParser::getStringTilEndCommand(const std::string &theCommand, const TokenList &tokList) {
+
+ if (m_tokenListIt == tokList.end()) {
+ return "";
+ }
+
+ string description;
+ while (m_tokenListIt != tokList.end()) {
+
+ if (m_tokenListIt->m_tokenType == PLAINSTRING) {
+ description += m_tokenListIt->m_tokenString;
+ } else if (m_tokenListIt->m_tokenType == END_LINE) {
+ description += "\n";
+ } else if (m_tokenListIt->m_tokenString == theCommand) {
+ m_tokenListIt++;
+ return description;
+ }
+
+ m_tokenListIt++;
+ }
+
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: " + theCommand + ".");
+
+ return description;
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndOfParagraph(const TokenList &tokList) {
+
+ TokenListCIt endOfParagraph = m_tokenListIt;
+
+ while (endOfParagraph != tokList.end()) {
+ // If \code or \verbatim is encountered within a paragraph, then
+ // go all the way to the end of that command, since the content
+ // could contain empty lines that would appear to be paragraph
+ // ends:
+ if (endOfParagraph->m_tokenType == COMMAND &&
+ (endOfParagraph->m_tokenString == "code" ||
+ endOfParagraph->m_tokenString == "verbatim")) {
+ const string theCommand = endOfParagraph->m_tokenString;
+ endOfParagraph = getEndCommand("end" + theCommand, tokList);
+ endOfParagraph++; // Move after the end command
+ return endOfParagraph;
+ }
+ if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ if (endOfParagraph != tokList.end()
+ && endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ //cout << "ENCOUNTERED END OF PARA" << endl;
+ return endOfParagraph;
+ }
+
+ } else if (endOfParagraph->m_tokenType == COMMAND) {
+
+ if (isSectionIndicator(getBaseCommand(endOfParagraph->m_tokenString))) {
+ return endOfParagraph;
+ } else {
+ endOfParagraph++;
+ }
+
+ } else if (endOfParagraph->m_tokenType == PLAINSTRING) {
+ endOfParagraph++;
+ } else {
+ return tokList.end();
+ }
+ }
+
+ return tokList.end();
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndOfSection(const std::string &theCommand, const TokenList &tokList) {
+
+ TokenListCIt endOfParagraph = m_tokenListIt;
+
+ while (endOfParagraph != tokList.end()) {
+ if (endOfParagraph->m_tokenType == COMMAND) {
+ if (theCommand == endOfParagraph->m_tokenString)
+ return endOfParagraph;
+ else
+ endOfParagraph++;
+ } else if (endOfParagraph->m_tokenType == PLAINSTRING) {
+ endOfParagraph++;
+ } else if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ if (endOfParagraph->m_tokenType == END_LINE) {
+ endOfParagraph++;
+ return endOfParagraph;
+ }
+ }
+ }
+ return tokList.end();
+}
+
+DoxygenParser::TokenListCIt DoxygenParser::getEndCommand(const std::string &theCommand, const TokenList &tokList) {
+
+ TokenListCIt endOfCommand = m_tokenListIt;
+
+ while (endOfCommand != tokList.end()) {
+ endOfCommand++;
+ if ((*endOfCommand).m_tokenType == COMMAND) {
+ if (theCommand == (*endOfCommand).m_tokenString) {
+ return endOfCommand;
+ }
+ }
+ }
+ //End command not found
+ return tokList.end();
+}
+
+void DoxygenParser::skipEndOfLine() {
+ if (m_tokenListIt != m_tokenList.end()
+ && m_tokenListIt->m_tokenType == END_LINE) {
+ m_tokenListIt++;
+ }
+}
+
+void DoxygenParser::addSimpleCommand(const std::string &theCommand, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ doxyList.push_back(DoxygenEntity(theCommand));
+}
+
+void DoxygenParser::addCommandWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ if (isEndOfLine()) {
+ // handles cases when command is at the end of line (for example "\c\nreally"
+ skipWhitespaceTokens();
+ doxyList.push_back(DoxygenEntity("plainstd::endl"));
+ }
+ std::string name = getNextWord();
+ if (!name.empty()) {
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ } else {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ }
+}
+
+void DoxygenParser::addCommandLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ DoxygenEntityList aNewList = parse(endOfLine, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ skipEndOfLine();
+}
+
+void DoxygenParser::addCommandParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfParagraph, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandEndCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ TokenListCIt endCommand = getEndCommand("end" + theCommand, tokList);
+ if (endCommand == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: end" + theCommand + ".");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ aNewList = parse(endCommand, tokList);
+ m_tokenListIt++;
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandWordParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfParagraph, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandWordLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+
+ TokenListCIt endOfLine = getOneLine(tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfLine, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ //else cout << "No line followed " << theCommand << " command. Not added" << endl;
+}
+
+void DoxygenParser::addCommandWordOWordOWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ std::string headerfile = getNextWord();
+ std::string headername = getNextWord();
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ if (!headerfile.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", headerfile));
+ if (!headername.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", headername));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandOWord(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string name = getNextWord();
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandErrorThrow(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &) {
+
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": Unexpectedly encountered this command.");
+ m_tokenListIt = getOneLine(tokList);
+}
+
+void DoxygenParser::addCommandHtml(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string htmlTagArgs = getNextToken();
+ doxyList.push_back(DoxygenEntity(theCommand, htmlTagArgs));
+}
+
+void DoxygenParser::addCommandHtmlEntity(const std::string &theCommand, const TokenList &, DoxygenEntityList &doxyList) {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ DoxygenEntityList aNewList;
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+}
+
+void DoxygenParser::addCommandUnique(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+
+ static std::map<std::string, std::string> endCommands;
+ DoxygenEntityList aNewList;
+ if (theCommand == "arg" || theCommand == "li") {
+ TokenListCIt endOfSection = getEndOfSection(theCommand, tokList);
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfSection, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \xrefitem <key> "(heading)" "(std::list title)" {text}
+ else if (theCommand == "xrefitem") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string key = getNextWord();
+ if (key.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No key followed the command. Command ignored.");
+ return;
+ }
+ std::string heading = getNextWord();
+ if (key.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No heading followed the command. Command ignored.");
+ return;
+ }
+ std::string title = getNextWord();
+ if (title.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No title followed the command. Command ignored.");
+ return;
+ }
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ aNewList = parse(endOfParagraph, tokList);
+ aNewList.push_front(DoxygenEntity("plainstd::string", title));
+ aNewList.push_front(DoxygenEntity("plainstd::string", heading));
+ aNewList.push_front(DoxygenEntity("plainstd::string", key));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \ingroup (<groupname> [<groupname> <groupname>])
+ else if (theCommand == "ingroup") {
+ std::string name = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \par [(paragraph title)] { paragraph }
+ else if (theCommand == "par") {
+ TokenListCIt endOfLine = getOneLine(tokList);
+ aNewList = parse(endOfLine, tokList);
+ DoxygenEntityList aNewList2;
+ TokenListCIt endOfParagraph = getEndOfParagraph(tokList);
+ aNewList2 = parse(endOfParagraph, tokList);
+ aNewList.splice(aNewList.end(), aNewList2);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \headerfile <header-file> [<header-name>]
+ else if (theCommand == "headerfile") {
+ DoxygenEntityList aNewList;
+ std::string name = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ name = getNextWord();
+ if (!name.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \overload [(function declaration)]
+ else if (theCommand == "overload") {
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ DoxygenEntityList aNewList;
+ aNewList = parse(endOfLine, tokList);
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ } else
+ doxyList.push_back(DoxygenEntity(theCommand));
+ }
+ // \weakgroup <name> [(title)]
+ else if (theCommand == "weakgroup") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ aNewList = parse(endOfLine, tokList);
+ }
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \ref <name> ["(text)"]
+ else if (theCommand == "ref") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No key followed the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ // TokenListCIt endOfLine = getOneLine(tokList);
+ // if (endOfLine != m_tokenListIt) {
+ // aNewList = parse(endOfLine, tokList);
+ //}
+ TokenListCIt tmpIt = m_tokenListIt;
+ std::string refTitle = getNextWord();
+ // If title is following the ref tag, it must be quoted. Otherwise
+ // doxy puts link on ref id.
+ if (refTitle.size() > 1 && refTitle[0] == '"') {
+ // remove quotes
+ refTitle = refTitle.substr(1, refTitle.size() - 2);
+ aNewList.push_back(DoxygenEntity("plainstd::string", refTitle));
+ } else {
+ // no quoted string is following, so we have to restore iterator
+ m_tokenListIt = tmpIt;
+ }
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \subpage <name> ["(text)"]
+ else if (theCommand == "subpage") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No name followed the command. Command ignored.");
+ return;
+ }
+ std::string text = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", name));
+ if (!text.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", text));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \code ... \endcode
+ // \verbatim ... \endverbatim
+ // \dot dotcode \enddot
+ // \msc msccode \endmsc
+ // \f[ ... \f]
+ // \f{ ... \f}
+ // \f{env}{ ... \f}
+ // \f$ ... \f$
+ else if (getBaseCommand(theCommand) == "code" || theCommand == "verbatim"
+ || theCommand == "dot" || theCommand == "msc" || theCommand == "f[" || theCommand == "f{" || theCommand == "f$") {
+ if (!endCommands.size()) {
+ // fill in static table of end commands
+ endCommands["f["] = "f]";
+ endCommands["f{"] = "f}";
+ endCommands["f$"] = "f$";
+ }
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string endCommand;
+ std::map<std::string, std::string>::iterator it;
+ it = endCommands.find(theCommand);
+ if (it != endCommands.end())
+ endCommand = it->second;
+ else
+ endCommand = "end" + getBaseCommand(theCommand);
+
+ std::string content = getStringTilEndCommand(endCommand, tokList);
+ aNewList.push_back(DoxygenEntity("plainstd::string", content));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \dotfile <file> ["caption"]
+ // \mscfile <file> ["caption"]
+ else if (theCommand == "dotfile" || theCommand == "mscfile") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string file = getNextWord();
+ if (file.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No file followed the command. Command ignored.");
+ return;
+ }
+ std::string caption = getNextWord();
+ aNewList.push_back(DoxygenEntity("plainstd::string", file));
+ if (!caption.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", caption));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \image <format> <file> ["caption"] [<sizeindication>=<size>]
+ else if (theCommand == "image") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string format = getNextWord();
+ if (format.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No format followed the command. Command ignored.");
+ return;
+ }
+ std::string file = getNextWord();
+ if (file.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No name followed the command. Command ignored.");
+ return;
+ }
+ std::string caption = getNextWord();
+ std::string size = getNextWord();
+
+ DoxygenEntityList aNewList;
+ aNewList.push_back(DoxygenEntity("plainstd::string", format));
+ aNewList.push_back(DoxygenEntity("plainstd::string", file));
+ if (!caption.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", caption));
+ if (!size.empty())
+ aNewList.push_back(DoxygenEntity("plainstd::string", size));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+ // \addtogroup <name> [(title)]
+ else if (theCommand == "addtogroup") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+ std::string name = getNextWord();
+ if (name.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": There should be at least one word following the command. Command ignored.");
+ return;
+ }
+ DoxygenEntityList aNewList;
+ TokenListCIt endOfLine = getOneLine(tokList);
+ if (endOfLine != m_tokenListIt) {
+ aNewList = parse(endOfLine, tokList);
+ }
+ aNewList.push_front(DoxygenEntity("plainstd::string", name));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ skipEndOfLine();
+ }
+ // \if <cond> [\else ...] [\elseif <cond> ...] \endif
+ else if (theCommand == "if" || theCommand == "ifnot" || theCommand == "else" || theCommand == "elseif") {
+ if (noisy)
+ cout << "Parsing " << theCommand << endl;
+
+ std::string cond;
+ bool skipEndif = false; // if true then we skip endif after parsing block of code
+ bool needsCond = (theCommand == "if" || theCommand == "ifnot" || theCommand == "elseif");
+ if (needsCond) {
+ cond = getNextWord();
+ if (cond.empty()) {
+ printListError(WARN_DOXYGEN_COMMAND_ERROR, "Error parsing Doxygen command " + theCommand + ": No word followed the command. Command ignored.");
+ return;
+ }
+ }
+
+ int nestedCounter = 1;
+ TokenListCIt endCommand = tokList.end();
+
+ // go through the commands and find closing endif or else or elseif
+ for (TokenListCIt it = m_tokenListIt; it != tokList.end(); it++) {
+ if (it->m_tokenType == COMMAND) {
+ if (it->m_tokenString == "if" || it->m_tokenString == "ifnot")
+ nestedCounter++;
+ else if (it->m_tokenString == "endif")
+ nestedCounter--;
+ if (nestedCounter == 1 && (it->m_tokenString == "else" || it->m_tokenString == "elseif")) { // else found
+ endCommand = it;
+ break;
+ }
+ if (nestedCounter == 0) { // endif found
+ endCommand = it;
+ skipEndif = true;
+ break;
+ }
+ }
+ }
+
+ if (endCommand == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: endif.");
+ return;
+ }
+
+ DoxygenEntityList aNewList;
+ aNewList = parse(endCommand, tokList);
+ if (skipEndif)
+ m_tokenListIt++;
+ if (needsCond)
+ aNewList.push_front(DoxygenEntity("plainstd::string", cond));
+ doxyList.push_back(DoxygenEntity(theCommand, aNewList));
+ }
+}
+
+void DoxygenParser::aliasCommand(const std::string &theCommand, const TokenList &/* tokList */ , DoxygenEntityList &doxyList) {
+ String *const alias = Getattr(m_node, ("feature:doxygen:alias:" + theCommand).c_str());
+ if (!alias)
+ return;
+
+ doxyList.push_back(DoxygenEntity("plainstd::string", Char(alias)));
+}
+
+String *DoxygenParser::getIgnoreFeature(const std::string &theCommand, const char *argument) const {
+ string feature_name = "feature:doxygen:ignore:" + theCommand;
+ if (argument) {
+ feature_name += ':';
+ feature_name += argument;
+ }
+
+ return Getattr(m_node, feature_name.c_str());
+}
+
+string DoxygenParser::getIgnoreFeatureEndCommand(const std::string &theCommand) const {
+ // We may be dealing either with a simple command or with the starting command
+ // of a block, as indicated by the value of "range" starting with "end".
+ string endCommand;
+ if (String *const range = getIgnoreFeature(theCommand, "range")) {
+ const char *const p = Char(range);
+ if (strncmp(p, "end", 3) == 0) {
+ if (p[3] == ':') {
+ // Normally the end command name follows after the colon.
+ endCommand = p + 4;
+ } else if (p[3] == '\0') {
+ // But it may be omitted in which case the default Doxygen convention of
+ // using "something"/"endsomething" is used.
+ endCommand = "end" + theCommand;
+ }
+ }
+ }
+
+ return endCommand;
+}
+
+void DoxygenParser::ignoreCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList) {
+ const string endCommand = getIgnoreFeatureEndCommand(theCommand);
+ if (!endCommand.empty()) {
+ TokenListCIt itEnd = getEndCommand(endCommand, tokList);
+ if (itEnd == tokList.end()) {
+ printListError(WARN_DOXYGEN_COMMAND_EXPECTED, "Expected Doxygen command: " + endCommand + ".");
+ return;
+ }
+ // If we ignore the command, also ignore any whitespace preceding it as we
+ // want to avoid having lines consisting of whitespace only or trailing
+ // whitespace in general (at least Python, with its pep8 tool, really
+ // doesn't like it).
+ if (!doxyList.empty()) {
+ DoxygenEntityList::iterator i = doxyList.end();
+ --i;
+ if (i->typeOfEntity == "plainstd::string" && i->data.find_first_not_of(" \t") == std::string::npos) {
+ doxyList.erase(i);
+ }
+ }
+ // Determine what to do with the part of the comment between the start and
+ // end commands: by default, we simply throw it away, but "contents"
+ // attribute may be used to change this.
+ if (String *const contents = getIgnoreFeature(theCommand, "contents")) {
+ // Currently only "parse" is supported but we may need to add "copy" to
+ // handle custom tags which contain text that is supposed to be copied
+ // verbatim in the future.
+ if (Strcmp(contents, "parse") == 0) {
+ DoxygenEntityList aNewList = parse(itEnd, tokList);
+ doxyList.splice(doxyList.end(), aNewList);
+ } else {
+ Swig_error(m_fileName.c_str(), m_fileLineNo, "Invalid \"doxygen:ignore\" feature \"contents\" attribute \"%s\".\n", Char(contents));
+ return;
+ }
+ }
+
+ m_tokenListIt = itEnd;
+ m_tokenListIt++;
+ } else if (String *const range = getIgnoreFeature(theCommand, "range")) {
+ // Currently we only support "line" but, in principle, we should also
+ // support "word" and "paragraph" for consistency with the built-in Doxygen
+ // commands which can have either of these three ranges (which are indicated
+ // using <word-arg>, (line-arg) and {para-arg} respectively in Doxygen
+ // documentation).
+ if (Strcmp(range, "line") == 0) {
+ // Consume everything until the end of line.
+ m_tokenListIt = getOneLine(tokList);
+ skipEndOfLine();
+ } else {
+ Swig_error(m_fileName.c_str(), m_fileLineNo, "Invalid \"doxygen:ignore\" feature \"range\" attribute \"%s\".\n", Char(range));
+ return;
+ }
+ }
+}
+
+void DoxygenParser::addCommand(const std::string &commandString, const TokenList &tokList, DoxygenEntityList &doxyList) {
+
+ string theCommand = stringToLower(commandString);
+
+ if (theCommand == "plainstd::string") {
+ string nextPhrase = getStringTilCommand(tokList);
+ if (noisy)
+ cout << "Parsing plain std::string :" << nextPhrase << endl;
+ doxyList.push_back(DoxygenEntity("plainstd::string", nextPhrase));
+ return;
+ }
+
+ switch (commandBelongs(commandString)) {
+ case SIMPLECOMMAND:
+ addSimpleCommand(theCommand, doxyList);
+ break;
+ case COMMANDWORD:
+ addCommandWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDLINE:
+ addCommandLine(theCommand, tokList, doxyList);
+ break;
+ case COMMANDPARAGRAPH:
+ addCommandParagraph(theCommand, tokList, doxyList);
+ break;
+ case COMMANDENDCOMMAND:
+ addCommandEndCommand(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDPARAGRAPH:
+ addCommandWordParagraph(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDLINE:
+ addCommandWordLine(theCommand, tokList, doxyList);
+ break;
+ case COMMANDWORDOWORDWORD:
+ addCommandWordOWordOWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDOWORD:
+ addCommandOWord(theCommand, tokList, doxyList);
+ break;
+ case COMMANDERRORTHROW:
+ addCommandErrorThrow(theCommand, tokList, doxyList);
+ break;
+ case COMMANDUNIQUE:
+ addCommandUnique(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_HTML:
+ addCommandHtml(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_HTML_ENTITY:
+ addCommandHtmlEntity(theCommand, tokList, doxyList);
+ break;
+ case COMMAND_ALIAS:
+ aliasCommand(commandString, tokList, doxyList);
+ break;
+ case COMMAND_IGNORE:
+ ignoreCommand(commandString, tokList, doxyList);
+ break;
+ case NONE:
+ case END_LINE:
+ case PARAGRAPH_END:
+ case PLAINSTRING:
+ case COMMAND:
+ // TODO: Ensure that these values either are correctly ignored here or can't happen.
+ break;
+ }
+}
+
+/**
+ * This method converts TokenList to DoxygenEntryList.
+ */
+DoxygenEntityList DoxygenParser::parse(TokenListCIt endParsingIndex, const TokenList &tokList, bool root) {
+ // if we are root, than any strings should be added as 'partofdescription', else as 'plainstd::string'
+ std::string currPlainstringCommandType = root ? "partofdescription" : "plainstd::string";
+ DoxygenEntityList aNewList;
+
+ // Less than check (instead of not equal) is a safeguard in case the
+ // iterator is incremented past the end
+ while (m_tokenListIt < endParsingIndex) {
+
+ Token currToken = *m_tokenListIt;
+
+ if (noisy)
+ cout << "Parsing for phrase starting in:" << currToken.toString() << endl;
+
+ if (currToken.m_tokenType == END_LINE) {
+ aNewList.push_back(DoxygenEntity("plainstd::endl"));
+ m_tokenListIt++;
+ } else if (currToken.m_tokenType == COMMAND) {
+ m_tokenListIt++;
+ addCommand(currToken.m_tokenString, tokList, aNewList);
+ } else if (currToken.m_tokenType == PLAINSTRING) {
+ addCommand(currPlainstringCommandType, tokList, aNewList);
+ }
+
+ // If addCommand above misbehaves, it can move the iterator past endParsingIndex
+ if (m_tokenListIt > endParsingIndex)
+ printListError(WARN_DOXYGEN_UNEXPECTED_ITERATOR_VALUE, "Unexpected iterator value in DoxygenParser::parse");
+
+ if (endParsingIndex != tokList.end() && m_tokenListIt == tokList.end()) {
+ // this could happen if we can't reach the original endParsingIndex
+ printListError(WARN_DOXYGEN_UNEXPECTED_END_OF_COMMENT, "Unexpected end of Doxygen comment encountered.");
+ break;
+ }
+ }
+ return aNewList;
+}
+
+DoxygenEntityList DoxygenParser::createTree(Node *node, String *documentation) {
+ m_node = node;
+
+ tokenizeDoxygenComment(Char(documentation), Char(Getfile(documentation)), Getline(documentation));
+
+ if (noisy) {
+ cout << "---TOKEN LIST---" << endl;
+ printList();
+ }
+
+ DoxygenEntityList rootList = parse(m_tokenList.end(), m_tokenList, true);
+
+ if (noisy) {
+ cout << "PARSED LIST" << endl;
+ printTree(rootList);
+ }
+ return rootList;
+}
+
+/*
+ * Splits 'text' on 'separator' chars. Separator chars are not part of the
+ * strings.
+ */
+DoxygenParser::StringVector DoxygenParser::split(const std::string &text, char separator) {
+ StringVector lines;
+ size_t prevPos = 0, pos = 0;
+
+ while (pos < string::npos) {
+ pos = text.find(separator, prevPos);
+ lines.push_back(text.substr(prevPos, pos - prevPos));
+ prevPos = pos + 1;
+ }
+
+ return lines;
+}
+
+/*
+ * Returns true, if 'c' is one of doxygen comment block start
+ * characters: *, /, or !
+ */
+bool DoxygenParser::isStartOfDoxyCommentChar(char c) {
+ return (strchr("*/!", c) != NULL);
+}
+
+/*
+ * Adds token with Doxygen command to token list, but only if command is one of
+ * Doxygen commands. In that case true is returned. If the command is not
+ * recognized as a doxygen command, it is ignored and false is returned.
+ */
+bool DoxygenParser::addDoxyCommand(DoxygenParser::TokenList &tokList, const std::string &cmd) {
+ if (commandBelongs(cmd) != NONE) {
+ tokList.push_back(Token(COMMAND, cmd));
+ return true;
+ } else {
+ if (cmd.empty()) {
+ // This actually indicates a bug in the code in this file, as this
+ // function shouldn't be called at all in this case.
+ printListError(WARN_DOXYGEN_UNKNOWN_COMMAND, "Unexpected empty Doxygen command.");
+ return false;
+ }
+
+ // This function is called for the special Doxygen commands, but also for
+ // HTML commands (or anything that looks like them, actually) and entities.
+ // We don't recognize all of those, so just ignore them and pass them
+ // through, but warn about unknown Doxygen commands as ignoring them will
+ // often result in wrong output being generated.
+ const char ch = *cmd.begin();
+ if (ch != '<' && ch != '&') {
+ // Before calling printListError() we must ensure that m_tokenListIt used
+ // by it is valid.
+ const TokenListCIt itSave = m_tokenListIt;
+ m_tokenListIt = m_tokenList.end();
+
+ printListError(WARN_DOXYGEN_UNKNOWN_COMMAND, "Unknown Doxygen command: " + cmd + ".");
+
+ m_tokenListIt = itSave;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * This method copies comment text to output as it is - no processing is
+ * done, Doxygen commands are ignored. It is used for commands \verbatim,
+ * \htmlonly, \f$, \f[, and \f{.
+ */
+size_t DoxygenParser::processVerbatimText(size_t pos, const std::string &line) {
+ if (line[pos] == '\\' || line[pos] == '@') { // check for end commands
+
+ pos++;
+ size_t endOfWordPos = line.find_first_not_of(DOXYGEN_WORD_CHARS, pos);
+ string cmd = line.substr(pos, endOfWordPos - pos);
+
+ if (cmd == CMD_END_HTML_ONLY || cmd == CMD_END_VERBATIM || cmd == CMD_END_LATEX_1 || cmd == CMD_END_LATEX_2 || cmd == CMD_END_LATEX_3 || cmd == CMD_END_CODE) {
+
+ m_isVerbatimText = false;
+ addDoxyCommand(m_tokenList, cmd);
+
+ } else {
+
+ m_tokenList.push_back(Token(PLAINSTRING,
+ // include '\' or '@'
+ line.substr(pos - 1, endOfWordPos - pos + 1)));
+ }
+
+ pos = endOfWordPos;
+
+ } else {
+
+ // whitespaces are stored as plain strings
+ size_t startOfPossibleEndCmd = line.find_first_of("\\@", pos);
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, startOfPossibleEndCmd - pos)));
+ pos = startOfPossibleEndCmd;
+ }
+
+ return pos;
+}
+
+/*
+ * Processes doxy commands for escaped characters: \$ \@ \\ \& \~ \< \> \# \% \" \. \::
+ * Handling this separately supports documentation text like \@someText.
+ */
+bool DoxygenParser::processEscapedChars(size_t &pos, const std::string &line) {
+ if ((pos + 1) < line.size()) {
+
+ // \ and @ with trailing whitespace or quoted get to output as plain string
+ string whitespaces = " '\t\n";
+ if (whitespaces.find(line[pos + 1]) != string::npos) {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, 1)));
+ pos++;
+ return true;
+ }
+ // these chars can be escaped for doxygen
+ string escapedChars = "$@\\&~<>#%\".";
+ if (escapedChars.find(line[pos + 1]) != string::npos) {
+
+ addDoxyCommand(m_tokenList, line.substr(pos + 1, 1));
+ pos += 2;
+ return true;
+
+ } else if ((pos + 2) < line.size() && line[pos + 1] == ':' && line[pos + 2] == ':') {
+
+ // add command \:: - handling this separately supports documentation
+ // text like \::someText
+ addDoxyCommand(m_tokenList, line.substr(pos + 1, 2));
+ pos += 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Processes word doxygen commands, like \arg, \c, \b, \return, ...
+ */
+void DoxygenParser::processWordCommands(size_t &pos, const std::string &line) {
+ pos++;
+ size_t endOfWordPos = getEndOfWordCommand(line, pos);
+
+ string cmd = line.substr(pos, endOfWordPos - pos);
+ if (cmd.empty()) {
+ // This was a bare backslash, just ignore it.
+ return;
+ }
+
+ addDoxyCommand(m_tokenList, cmd);
+
+ // A flag for whether we want to skip leading spaces after the command
+ bool skipLeadingSpace = true;
+
+ if (cmd == CMD_HTML_ONLY || cmd == CMD_VERBATIM || cmd == CMD_LATEX_1 || cmd == CMD_LATEX_2 || cmd == CMD_LATEX_3 || getBaseCommand(cmd) == CMD_CODE) {
+
+ m_isVerbatimText = true;
+
+ // Skipping leading space is necessary with inline \code command,
+ // and it won't hurt anything for block \code (TODO: are the other
+ // commands also compatible with skip leading space? If so, just
+ // do it every time.)
+ if (getBaseCommand(cmd) == CMD_CODE) skipLeadingSpace = true;
+ else skipLeadingSpace = false;
+ } else if (cmd.substr(0,3) == "end") {
+ // If processing an "end" command such as "endlink", don't skip
+ // the space before the next string
+ skipLeadingSpace = false;
+ }
+
+ if (skipLeadingSpace) {
+ // skip any possible spaces after command, because some commands have parameters,
+ // and spaces between command and parameter must be ignored.
+ if (endOfWordPos != string::npos) {
+ endOfWordPos = line.find_first_not_of(" \t", endOfWordPos);
+ }
+ }
+
+ pos = endOfWordPos;
+}
+
+void DoxygenParser::processHtmlTags(size_t &pos, const std::string &line) {
+ bool isEndHtmlTag = false;
+ pos++;
+ if (line.size() > pos && line[pos] == '/') {
+ isEndHtmlTag = true;
+ pos++;
+ }
+
+ size_t endHtmlPos = line.find_first_of("\t >", pos);
+
+ string cmd = line.substr(pos, endHtmlPos - pos);
+ pos = endHtmlPos;
+
+ // prepend '<' to distinguish HTML tags from doxygen commands
+ if (!cmd.empty() && addDoxyCommand(m_tokenList, '<' + cmd)) {
+ // it is a valid HTML command
+ if (pos == string::npos) {
+ pos = line.size();
+ }
+ if (line[pos] != '>') {
+ // it should be HTML tag with args,
+ // for example <A ...>, <IMG ...>, ...
+ if (isEndHtmlTag) {
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_HTML_ERROR, "Doxygen HTML error for tag " + cmd + ": Illegal end HTML tag without greater-than ('>') found.");
+ }
+
+ endHtmlPos = line.find('>', pos);
+ if (endHtmlPos == string::npos) {
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_HTML_ERROR, "Doxygen HTML error for tag " + cmd + ": HTML tag without greater-than ('>') found.");
+ }
+ // add args of HTML command, like link URL, image URL, ...
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, endHtmlPos - pos)));
+ pos = endHtmlPos;
+ } else {
+ if (isEndHtmlTag) {
+ m_tokenList.push_back(Token(PLAINSTRING, END_HTML_TAG_MARK));
+ } else {
+ // it is a simple tag, so push empty string
+ m_tokenList.push_back(Token(PLAINSTRING, ""));
+ }
+ }
+
+ if (pos < line.size()) {
+ pos++; // skip '>'
+ }
+ } else {
+ // the command is not HTML supported by Doxygen, < and > will be
+ // replaced by HTML entities &lt; and &gt; respectively,
+ addDoxyCommand(m_tokenList, "&lt");
+ m_tokenList.push_back(Token(PLAINSTRING, cmd));
+ }
+}
+
+void DoxygenParser::processHtmlEntities(size_t &pos, const std::string &line) {
+ size_t endOfWordPos = line.find_first_not_of("abcdefghijklmnopqrstuvwxyz", pos + 1);
+
+ if (endOfWordPos != string::npos) {
+
+ if (line[endOfWordPos] == ';' && (endOfWordPos - pos) > 1) {
+ // if entity is not recognized by Doxygen (not in the list of
+ // commands) nothing is added (here and in Doxygen).
+ addDoxyCommand(m_tokenList, line.substr(pos, endOfWordPos - pos));
+ endOfWordPos++; // skip ';'
+ } else {
+ // it is not an entity - add entity for ampersand and the rest of string
+ addDoxyCommand(m_tokenList, "&amp");
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos + 1, endOfWordPos - pos - 1)));
+ }
+ }
+ pos = endOfWordPos;
+}
+
+/*
+ * This method processes normal comment, which has to be tokenized.
+ */
+size_t DoxygenParser::processNormalComment(size_t pos, const std::string &line) {
+ switch (line[pos]) {
+ case '\\':
+ case '@':
+ if (processEscapedChars(pos, line)) {
+ break;
+ }
+ // handle word commands \arg, \c, \return, ... and \f[, \f$, ... commands
+ processWordCommands(pos, line);
+ break;
+
+ case ' ': // whitespace
+ case '\t':
+ {
+ // whitespaces are stored as plain strings
+ size_t startOfNextWordPos = line.find_first_not_of(" \t", pos + 1);
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, startOfNextWordPos - pos)));
+ pos = startOfNextWordPos;
+ }
+ break;
+
+ case '<':
+ processHtmlTags(pos, line);
+ break;
+ case '>': // this char is detected here only when it is not part of HTML tag
+ addDoxyCommand(m_tokenList, "&gt");
+ pos++;
+ break;
+ case '&':
+ processHtmlEntities(pos, line);
+ break;
+ case '"':
+ m_isInQuotedString = true;
+ m_tokenList.push_back(Token(PLAINSTRING, "\""));
+ pos++;
+ break;
+ default:
+ m_tokenListIt = m_tokenList.end();
+ printListError(WARN_DOXYGEN_UNKNOWN_CHARACTER, std::string("Unknown special character in Doxygen comment: ") + line[pos] + ".");
+ }
+
+ return pos;
+}
+
+/*
+ * This is the main method, which tokenizes Doxygen comment to words and
+ * doxygen commands.
+ */
+void DoxygenParser::tokenizeDoxygenComment(const std::string &doxygenComment, const std::string &fileName, int fileLine) {
+ m_isVerbatimText = false;
+ m_isInQuotedString = false;
+ m_tokenList.clear();
+ m_fileLineNo = fileLine;
+ m_fileName = fileName;
+
+ StringVector lines = split(doxygenComment, '\n');
+
+ // remove trailing spaces, because they cause additional new line at the end
+ // comment, which is wrong, because these spaces are space preceding
+ // end of comment : ' */'
+ if (!doxygenComment.empty() && doxygenComment[doxygenComment.size() - 1] == ' ') {
+
+ string lastLine = lines[lines.size() - 1];
+
+ if (trim(lastLine).empty()) {
+ lines.pop_back(); // remove trailing empty line
+ }
+ }
+
+ for (StringVectorCIt it = lines.begin(); it != lines.end(); it++) {
+ const string &line = *it;
+ size_t pos = line.find_first_not_of(" \t");
+
+ if (pos == string::npos) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ // skip sequences of '*', '/', and '!' of any length
+ bool isStartOfCommentLineCharFound = false;
+ while (pos < line.size() && isStartOfDoxyCommentChar(line[pos])) {
+ pos++;
+ isStartOfCommentLineCharFound = true;
+ }
+
+ if (pos == line.size()) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ // if 'isStartOfCommentLineCharFound' then preserve leading spaces, so
+ // ' * comment' gets translated to ' * comment', not ' * comment'
+ // This is important to keep formatting for comments translated to Python.
+ if (isStartOfCommentLineCharFound && line[pos] == ' ') {
+ pos++; // points to char after ' * '
+ if (pos == line.size()) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+ }
+ // line[pos] may be ' \t' or start of word, it there was no '*', '/' or '!'
+ // at beginning of the line. Make sure it points to start of the first word
+ // in the line.
+ if (isStartOfCommentLineCharFound) {
+ size_t firstWordPos = line.find_first_not_of(" \t", pos);
+ if (firstWordPos == string::npos) {
+ m_tokenList.push_back(Token(END_LINE, "\n"));
+ continue;
+ }
+
+ if (firstWordPos > pos) {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, firstWordPos - pos)));
+ pos = firstWordPos;
+ }
+ } else {
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(0, pos)));
+ }
+
+ while (pos != string::npos) {
+ // find the end of the word
+ size_t doxyCmdOrHtmlTagPos = line.find_first_of("\\@<>&\" \t", pos);
+ if (doxyCmdOrHtmlTagPos != pos) {
+ // plain text found
+ // if the last char is punctuation, make it a separate word, otherwise
+ // it may be included with word also when not appropriate, for example:
+ // colors are \b red, green, and blue --> colors are <b>red,</b> green, and blue
+ // instead of (comma not bold):
+ // colors are \b red, green, and blue --> colors are <b>red</b>, green, and blue
+ // In Python it looks even worse:
+ // colors are \b red, green, and blue --> colors are 'red,' green, and blue
+ string text = line.substr(pos, doxyCmdOrHtmlTagPos - pos);
+ string punctuations(".,:");
+ size_t textSize = text.size();
+
+ if (!text.empty()
+ && punctuations.find(text[text.size() - 1]) != string::npos &&
+ // but do not break ellipsis (...)
+ !(textSize > 1 && text[textSize - 2] == '.')) {
+ m_tokenList.push_back(Token(PLAINSTRING, text.substr(0, text.size() - 1)));
+ m_tokenList.push_back(Token(PLAINSTRING, text.substr(text.size() - 1)));
+ } else {
+ m_tokenList.push_back(Token(PLAINSTRING, text));
+ }
+ }
+
+ pos = doxyCmdOrHtmlTagPos;
+ if (pos != string::npos) {
+ if (m_isVerbatimText) {
+ pos = processVerbatimText(pos, line);
+
+ } else if (m_isInQuotedString) {
+
+ if (line[pos] == '"') {
+ m_isInQuotedString = false;
+ }
+ m_tokenList.push_back(Token(PLAINSTRING, line.substr(pos, 1)));
+ pos++;
+
+ } else {
+ pos = processNormalComment(pos, line);
+ }
+ }
+ }
+ m_tokenList.push_back(Token(END_LINE, "\n")); // add when pos == npos - end of line
+ }
+
+ m_tokenListIt = m_tokenList.begin();
+}
+
+void DoxygenParser::printList() {
+
+ int tokNo = 0;
+ for (TokenListCIt it = m_tokenList.begin(); it != m_tokenList.end(); it++, tokNo++) {
+
+ cout << it->toString() << ' ';
+
+ if ((tokNo % TOKENSPERLINE) == 0) {
+ cout << endl;
+ }
+ }
+}
+
+void DoxygenParser::printListError(int warningType, const std::string &message) {
+ int curLine = m_fileLineNo;
+ for (TokenListCIt it = m_tokenList.begin(); it != m_tokenListIt; it++) {
+ if (it->m_tokenType == END_LINE) {
+ curLine++;
+ }
+ }
+
+ Swig_warning(warningType, m_fileName.c_str(), curLine, "%s\n", message.c_str());
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxyparser.h b/contrib/tools/swig/Source/Doxygen/doxyparser.h
new file mode 100644
index 00000000000..a60446517bf
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxyparser.h
@@ -0,0 +1,377 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxyparser.h
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYPARSER_H
+#define SWIG_DOXYPARSER_H
+#include <string>
+#include <list>
+#include <map>
+#include <vector>
+#include <set>
+
+#include "swig.h"
+
+#include "doxyentity.h"
+
+// Utility function to return the base part of a command that may
+// include options, e.g. param[in] -> param
+std::string getBaseCommand(const std::string &cmd);
+
+
+class DoxygenParser {
+private:
+
+ enum DoxyCommandEnum {
+ NONE = -1,
+ SIMPLECOMMAND,
+ COMMANDWORD,
+ COMMANDLINE,
+ COMMANDPARAGRAPH,
+ COMMANDENDCOMMAND,
+ COMMANDWORDPARAGRAPH,
+ COMMANDWORDLINE,
+ COMMANDWORDOWORDWORD,
+ COMMANDOWORD,
+ COMMANDERRORTHROW,
+ COMMANDUNIQUE,
+ COMMAND_HTML,
+ COMMAND_HTML_ENTITY,
+ COMMAND_ALIAS,
+ COMMAND_IGNORE,
+ END_LINE,
+ PARAGRAPH_END,
+ PLAINSTRING,
+ COMMAND
+ };
+
+
+ /** This class contains parts of Doxygen comment as a token. */
+ class Token {
+ public:
+ DoxyCommandEnum m_tokenType;
+ std::string m_tokenString; /* the data , such as param for @param */
+
+ Token(DoxyCommandEnum tType, std::string tString) : m_tokenType(tType), m_tokenString(tString) {
+ }
+
+ std::string toString() const {
+ switch (m_tokenType) {
+ case END_LINE:
+ return "{END OF LINE}";
+ case PARAGRAPH_END:
+ return "{END OF PARAGRAPH}";
+ case PLAINSTRING:
+ return "{PLAINSTRING :" + m_tokenString + "}";
+ case COMMAND:
+ return "{COMMAND : " + m_tokenString + "}";
+ default:
+ return "";
+ }
+ }
+ };
+
+
+ typedef std::vector<Token> TokenList;
+ typedef TokenList::const_iterator TokenListCIt;
+ typedef TokenList::iterator TokenListIt;
+
+ TokenList m_tokenList;
+ TokenListCIt m_tokenListIt;
+
+ typedef std::map<std::string, DoxyCommandEnum> DoxyCommandsMap;
+ typedef DoxyCommandsMap::iterator DoxyCommandsMapIt;
+
+ /*
+ * Map of Doxygen commands to determine if a string is a
+ * command and how it needs to be parsed
+ */
+ static DoxyCommandsMap doxygenCommands;
+ static std::set<std::string> doxygenSectionIndicators;
+
+ bool m_isVerbatimText; // used to handle \htmlonly and \verbatim commands
+ bool m_isInQuotedString;
+
+ Node *m_node;
+ std::string m_fileName;
+ int m_fileLineNo;
+
+ /*
+ * Return the end command for a command appearing in "ignore" feature or empty
+ * string if this is a simple command and not a block one.
+ */
+ std::string getIgnoreFeatureEndCommand(const std::string &theCommand) const;
+
+ /*
+ * Helper for getting the value of doxygen:ignore feature or its argument.
+ */
+ String *getIgnoreFeature(const std::string &theCommand, const char *argument = NULL) const;
+
+ /*
+ * Whether to print lots of debug info during parsing
+ */
+ bool noisy;
+
+ /*
+ *Changes a std::string to all lower case
+ */
+ std::string stringToLower(const std::string &stringToConvert);
+
+ /*
+ * isSectionIndicator returns a boolean if the command is a section indicator
+ * This is a helper method for finding the end of a paragraph
+ * by Doxygen's terms
+ */
+ bool isSectionIndicator(const std::string &smallString);
+ /*
+ * Determines how a command should be handled (what group it belongs to
+ * for parsing rules
+ */
+ DoxyCommandEnum commandBelongs(const std::string &theCommand);
+
+ /*
+ *prints the parse tree
+ */
+ void printTree(const std::list<DoxygenEntity> &rootList);
+
+ /**
+ * Returns true if the next token is end of line token. This is important
+ * when single word commands like \c are at the end of line.
+ */
+ bool isEndOfLine();
+
+ /**
+ * Skips spaces, tabs, and end of line tokens.
+ */
+ void skipWhitespaceTokens();
+
+ /**
+ * Removes all spaces and tabs from beginning end end of string.
+ */
+ std::string trim(const std::string &text);
+
+ /*
+ * Returns string of the next token if the next token is PLAINSTRING. Returns
+ * empty string otherwise.
+ */
+ std::string getNextToken();
+
+ /*
+ * Returns the next word ON THE CURRENT LINE ONLY
+ * if a new line is encountered, returns a blank std::string.
+ * Updates the iterator if successful.
+ */
+ std::string getNextWord();
+
+ /*
+ * Returns the next word, which is not necessarily on the same line.
+ * Updates the iterator if successful.
+ */
+ std::string getNextWordInComment();
+
+ /*
+ * Returns the location of the end of the line as
+ * an iterator.
+ */
+ TokenListCIt getOneLine(const TokenList &tokList);
+
+ /*
+ * Returns a properly formatted std::string
+ * up til ANY command or end of line is encountered.
+ */
+ std::string getStringTilCommand(const TokenList &tokList);
+
+ /*
+ * Returns a properly formatted std::string
+ * up til the command specified is encountered
+ */
+ //TODO check that this behaves properly for formulas
+ std::string getStringTilEndCommand(const std::string &theCommand, const TokenList &tokList);
+
+ /*
+ * Returns the end of a Paragraph as an iterator-
+ * Paragraph is defined in Doxygen to be a paragraph of text
+ * separated by either a structural command or a blank line
+ */
+ TokenListCIt getEndOfParagraph(const TokenList &tokList);
+
+ /*
+ * Returns the end of a section, defined as the first blank line OR first
+ * encounter of the same command. Example of this behaviour is \arg.
+ * If no end is encountered, returns the last token of the std::list.
+ */
+ TokenListCIt getEndOfSection(const std::string &theCommand, const TokenList &tokList);
+
+ /*
+ * This method is for returning the end of a specific form of doxygen command
+ * that begins with a \command and ends in \endcommand
+ * such as \code and \endcode. The proper usage is
+ * progressTilEndCommand("endcode", tokenList);
+ * If the end is never encountered, it returns the end of the std::list.
+ */
+ TokenListCIt getEndCommand(const std::string &theCommand, const TokenList &tokList);
+ /*
+ * A special method for commands such as \arg that end at the end of a
+ * paragraph OR when another \arg is encountered
+ //TODO getTilAnyCommand
+ TokenListCIt getTilAnyCommand(const std::string &theCommand, const TokenList &tokList);
+ */
+
+ /**
+ * This methods skips end of line token, if it is the next token to be
+ * processed. It is called with comment commands which have args till the
+ * end of line, such as 'addtogroup' or 'addindex'.
+ * It is up to translator to specific language to decide whether
+ * to insert eol or not. For example, if a command is ignored in target
+ * language, new lines may make formatting ugly (Python).
+ */
+ void skipEndOfLine();
+
+ /*
+ * Method for Adding a Simple Command
+ * Format: @command
+ * Plain commands, such as newline etc, they contain no other data
+ * \n \\ \@ \& \$ \# \< \> \% \{ \}
+ */
+ void addSimpleCommand(const std::string &theCommand, DoxygenEntityList &doxyList);
+ /*
+ * CommandWord
+ * Format: @command <word>
+ * Commands with a single WORD after then such as @b
+ * "a", "b", "c", "e", "em", "p", "def", "enum", "example", "package",
+ * "relates", "namespace", "relatesalso","anchor", "dontinclude", "include",
+ * "includelineno"
+ */
+ void addCommandWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandLine
+ * Format: @command (line)
+ * Commands with a single LINE after then such as @var
+ * "addindex", "fn", "name", "line", "var", "skipline", "typedef", "skip",
+ * "until", "property"
+ */
+ void addCommandLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandParagraph
+ * Format: @command {paragraph}
+ * Commands with a single paragraph after then such as @return
+ * "return", "remarks", "since", "test", "sa", "see", "pre", "post",
+ * "details", "invariant", "deprecated", "date", "note", "warning",
+ * "version", "todo", "bug", "attention", "brief", "arg", "author"
+ */
+ void addCommandParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command EndCommand
+ * Format: @command and ends at @endcommand
+ * Commands that take in a block of text such as @code:
+ * "code", "dot", "msc", "f$", "f[", "f{environment}{", "htmlonly",
+ * "latexonly", "manonly", "verbatim", "xmlonly", "cond", "if", "ifnot",
+ * "link"
+ * Returns 1 if success, 0 if the endcommand is never encountered.
+ */
+ void addCommandEndCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandWordParagraph
+ * Format: @command <word> {paragraph}
+ * Commands such as param
+ * "param", "tparam", "throw", "throws", "retval", "exception"
+ */
+ void addCommandWordParagraph(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * CommandWordLine
+ * Format: @command <word> (line)
+ * Commands such as param
+ * "page", "subsection", "subsubsection", "section", "paragraph", "defgroup"
+ */
+ void addCommandWordLine(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command Word Optional Word Optional Word
+ * Format: @command <word> [<header-file>] [<header-name>]
+ * Commands such as class
+ * "category", "class", "protocol", "interface", "struct", "union"
+ */
+ void addCommandWordOWordOWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+ /*
+ * Command Optional Word
+ * Format: @command [<word>]
+ * Commands such as dir
+ * "dir", "file", "cond"
+ */
+ void addCommandOWord(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Commands that should not be encountered (such as PHP only)
+ * goes til the end of line then returns
+ */
+ void addCommandErrorThrow(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ void addCommandHtml(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ void addCommandHtmlEntity(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ *Adds the unique commands- different process for each unique command
+ */
+ void addCommandUnique(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Replace the given command with its predefined alias expansion.
+ */
+ void aliasCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * Simply ignore the given command, possibly with the word following it or
+ * until the matching end command.
+ */
+ void ignoreCommand(const std::string &theCommand, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ /*
+ * The actual "meat" of the doxygen parser. Calls the correct addCommand...()
+ * function.
+ */
+ void addCommand(const std::string &commandString, const TokenList &tokList, DoxygenEntityList &doxyList);
+
+ DoxygenEntityList parse(TokenListCIt endParsingIndex, const TokenList &tokList, bool root = false);
+
+ /*
+ * Fill static doxygenCommands and sectionIndicators containers
+ */
+ void fillTables();
+
+ /** Processes comment when \htmlonly and \verbatim commands are encountered. */
+ size_t processVerbatimText(size_t pos, const std::string &line);
+
+ bool processEscapedChars(size_t &pos, const std::string &line);
+ void processWordCommands(size_t &pos, const std::string &line);
+ void processHtmlTags(size_t &pos, const std::string &line);
+ void processHtmlEntities(size_t &pos, const std::string &line);
+
+
+ /** Processes comment outside \htmlonly and \verbatim commands. */
+ size_t processNormalComment(size_t pos, const std::string &line);
+
+ void tokenizeDoxygenComment(const std::string &doxygenComment, const std::string &fileName, int fileLine);
+ void printList();
+ void printListError(int warningType, const std::string &message);
+
+ typedef std::vector<std::string> StringVector;
+ typedef StringVector::const_iterator StringVectorCIt;
+
+ StringVector split(const std::string &text, char separator);
+ bool isStartOfDoxyCommentChar(char c);
+ bool addDoxyCommand(DoxygenParser::TokenList &tokList, const std::string &cmd);
+
+public:
+ DoxygenParser(bool noisy = false);
+ virtual ~DoxygenParser();
+ DoxygenEntityList createTree(Node *node, String *documentation);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx b/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx
new file mode 100644
index 00000000000..a638717eca1
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxytranslator.cxx
@@ -0,0 +1,69 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxytranslator.cxx
+ *
+ * Module to return documentation for nodes formatted for various documentation
+ * systems.
+ * ----------------------------------------------------------------------------- */
+
+#include "doxytranslator.h"
+
+DoxygenTranslator::DoxygenTranslator(int flags) : m_flags(flags), parser((flags &debug_parser) != 0) {
+}
+
+
+DoxygenTranslator::~DoxygenTranslator() {
+}
+
+
+bool DoxygenTranslator::hasDocumentation(Node *node) {
+ return getDoxygenComment(node) != NULL;
+}
+
+
+String *DoxygenTranslator::getDoxygenComment(Node *node) {
+ return Getattr(node, "doxygen");
+}
+
+/**
+ * Indent all lines in the comment by given indentation string
+ */
+void DoxygenTranslator::extraIndentation(String *comment, const_String_or_char_ptr indentationString) {
+ if (indentationString || Len(indentationString) > 0) {
+ int len = Len(comment);
+ bool trailing_newline = len > 0 && *(Char(comment) + len - 1) == '\n';
+ Insert(comment, 0, indentationString);
+ String *replace = NewStringf("\n%s", indentationString);
+ Replaceall(comment, "\n", replace);
+ if (trailing_newline) {
+ len = Len(comment);
+ Delslice(comment, len - 2, len); // Remove added trailing spaces on last line
+ }
+ Delete(replace);
+ }
+}
+
+String *DoxygenTranslator::getDocumentation(Node *node, const_String_or_char_ptr indentationString) {
+
+ if (!hasDocumentation(node)) {
+ return NewString("");
+ }
+
+ String *documentation = makeDocumentation(node);
+ extraIndentation(documentation, indentationString);
+ return documentation;
+}
+
+
+void DoxygenTranslator::printTree(const DoxygenEntityList &entityList) {
+
+ for (DoxygenEntityListCIt p = entityList.begin(); p != entityList.end(); p++) {
+ p->printEntity(0);
+ }
+}
diff --git a/contrib/tools/swig/Source/Doxygen/doxytranslator.h b/contrib/tools/swig/Source/Doxygen/doxytranslator.h
new file mode 100644
index 00000000000..f0d3b1b0607
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/doxytranslator.h
@@ -0,0 +1,90 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * doxytranslator.h
+ *
+ * Module to return documentation for nodes formatted for various documentation
+ * systems.
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_DOXYTRANSLATOR_H
+#define SWIG_DOXYTRANSLATOR_H
+
+#include "swig.h"
+#include "doxyentity.h"
+#include "doxyparser.h"
+#include <list>
+#include <string>
+
+
+/*
+ * This is a base class for translator classes. It defines the basic interface
+ * for translators, which convert Doxygen comments into alternative formats for
+ * target languages.
+ */
+class DoxygenTranslator {
+public:
+ /*
+ * Bit flags for the translator ctor.
+ *
+ * Derived classes may define additional flags.
+ */
+ enum {
+ // Use DoxygenParser in "noisy" mode.
+ debug_parser = 1,
+
+ // Output results of translating Doxygen comments.
+ debug_translator = 2
+ };
+
+ /*
+ * Constructor
+ */
+ DoxygenTranslator(int flags = 0);
+
+ /*
+ * Virtual destructor.
+ */
+ virtual ~DoxygenTranslator();
+
+ /*
+ * Return the documentation for a given node formatted for the correct
+ * documentation system.
+ */
+ String *getDocumentation(Node *node, const_String_or_char_ptr indentationString);
+
+ /*
+ * Returns true if the specified node has comment attached.
+ */
+ bool hasDocumentation(Node *node);
+
+ /*
+ * Get original comment string in Doxygen-format.
+ */
+ String *getDoxygenComment(Node *node);
+
+protected:
+ // The flags passed to the ctor.
+ const int m_flags;
+
+ DoxygenParser parser;
+
+ /*
+ * Returns the documentation formatted for a target language.
+ */
+ virtual String *makeDocumentation(Node *node) = 0;
+
+ /*
+ * Prints the details of a parsed entity list to stdout (for debugging).
+ */
+ void printTree(const DoxygenEntityList &entityList);
+
+ void extraIndentation(String *comment, const_String_or_char_ptr indentationString);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/javadoc.cxx b/contrib/tools/swig/Source/Doxygen/javadoc.cxx
new file mode 100644
index 00000000000..17c7d03abb5
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/javadoc.cxx
@@ -0,0 +1,849 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * javadoc.cxx
+ * ----------------------------------------------------------------------------- */
+
+#include "javadoc.h"
+#include "doxyparser.h"
+#include <iostream>
+#include <vector>
+#include <list>
+#include "swigmod.h"
+#define APPROX_LINE_LENGTH 64 // characters per line allowed
+#define TAB_SIZE 8 // current tab size in spaces
+//TODO {@link} {@linkplain} {@docRoot}, and other useful doxy commands that are not a javadoc tag
+
+// define static tables, they are filled in JavaDocConverter's constructor
+std::map<std::string, std::pair<JavaDocConverter::tagHandler, std::string> > JavaDocConverter::tagHandlers;
+
+using std::string;
+using std::list;
+using std::vector;
+
+void JavaDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+ /*
+ * Some translation rules:
+ *
+ * @ and \ must be escaped for both Java and Python to appear on output: \@, \\,
+ * while Doxygen produces output in both cases.
+ * Rule: @ and \ with space on the right should get to output.
+ *
+ * :: remains intact, even in class::method(). But you can use class#method also
+ * in C++ comment and it is properly translated to C++ output (changed by doxygen to ::)
+ * and Java output (remains #).
+ * Rule: SWIG type system can't be used to convert C::m to C#m, because in Java it is C.m
+ * Use string replacement :: --> # in tag see and links.
+ *
+ * HTML tags must be translated - remain in Java, to markdown in Python
+ *
+ * Unknown HTML tags, for example <x> is translated to &lt;x&gt; by doxygen, while
+ * Java src is <x> and therefore invisible on output - browser ignores unknown command.
+ * This is handy in syntax descriptions, for example: more <fileName>.
+ *
+ * Standalone < and > need not be translated, they are rendered properly in
+ * all three outputs.
+ *
+ * ., %, and " need not to be translated
+ *
+ * entities must be translated - remain in Java, something meaningful in Python (&lt, ...)
+ *
+ * - Python
+ * - add comments also to auto-generated methods like equals(), delete() in Java,
+ * and methods for std::vector(), ...
+ * Commenting methods of std types is simple - add comment to std_*.i file.
+ */
+
+ // these commands insert HTML tags
+ tagHandlers["a"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["arg"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
+ tagHandlers["b"] = make_pair(&JavaDocConverter::handleTagHtml, "b");
+ tagHandlers["c"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
+ tagHandlers["cite"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["e"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["em"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
+ tagHandlers["li"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
+ tagHandlers["p"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["@"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["\\"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["<"] = make_pair(&JavaDocConverter::handleTagChar, "&lt;");
+ tagHandlers[">"] = make_pair(&JavaDocConverter::handleTagChar, "&gt;");
+ tagHandlers["&"] = make_pair(&JavaDocConverter::handleTagChar, "&amp;");
+ tagHandlers["#"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["%"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["~"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["\""] = make_pair(&JavaDocConverter::handleTagChar, "&quot;");
+ tagHandlers["."] = make_pair(&JavaDocConverter::handleTagChar, "");
+ tagHandlers["::"] = make_pair(&JavaDocConverter::handleTagChar, "");
+ // these commands are stripped out
+ tagHandlers["attention"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["anchor"] = make_pair(&JavaDocConverter::handleTagAnchor, "");
+ tagHandlers["brief"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["bug"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["date"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["details"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ // this command is inserts text accumulated after cmd htmlonly -
+ // see DoxygenParser - CMD_HTML_ONLY.
+ tagHandlers["htmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["invariant"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["latexonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["manonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["partofdescription"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["rtfonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["short"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ tagHandlers["xmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
+ // these commands are kept as-is, they are supported by JavaDoc
+ tagHandlers["author"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["authors"] = make_pair(&JavaDocConverter::handleTagSame, "author");
+ tagHandlers["deprecated"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["exception"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["package"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["param"] = make_pair(&JavaDocConverter::handleTagParam, "");
+ tagHandlers["tparam"] = make_pair(&JavaDocConverter::handleTagParam, "");
+ tagHandlers["ref"] = make_pair(&JavaDocConverter::handleTagRef, "");
+ tagHandlers["result"] = make_pair(&JavaDocConverter::handleTagSame, "return");
+ tagHandlers["return"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["returns"] = make_pair(&JavaDocConverter::handleTagSame, "return");
+ //tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ //tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSame, "see");
+ tagHandlers["since"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["throws"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ tagHandlers["throw"] = make_pair(&JavaDocConverter::handleTagSame, "throws");
+ tagHandlers["version"] = make_pair(&JavaDocConverter::handleTagSame, "");
+ // these commands have special handlers
+ tagHandlers["code"] = make_pair(&JavaDocConverter::handleTagExtended, "code");
+ tagHandlers["cond"] = make_pair(&JavaDocConverter::handleTagMessage, "Conditional comment: ");
+ tagHandlers["copyright"] = make_pair(&JavaDocConverter::handleTagMessage, "Copyright: ");
+ tagHandlers["else"] = make_pair(&JavaDocConverter::handleTagIf, "Else: ");
+ tagHandlers["elseif"] = make_pair(&JavaDocConverter::handleTagIf, "Else if: ");
+ tagHandlers["endcond"] = make_pair(&JavaDocConverter::handleTagMessage, "End of conditional comment.");
+ // space in second arg prevents Javadoc to treat '@ example' as command. File name of
+ // example is still informative to user.
+ tagHandlers["example"] = make_pair(&JavaDocConverter::handleTagSame, " example");
+ tagHandlers["if"] = make_pair(&JavaDocConverter::handleTagIf, "If: ");
+ tagHandlers["ifnot"] = make_pair(&JavaDocConverter::handleTagIf, "If not: ");
+ tagHandlers["image"] = make_pair(&JavaDocConverter::handleTagImage, "");
+ tagHandlers["link"] = make_pair(&JavaDocConverter::handleTagLink, "");
+ tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSee, "");
+ tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSee, "");
+ tagHandlers["note"] = make_pair(&JavaDocConverter::handleTagMessage, "Note: ");
+ tagHandlers["overload"] = make_pair(&JavaDocConverter::handleTagMessage,
+ "This is an overloaded member function, provided for"
+ " convenience. It differs from the above function only in what" " argument(s) it accepts.");
+ tagHandlers["par"] = make_pair(&JavaDocConverter::handleTagPar, "");
+ tagHandlers["remark"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
+ tagHandlers["remarks"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
+ tagHandlers["todo"] = make_pair(&JavaDocConverter::handleTagMessage, "TODO: ");
+ tagHandlers["verbatim"] = make_pair(&JavaDocConverter::handleTagExtended, "literal");
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+ tagHandlers["f["] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+ tagHandlers["f{"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
+
+ tagHandlers["warning"] = make_pair(&JavaDocConverter::handleTagMessage, "Warning: ");
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_pair(&JavaDocConverter::handlePlainString, "");
+ tagHandlers["plainstd::endl"] = make_pair(&JavaDocConverter::handleNewLine, "");
+ tagHandlers["n"] = make_pair(&JavaDocConverter::handleNewLine, "");
+
+ // HTML tags
+ tagHandlers["<a"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<a");
+ tagHandlers["<b"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<b");
+ tagHandlers["<blockquote"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<blockquote");
+ tagHandlers["<body"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<body");
+ tagHandlers["<br"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<br");
+ tagHandlers["<center"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<center");
+ tagHandlers["<caption"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<caption");
+ tagHandlers["<code"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<code");
+ tagHandlers["<dd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dd");
+ tagHandlers["<dfn"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dfn");
+ tagHandlers["<div"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<div");
+ tagHandlers["<dl"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dl");
+ tagHandlers["<dt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dt");
+ tagHandlers["<em"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<em");
+ tagHandlers["<form"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<form");
+ tagHandlers["<hr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<hr");
+ tagHandlers["<h1"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h1");
+ tagHandlers["<h2"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h2");
+ tagHandlers["<h3"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h3");
+ tagHandlers["<i"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<i");
+ tagHandlers["<input"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<input");
+ tagHandlers["<img"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<img");
+ tagHandlers["<li"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<li");
+ tagHandlers["<meta"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<meta");
+ tagHandlers["<multicol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<multicol");
+ tagHandlers["<ol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ol");
+ tagHandlers["<p"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<p");
+ tagHandlers["<pre"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<pre");
+ tagHandlers["<small"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<small");
+ tagHandlers["<span"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<span");
+ tagHandlers["<strong"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<strong");
+ tagHandlers["<sub"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sub");
+ tagHandlers["<sup"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sup");
+ tagHandlers["<table"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<table");
+ tagHandlers["<td"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<td");
+ tagHandlers["<th"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<th");
+ tagHandlers["<tr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tr");
+ tagHandlers["<tt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tt");
+ tagHandlers["<kbd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<kbd");
+ tagHandlers["<ul"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ul");
+ tagHandlers["<var"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<var");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&copy");
+ tagHandlers["&trade"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&trade");
+ tagHandlers["&reg"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&reg");
+ tagHandlers["&lt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lt");
+ tagHandlers["&gt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&gt");
+ tagHandlers["&amp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&amp");
+ tagHandlers["&apos"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&apos");
+ tagHandlers["&quot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&quot");
+ tagHandlers["&lsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lsquo");
+ tagHandlers["&rsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rsquo");
+ tagHandlers["&ldquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ldquo");
+ tagHandlers["&rdquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rdquo");
+ tagHandlers["&ndash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ndash");
+ tagHandlers["&mdash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&mdash");
+ tagHandlers["&nbsp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&nbsp");
+ tagHandlers["&times"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&times");
+ tagHandlers["&minus"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&minus");
+ tagHandlers["&sdot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sdot");
+ tagHandlers["&sim"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sim");
+ tagHandlers["&le"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&le");
+ tagHandlers["&ge"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ge");
+ tagHandlers["&larr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&larr");
+ tagHandlers["&rarr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rarr");
+}
+
+JavaDocConverter::JavaDocConverter(int flags) :
+ DoxygenTranslator(flags) {
+ fillStaticTables();
+}
+
+/**
+ * Formats comment lines by inserting '\n *' at to long lines and tabs for
+ * indent. Currently it is disabled, which means original comment format is
+ * preserved. Experience shows, that this is usually better than breaking
+ * lines automatically, especially because original line endings are not removed,
+ * which results in short lines. To be useful, this function should have much
+ * better algorithm.
+ */
+std::string JavaDocConverter::formatCommand(std::string unformattedLine, int indent) {
+ std::string formattedLines;
+ return unformattedLine; // currently disabled
+ std::string::size_type lastPosition = 0;
+ std::string::size_type i = 0;
+ int isFirstLine = 1;
+ while (i != std::string::npos && i < unformattedLine.length()) {
+ lastPosition = i;
+ if (isFirstLine) {
+ i += APPROX_LINE_LENGTH;
+ } else {
+ i += APPROX_LINE_LENGTH - indent * TAB_SIZE;
+ }
+
+ i = unformattedLine.find(' ', i);
+
+ if (i > 0 && i + 1 < unformattedLine.length()) {
+ if (!isFirstLine)
+ for (int j = 0; j < indent; j++) {
+ formattedLines.append("\t");
+ } else {
+ isFirstLine = 0;
+ }
+ formattedLines.append(unformattedLine.substr(lastPosition, i - lastPosition + 1));
+ formattedLines.append("\n *");
+
+ }
+ }
+ if (lastPosition < unformattedLine.length()) {
+ if (!isFirstLine) {
+ for (int j = 0; j < indent; j++) {
+ formattedLines.append("\t");
+ }
+ }
+ formattedLines.append(unformattedLine.substr(lastPosition, unformattedLine.length() - lastPosition));
+ }
+
+ return formattedLines;
+}
+
+/**
+ * Returns true, if the given parameter exists in the current node
+ * (for example param is a name of function parameter). If feature
+ * 'doxygen:nostripparams' is set, then this method always returns
+ * true - parameters are copied to output regardless of presence in
+ * function params list.
+ */
+bool JavaDocConverter::paramExists(std::string param) {
+
+ if (GetFlag(currentNode, "feature:doxygen:nostripparams")) {
+ return true;
+ }
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+
+ for (Parm *p = plist; p;) {
+
+ if (Getattr(p, "name") && Char(Getattr(p, "name")) == param) {
+ return true;
+ }
+ /* doesn't seem to work always: in some cases (especially for 'self' parameters)
+ * tmap:in is present, but tmap:in:next is not and so this code skips all the parameters
+ */
+ //p = Getattr(p, "tmap:in") ? Getattr(p, "tmap:in:next") : nextSibling(p);
+ p = nextSibling(p);
+ }
+
+ Delete(plist);
+
+ return false;
+}
+
+std::string JavaDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
+ std::string translatedComment;
+
+ if (doxygenEntity.isLeaf) {
+ return translatedComment;
+ }
+
+ for (DoxygenEntityListIt p = doxygenEntity.entityList.begin(); p != doxygenEntity.entityList.end(); p++) {
+
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ }
+
+ return translatedComment;
+}
+
+/**
+ * Checks if a handler for the given tag exists, and calls it.
+ */
+void JavaDocConverter::translateEntity(DoxygenEntity &tag, std::string &translatedComment) {
+
+ std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
+ it = tagHandlers.find(getBaseCommand(tag.typeOfEntity));
+
+ if (it != tagHandlers.end()) {
+ (this->*(it->second.first))(tag, translatedComment, it->second.second);
+ } else {
+ // do NOT print warning, since there are many tags, which are not
+ // translatable - many warnings hide important ones
+ // addError(WARN_DOXYGEN_COMMAND_ERROR, "Unknown doxygen or HTML tag: " + tag.typeOfEntity);
+ }
+}
+
+
+void JavaDocConverter::handleTagAnchor(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += "<a id=\"" + translateSubtree(tag) + "\"></a>";
+}
+
+
+void JavaDocConverter::handleTagHtml(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ if (tag.entityList.size()) { // do not include empty tags
+ std::string tagData = translateSubtree(tag);
+ // wrap the thing, ignoring whitespace
+ size_t wsPos = tagData.find_last_not_of("\n\t ");
+ if (wsPos != std::string::npos)
+ translatedComment += "<" + arg + ">" + tagData.substr(0, wsPos + 1) + "</" + arg + ">" + tagData.substr(wsPos + 1);
+ else
+ translatedComment += "<" + arg + ">" + translateSubtree(tag) + "</" + arg + "> ";
+ }
+}
+
+void JavaDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ translatedComment += "</" + arg.substr(1) + ">";
+ } else {
+ translatedComment += arg + htmlTagArgs + ">";
+ }
+}
+
+void JavaDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, std::string &arg) {
+ // html entities can be preserved for Java
+ translatedComment += arg + ';';
+}
+
+void JavaDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, std::string &) {
+ // <br> tag is added, because otherwise to much text is joined
+ // into same paragraph by javadoc. For example, doxy list:
+ // - item one
+ // - item two
+ // becomes one paragraph with surrounding text without newlines.
+ // This way we get to many empty lines in javadoc output, but this
+ // is still better than joined lines. Possibility for improvements
+ // exists.
+ translatedComment += "<br>\n * ";
+}
+
+void JavaDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ // escape it if we need to, else just print
+ if (arg.size())
+ translatedComment += arg;
+ else
+ translatedComment += tag.typeOfEntity;
+}
+
+// handles tags which are the same in Doxygen and Javadoc.
+void JavaDocConverter::handleTagSame(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ if (arg.size())
+ tag.typeOfEntity = arg;
+ translatedComment += formatCommand(std::string("@" + tag.typeOfEntity + " " + translateSubtree(tag)), 2);
+}
+
+void JavaDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += formatCommand(translateSubtree(tag), 0);
+}
+
+void JavaDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ translatedComment += tag.data;
+ // if (tag.data.size() && tag.data[tag.data.size()-1] != ' ')
+ // translatedComment += " ";
+}
+
+void JavaDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ translatedComment += arg + " ";
+ for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ translatedComment += it->data;
+ }
+}
+
+void JavaDocConverter::handleTagExtended(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += "{@" + arg + " ";
+ handleParagraph(tag, translatedComment, dummy);
+ translatedComment += "}";
+}
+
+void JavaDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += arg;
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += " {" + translateSubtree(tag) + "}";
+ }
+}
+
+void JavaDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
+ std::string dummy;
+ translatedComment += formatCommand(arg, 0);
+ handleParagraph(tag, translatedComment, dummy);
+}
+
+void JavaDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+
+ std::string file;
+ std::string title;
+
+ std::list<DoxygenEntity>::iterator it = tag.entityList.begin();
+ if (it->data != "html")
+ return;
+
+ it++;
+ file = it->data;
+
+ it++;
+ if (it != tag.entityList.end())
+ title = it->data;
+
+ translatedComment += "<img src=";
+ if (file.size() >= 2 && file[0] == '"' && file[file.size() - 1] == '"')
+ translatedComment += file;
+ else
+ translatedComment += "\"" + file + "\"";
+ if (title.size())
+ translatedComment += " alt=" + title;
+
+ // the size indication is supported for Latex only in Doxygen, see manual
+
+ translatedComment += "/>";
+}
+
+void JavaDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ translatedComment += "<p";
+ if (tag.entityList.size()) {
+ translatedComment += " alt=\"" + tag.entityList.begin()->data + "\"";
+ translatedComment += ">";
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment, dummy);
+ }
+ translatedComment += "</p>";
+}
+
+
+void JavaDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+
+ if (!tag.entityList.size())
+ return;
+ if (!paramExists(tag.entityList.begin()->data))
+ return;
+
+ translatedComment += "@param ";
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment, dummy);
+}
+
+
+void JavaDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ // we translate to link, although \page is not supported in Java, but
+ // reader at least knows what to look at. Also for \anchor tag on the same
+ // page this link works.
+ string anchor = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ string anchorText = anchor;
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "<a href=\"#" + anchor + "\">" + anchorText + "</a>";
+}
+
+
+string JavaDocConverter::convertLink(string linkObject) {
+ if (GetFlag(currentNode, "feature:doxygen:nolinktranslate"))
+ return linkObject;
+ // find the params in function in linkObject (if any)
+ size_t lbracePos = linkObject.find('(', 0);
+ size_t rbracePos = linkObject.find(')', 0);
+ if (lbracePos == string::npos || rbracePos == string::npos || lbracePos >= rbracePos)
+ return "";
+
+ string paramsStr = linkObject.substr(lbracePos + 1,
+ rbracePos - lbracePos - 1);
+ // strip the params, to fill them later
+ string additionalObject = linkObject.substr(rbracePos + 1, string::npos);
+ linkObject = linkObject.substr(0, lbracePos);
+
+ // find all the params
+ vector<string> params;
+ size_t lastPos = 0, commaPos = 0;
+ while (true) {
+ commaPos = paramsStr.find(',', lastPos);
+ if (commaPos == string::npos)
+ commaPos = paramsStr.size();
+ string param = paramsStr.substr(lastPos, commaPos - lastPos);
+ // if any param type is empty, we are failed
+ if (!param.size())
+ return "";
+ params.push_back(param);
+ lastPos = commaPos + 1;
+ if (lastPos >= paramsStr.size())
+ break;
+ }
+
+ linkObject += "(";
+ for (size_t i = 0; i < params.size(); i++) {
+ // translate c/c++ type string to swig's type
+ // i e 'int **arr[100][10]' -> 'a(100).a(10).p.p.int'
+ // also converting arrays to pointers
+ string paramStr = params[i];
+ String *swigType = NewString("");
+
+ // handle const qualifier
+ if (paramStr.find("const") != string::npos)
+ SwigType_add_qualifier(swigType, "const");
+
+ // handle pointers, references and arrays
+ for (int j = (int)params[i].size() - 1; j >= 0; j--) {
+ // skip all the [...] blocks, write 'p.' for every of it
+ if (paramStr[j] == ']') {
+ while (j >= 0 && paramStr[j] != '[')
+ j--;
+ // no closing brace
+ if (j < 0)
+ return "";
+ SwigType_add_pointer(swigType);
+ continue;
+ } else if (paramStr[j] == '*')
+ SwigType_add_pointer(swigType);
+ else if (paramStr[j] == '&')
+ SwigType_add_reference(swigType);
+ else if (isalnum(paramStr[j])) {
+ size_t typeNameStart = paramStr.find_last_of(' ', j + 1);
+ if (typeNameStart == string::npos)
+ typeNameStart = 0;
+ else
+ typeNameStart++;
+ Append(swigType, paramStr.substr(typeNameStart, j - typeNameStart + 1).c_str());
+ break;
+ }
+ }
+
+ // make dummy param list, to lookup typemaps for it
+ Parm *dummyParam = NewParm(swigType, "", 0);
+ Swig_typemap_attach_parms("jstype", dummyParam, NULL);
+ Language::instance()->replaceSpecialVariables(0, Getattr(dummyParam, "tmap:jstype"), dummyParam);
+
+ //Swig_print(dummyParam, 1);
+ linkObject += Char(Getattr(dummyParam, "tmap:jstype"));
+ if (i != params.size() - 1)
+ linkObject += ",";
+
+ Delete(dummyParam);
+ Delete(swigType);
+ }
+ linkObject += ")";
+
+ return linkObject + additionalObject;
+}
+
+void JavaDocConverter::handleTagLink(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ string linkObject = convertLink(tag.entityList.begin()->data);
+ if (!linkObject.size())
+ linkObject = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+
+ translatedComment += "{@link ";
+ translatedComment += linkObject + " ";
+ handleParagraph(tag, translatedComment, dummy);
+ translatedComment += "}";
+}
+
+void JavaDocConverter::handleTagSee(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
+ std::string dummy;
+ if (!tag.entityList.size())
+ return;
+
+ // tag.entity list contains contents of the @see paragraph. It should contain
+ // one link (references) to method with or without parameters. Doxygen supports
+ // arbitrary text and types mixed, but this feature is not supported here.
+ // :: or # may be used as a separator between class name and method name.
+ list<DoxygenEntity>::iterator it;
+ string methodRef;
+ for (it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ if (it->typeOfEntity == "plainstd::endl") {
+ // handleNewLine(*it, translatedComment, dummy);
+ continue;
+ }
+ // restore entities which may be used in C++ type declaration
+ if (it->typeOfEntity == "&amp") {
+ methodRef += '&';
+ } else if (it->typeOfEntity == "&lt") {
+ methodRef += '<';
+ } else if (it->typeOfEntity == "&gt") {
+ methodRef += '>';
+ } else {
+ methodRef += it->data;
+ }
+ }
+
+ // replace :: with #, but only if it appears before left brace
+ size_t lbrace = methodRef.find('(');
+ size_t dblColon = methodRef.find("::");
+ if (dblColon < lbrace) {
+ methodRef = methodRef.substr(0, dblColon) + '#' + methodRef.substr(dblColon + 2);
+ }
+
+ translatedComment += "@see ";
+ string linkObject = convertLink(methodRef);
+ if (!linkObject.size()) {
+ linkObject = methodRef;
+ }
+ translatedComment += linkObject;
+}
+
+/* This function moves all line endings at the end of child entities
+ * out of the child entities to the parent.
+ * For example, entity tree:
+
+ -root
+ |-param
+ |-paramText
+ |-endline
+
+ should be turned to
+
+ -root
+ |-param
+ |-paramText
+ |-endline
+ *
+ */
+int JavaDocConverter::shiftEndlinesUpTree(DoxygenEntity &root, int level) {
+ DoxygenEntityListIt it = root.entityList.begin();
+ while (it != root.entityList.end()) {
+ // remove line endings
+ int ret = shiftEndlinesUpTree(*it, level + 1);
+ // insert them after this element
+ it++;
+ for (int i = 0; i < ret; i++) {
+ root.entityList.insert(it, DoxygenEntity("plainstd::endl"));
+ }
+ }
+
+ // continue only if we are not root
+ if (!level) {
+ return 0;
+ }
+
+ int removedCount = 0;
+ while (!root.entityList.empty()
+ && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_back();
+ removedCount++;
+ }
+ return removedCount;
+}
+
+/**
+ * This makes sure that all comment lines contain '*'. It is not mandatory in doxygen,
+ * but highly recommended for Javadoc. '*' in empty lines are indented according
+ * to indentation of the first line. Indentation of non-empty lines is not
+ * changed - garbage in garbage out.
+ */
+std::string JavaDocConverter::indentAndInsertAsterisks(const string &doc) {
+
+ size_t idx = doc.find('\n');
+ size_t indent = 0;
+ bool singleLineComment = idx == string::npos;
+ // Detect indentation.
+ // The first line in comment is the one after '/**', which may be
+ // spaces and '\n' or the text. In any case it is not suitable to detect
+ // indentation, so we have to skip the first '\n'.
+ // However, if there is just one line, then use that line to detect indentation.
+ if (idx != string::npos) {
+ size_t nonspaceIdx = doc.find_first_not_of(" \t", idx + 1);
+ if (nonspaceIdx != string::npos) {
+ indent = nonspaceIdx - idx;
+ }
+ }
+
+ if (indent == 0) {
+ // we can't indent the first line less than 0
+ indent = 1;
+ }
+ // Create the first line of Javadoc comment.
+ string indentStr(indent - 1, ' ');
+ string translatedStr = indentStr + "/**";
+ if (indent > 1) {
+ // remove the first space, so that '*' will be aligned
+ translatedStr = translatedStr.substr(1);
+ }
+
+ translatedStr += doc;
+
+ // insert '*' before each comment line, if it does not have it
+ idx = translatedStr.find('\n');
+
+ while (idx != string::npos) {
+
+ size_t nonspaceIdx = translatedStr.find_first_not_of(" \t", idx + 1);
+ if (nonspaceIdx != string::npos && translatedStr[nonspaceIdx] != '*') {
+ // line without '*' found - is it empty?
+ if (translatedStr[nonspaceIdx] != '\n') {
+ // add '* ' to each line without it
+ translatedStr = translatedStr.substr(0, nonspaceIdx) + "* " + translatedStr.substr(nonspaceIdx);
+ //printf(translatedStr.c_str());
+ } else {
+ // we found empty line, replace it with indented '*'
+ translatedStr = translatedStr.substr(0, idx + 1) + indentStr + "* " + translatedStr.substr(nonspaceIdx);
+ }
+ }
+ idx = translatedStr.find('\n', nonspaceIdx);
+ }
+
+ // Add the last comment line properly indented
+ size_t nonspaceEndIdx = translatedStr.find_last_not_of(" \t");
+ if (nonspaceEndIdx != string::npos) {
+ if (translatedStr[nonspaceEndIdx] != '\n') {
+ if (!singleLineComment)
+ translatedStr += '\n';
+ } else {
+ // remove trailing spaces
+ translatedStr = translatedStr.substr(0, nonspaceEndIdx + 1);
+ }
+ }
+ translatedStr += indentStr + "*/\n";
+
+ return translatedStr;
+}
+
+String *JavaDocConverter::makeDocumentation(Node *node) {
+
+ String *documentation = getDoxygenComment(node);
+
+ if (documentation == NULL) {
+ return NewString("");
+ }
+
+ if (GetFlag(node, "feature:doxygen:notranslate")) {
+
+ string doc = Char(documentation);
+
+ string translatedStr = indentAndInsertAsterisks(doc);
+
+ return NewString(translatedStr.c_str());
+ }
+
+ DoxygenEntityList entityList = parser.createTree(node, documentation);
+
+ // entityList.sort(CompareDoxygenEntities()); sorting currently not used,
+
+ if (m_flags & debug_translator) {
+ std::cout << "---RESORTED LIST---" << std::endl;
+ printTree(entityList);
+ }
+ // store the current node
+ // (currently just to handle params)
+ currentNode = node;
+
+ std::string javaDocString = "/**\n * ";
+
+ DoxygenEntity root("root", entityList);
+
+ shiftEndlinesUpTree(root);
+
+ // strip line endings at the beginning
+ while (!root.entityList.empty()
+ && root.entityList.begin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_front();
+ }
+
+ // and at the end
+ while (!root.entityList.empty()
+ && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
+ root.entityList.pop_back();
+ }
+
+ javaDocString += translateSubtree(root);
+
+ javaDocString += "\n */\n";
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN JAVADOC---" << std::endl;
+ std::cout << javaDocString;
+ }
+
+ return NewString(javaDocString.c_str());
+}
+
+void JavaDocConverter::addError(int warningType, const std::string &message) {
+ Swig_warning(warningType, "", 0, "Doxygen parser warning: %s. \n", message.c_str());
+}
diff --git a/contrib/tools/swig/Source/Doxygen/javadoc.h b/contrib/tools/swig/Source/Doxygen/javadoc.h
new file mode 100644
index 00000000000..d2c956c9dbf
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/javadoc.h
@@ -0,0 +1,169 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * javadoc.h
+ *
+ * Module to return documentation for nodes formatted for JavaDoc
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_JAVADOC_H
+#define SWIG_JAVADOC_H
+
+#include "doxytranslator.h"
+#include <map>
+
+/*
+ * A class to translate doxygen comments into JavaDoc style comments.
+ */
+class JavaDocConverter : public DoxygenTranslator {
+public:
+ JavaDocConverter(int flags = 0);
+ String *makeDocumentation(Node *node);
+
+protected:
+ /*
+ * Used to properly format JavaDoc-style command
+ */
+ std::string formatCommand(std::string unformattedLine, int indent);
+
+ /*
+ * Translate every entity in a tree.
+ */
+ std::string translateSubtree(DoxygenEntity &doxygenEntity);
+
+ /*
+ * Translate one entity with the appropriate handler, according
+ * to the tagHandlers
+ */
+ void translateEntity(DoxygenEntity &tag, std::string &translatedComment);
+
+ /*
+ * Fix all endlines location, etc
+ */
+ int shiftEndlinesUpTree(DoxygenEntity &root, int level = 0);
+
+ /*
+ * Convert params in link-objects and references
+ */
+ std::string convertLink(std::string linkObject);
+
+ /*
+ * Typedef for the function that handles one tag
+ * arg - some string argument to easily pass it through lookup table
+ */
+ typedef void (JavaDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /**
+ * Copies verbatim args of the tag to output, used for commands like \f$, ...
+ */
+ void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /** Creates anchor link. */
+ void handleTagAnchor(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Wrap the command data with the html tag
+ * arg - html tag, with no braces
+ */
+ void handleTagHtml(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
+ void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
+ void handleHtmlEntity(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Just prints new line
+ */
+ void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print the name of tag to the output, used for escape-commands
+ * arg - html-escaped variant, if not provided the command data is used
+ */
+ void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Do not translate and print as-is
+ * arg - the new tag name, if it needs to be renamed
+ */
+ void handleTagSame(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print only the content and strip original tag
+ */
+ void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print only data part of code
+ */
+ void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print extended Javadoc command, like {@code ...} or {@literal ...}
+ * arg - command name
+ */
+ void handleTagExtended(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Print the if-elseif-else-endif section
+ */
+ void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Prints the specified message, than the contents of the tag
+ * arg - message
+ */
+ void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert <img src=... /> tag if the 'format' field is specified as 'html'
+ */
+ void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert <p alt='title'>...</p>
+ */
+ void handleTagPar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert \@param command, if it is really a function param
+ */
+ void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Writes link for \ref tag.
+ */
+ void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, std::string &);
+
+ /*
+ * Insert {@link...} command, and handle all the <link-object>s correctly
+ * (like converting types of params, etc)
+ */
+ void handleTagLink(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+ /*
+ * Insert @see command, and handle all the <link-object>s correctly
+ * (like converting types of params, etc)
+ */
+ void handleTagSee(DoxygenEntity &tag, std::string &translatedComment, std::string &arg);
+
+private:
+ Node *currentNode;
+ // this contains the handler pointer and one string argument
+ static std::map<std::string, std::pair<tagHandler, std::string> > tagHandlers;
+ void fillStaticTables();
+
+ bool paramExists(std::string param);
+ std::string indentAndInsertAsterisks(const std::string &doc);
+
+ void addError(int warningType, const std::string &message);
+};
+
+#endif
diff --git a/contrib/tools/swig/Source/Doxygen/pydoc.cxx b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
new file mode 100644
index 00000000000..03986a49c9a
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/pydoc.cxx
@@ -0,0 +1,993 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * pydoc.cxx
+ *
+ * Module to return documentation for nodes formatted for PyDoc
+ * ----------------------------------------------------------------------------- */
+
+#include "pydoc.h"
+#include "doxyparser.h"
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "swigmod.h"
+
+// define static tables, they are filled in PyDocConverter's constructor
+PyDocConverter::TagHandlersMap PyDocConverter::tagHandlers;
+std::map<std::string, std::string> PyDocConverter::sectionTitles;
+
+using std::string;
+
+// Helper class increasing the provided indent string in its ctor and decreasing
+// it in its dtor.
+class IndentGuard {
+public:
+ // One indent level.
+ static const char *Level() {
+ return " ";
+ }
+ // Default ctor doesn't do anything and prevents the dtor from doing anything// too and should only be used when the guard needs to be initialized// conditionally as Init() can then be called after checking some condition.// Otherwise, prefer to use the non default ctor below.
+ IndentGuard() {
+ m_initialized = false;
+ }
+
+ // Ctor takes the output to determine the current indent and to remove the
+ // extra indent added to it in the dtor and the variable containing the indent
+ // to use, which must be used after every new line by the code actually
+ // updating the output.
+ IndentGuard(string &output, string &indent) {
+ Init(output, indent);
+ }
+
+ // Really initializes the object created using the default ctor.
+ void Init(string &output, string &indent) {
+ m_output = &output;
+ m_indent = &indent;
+
+ const string::size_type lastNonSpace = m_output->find_last_not_of(' ');
+ if (lastNonSpace == string::npos) {
+ m_firstLineIndent = m_output->length();
+ } else if ((*m_output)[lastNonSpace] == '\n') {
+ m_firstLineIndent = m_output->length() - (lastNonSpace + 1);
+ } else {
+ m_firstLineIndent = 0;
+ }
+
+ // Notice that the indent doesn't include the first line indent because it's
+ // implicit, i.e. it is present in the input and so is copied into the
+ // output anyhow.
+ *m_indent = Level();
+
+ m_initialized = true;
+ }
+
+ // Get the indent for the first line of the paragraph, which is smaller than
+ // the indent for the subsequent lines.
+ string getFirstLineIndent() const {
+ return string(m_firstLineIndent, ' ');
+ }
+
+ ~IndentGuard() {
+ if (!m_initialized)
+ return;
+
+ m_indent->clear();
+
+ // Get rid of possible remaining extra indent, e.g. if there were any trailing
+ // new lines: we shouldn't add the extra indent level to whatever follows
+ // this paragraph.
+ static const size_t lenIndentLevel = strlen(Level());
+ if (m_output->length() > lenIndentLevel) {
+ const size_t start = m_output->length() - lenIndentLevel;
+ if (m_output->compare(start, string::npos, Level()) == 0)
+ m_output->erase(start);
+ }
+ }
+
+private:
+ string *m_output;
+ string *m_indent;
+ string::size_type m_firstLineIndent;
+ bool m_initialized;
+
+ IndentGuard(const IndentGuard &);
+ IndentGuard &operator=(const IndentGuard &);
+};
+
+// Return the indent of the given multiline string, i.e. the maximal number of
+// spaces present in the beginning of all its non-empty lines.
+static size_t determineIndent(const string &s) {
+ size_t minIndent = static_cast<size_t>(-1);
+
+ for (size_t lineStart = 0; lineStart < s.length();) {
+ const size_t lineEnd = s.find('\n', lineStart);
+ const size_t firstNonSpace = s.find_first_not_of(' ', lineStart);
+
+ // If inequality doesn't hold, it means that this line contains only spaces
+ // (notice that this works whether lineEnd is valid or string::npos), in
+ // which case it doesn't matter when determining the indent.
+ if (firstNonSpace < lineEnd) {
+ // Here we can be sure firstNonSpace != string::npos.
+ const size_t lineIndent = firstNonSpace - lineStart;
+ if (lineIndent < minIndent)
+ minIndent = lineIndent;
+ }
+
+ if (lineEnd == string::npos)
+ break;
+
+ lineStart = lineEnd + 1;
+ }
+
+ return minIndent;
+}
+
+static void trimWhitespace(string &s) {
+ const string::size_type lastNonSpace = s.find_last_not_of(' ');
+ if (lastNonSpace == string::npos)
+ s.clear();
+ else
+ s.erase(lastNonSpace + 1);
+}
+
+// Erase the first character in the string if it is a newline
+static void eraseLeadingNewLine(string &s) {
+ if (!s.empty() && s[0] == '\n')
+ s.erase(s.begin());
+}
+
+// Erase the last character in the string if it is a newline
+static void eraseTrailingNewLine(string &s) {
+ if (!s.empty() && s[s.size() - 1] == '\n')
+ s.erase(s.size() - 1);
+}
+
+// Check the generated docstring line by line and make sure that any
+// code and verbatim blocks have an empty line preceding them, which
+// is necessary for Sphinx. Additionally, this strips any empty lines
+// appearing at the beginning of the docstring.
+static string padCodeAndVerbatimBlocks(const string &docString) {
+ std::string result;
+
+ std::istringstream iss(docString);
+
+ // Initialize to false because there is no previous line yet
+ bool lastLineWasNonBlank = false;
+
+ for (string line; std::getline(iss, line); result += line) {
+ if (!result.empty()) {
+ // Terminate the previous line
+ result += '\n';
+ }
+
+ const size_t pos = line.find_first_not_of(" \t");
+ if (pos == string::npos) {
+ lastLineWasNonBlank = false;
+ } else {
+ if (lastLineWasNonBlank &&
+ (line.compare(pos, 13, ".. code-block") == 0 ||
+ line.compare(pos, 7, ".. math") == 0 ||
+ line.compare(pos, 3, ">>>") == 0)) {
+ // Must separate code or math blocks from the previous line
+ result += '\n';
+ }
+ lastLineWasNonBlank = true;
+ }
+ }
+ return result;
+}
+
+// Helper function to extract the option value from a command,
+// e.g. param[in] -> in
+static std::string getCommandOption(const std::string &command, char openChar, char closeChar) {
+ string option;
+
+ size_t opt_begin, opt_end;
+ opt_begin = command.find(openChar);
+ opt_end = command.find(closeChar);
+ if (opt_begin != string::npos && opt_end != string::npos)
+ option = command.substr(opt_begin+1, opt_end-opt_begin-1);
+
+ return option;
+}
+
+
+/* static */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler) {
+ return make_pair(handler, std::string());
+}
+
+/* static */
+PyDocConverter::TagHandlersMap::mapped_type PyDocConverter::make_handler(tagHandler handler, const char *arg) {
+ return make_pair(handler, arg);
+}
+
+void PyDocConverter::fillStaticTables() {
+ if (tagHandlers.size()) // fill only once
+ return;
+
+ // table of section titles, they are printed only once
+ // for each group of specified doxygen commands
+ sectionTitles["author"] = "Author: ";
+ sectionTitles["authors"] = "Authors: ";
+ sectionTitles["copyright"] = "Copyright: ";
+ sectionTitles["deprecated"] = "Deprecated: ";
+ sectionTitles["example"] = "Example: ";
+ sectionTitles["note"] = "Notes: ";
+ sectionTitles["remark"] = "Remarks: ";
+ sectionTitles["remarks"] = "Remarks: ";
+ sectionTitles["warning"] = "Warning: ";
+// sectionTitles["sa"] = "See also: ";
+// sectionTitles["see"] = "See also: ";
+ sectionTitles["since"] = "Since: ";
+ sectionTitles["todo"] = "TODO: ";
+ sectionTitles["version"] = "Version: ";
+
+ tagHandlers["a"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["b"] = make_handler(&PyDocConverter::handleTagWrap, "**");
+ // \c command is translated as single quotes around next word
+ tagHandlers["c"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["cite"] = make_handler(&PyDocConverter::handleTagWrap, "'");
+ tagHandlers["e"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ // these commands insert just a single char, some of them need to be escaped
+ tagHandlers["$"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["@"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\\"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["<"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers[">"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["&"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["#"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["%"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["~"] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["\""] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["."] = make_handler(&PyDocConverter::handleTagChar);
+ tagHandlers["::"] = make_handler(&PyDocConverter::handleTagChar);
+ // these commands are stripped out, and only their content is printed
+ tagHandlers["attention"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["author"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["authors"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["brief"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["bug"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["code"] = make_handler(&PyDocConverter::handleCode);
+ tagHandlers["copyright"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["date"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["deprecated"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["details"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["em"] = make_handler(&PyDocConverter::handleTagWrap, "*");
+ tagHandlers["example"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["exception"] = tagHandlers["throw"] = tagHandlers["throws"] = make_handler(&PyDocConverter::handleTagException);
+ tagHandlers["htmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["invariant"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["latexonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["link"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["manonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["note"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["p"] = make_handler(&PyDocConverter::handleTagWrap, "``");
+ tagHandlers["partofdescription"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["rtfonly"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remark"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["remarks"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["sa"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["see"] = make_handler(&PyDocConverter::handleTagMessage, "See also: ");
+ tagHandlers["since"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["short"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["todo"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["version"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["verbatim"] = make_handler(&PyDocConverter::handleVerbatimBlock);
+ tagHandlers["warning"] = make_handler(&PyDocConverter::handleParagraph);
+ tagHandlers["xmlonly"] = make_handler(&PyDocConverter::handleParagraph);
+ // these commands have special handlers
+ tagHandlers["arg"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["cond"] = make_handler(&PyDocConverter::handleTagMessage, "Conditional comment: ");
+ tagHandlers["else"] = make_handler(&PyDocConverter::handleTagIf, "Else: ");
+ tagHandlers["elseif"] = make_handler(&PyDocConverter::handleTagIf, "Else if: ");
+ tagHandlers["endcond"] = make_handler(&PyDocConverter::handleTagMessage, "End of conditional comment.");
+ tagHandlers["if"] = make_handler(&PyDocConverter::handleTagIf, "If: ");
+ tagHandlers["ifnot"] = make_handler(&PyDocConverter::handleTagIf, "If not: ");
+ tagHandlers["image"] = make_handler(&PyDocConverter::handleTagImage);
+ tagHandlers["li"] = make_handler(&PyDocConverter::handleTagMessage, "* ");
+ tagHandlers["overload"] = make_handler(&PyDocConverter::handleTagMessage,
+ "This is an overloaded member function, provided for"
+ " convenience.\nIt differs from the above function only in what" " argument(s) it accepts.");
+ tagHandlers["par"] = make_handler(&PyDocConverter::handleTagPar);
+ tagHandlers["param"] = tagHandlers["tparam"] = make_handler(&PyDocConverter::handleTagParam);
+ tagHandlers["ref"] = make_handler(&PyDocConverter::handleTagRef);
+ tagHandlers["result"] = tagHandlers["return"] = tagHandlers["returns"] = make_handler(&PyDocConverter::handleTagReturn);
+
+ // this command just prints its contents
+ // (it is internal command of swig's parser, contains plain text)
+ tagHandlers["plainstd::string"] = make_handler(&PyDocConverter::handlePlainString);
+ tagHandlers["plainstd::endl"] = make_handler(&PyDocConverter::handleNewLine);
+ tagHandlers["n"] = make_handler(&PyDocConverter::handleNewLine);
+
+ // \f commands output literal Latex formula, which is still better than nothing.
+ tagHandlers["f$"] = tagHandlers["f["] = tagHandlers["f{"] = make_handler(&PyDocConverter::handleMath);
+
+ // HTML tags
+ tagHandlers["<a"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A);
+ tagHandlers["<b"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<blockquote"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_A, "Quote: ");
+ tagHandlers["<body"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<br"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "\n");
+
+ // there is no formatting for this tag as it was deprecated in HTML 4.01 and
+ // not used in HTML 5
+ tagHandlers["<center"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<caption"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<code"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "``");
+
+ tagHandlers["<dl"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<dd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<dt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+
+ tagHandlers["<dfn"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<div"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<em"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+ tagHandlers["<form"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<hr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "--------------------------------------------------------------------\n");
+ tagHandlers["<h1"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "# ");
+ tagHandlers["<h2"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "## ");
+ tagHandlers["<h3"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "### ");
+ tagHandlers["<i"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+ tagHandlers["<input"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<img"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "Image:");
+ tagHandlers["<li"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "* ");
+ tagHandlers["<meta"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<multicol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ol"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<p"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, "\n");
+ tagHandlers["<pre"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<small"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<span"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "'");
+ tagHandlers["<strong"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "**");
+
+ // make a space between text and super/sub script.
+ tagHandlers["<sub"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+ tagHandlers["<sup"] = make_handler(&PyDocConverter::handleDoxyHtmlTag, " ");
+
+ tagHandlers["<table"] = make_handler(&PyDocConverter::handleDoxyHtmlTagNoParam);
+ tagHandlers["<td"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_td);
+ tagHandlers["<th"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_th);
+ tagHandlers["<tr"] = make_handler(&PyDocConverter::handleDoxyHtmlTag_tr);
+ tagHandlers["<tt"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<kbd"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<ul"] = make_handler(&PyDocConverter::handleDoxyHtmlTag);
+ tagHandlers["<var"] = make_handler(&PyDocConverter::handleDoxyHtmlTag2, "*");
+
+ // HTML entities
+ tagHandlers["&copy"] = make_handler(&PyDocConverter::handleHtmlEntity, "(C)");
+ tagHandlers["&trade"] = make_handler(&PyDocConverter::handleHtmlEntity, " TM");
+ tagHandlers["&reg"] = make_handler(&PyDocConverter::handleHtmlEntity, "(R)");
+ tagHandlers["&lt"] = make_handler(&PyDocConverter::handleHtmlEntity, "<");
+ tagHandlers["&gt"] = make_handler(&PyDocConverter::handleHtmlEntity, ">");
+ tagHandlers["&amp"] = make_handler(&PyDocConverter::handleHtmlEntity, "&");
+ tagHandlers["&apos"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&quot"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&lsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "`");
+ tagHandlers["&rsquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "'");
+ tagHandlers["&ldquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&rdquo"] = make_handler(&PyDocConverter::handleHtmlEntity, "\"");
+ tagHandlers["&ndash"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&mdash"] = make_handler(&PyDocConverter::handleHtmlEntity, "--");
+ tagHandlers["&nbsp"] = make_handler(&PyDocConverter::handleHtmlEntity, " ");
+ tagHandlers["&times"] = make_handler(&PyDocConverter::handleHtmlEntity, "x");
+ tagHandlers["&minus"] = make_handler(&PyDocConverter::handleHtmlEntity, "-");
+ tagHandlers["&sdot"] = make_handler(&PyDocConverter::handleHtmlEntity, ".");
+ tagHandlers["&sim"] = make_handler(&PyDocConverter::handleHtmlEntity, "~");
+ tagHandlers["&le"] = make_handler(&PyDocConverter::handleHtmlEntity, "<=");
+ tagHandlers["&ge"] = make_handler(&PyDocConverter::handleHtmlEntity, ">=");
+ tagHandlers["&larr"] = make_handler(&PyDocConverter::handleHtmlEntity, "<--");
+ tagHandlers["&rarr"] = make_handler(&PyDocConverter::handleHtmlEntity, "-->");
+}
+
+PyDocConverter::PyDocConverter(int flags):
+DoxygenTranslator(flags), m_tableLineLen(0), m_prevRowIsTH(false) {
+ fillStaticTables();
+}
+
+// Return the type as it should appear in the output documentation.
+static std::string getPyDocType(Node *n, const_String_or_char_ptr lname = "") {
+ std::string type;
+
+ String *s = Swig_typemap_lookup("doctype", n, lname, 0);
+ if (!s) {
+ if (String *t = Getattr(n, "type"))
+ s = SwigType_str(t, NULL);
+ }
+
+ if (!s)
+ return type;
+
+ if (Language::classLookup(s)) {
+ // In Python C++ namespaces are flattened, so remove all but last component
+ // of the name.
+ String *const last = Swig_scopename_last(s);
+
+ // We are not actually sure whether it's a documented class or not, but
+ // there doesn't seem to be any harm in making it a reference if it isn't,
+ // while there is a lot of benefit in having a hyperlink if it is.
+ type = ":py:class:`";
+ type += Char(last);
+ type += "`";
+
+ Delete(last);
+ } else {
+ type = Char(s);
+ }
+
+ Delete(s);
+
+ return type;
+}
+
+std::string PyDocConverter::getParamType(std::string param) {
+ std::string type;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ type = getPyDocType(p, pname);
+ break;
+ }
+ }
+ Delete(plist);
+ return type;
+}
+
+std::string PyDocConverter::getParamValue(std::string param) {
+ std::string value;
+
+ ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
+ for (Parm *p = plist; p; p = nextSibling(p)) {
+ String *pname = Getattr(p, "name");
+ if (pname && Char(pname) == param) {
+ String *pval = Getattr(p, "value");
+ if (pval)
+ value = Char(pval);
+ break;
+ }
+ }
+ Delete(plist);
+ return value;
+}
+
+std::string PyDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
+ std::string translatedComment;
+
+ if (doxygenEntity.isLeaf)
+ return translatedComment;
+
+ std::string currentSection;
+ std::list<DoxygenEntity>::iterator p = doxygenEntity.entityList.begin();
+ while (p != doxygenEntity.entityList.end()) {
+ std::map<std::string, std::string>::iterator it;
+ it = sectionTitles.find(p->typeOfEntity);
+ if (it != sectionTitles.end()) {
+ if (it->second != currentSection) {
+ currentSection = it->second;
+ translatedComment += currentSection;
+ }
+ }
+ translateEntity(*p, translatedComment);
+ translateSubtree(*p);
+ p++;
+ }
+
+ return translatedComment;
+}
+
+void PyDocConverter::translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment) {
+ // check if we have needed handler and call it
+ std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
+ it = tagHandlers.find(getBaseCommand(doxyEntity.typeOfEntity));
+ if (it != tagHandlers.end())
+ (this->*(it->second.first)) (doxyEntity, translatedComment, it->second.second);
+}
+
+void PyDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += translateSubtree(tag);
+}
+
+void PyDocConverter::handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ string verb = translateSubtree(tag);
+
+ eraseLeadingNewLine(verb);
+
+ // Remove the last newline to prevent doubling the newline already present after \endverbatim
+ trimWhitespace(verb); // Needed to catch trailing newline below
+ eraseTrailingNewLine(verb);
+ translatedComment += verb;
+}
+
+void PyDocConverter::handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent;
+
+ // Only \f$ is translated to inline formulae, \f[ and \f{ are for the block ones.
+ const bool inlineFormula = tag.typeOfEntity == "f$";
+
+ string formulaNL;
+
+ if (inlineFormula) {
+ translatedComment += ":math:`";
+ } else {
+ indent.Init(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ const string formulaIndent = indent.getFirstLineIndent();
+ translatedComment += formulaIndent;
+ translatedComment += ".. math::\n";
+
+ formulaNL = '\n';
+ formulaNL += formulaIndent;
+ formulaNL += m_indent;
+ translatedComment += formulaNL;
+ }
+
+ std::string formula;
+ handleTagVerbatim(tag, formula, arg);
+
+ // It is important to ensure that we have no spaces around the inline math
+ // contents, so strip them.
+ const size_t start = formula.find_first_not_of(" \t\n");
+ const size_t end = formula.find_last_not_of(" \t\n");
+ if (start != std::string::npos) {
+ for (size_t n = start; n <= end; n++) {
+ if (formula[n] == '\n') {
+ // New lines must be suppressed in inline maths and indented in the block ones.
+ if (!inlineFormula)
+ translatedComment += formulaNL;
+ } else {
+ // Just copy everything else.
+ translatedComment += formula[n];
+ }
+ }
+ }
+
+ if (inlineFormula) {
+ translatedComment += "`";
+ }
+}
+
+void PyDocConverter::handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ trimWhitespace(translatedComment);
+
+ // Check for an option given to the code command (e.g. code{.py}),
+ // and try to set the code-block language accordingly.
+ string option = getCommandOption(tag.typeOfEntity, '{', '}');
+ // Set up the language option to the code-block command, which can
+ // be any language supported by pygments:
+ string codeLanguage;
+ if (option == ".py")
+ // Other possibilities here are "default" or "python3". In Sphinx
+ // 2.1.2, basic syntax doesn't render quite the same in these as
+ // with "python", which for basic keywords seems to provide
+ // slightly richer formatting. Another option would be to leave
+ // the language empty, but testing with Sphinx 1.8.5 has produced
+ // an error "1 argument required".
+ codeLanguage = "python";
+ else if (option == ".java")
+ codeLanguage = "java";
+ else if (option == ".c")
+ codeLanguage = "c";
+ else
+ // If there is not a match, or if no option was given, go out on a
+ // limb and assume that the examples in the C or C++ sources use
+ // C++.
+ codeLanguage = "c++";
+
+ std::string code;
+ handleTagVerbatim(tag, code, arg);
+
+ // Try and remove leading newline, which is present for block \code
+ // command:
+ eraseLeadingNewLine(code);
+
+ // Check for python doctest blocks, and treat them specially:
+ bool isDocTestBlock = false;
+ size_t startPos;
+ // ">>>" would normally appear at the beginning, but doxygen comment
+ // style may have space in front, so skip leading whitespace
+ if ((startPos=code.find_first_not_of(" \t")) != string::npos && code.substr(startPos,3) == ">>>")
+ isDocTestBlock = true;
+
+ string codeIndent;
+ if (! isDocTestBlock) {
+ // Use the current indent for the code-block line itself.
+ translatedComment += indent.getFirstLineIndent();
+ translatedComment += ".. code-block:: " + codeLanguage + "\n\n";
+
+ // Specify the level of extra indentation that will be used for
+ // subsequent lines within the code block. Note that the correct
+ // "starting indentation" is already present in the input, so we
+ // only need to add the desired code block indentation.
+ codeIndent = m_indent;
+ }
+
+ translatedComment += codeIndent;
+ for (size_t n = 0; n < code.length(); n++) {
+ if (code[n] == '\n') {
+ // Don't leave trailing white space, this results in PEP8 validation
+ // errors in Python code (which are performed by our own unit tests).
+ trimWhitespace(translatedComment);
+ translatedComment += '\n';
+
+ // Ensure that we indent all the lines by the code indent.
+ translatedComment += codeIndent;
+ } else {
+ // Just copy everything else.
+ translatedComment += code[n];
+ }
+ }
+
+ trimWhitespace(translatedComment);
+
+ // For block commands, the translator adds the newline after
+ // \endcode, so try and compensate by removing the last newline from
+ // the code text:
+ eraseTrailingNewLine(translatedComment);
+}
+
+void PyDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.data;
+}
+
+void PyDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
+ translatedComment += it->data;
+ }
+}
+
+void PyDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += tag.typeOfEntity;
+}
+
+void PyDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ translatedComment += arg;
+ if (tag.entityList.size()) {
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ translatedComment += " {" + translateSubtree(tag) + "}";
+ }
+}
+
+void PyDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ translatedComment += "Title: ";
+ if (tag.entityList.size())
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ handleParagraph(tag, translatedComment);
+}
+
+void PyDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+ tag.entityList.pop_front();
+ translatedComment += "Image: ";
+ translatedComment += tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ if (tag.entityList.size())
+ translatedComment += "(" + tag.entityList.begin()->data + ")";
+}
+
+void PyDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (tag.entityList.size() < 2)
+ return;
+
+ IndentGuard indent(translatedComment, m_indent);
+
+ DoxygenEntity paramNameEntity = *tag.entityList.begin();
+ tag.entityList.pop_front();
+
+ const std::string &paramName = paramNameEntity.data;
+
+ const std::string paramType = getParamType(paramName);
+ const std::string paramValue = getParamValue(paramName);
+
+ // Get command option, e.g. "in", "out", or "in,out"
+ string commandOpt = getCommandOption(tag.typeOfEntity, '[', ']');
+ if (commandOpt == "in,out") commandOpt = "in/out";
+
+ // If provided, append the parameter direction to the type
+ // information via a suffix:
+ std::string suffix;
+ if (commandOpt.size() > 0)
+ suffix = ", " + commandOpt;
+
+ // If the parameter has a default value, flag it as optional in the
+ // generated type definition. Particularly helpful when the python
+ // call is generated with *args, **kwargs.
+ if (paramValue.size() > 0)
+ suffix += ", optional";
+
+ if (!paramType.empty()) {
+ translatedComment += ":type " + paramName + ": " + paramType + suffix + "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":param " + paramName + ":";
+
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ const std::string pytype = getPyDocType(currentNode);
+ if (!pytype.empty()) {
+ translatedComment += ":rtype: ";
+ translatedComment += pytype;
+ translatedComment += "\n";
+ translatedComment += indent.getFirstLineIndent();
+ }
+
+ translatedComment += ":return: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ IndentGuard indent(translatedComment, m_indent);
+
+ translatedComment += ":raises: ";
+ handleParagraph(tag, translatedComment);
+}
+
+
+void PyDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ if (!tag.entityList.size())
+ return;
+
+ string anchor = tag.entityList.begin()->data;
+ tag.entityList.pop_front();
+ string anchorText = anchor;
+ if (!tag.entityList.empty()) {
+ anchorText = tag.entityList.begin()->data;
+ }
+ translatedComment += "'" + anchorText + "'";
+}
+
+
+void PyDocConverter::handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ if (tag.entityList.size()) { // do not include empty tags
+ std::string tagData = translateSubtree(tag);
+ // wrap the thing, ignoring whitespace
+ size_t wsPos = tagData.find_last_not_of("\n\t ");
+ if (wsPos != std::string::npos && wsPos != tagData.size() - 1)
+ translatedComment += arg + tagData.substr(0, wsPos + 1) + arg + tagData.substr(wsPos + 1);
+ else
+ translatedComment += arg + tagData + arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ // translatedComment += "</" + arg.substr(1) + ">";
+ } else {
+ translatedComment += arg + htmlTagArgs;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</ul>
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, "</a>
+ translatedComment += " (" + m_url + ')';
+ m_url.clear();
+ } else {
+ m_url.clear();
+ size_t pos = htmlTagArgs.find('=');
+ if (pos != string::npos) {
+ m_url = htmlTagArgs.substr(pos + 1);
+ }
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end html tag, for example "</em>
+ translatedComment += arg;
+ } else {
+ translatedComment += arg;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ size_t nlPos = translatedComment.rfind('\n');
+ if (htmlTagArgs == "/") {
+ // end tag, </tr> appends vertical table line '|'
+ translatedComment += '|';
+ if (nlPos != string::npos) {
+ size_t startOfTableLinePos = translatedComment.find_first_not_of(" \t", nlPos + 1);
+ if (startOfTableLinePos != string::npos) {
+ m_tableLineLen = translatedComment.size() - startOfTableLinePos;
+ }
+ }
+ } else {
+ if (m_prevRowIsTH) {
+ // if previous row contained <th> tag, add horizontal separator
+ // but first get leading spaces, because they'll be needed for the next row
+ size_t numLeadingSpaces = translatedComment.size() - nlPos - 1;
+
+ translatedComment += string(m_tableLineLen, '-') + '\n';
+
+ if (nlPos != string::npos) {
+ translatedComment += string(numLeadingSpaces, ' ');
+ }
+ m_prevRowIsTH = false;
+ }
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </th> is ignored
+ } else {
+ translatedComment += '|';
+ m_prevRowIsTH = true;
+ }
+}
+
+void PyDocConverter::handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &) {
+ std::string htmlTagArgs = tag.data;
+ if (htmlTagArgs == "/") {
+ // end tag, </td> is ignored
+ } else {
+ translatedComment += '|';
+ }
+}
+
+void PyDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg) {
+ // html entities
+ translatedComment += arg;
+}
+
+void PyDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, const std::string &) {
+ trimWhitespace(translatedComment);
+
+ translatedComment += "\n";
+ if (!m_indent.empty())
+ translatedComment += m_indent;
+}
+
+String *PyDocConverter::makeDocumentation(Node *n) {
+ String *documentation;
+ std::string pyDocString;
+
+ // store the node, we may need it later
+ currentNode = n;
+
+ // for overloaded functions we must concat documentation for underlying overloads
+ if (Getattr(n, "sym:overloaded")) {
+ // rewind to the first overload
+ while (Getattr(n, "sym:previousSibling"))
+ n = Getattr(n, "sym:previousSibling");
+
+ std::vector<std::string> allDocumentation;
+
+ // minimal indent of any documentation comments, not initialized yet
+ size_t minIndent = static_cast<size_t>(-1);
+
+ // for each real method (not a generated overload) append the documentation
+ string oneDoc;
+ while (n) {
+ documentation = getDoxygenComment(n);
+ if (!Swig_is_generated_overload(n) && documentation) {
+ currentNode = n;
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ oneDoc = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+
+ oneDoc = translateSubtree(root);
+ }
+
+ // find the minimal indent of this documentation comment, we need to
+ // ensure that the entire comment is indented by it to avoid the leading
+ // parts of the other lines being simply discarded later
+ const size_t oneIndent = determineIndent(oneDoc);
+ if (oneIndent < minIndent)
+ minIndent = oneIndent;
+
+ allDocumentation.push_back(oneDoc);
+ }
+ n = Getattr(n, "sym:nextSibling");
+ }
+
+ // construct final documentation string
+ if (allDocumentation.size() > 1) {
+ string indentStr;
+ if (minIndent != static_cast<size_t>(-1))
+ indentStr.assign(minIndent, ' ');
+
+ std::ostringstream concatDocString;
+ for (size_t realOverloadCount = 0; realOverloadCount < allDocumentation.size(); realOverloadCount++) {
+ if (realOverloadCount != 0) {
+ // separate it from the preceding one.
+ concatDocString << '\n' << indentStr << "|\n\n";
+ }
+
+ oneDoc = allDocumentation[realOverloadCount];
+ trimWhitespace(oneDoc);
+ concatDocString << indentStr << "*Overload " << (realOverloadCount + 1) << ":*\n" << oneDoc;
+ }
+ pyDocString = concatDocString.str();
+ } else if (allDocumentation.size() == 1) {
+ pyDocString = *(allDocumentation.begin());
+ }
+ }
+ // for other nodes just process as normal
+ else {
+ documentation = getDoxygenComment(n);
+ if (documentation != NULL) {
+ if (GetFlag(n, "feature:doxygen:notranslate")) {
+ String *comment = NewString("");
+ Append(comment, documentation);
+ Replaceall(comment, "\n *", "\n");
+ pyDocString = Char(comment);
+ Delete(comment);
+ } else {
+ std::list<DoxygenEntity> entityList = parser.createTree(n, documentation);
+ DoxygenEntity root("root", entityList);
+ pyDocString = translateSubtree(root);
+ }
+ }
+ }
+
+ // if we got something log the result
+ if (!pyDocString.empty()) {
+
+ // remove the last '\n' since additional one is added during writing to file
+ eraseTrailingNewLine(pyDocString);
+
+ // ensure that a blank line occurs before code or math blocks
+ pyDocString = padCodeAndVerbatimBlocks(pyDocString);
+
+ if (m_flags & debug_translator) {
+ std::cout << "\n---RESULT IN PYDOC---" << std::endl;
+ std::cout << pyDocString;
+ std::cout << std::endl;
+ }
+ }
+
+ return NewString(pyDocString.c_str());
+}
+
diff --git a/contrib/tools/swig/Source/Doxygen/pydoc.h b/contrib/tools/swig/Source/Doxygen/pydoc.h
new file mode 100644
index 00000000000..880ee3069c8
--- /dev/null
+++ b/contrib/tools/swig/Source/Doxygen/pydoc.h
@@ -0,0 +1,208 @@
+/* -----------------------------------------------------------------------------
+ * This file is part of SWIG, which is licensed as a whole under version 3
+ * (or any later version) of the GNU General Public License. Some additional
+ * terms also apply to certain portions of SWIG. The full details of the SWIG
+ * license and copyrights can be found in the LICENSE and COPYRIGHT files
+ * included with the SWIG source code as distributed by the SWIG developers
+ * and at https://www.swig.org/legal.html.
+ *
+ * pydoc.h
+ *
+ * Module to return documentation for nodes formatted for PyDoc
+ * ----------------------------------------------------------------------------- */
+
+#ifndef SWIG_PYDOC_H
+#define SWIG_PYDOC_H
+
+#include <list>
+#include <string>
+#include "swig.h"
+#include "doxyentity.h"
+#include "doxytranslator.h"
+
+#define DOC_STRING_LENGTH 64 // characters per line allowed
+#define DOC_PARAM_STRING_LENGTH 30 // characters reserved for param name / type
+
+class PyDocConverter : public DoxygenTranslator {
+public:
+ PyDocConverter(int flags = 0);
+ String *makeDocumentation(Node *node);
+
+protected:
+
+ size_t m_tableLineLen;
+ bool m_prevRowIsTH;
+ std::string m_url;
+
+ /*
+ * Translate every entity in a tree, also manages sections
+ * display. Prints title for every group of tags that have
+ * a section title associated with them
+ */
+ std::string translateSubtree(DoxygenEntity &doxygenEntity);
+
+ /*
+ * Translate one entity with the appropriate handler, according
+ * to the tagHandlers
+ */
+ void translateEntity(DoxygenEntity &doxyEntity, std::string &translatedComment);
+
+ /*
+ * Typedef for the function that handles one tag
+ * arg - some string argument to easily pass it through lookup table
+ */
+ typedef void (PyDocConverter::*tagHandler) (DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Wrap the command data with the some string
+ * arg - string to wrap with, like '_' or '*'
+ */
+ void handleTagWrap(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Just prints new line
+ */
+ void handleNewLine(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the name of tag to the output, used for escape-commands
+ */
+ void handleTagChar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \exception tag or its synonyms.
+ */
+ void handleTagException(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print only the content and strip original tag
+ */
+ void handleParagraph(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle Doxygen verbatim tag
+ */
+ void handleVerbatimBlock(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg = std::string());
+
+ /*
+ * Handle one of the Doxygen formula-related tags.
+ */
+ void handleMath(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handle a code snippet.
+ */
+ void handleCode(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print only data part of code
+ */
+ void handlePlainString(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /**
+ * Copies verbatim args of the tag to output, used for commands like \f$, ...
+ */
+ void handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Print the if-elseif-else-endif section
+ */
+ void handleTagIf(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Prints the specified message, than the contents of the tag
+ * arg - message
+ */
+ void handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Insert 'Title: ...'
+ */
+ void handleTagPar(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Insert 'Image: ...'
+ */
+ void handleTagImage(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format nice param description with type information
+ */
+ void handleTagParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Format the contents of the \return tag or its synonyms.
+ */
+ void handleTagReturn(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Writes text for \ref tag.
+ */
+ void handleTagRef(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML tags recognized by Doxygen, like <A ...>, <ul>, <table>, ... */
+
+ void handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Does not output params of HTML tag, for example in <table border='1'>
+ * 'border=1' is not written to output.
+ */
+ void handleDoxyHtmlTagNoParam(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /** Translates tag <a href = "url">text</a> to: text ("url"). */
+ void handleDoxyHtmlTag_A(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /*
+ * Handles HTML tags, which are translated to markdown-like syntax, for example
+ * <i>text</i> --> _text_. Appends arg for start HTML tag and end HTML tag.
+ */
+ void handleDoxyHtmlTag2(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <tr> */
+ void handleDoxyHtmlTag_tr(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <th> */
+ void handleDoxyHtmlTag_th(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML table, tag <td> */
+ void handleDoxyHtmlTag_td(DoxygenEntity &tag, std::string &translatedComment, const std::string &arg);
+
+ /* Handles HTML entities recognized by Doxygen, like &lt;, &copy;, ... */
+ void handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, const std::string &arg);
+
+
+ /*
+ * Simple helper function that calculates correct parameter type
+ * of the node stored in 'currentNode'
+ * If param with specified name is not found, empty string is returned
+ */
+ std::string getParamType(std::string name);
+
+ /*
+ * Simple helper function to retrieve the parameter value
+ */
+ std::string getParamValue(std::string name);
+
+private:
+ // temporary thing, should be refactored somehow
+ Node *currentNode;
+
+ // Extra indent for the current paragraph, must be output after each new line.
+ std::string m_indent;
+
+
+ // this contains the handler pointer and one string argument
+ typedef std::map<std::string, std::pair<tagHandler, std::string> >TagHandlersMap;
+ static TagHandlersMap tagHandlers;
+
+ // this contains the sections titles, like 'Arguments:' or 'Notes:', that are printed only once
+ static std::map<std::string, std::string> sectionTitles;
+
+ // Helper functions for fillStaticTables(): make a new tag handler object.
+ TagHandlersMap::mapped_type make_handler(tagHandler handler);
+ TagHandlersMap::mapped_type make_handler(tagHandler handler, const char *arg);
+
+ void fillStaticTables();
+};
+
+#endif