1 /* -----------------------------------------------------------------------------
2  * This file is part of SWIG, which is licensed as a whole under version 3
3  * (or any later version) of the GNU General Public License. Some additional
4  * terms also apply to certain portions of SWIG. The full details of the SWIG
5  * license and copyrights can be found in the LICENSE and COPYRIGHT files
6  * included with the SWIG source code as distributed by the SWIG developers
7  * and at http://www.swig.org/legal.html.
8  *
9  * javadoc.cxx
10  * ----------------------------------------------------------------------------- */
11 
12 #include "javadoc.h"
13 #include "doxyparser.h"
14 #include <iostream>
15 #include <vector>
16 #include <list>
17 #include "swigmod.h"
18 #define APPROX_LINE_LENGTH 64   // characters per line allowed
19 #define TAB_SIZE 8              // current tab size in spaces
20 //TODO {@link} {@linkplain} {@docRoot}, and other useful doxy commands that are not a javadoc tag
21 
22 // define static tables, they are filled in JavaDocConverter's constructor
23 std::map<std::string, std::pair<JavaDocConverter::tagHandler, std::string> > JavaDocConverter::tagHandlers;
24 
25 using std::string;
26 using std::list;
27 using std::vector;
28 
fillStaticTables()29 void JavaDocConverter::fillStaticTables() {
30   if (tagHandlers.size()) // fill only once
31     return;
32 
33   /*
34    * Some translation rules:
35    *
36    * @ and \ must be escaped for both Java and Python to appear on output: \@, \\,
37    *         while Doxygen produces output in both cases.
38    * Rule:   @ and \ with space on the right should get to output.
39    *
40    * :: remains intact, even in class::method(). But you can use class#method also
41    *    in C++ comment and it is properly translated to C++ output (changed by doxygen to ::)
42    *    and Java output (remains #).
43    * Rule: SWIG type system can't be used to convert C::m to C#m, because in Java it is C.m
44    *       Use string replacement :: --> # in tag see and links.
45    *
46    * HTML tags must be translated - remain in Java, to markdown in Python
47    *
48    * Unknown HTML tags, for example <x> is translated to &lt;x&gt; by doxygen, while
49    *     Java src is <x> and therefore invisible on output - browser ignores unknown command.
50    *     This is handy in syntax descriptions, for example: more <fileName>.
51    *
52    * Standalone < and > need not be translated, they are rendered properly in
53    *      all three outputs.
54    *
55    * ., %, and " need not to be translated
56    *
57    * entities must be translated - remain in Java, something meaningful in Python (&lt, ...)
58    *
59    * - Python
60    * - add comments also to auto-generated methods like equals(), delete() in Java,
61    *   and methods for std::vector(), ...
62    *   Commenting methods of std types is simple - add comment to std_*.i file.
63    */
64 
65   // these commands insert HTML tags
66   tagHandlers["a"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
67   tagHandlers["arg"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
68   tagHandlers["b"] = make_pair(&JavaDocConverter::handleTagHtml, "b");
69   tagHandlers["c"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
70   tagHandlers["cite"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
71   tagHandlers["e"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
72   tagHandlers["em"] = make_pair(&JavaDocConverter::handleTagHtml, "i");
73   tagHandlers["li"] = make_pair(&JavaDocConverter::handleTagHtml, "li");
74   tagHandlers["p"] = make_pair(&JavaDocConverter::handleTagHtml, "code");
75   // these commands insert just a single char, some of them need to be escaped
76   tagHandlers["$"] = make_pair(&JavaDocConverter::handleTagChar, "");
77   tagHandlers["@"] = make_pair(&JavaDocConverter::handleTagChar, "");
78   tagHandlers["\\"] = make_pair(&JavaDocConverter::handleTagChar, "");
79   tagHandlers["<"] = make_pair(&JavaDocConverter::handleTagChar, "&lt;");
80   tagHandlers[">"] = make_pair(&JavaDocConverter::handleTagChar, "&gt;");
81   tagHandlers["&"] = make_pair(&JavaDocConverter::handleTagChar, "&amp;");
82   tagHandlers["#"] = make_pair(&JavaDocConverter::handleTagChar, "");
83   tagHandlers["%"] = make_pair(&JavaDocConverter::handleTagChar, "");
84   tagHandlers["~"] = make_pair(&JavaDocConverter::handleTagChar, "");
85   tagHandlers["\""] = make_pair(&JavaDocConverter::handleTagChar, "&quot;");
86   tagHandlers["."] = make_pair(&JavaDocConverter::handleTagChar, "");
87   tagHandlers["::"] = make_pair(&JavaDocConverter::handleTagChar, "");
88   // these commands are stripped out
89   tagHandlers["attention"] = make_pair(&JavaDocConverter::handleParagraph, "");
90   tagHandlers["anchor"] = make_pair(&JavaDocConverter::handleTagAnchor, "");
91   tagHandlers["brief"] = make_pair(&JavaDocConverter::handleParagraph, "");
92   tagHandlers["bug"] = make_pair(&JavaDocConverter::handleParagraph, "");
93   tagHandlers["date"] = make_pair(&JavaDocConverter::handleParagraph, "");
94   tagHandlers["details"] = make_pair(&JavaDocConverter::handleParagraph, "");
95   // this command is inserts text accumulated after cmd htmlonly -
96   // see DoxygenParser - CMD_HTML_ONLY.
97   tagHandlers["htmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
98   tagHandlers["invariant"] = make_pair(&JavaDocConverter::handleParagraph, "");
99   tagHandlers["latexonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
100   tagHandlers["manonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
101   tagHandlers["partofdescription"] = make_pair(&JavaDocConverter::handleParagraph, "");
102   tagHandlers["rtfonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
103   tagHandlers["short"] = make_pair(&JavaDocConverter::handleParagraph, "");
104   tagHandlers["xmlonly"] = make_pair(&JavaDocConverter::handleParagraph, "");
105   // these commands are kept as-is, they are supported by JavaDoc
106   tagHandlers["author"] = make_pair(&JavaDocConverter::handleTagSame, "");
107   tagHandlers["authors"] = make_pair(&JavaDocConverter::handleTagSame, "author");
108   tagHandlers["deprecated"] = make_pair(&JavaDocConverter::handleTagSame, "");
109   tagHandlers["exception"] = make_pair(&JavaDocConverter::handleTagSame, "");
110   tagHandlers["package"] = make_pair(&JavaDocConverter::handleTagSame, "");
111   tagHandlers["param"] = make_pair(&JavaDocConverter::handleTagParam, "");
112   tagHandlers["tparam"] = make_pair(&JavaDocConverter::handleTagParam, "");
113   tagHandlers["ref"] = make_pair(&JavaDocConverter::handleTagRef, "");
114   tagHandlers["result"] = make_pair(&JavaDocConverter::handleTagSame, "return");
115   tagHandlers["return"] = make_pair(&JavaDocConverter::handleTagSame, "");
116   tagHandlers["returns"] = make_pair(&JavaDocConverter::handleTagSame, "return");
117   //tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSame, "");
118   //tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSame, "see");
119   tagHandlers["since"] = make_pair(&JavaDocConverter::handleTagSame, "");
120   tagHandlers["throws"] = make_pair(&JavaDocConverter::handleTagSame, "");
121   tagHandlers["throw"] = make_pair(&JavaDocConverter::handleTagSame, "throws");
122   tagHandlers["version"] = make_pair(&JavaDocConverter::handleTagSame, "");
123   // these commands have special handlers
124   tagHandlers["code"] = make_pair(&JavaDocConverter::handleTagExtended, "code");
125   tagHandlers["cond"] = make_pair(&JavaDocConverter::handleTagMessage, "Conditional comment: ");
126   tagHandlers["copyright"] = make_pair(&JavaDocConverter::handleTagMessage, "Copyright: ");
127   tagHandlers["else"] = make_pair(&JavaDocConverter::handleTagIf, "Else: ");
128   tagHandlers["elseif"] = make_pair(&JavaDocConverter::handleTagIf, "Else if: ");
129   tagHandlers["endcond"] = make_pair(&JavaDocConverter::handleTagMessage, "End of conditional comment.");
130   // space in second arg prevents Javadoc to treat '@ example' as command. File name of
131   // example is still informative to user.
132   tagHandlers["example"] = make_pair(&JavaDocConverter::handleTagSame, " example");
133   tagHandlers["if"] = make_pair(&JavaDocConverter::handleTagIf, "If: ");
134   tagHandlers["ifnot"] = make_pair(&JavaDocConverter::handleTagIf, "If not: ");
135   tagHandlers["image"] = make_pair(&JavaDocConverter::handleTagImage, "");
136   tagHandlers["link"] = make_pair(&JavaDocConverter::handleTagLink, "");
137   tagHandlers["see"] = make_pair(&JavaDocConverter::handleTagSee, "");
138   tagHandlers["sa"] = make_pair(&JavaDocConverter::handleTagSee, "");
139   tagHandlers["note"] = make_pair(&JavaDocConverter::handleTagMessage, "Note: ");
140   tagHandlers["overload"] = make_pair(&JavaDocConverter::handleTagMessage,
141                                       "This is an overloaded member function, provided for"
142                                       " convenience. It differs from the above function only in what" " argument(s) it accepts.");
143   tagHandlers["par"] = make_pair(&JavaDocConverter::handleTagPar, "");
144   tagHandlers["remark"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
145   tagHandlers["remarks"] = make_pair(&JavaDocConverter::handleTagMessage, "Remarks: ");
146   tagHandlers["todo"] = make_pair(&JavaDocConverter::handleTagMessage, "TODO: ");
147   tagHandlers["verbatim"] = make_pair(&JavaDocConverter::handleTagExtended, "literal");
148 
149   // \f commands output literal Latex formula, which is still better than nothing.
150   tagHandlers["f$"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
151   tagHandlers["f["] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
152   tagHandlers["f{"] = make_pair(&JavaDocConverter::handleTagVerbatim, "");
153 
154   tagHandlers["warning"] = make_pair(&JavaDocConverter::handleTagMessage, "Warning: ");
155   // this command just prints it's contents
156   // (it is internal command of swig's parser, contains plain text)
157   tagHandlers["plainstd::string"] = make_pair(&JavaDocConverter::handlePlainString, "");
158   tagHandlers["plainstd::endl"] = make_pair(&JavaDocConverter::handleNewLine, "");
159   tagHandlers["n"] = make_pair(&JavaDocConverter::handleNewLine, "");
160 
161   // HTML tags
162   tagHandlers["<a"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<a");
163   tagHandlers["<b"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<b");
164   tagHandlers["<blockquote"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<blockquote");
165   tagHandlers["<body"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<body");
166   tagHandlers["<br"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<br");
167   tagHandlers["<center"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<center");
168   tagHandlers["<caption"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<caption");
169   tagHandlers["<code"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<code");
170   tagHandlers["<dd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dd");
171   tagHandlers["<dfn"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dfn");
172   tagHandlers["<div"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<div");
173   tagHandlers["<dl"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dl");
174   tagHandlers["<dt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<dt");
175   tagHandlers["<em"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<em");
176   tagHandlers["<form"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<form");
177   tagHandlers["<hr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<hr");
178   tagHandlers["<h1"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h1");
179   tagHandlers["<h2"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h2");
180   tagHandlers["<h3"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<h3");
181   tagHandlers["<i"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<i");
182   tagHandlers["<input"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<input");
183   tagHandlers["<img"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<img");
184   tagHandlers["<li"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<li");
185   tagHandlers["<meta"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<meta");
186   tagHandlers["<multicol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<multicol");
187   tagHandlers["<ol"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ol");
188   tagHandlers["<p"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<p");
189   tagHandlers["<pre"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<pre");
190   tagHandlers["<small"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<small");
191   tagHandlers["<span"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<span");
192   tagHandlers["<strong"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<strong");
193   tagHandlers["<sub"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sub");
194   tagHandlers["<sup"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<sup");
195   tagHandlers["<table"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<table");
196   tagHandlers["<td"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<td");
197   tagHandlers["<th"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<th");
198   tagHandlers["<tr"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tr");
199   tagHandlers["<tt"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<tt");
200   tagHandlers["<kbd"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<kbd");
201   tagHandlers["<ul"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<ul");
202   tagHandlers["<var"] = make_pair(&JavaDocConverter::handleDoxyHtmlTag, "<var");
203 
204   // HTML entities
205   tagHandlers["&copy"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&copy");
206   tagHandlers["&trade"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&trade");
207   tagHandlers["&reg"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&reg");
208   tagHandlers["&lt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lt");
209   tagHandlers["&gt"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&gt");
210   tagHandlers["&amp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&amp");
211   tagHandlers["&apos"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&apos");
212   tagHandlers["&quot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&quot");
213   tagHandlers["&lsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&lsquo");
214   tagHandlers["&rsquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rsquo");
215   tagHandlers["&ldquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ldquo");
216   tagHandlers["&rdquo"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rdquo");
217   tagHandlers["&ndash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ndash");
218   tagHandlers["&mdash"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&mdash");
219   tagHandlers["&nbsp"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&nbsp");
220   tagHandlers["&times"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&times");
221   tagHandlers["&minus"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&minus");
222   tagHandlers["&sdot"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sdot");
223   tagHandlers["&sim"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&sim");
224   tagHandlers["&le"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&le");
225   tagHandlers["&ge"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&ge");
226   tagHandlers["&larr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&larr");
227   tagHandlers["&rarr"] = make_pair(&JavaDocConverter::handleHtmlEntity, "&rarr");
228 }
229 
JavaDocConverter(int flags)230 JavaDocConverter::JavaDocConverter(int flags) :
231   DoxygenTranslator(flags) {
232   fillStaticTables();
233 }
234 
235 /**
236  * Formats comment lines by inserting '\n *' at to long lines and tabs for
237  * indent. Currently it is disabled, which means original comment format is
238  * preserved. Experience shows, that this is usually better than breaking
239  * lines automatically, especially because original line endings are not removed,
240  * which results in short lines. To be useful, this function should have much
241  * better algorithm.
242  */
formatCommand(std::string unformattedLine,int indent)243 std::string JavaDocConverter::formatCommand(std::string unformattedLine, int indent) {
244   std::string formattedLines;
245   return unformattedLine; // currently disabled
246   std::string::size_type lastPosition = 0;
247   std::string::size_type i = 0;
248   int isFirstLine = 1;
249   while (i != std::string::npos && i < unformattedLine.length()) {
250     lastPosition = i;
251     if (isFirstLine) {
252       i += APPROX_LINE_LENGTH;
253     } else {
254       i += APPROX_LINE_LENGTH - indent * TAB_SIZE;
255     }
256 
257     i = unformattedLine.find(" ", i);
258 
259     if (i > 0 && i + 1 < unformattedLine.length()) {
260       if (!isFirstLine)
261         for (int j = 0; j < indent; j++) {
262           formattedLines.append("\t");
263       } else {
264         isFirstLine = 0;
265       }
266       formattedLines.append(unformattedLine.substr(lastPosition, i - lastPosition + 1));
267       formattedLines.append("\n *");
268 
269     }
270   }
271   if (lastPosition < unformattedLine.length()) {
272     if (!isFirstLine) {
273       for (int j = 0; j < indent; j++) {
274         formattedLines.append("\t");
275       }
276     }
277     formattedLines.append(unformattedLine.substr(lastPosition, unformattedLine.length() - lastPosition));
278   }
279 
280   return formattedLines;
281 }
282 
283 /**
284  * Returns true, if the given parameter exists in the current node
285  * (for example param is a name of function parameter). If feature
286  * 'doxygen:nostripparams' is set, then this method always returns
287  * true - parameters are copied to output regardless of presence in
288  * function params list.
289  */
paramExists(std::string param)290 bool JavaDocConverter::paramExists(std::string param) {
291 
292   if (GetFlag(currentNode, "feature:doxygen:nostripparams")) {
293     return true;
294   }
295 
296   ParmList *plist = CopyParmList(Getattr(currentNode, "parms"));
297 
298   for (Parm *p = plist; p;) {
299 
300     if (Getattr(p, "name") && Char(Getattr(p, "name")) == param) {
301       return true;
302     }
303     /* doesn't seem to work always: in some cases (especially for 'self' parameters)
304      * tmap:in is present, but tmap:in:next is not and so this code skips all the parameters
305      */
306     //p = Getattr(p, "tmap:in") ? Getattr(p, "tmap:in:next") : nextSibling(p);
307     p = nextSibling(p);
308   }
309 
310   Delete(plist);
311 
312   return false;
313 }
314 
translateSubtree(DoxygenEntity & doxygenEntity)315 std::string JavaDocConverter::translateSubtree(DoxygenEntity &doxygenEntity) {
316   std::string translatedComment;
317 
318   if (doxygenEntity.isLeaf) {
319     return translatedComment;
320   }
321 
322   for (DoxygenEntityListIt p = doxygenEntity.entityList.begin(); p != doxygenEntity.entityList.end(); p++) {
323 
324     translateEntity(*p, translatedComment);
325     translateSubtree(*p);
326   }
327 
328   return translatedComment;
329 }
330 
331 /**
332  * Checks if a handler for the given tag exists, and calls it.
333  */
translateEntity(DoxygenEntity & tag,std::string & translatedComment)334 void JavaDocConverter::translateEntity(DoxygenEntity &tag, std::string &translatedComment) {
335 
336   std::map<std::string, std::pair<tagHandler, std::string> >::iterator it;
337   it = tagHandlers.find(getBaseCommand(tag.typeOfEntity));
338 
339   if (it != tagHandlers.end()) {
340     (this->*(it->second.first))(tag, translatedComment, it->second.second);
341   } else {
342     // do NOT print warning, since there are many tags, which are not
343     // translatable - many warnings hide important ones
344     // addError(WARN_DOXYGEN_COMMAND_ERROR, "Unknown doxygen or HTML tag: " + tag.typeOfEntity);
345   }
346 }
347 
348 
handleTagAnchor(DoxygenEntity & tag,std::string & translatedComment,std::string &)349 void JavaDocConverter::handleTagAnchor(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
350   translatedComment += "<a id=\"" + translateSubtree(tag) + "\"></a>";
351 }
352 
353 
handleTagHtml(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)354 void JavaDocConverter::handleTagHtml(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
355   if (tag.entityList.size()) { // do not include empty tags
356     std::string tagData = translateSubtree(tag);
357     // wrap the thing, ignoring whitespace
358     size_t wsPos = tagData.find_last_not_of("\n\t ");
359     if (wsPos != std::string::npos)
360       translatedComment += "<" + arg + ">" + tagData.substr(0, wsPos + 1) + "</" + arg + ">" + tagData.substr(wsPos + 1);
361     else
362       translatedComment += "<" + arg + ">" + translateSubtree(tag) + "</" + arg + "> ";
363   }
364 }
365 
handleDoxyHtmlTag(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)366 void JavaDocConverter::handleDoxyHtmlTag(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
367   std::string htmlTagArgs = tag.data;
368   if (htmlTagArgs == "/") {
369     // end html tag, for example "</ul>
370     translatedComment += "</" + arg.substr(1) + ">";
371   } else {
372     translatedComment += arg + htmlTagArgs + ">";
373   }
374 }
375 
handleHtmlEntity(DoxygenEntity &,std::string & translatedComment,std::string & arg)376 void JavaDocConverter::handleHtmlEntity(DoxygenEntity &, std::string &translatedComment, std::string &arg) {
377   // html entities can be preserved for Java
378   translatedComment += arg + ';';
379 }
380 
handleNewLine(DoxygenEntity &,std::string & translatedComment,std::string &)381 void JavaDocConverter::handleNewLine(DoxygenEntity &, std::string &translatedComment, std::string &) {
382   // <br> tag is added, because otherwise to much text is joined
383   // into same paragraph by javadoc. For example, doxy list:
384   // - item one
385   // - item two
386   // becomes one paragraph with surrounding text without newlines.
387   // This way we get to many empty lines in javadoc output, but this
388   // is still better than joined lines. Possibility for improvements
389   // exists.
390   translatedComment += "<br>\n * ";
391 }
392 
handleTagChar(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)393 void JavaDocConverter::handleTagChar(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
394   // escape it if we need to, else just print
395   if (arg.size())
396     translatedComment += arg;
397   else
398     translatedComment += tag.typeOfEntity;
399 }
400 
401 // handles tags which are the same in Doxygen and Javadoc.
handleTagSame(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)402 void JavaDocConverter::handleTagSame(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
403   if (arg.size())
404     tag.typeOfEntity = arg;
405   translatedComment += formatCommand(std::string("@" + tag.typeOfEntity + " " + translateSubtree(tag)), 2);
406 }
407 
handleParagraph(DoxygenEntity & tag,std::string & translatedComment,std::string &)408 void JavaDocConverter::handleParagraph(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
409   translatedComment += formatCommand(translateSubtree(tag), 0);
410 }
411 
handlePlainString(DoxygenEntity & tag,std::string & translatedComment,std::string &)412 void JavaDocConverter::handlePlainString(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
413   translatedComment += tag.data;
414   // if (tag.data.size() && tag.data[tag.data.size()-1] != ' ')
415   //    translatedComment += " ";
416 }
417 
handleTagVerbatim(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)418 void JavaDocConverter::handleTagVerbatim(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
419   translatedComment += arg + " ";
420   for (DoxygenEntityListCIt it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
421     translatedComment += it->data;
422   }
423 }
424 
handleTagExtended(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)425 void JavaDocConverter::handleTagExtended(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
426   std::string dummy;
427   translatedComment += "{@" + arg + " ";
428   handleParagraph(tag, translatedComment, dummy);
429   translatedComment += "}";
430 }
431 
handleTagIf(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)432 void JavaDocConverter::handleTagIf(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
433   std::string dummy;
434   translatedComment += arg;
435   if (tag.entityList.size()) {
436     translatedComment += tag.entityList.begin()->data;
437     tag.entityList.pop_front();
438     translatedComment += " {" + translateSubtree(tag) + "}";
439   }
440 }
441 
handleTagMessage(DoxygenEntity & tag,std::string & translatedComment,std::string & arg)442 void JavaDocConverter::handleTagMessage(DoxygenEntity &tag, std::string &translatedComment, std::string &arg) {
443   std::string dummy;
444   translatedComment += formatCommand(arg, 0);
445   handleParagraph(tag, translatedComment, dummy);
446 }
447 
handleTagImage(DoxygenEntity & tag,std::string & translatedComment,std::string &)448 void JavaDocConverter::handleTagImage(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
449   if (tag.entityList.size() < 2)
450     return;
451 
452   std::string file;
453   std::string title;
454 
455   std::list<DoxygenEntity>::iterator it = tag.entityList.begin();
456   if (it->data != "html")
457     return;
458 
459   it++;
460   file = it->data;
461 
462   it++;
463   if (it != tag.entityList.end())
464     title = it->data;
465 
466   translatedComment += "<img src=" + file;
467   if (title.size())
468     translatedComment += " alt=" + title;
469 
470   // the size indication is supported for Latex only in Doxygen, see manual
471 
472   translatedComment += "/>";
473 }
474 
handleTagPar(DoxygenEntity & tag,std::string & translatedComment,std::string &)475 void JavaDocConverter::handleTagPar(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
476   std::string dummy;
477   translatedComment += "<p";
478   if (tag.entityList.size()) {
479     translatedComment += " alt=\"" + tag.entityList.begin()->data + "\"";
480     translatedComment += ">";
481     tag.entityList.pop_front();
482     handleParagraph(tag, translatedComment, dummy);
483   }
484   translatedComment += "</p>";
485 }
486 
487 
handleTagParam(DoxygenEntity & tag,std::string & translatedComment,std::string &)488 void JavaDocConverter::handleTagParam(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
489   std::string dummy;
490 
491   if (!tag.entityList.size())
492     return;
493   if (!paramExists(tag.entityList.begin()->data))
494     return;
495 
496   translatedComment += "@param ";
497   translatedComment += tag.entityList.begin()->data;
498   tag.entityList.pop_front();
499   handleParagraph(tag, translatedComment, dummy);
500 }
501 
502 
handleTagRef(DoxygenEntity & tag,std::string & translatedComment,std::string &)503 void JavaDocConverter::handleTagRef(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
504   std::string dummy;
505   if (!tag.entityList.size())
506     return;
507 
508   // we translate to link, although \page is not supported in Java, but
509   // reader at least knows what to look at. Also for \anchor tag on the same
510   // page this link works.
511   string anchor = tag.entityList.begin()->data;
512   tag.entityList.pop_front();
513   string anchorText = anchor;
514   if (!tag.entityList.empty()) {
515     anchorText = tag.entityList.begin()->data;
516   }
517   translatedComment += "<a href=\"#" + anchor + "\">" + anchorText + "</a>";
518 }
519 
520 
convertLink(string linkObject)521 string JavaDocConverter::convertLink(string linkObject) {
522   if (GetFlag(currentNode, "feature:doxygen:nolinktranslate"))
523     return linkObject;
524   // find the params in function in linkObject (if any)
525   size_t lbracePos = linkObject.find('(', 0);
526   size_t rbracePos = linkObject.find(')', 0);
527   if (lbracePos == string::npos || rbracePos == string::npos || lbracePos >= rbracePos)
528     return "";
529 
530   string paramsStr = linkObject.substr(lbracePos + 1,
531                                        rbracePos - lbracePos - 1);
532   // strip the params, to fill them later
533   string additionalObject = linkObject.substr(rbracePos + 1, string::npos);
534   linkObject = linkObject.substr(0, lbracePos);
535 
536   // find all the params
537   vector<string> params;
538   size_t lastPos = 0, commaPos = 0;
539   while (true) {
540     commaPos = paramsStr.find(',', lastPos);
541     if (commaPos == string::npos)
542       commaPos = paramsStr.size();
543     string param = paramsStr.substr(lastPos, commaPos - lastPos);
544     // if any param type is empty, we are failed
545     if (!param.size())
546       return "";
547     params.push_back(param);
548     lastPos = commaPos + 1;
549     if (lastPos >= paramsStr.size())
550       break;
551   }
552 
553   linkObject += "(";
554   for (size_t i = 0; i < params.size(); i++) {
555     // translate c/c++ type string to swig's type
556     // i e 'int **arr[100][10]' -> 'a(100).a(10).p.p.int'
557     // also converting arrays to pointers
558     string paramStr = params[i];
559     String *swigType = NewString("");
560 
561     // handle const qualifier
562     if (paramStr.find("const") != string::npos)
563       SwigType_add_qualifier(swigType, "const");
564 
565     // handle pointers, references and arrays
566     for (int j = (int)params[i].size() - 1; j >= 0; j--) {
567       // skip all the [...] blocks, write 'p.' for every of it
568       if (paramStr[j] == ']') {
569         while (j >= 0 && paramStr[j] != '[')
570           j--;
571         // no closing brace
572         if (j < 0)
573           return "";
574         SwigType_add_pointer(swigType);
575         continue;
576       } else if (paramStr[j] == '*')
577         SwigType_add_pointer(swigType);
578       else if (paramStr[j] == '&')
579         SwigType_add_reference(swigType);
580       else if (isalnum(paramStr[j])) {
581         size_t typeNameStart = paramStr.find_last_of(' ', j + 1);
582         if (typeNameStart == string::npos)
583           typeNameStart = 0;
584         else
585           typeNameStart++;
586         Append(swigType, paramStr.substr(typeNameStart, j - typeNameStart + 1).c_str());
587         break;
588       }
589     }
590 
591     // make dummy param list, to lookup typemaps for it
592     Parm *dummyParam = NewParm(swigType, "", 0);
593     Swig_typemap_attach_parms("jstype", dummyParam, NULL);
594     Language::instance()->replaceSpecialVariables(0, Getattr(dummyParam, "tmap:jstype"), dummyParam);
595 
596     //Swig_print(dummyParam, 1);
597     linkObject += Char(Getattr(dummyParam, "tmap:jstype"));
598     if (i != params.size() - 1)
599       linkObject += ",";
600 
601     Delete(dummyParam);
602     Delete(swigType);
603   }
604   linkObject += ")";
605 
606   return linkObject + additionalObject;
607 }
608 
handleTagLink(DoxygenEntity & tag,std::string & translatedComment,std::string &)609 void JavaDocConverter::handleTagLink(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
610   std::string dummy;
611   if (!tag.entityList.size())
612     return;
613 
614   string linkObject = convertLink(tag.entityList.begin()->data);
615   if (!linkObject.size())
616     linkObject = tag.entityList.begin()->data;
617   tag.entityList.pop_front();
618 
619   translatedComment += "{@link ";
620   translatedComment += linkObject + " ";
621   handleParagraph(tag, translatedComment, dummy);
622   translatedComment += "}";
623 }
624 
handleTagSee(DoxygenEntity & tag,std::string & translatedComment,std::string &)625 void JavaDocConverter::handleTagSee(DoxygenEntity &tag, std::string &translatedComment, std::string &) {
626   std::string dummy;
627   if (!tag.entityList.size())
628     return;
629 
630   // tag.entity list contains contents of the @see paragraph. It should contain
631   // one link (references) to method with or without parameters. Doxygen supports
632   // arbitrary text and types mixed, but this feature is not supported here.
633   // :: or # may be used as a separator between class name and method name.
634   list<DoxygenEntity>::iterator it;
635   string methodRef;
636   for (it = tag.entityList.begin(); it != tag.entityList.end(); it++) {
637     if (it->typeOfEntity == "plainstd::endl") {
638       // handleNewLine(*it, translatedComment, dummy);
639       continue;
640     }
641     // restore entities which may be used in C++ type declaration
642     if (it->typeOfEntity == "&amp") {
643       methodRef += '&';
644     } else if (it->typeOfEntity == "&lt") {
645       methodRef += '<';
646     } else if (it->typeOfEntity == "&gt") {
647       methodRef += '>';
648     } else {
649       methodRef += it->data;
650     }
651   }
652 
653   // replace :: with #, but only if it appears before left brace
654   size_t lbrace = methodRef.find('(');
655   size_t dblColon = methodRef.find("::");
656   if (dblColon < lbrace) {
657     methodRef = methodRef.substr(0, dblColon) + '#' + methodRef.substr(dblColon + 2);
658   }
659 
660   translatedComment += "@see ";
661   string linkObject = convertLink(methodRef);
662   if (!linkObject.size()) {
663     linkObject = methodRef;
664   }
665   translatedComment += linkObject;
666 }
667 
668 /* This function moves all line endings at the end of child entities
669  * out of the child entities to the parent.
670  * For example, entity tree:
671 
672  -root
673  |-param
674  |-paramText
675  |-endline
676 
677  should be turned to
678 
679  -root
680  |-param
681  |-paramText
682  |-endline
683  *
684  */
shiftEndlinesUpTree(DoxygenEntity & root,int level)685 int JavaDocConverter::shiftEndlinesUpTree(DoxygenEntity &root, int level) {
686   DoxygenEntityListIt it = root.entityList.begin();
687   while (it != root.entityList.end()) {
688     // remove line endings
689     int ret = shiftEndlinesUpTree(*it, level + 1);
690     // insert them after this element
691     it++;
692     for (int i = 0; i < ret; i++) {
693       root.entityList.insert(it, DoxygenEntity("plainstd::endl"));
694     }
695   }
696 
697   // continue only if we are not root
698   if (!level) {
699     return 0;
700   }
701 
702   int removedCount = 0;
703   while (!root.entityList.empty()
704          && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
705     root.entityList.pop_back();
706     removedCount++;
707   }
708   return removedCount;
709 }
710 
711 /**
712  * This makes sure that all comment lines contain '*'. It is not mandatory in doxygen,
713  * but highly recommended for Javadoc. '*' in empty lines are indented according
714  * to indentation of the first line. Indentation of non-empty lines is not
715  * changed - garbage in garbage out.
716  */
indentAndInsertAsterisks(const string & doc)717 std::string JavaDocConverter::indentAndInsertAsterisks(const string &doc) {
718 
719   size_t idx = doc.find('\n');
720   size_t indent = 0;
721   bool singleLineComment = idx == string::npos;
722   // Detect indentation.
723   //   The first line in comment is the one after '/**', which may be
724   //   spaces and '\n' or the text. In any case it is not suitable to detect
725   //   indentation, so we have to skip the first '\n'.
726   //   However, if there is just one line, then use that line to detect indentation.
727   if (idx != string::npos) {
728     size_t nonspaceIdx = doc.find_first_not_of(" \t", idx + 1);
729     if (nonspaceIdx != string::npos) {
730       indent = nonspaceIdx - idx;
731     }
732   }
733 
734   if (indent == 0) {
735     // we can't indent the first line less than 0
736     indent = 1;
737   }
738   // Create the first line of Javadoc comment.
739   string indentStr(indent - 1, ' ');
740   string translatedStr = indentStr + "/**";
741   if (indent > 1) {
742     // remove the first space, so that '*' will be aligned
743     translatedStr = translatedStr.substr(1);
744   }
745 
746   translatedStr += doc;
747 
748   // insert '*' before each comment line, if it does not have it
749   idx = translatedStr.find('\n');
750 
751   while (idx != string::npos) {
752 
753     size_t nonspaceIdx = translatedStr.find_first_not_of(" \t", idx + 1);
754     if (nonspaceIdx != string::npos && translatedStr[nonspaceIdx] != '*') {
755       // line without '*' found - is it empty?
756       if (translatedStr[nonspaceIdx] != '\n') {
757         // add '* ' to each line without it
758         translatedStr = translatedStr.substr(0, nonspaceIdx) + "* " + translatedStr.substr(nonspaceIdx);
759         //printf(translatedStr.c_str());
760       } else {
761         // we found empty line, replace it with indented '*'
762         translatedStr = translatedStr.substr(0, idx + 1) + indentStr + "* " + translatedStr.substr(nonspaceIdx);
763       }
764     }
765     idx = translatedStr.find('\n', nonspaceIdx);
766   }
767 
768   // Add the last comment line properly indented
769   size_t nonspaceEndIdx = translatedStr.find_last_not_of(" \t");
770   if (nonspaceEndIdx != string::npos) {
771     if (translatedStr[nonspaceEndIdx] != '\n') {
772       if (!singleLineComment)
773 	translatedStr += '\n';
774     } else {
775       // remove trailing spaces
776       translatedStr = translatedStr.substr(0, nonspaceEndIdx + 1);
777     }
778   }
779   translatedStr += indentStr + "*/\n";
780 
781   return translatedStr;
782 }
783 
makeDocumentation(Node * node)784 String *JavaDocConverter::makeDocumentation(Node *node) {
785 
786   String *documentation = getDoxygenComment(node);
787 
788   if (documentation == NULL) {
789     return NewString("");
790   }
791 
792   if (GetFlag(node, "feature:doxygen:notranslate")) {
793 
794     string doc = Char(documentation);
795 
796     string translatedStr = indentAndInsertAsterisks(doc);
797 
798     return NewString(translatedStr.c_str());
799   }
800 
801   DoxygenEntityList entityList = parser.createTree(node, documentation);
802 
803   // entityList.sort(CompareDoxygenEntities()); sorting currently not used,
804 
805   if (m_flags & debug_translator) {
806     std::cout << "---RESORTED LIST---" << std::endl;
807     printTree(entityList);
808   }
809   // store the current node
810   // (currently just to handle params)
811   currentNode = node;
812 
813   std::string javaDocString = "/**\n * ";
814 
815   DoxygenEntity root("root", entityList);
816 
817   shiftEndlinesUpTree(root);
818 
819   // strip line endings at the beginning
820   while (!root.entityList.empty()
821          && root.entityList.begin()->typeOfEntity == "plainstd::endl") {
822     root.entityList.pop_front();
823   }
824 
825   // and at the end
826   while (!root.entityList.empty()
827          && root.entityList.rbegin()->typeOfEntity == "plainstd::endl") {
828     root.entityList.pop_back();
829   }
830 
831   javaDocString += translateSubtree(root);
832 
833   javaDocString += "\n */\n";
834 
835   if (m_flags & debug_translator) {
836     std::cout << "\n---RESULT IN JAVADOC---" << std::endl;
837     std::cout << javaDocString;
838   }
839 
840   return NewString(javaDocString.c_str());
841 }
842 
addError(int warningType,const std::string & message)843 void JavaDocConverter::addError(int warningType, const std::string &message) {
844   Swig_warning(warningType, "", 0, "Doxygen parser warning: %s. \n", message.c_str());
845 }
846