diff options
author | robot-piglet <[email protected]> | 2025-08-28 14:27:58 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-08-28 14:57:06 +0300 |
commit | 81d828c32c8d5477cb2f0ce5da06a1a8d9392ca3 (patch) | |
tree | 3081d566f0d5158d76e9093261344f6406fd09f7 /contrib/tools/swig/Source/Doxygen | |
parent | 77ea11423f959e51795cc3ef36a48d808b4ffb98 (diff) |
Intermediate changes
commit_hash:d5b1af16dbe9030537a04c27eb410c88c2f496cd
Diffstat (limited to 'contrib/tools/swig/Source/Doxygen')
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/csharpdoc.cxx | 989 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/csharpdoc.h | 243 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxycommands.h | 174 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxyentity.cxx | 69 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxyentity.h | 45 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxyparser.cxx | 1493 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxyparser.h | 377 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxytranslator.cxx | 69 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/doxytranslator.h | 90 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/javadoc.cxx | 849 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/javadoc.h | 169 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/pydoc.cxx | 993 | ||||
-rw-r--r-- | contrib/tools/swig/Source/Doxygen/pydoc.h | 208 |
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, "<"); + } else if (str[i] == '>') { + str.replace(i, 1, ">"); + } else if (str[i] == '&') { + str.replace(i, 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; +} + +/* 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, "<"); + tagHandlers[">"] = make_handler(&CSharpDocConverter::handleTagCharReplace, ">"); + tagHandlers["&"] = make_handler(&CSharpDocConverter::handleTagCharReplace, "&"); + 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["©"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(C)"); + tagHandlers["&trade"] = make_handler(&CSharpDocConverter::handleHtmlEntity, " TM"); + tagHandlers["®"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "(R)"); + tagHandlers["<"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "<"); + tagHandlers[">"] = make_handler(&CSharpDocConverter::handleHtmlEntity, ">"); + tagHandlers["&"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "&"); + tagHandlers["&apos"] = make_handler(&CSharpDocConverter::handleHtmlEntity, "'"); + tagHandlers["""] = 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[" "] = make_handler(&CSharpDocConverter::handleHtmlEntity, " "); + tagHandlers["×"] = 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 ¶mName = 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 ¶mName = 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 <, ©, ... */ + 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 <varName> 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[] = { + "©", // (C) + "&trade", // (TM) + "®", // (R) + "<", // less-than symbol + ">", // greater-than symbol + "&", // ampersand + "&apos", // single quotation mark (straight) + """, // 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", // -- + " ", // + "×", // 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 ¶m1) : 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 ¶m1); + 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 ¤tToken = *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 < and > respectively, + addDoxyCommand(m_tokenList, "<"); + 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, "&"); + 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, ">"); + 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 <x> 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 (<, ...) + * + * - 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, "<"); + tagHandlers[">"] = make_pair(&JavaDocConverter::handleTagChar, ">"); + tagHandlers["&"] = make_pair(&JavaDocConverter::handleTagChar, "&"); + tagHandlers["#"] = make_pair(&JavaDocConverter::handleTagChar, ""); + tagHandlers["%"] = make_pair(&JavaDocConverter::handleTagChar, ""); + tagHandlers["~"] = make_pair(&JavaDocConverter::handleTagChar, ""); + tagHandlers["\""] = make_pair(&JavaDocConverter::handleTagChar, """); + 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["©"] = make_pair(&JavaDocConverter::handleHtmlEntity, "©"); + tagHandlers["&trade"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&trade"); + tagHandlers["®"] = make_pair(&JavaDocConverter::handleHtmlEntity, "®"); + tagHandlers["<"] = make_pair(&JavaDocConverter::handleHtmlEntity, "<"); + tagHandlers[">"] = make_pair(&JavaDocConverter::handleHtmlEntity, ">"); + tagHandlers["&"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&"); + tagHandlers["&apos"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&apos"); + tagHandlers["""] = make_pair(&JavaDocConverter::handleHtmlEntity, """); + 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[" "] = make_pair(&JavaDocConverter::handleHtmlEntity, " "); + tagHandlers["×"] = make_pair(&JavaDocConverter::handleHtmlEntity, "×"); + 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 == "&") { + methodRef += '&'; + } else if (it->typeOfEntity == "<") { + methodRef += '<'; + } else if (it->typeOfEntity == ">") { + 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 <, ©, ... */ + 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["©"] = make_handler(&PyDocConverter::handleHtmlEntity, "(C)"); + tagHandlers["&trade"] = make_handler(&PyDocConverter::handleHtmlEntity, " TM"); + tagHandlers["®"] = make_handler(&PyDocConverter::handleHtmlEntity, "(R)"); + tagHandlers["<"] = make_handler(&PyDocConverter::handleHtmlEntity, "<"); + tagHandlers[">"] = make_handler(&PyDocConverter::handleHtmlEntity, ">"); + tagHandlers["&"] = make_handler(&PyDocConverter::handleHtmlEntity, "&"); + tagHandlers["&apos"] = make_handler(&PyDocConverter::handleHtmlEntity, "'"); + tagHandlers["""] = 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[" "] = make_handler(&PyDocConverter::handleHtmlEntity, " "); + tagHandlers["×"] = 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 ¶mName = 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 <, ©, ... */ + 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 |