]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_html_generator.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / compiler / cpp / src / thrift / generate / t_html_generator.cc
CommitLineData
f67539c2
TL
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#include <string>
21#include <fstream>
22#include <iostream>
23#include <vector>
24#include <map>
25
26#include <stdlib.h>
27#include <sys/stat.h>
28#include <sstream>
29#include "thrift/platform.h"
30#include "thrift/generate/t_generator.h"
31#include "thrift/generate/t_html_generator.h"
32
33using std::map;
34using std::ofstream;
35using std::ostringstream;
36using std::pair;
37using std::string;
38using std::stringstream;
39using std::vector;
40
41static const string endl = "\n"; // avoid ostream << std::endl flushes
42
43enum input_type { INPUT_UNKNOWN, INPUT_UTF8, INPUT_PLAIN };
44
45/**
46 * HTML code generator
47 *
48 * mostly copy/pasting/tweaking from mcslee's work.
49 */
50class t_html_generator : public t_generator {
51public:
52 t_html_generator(t_program* program,
53 const std::map<std::string, std::string>& parsed_options,
54 const std::string& option_string)
55 : t_generator(program) {
56 (void)option_string;
57 std::map<std::string, std::string>::const_iterator iter;
58
59 standalone_ = false;
60 unsafe_ = false;
61 for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
62 if( iter->first.compare("standalone") == 0) {
63 standalone_ = true;
64 } else if( iter->first.compare("noescape") == 0) {
65 unsafe_ = true;
66 } else {
67 throw "unknown option html:" + iter->first;
68 }
69 }
70
71
72 out_dir_base_ = "gen-html";
73 input_type_ = INPUT_UNKNOWN;
74
75 escape_.clear();
76 escape_['&'] = "&amp;";
77 escape_['<'] = "&lt;";
78 escape_['>'] = "&gt;";
79 escape_['"'] = "&quot;";
80 escape_['\''] = "&apos;";
81
82 init_allowed__markup();
83 }
84
85 void generate_program() override;
86 void generate_program_toc();
87 void generate_program_toc_row(t_program* tprog);
88 void generate_program_toc_rows(t_program* tprog, std::vector<t_program*>& finished);
89 void generate_index();
90 std::string escape_html(std::string const& str);
91 std::string escape_html_tags(std::string const& str);
92 void generate_css();
93 void generate_css_content(std::ostream& f_target);
94 void generate_style_tag();
95 std::string make_file_link(std::string name);
96 bool is_utf8_sequence(std::string const& str, size_t firstpos);
97 void detect_input_encoding(std::string const& str, size_t firstpos);
98 void init_allowed__markup();
99
100 /**
101 * Program-level generation functions
102 */
103
104 void generate_typedef(t_typedef* ttypedef) override;
105 void generate_enum(t_enum* tenum) override;
106 void generate_const(t_const* tconst) override;
107 void generate_struct(t_struct* tstruct) override;
108 void generate_service(t_service* tservice) override;
109 void generate_xception(t_struct* txception) override;
110
111 void print_doc(t_doc* tdoc);
112 int print_type(t_type* ttype);
113 void print_const_value(t_type* type, t_const_value* tvalue);
114 void print_fn_args_doc(t_function* tfunction);
115
116private:
117 ofstream_with_content_based_conditional_update f_out_;
118 std::string current_file_;
119 input_type input_type_;
120 std::map<std::string, int> allowed_markup;
121 bool standalone_;
122 bool unsafe_;
123};
124
125/**
126 * Emits the Table of Contents links at the top of the module's page
127 */
128void t_html_generator::generate_program_toc() {
129 f_out_ << "<table class=\"table-bordered table-striped "
130 "table-condensed\"><thead><tr><th>Module</th><th>Services</th>"
131 << "<th>Data types</th><th>Constants</th></tr></thead><tbody>" << endl;
132 generate_program_toc_row(program_);
133 f_out_ << "</tbody></table>" << endl;
134}
135
136/**
137 * Recurses through from the provided program and generates a ToC row
138 * for each discovered program exactly once by maintaining the list of
139 * completed rows in 'finished'
140 */
141void t_html_generator::generate_program_toc_rows(t_program* tprog,
142 std::vector<t_program*>& finished) {
143 for (auto & iter : finished) {
144 if (tprog->get_path() == iter->get_path()) {
145 return;
146 }
147 }
148 finished.push_back(tprog);
149 generate_program_toc_row(tprog);
150 vector<t_program*> includes = tprog->get_includes();
151 for (auto & include : includes) {
152 generate_program_toc_rows(include, finished);
153 }
154}
155
156/**
157 * Emits the Table of Contents links at the top of the module's page
158 */
159void t_html_generator::generate_program_toc_row(t_program* tprog) {
160 string fname = tprog->get_name() + ".html";
161 f_out_ << "<tr>" << endl << "<td>" << tprog->get_name() << "</td><td>";
162 if (!tprog->get_services().empty()) {
163 vector<t_service*> services = tprog->get_services();
164 vector<t_service*>::iterator sv_iter;
165 for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
166 string name = get_service_name(*sv_iter);
167 f_out_ << "<a href=\"" << make_file_link(fname) << "#Svc_" << name << "\">" << name
168 << "</a><br/>" << endl;
169 f_out_ << "<ul>" << endl;
170 map<string, string> fn_html;
171 vector<t_function*> functions = (*sv_iter)->get_functions();
172 vector<t_function*>::iterator fn_iter;
173 for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) {
174 string fn_name = (*fn_iter)->get_name();
175 string html = "<li><a href=\"" + make_file_link(fname) + "#Fn_" + name + "_" + fn_name
176 + "\">" + fn_name + "</a></li>";
177 fn_html.insert(pair<string, string>(fn_name, html));
178 }
179 for (auto & html_iter : fn_html) {
180 f_out_ << html_iter.second << endl;
181 }
182 f_out_ << "</ul>" << endl;
183 }
184 }
185 f_out_ << "</td>" << endl << "<td>";
186 map<string, string> data_types;
187 if (!tprog->get_enums().empty()) {
188 vector<t_enum*> enums = tprog->get_enums();
189 vector<t_enum*>::iterator en_iter;
190 for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
191 string name = (*en_iter)->get_name();
192 // f_out_ << "<a href=\"" << make_file_link(fname) << "#Enum_" << name << "\">" << name
193 // << "</a><br/>" << endl;
194 string html = "<a href=\"" + make_file_link(fname) + "#Enum_" + name + "\">" + name + "</a>";
195 data_types.insert(pair<string, string>(name, html));
196 }
197 }
198 if (!tprog->get_typedefs().empty()) {
199 vector<t_typedef*> typedefs = tprog->get_typedefs();
200 vector<t_typedef*>::iterator td_iter;
201 for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
202 string name = (*td_iter)->get_symbolic();
203 // f_out_ << "<a href=\"" << make_file_link(fname) << "#Typedef_" << name << "\">" << name
204 // << "</a><br/>" << endl;
205 string html = "<a href=\"" + make_file_link(fname) + "#Typedef_" + name + "\">" + name
206 + "</a>";
207 data_types.insert(pair<string, string>(name, html));
208 }
209 }
210 if (!tprog->get_objects().empty()) {
211 vector<t_struct*> objects = tprog->get_objects();
212 vector<t_struct*>::iterator o_iter;
213 for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
214 string name = (*o_iter)->get_name();
215 // f_out_ << "<a href=\"" << make_file_link(fname) << "#Struct_" << name << "\">" << name
216 //<< "</a><br/>" << endl;
217 string html = "<a href=\"" + make_file_link(fname) + "#Struct_" + name + "\">" + name
218 + "</a>";
219 data_types.insert(pair<string, string>(name, html));
220 }
221 }
222 for (auto & data_type : data_types) {
223 f_out_ << data_type.second << "<br/>" << endl;
224 }
225 f_out_ << "</td>" << endl << "<td>";
226 if (!tprog->get_consts().empty()) {
227 map<string, string> const_html;
228 vector<t_const*> consts = tprog->get_consts();
229 vector<t_const*>::iterator con_iter;
230 for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) {
231 string name = (*con_iter)->get_name();
232 string html = "<code><a href=\"" + make_file_link(fname) + "#Const_" + name + "\">" + name
233 + "</a></code>";
234 const_html.insert(pair<string, string>(name, html));
235 }
236 for (auto & con_iter : const_html) {
237 f_out_ << con_iter.second << "<br/>" << endl;
238 }
239 }
240 f_out_ << "</td>" << endl << "</tr>";
241}
242
243/**
244 * Prepares for file generation by opening up the necessary file output
245 * stream.
246 */
247void t_html_generator::generate_program() {
248 // Make output directory
249 MKDIR(get_out_dir().c_str());
250 current_file_ = program_->get_name() + ".html";
251 string fname = get_out_dir() + current_file_;
252 f_out_.open(fname.c_str());
253 f_out_ << "<!DOCTYPE html>" << endl;
254 f_out_ << "<html lang=\"en\">" << endl;
255 f_out_ << "<head>" << endl;
256 f_out_ << "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />" << endl;
257 generate_style_tag();
258 f_out_ << "<title>Thrift module: " << program_->get_name() << "</title></head><body>" << endl
259 << "<div class=\"container-fluid\">" << endl
260 << "<h1>Thrift module: " << program_->get_name() << "</h1>" << endl;
261
262 print_doc(program_);
263
264 generate_program_toc();
265
266 if (!program_->get_consts().empty()) {
267 f_out_ << "<hr/><h2 id=\"Constants\">Constants</h2>" << endl;
268 vector<t_const*> consts = program_->get_consts();
269 f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
270 f_out_ << "<thead><tr><th>Constant</th><th>Type</th><th>Value</th></tr></thead><tbody>" << endl;
271 generate_consts(consts);
272 f_out_ << "</tbody></table>";
273 }
274
275 if (!program_->get_enums().empty()) {
276 f_out_ << "<hr/><h2 id=\"Enumerations\">Enumerations</h2>" << endl;
277 // Generate enums
278 vector<t_enum*> enums = program_->get_enums();
279 vector<t_enum*>::iterator en_iter;
280 for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) {
281 generate_enum(*en_iter);
282 }
283 }
284
285 if (!program_->get_typedefs().empty()) {
286 f_out_ << "<hr/><h2 id=\"Typedefs\">Type declarations</h2>" << endl;
287 // Generate typedefs
288 vector<t_typedef*> typedefs = program_->get_typedefs();
289 vector<t_typedef*>::iterator td_iter;
290 for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) {
291 generate_typedef(*td_iter);
292 }
293 }
294
295 if (!program_->get_objects().empty()) {
296 f_out_ << "<hr/><h2 id=\"Structs\">Data structures</h2>" << endl;
297 // Generate structs and exceptions in declared order
298 vector<t_struct*> objects = program_->get_objects();
299 vector<t_struct*>::iterator o_iter;
300 for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) {
301 if ((*o_iter)->is_xception()) {
302 generate_xception(*o_iter);
303 } else {
304 generate_struct(*o_iter);
305 }
306 }
307 }
308
309 if (!program_->get_services().empty()) {
310 f_out_ << "<hr/><h2 id=\"Services\">Services</h2>" << endl;
311 // Generate services
312 vector<t_service*> services = program_->get_services();
313 vector<t_service*>::iterator sv_iter;
314 for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) {
315 service_name_ = get_service_name(*sv_iter);
316 generate_service(*sv_iter);
317 }
318 }
319
320 f_out_ << "</div></body></html>" << endl;
321 f_out_.close();
322
323 generate_index();
324 generate_css();
325}
326
327/**
328 * Emits the index.html file for the recursive set of Thrift programs
329 */
330void t_html_generator::generate_index() {
331 current_file_ = "index.html";
332 string index_fname = get_out_dir() + current_file_;
333 f_out_.open(index_fname.c_str());
334 f_out_ << "<!DOCTYPE html>" << endl << "<html lang=\"en\"><head>" << endl;
335 generate_style_tag();
336 f_out_ << "<title>All Thrift declarations</title></head><body>" << endl
337 << "<div class=\"container-fluid\">" << endl << "<h1>All Thrift declarations</h1>" << endl;
338 f_out_ << "<table class=\"table-bordered table-striped "
339 "table-condensed\"><thead><tr><th>Module</th><th>Services</th><th>Data types</th>"
340 << "<th>Constants</th></tr></thead><tbody>" << endl;
341 vector<t_program*> programs;
342 generate_program_toc_rows(program_, programs);
343 f_out_ << "</tbody></table>" << endl;
344 f_out_ << "</div></body></html>" << endl;
345 f_out_.close();
346}
347
348void t_html_generator::generate_css() {
349 if (!standalone_) {
350 current_file_ = "style.css";
351 string css_fname = get_out_dir() + current_file_;
352 f_out_.open(css_fname.c_str());
353 generate_css_content(f_out_);
354 f_out_.close();
355 }
356}
357
358void t_html_generator::generate_css_content(std::ostream& f_target) {
359 f_target << BOOTSTRAP_CSS() << endl;
360 f_target << "/* Auto-generated CSS for generated Thrift docs */" << endl;
361 f_target << "h3, h4 { margin-bottom: 6px; }" << endl;
362 f_target << "div.definition { border: 1px solid #CCC; margin-bottom: 10px; padding: 10px; }"
363 << endl;
364 f_target << "div.extends { margin: -0.5em 0 1em 5em }" << endl;
365 f_target << "td { vertical-align: top; }" << endl;
366 f_target << "table { empty-cells: show; }" << endl;
367 f_target << "code { line-height: 20px; }" << endl;
368 f_target << ".table-bordered th, .table-bordered td { border-bottom: 1px solid #DDDDDD; }"
369 << endl;
370}
371
372/**
373 * Generates the CSS tag.
374 * Depending on "standalone", either a CSS file link (default), or the entire CSS is embedded
375 * inline.
376 */
377void t_html_generator::generate_style_tag() {
378 if (!standalone_) {
379 f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>" << endl;
380 } else {
381 f_out_ << "<style type=\"text/css\"/><!--" << endl;
382 generate_css_content(f_out_);
383 f_out_ << "--></style>" << endl;
384 }
385}
386
387/**
388 * Returns the target file for a <a href> link
389 * The returned string is empty, whenever filename refers to the current file.
390 */
391std::string t_html_generator::make_file_link(std::string filename) {
392 return (current_file_.compare(filename) != 0) ? filename : "";
393}
394
395/**
396 * If the provided documentable object has documentation attached, this
397 * will emit it to the output stream in HTML format.
398 */
399void t_html_generator::print_doc(t_doc* tdoc) {
400 if (tdoc->has_doc()) {
401 if (unsafe_) {
402 f_out_ << tdoc->get_doc() << "<br/>";
403 } else {
404 f_out_ << "<pre>" << escape_html(tdoc->get_doc()) << "</pre><br/>";
405 }
406 }
407}
408
409bool t_html_generator::is_utf8_sequence(std::string const& str, size_t firstpos) {
410 // leading char determines the length of the sequence
411 unsigned char c = str.at(firstpos);
412 int count = 0;
413 if ((c & 0xE0) == 0xC0) {
414 count = 1;
415 } else if ((c & 0xF0) == 0xE0) {
416 count = 2;
417 } else if ((c & 0xF8) == 0xF0) {
418 count = 3;
419 } else if ((c & 0xFC) == 0xF8) {
420 count = 4;
421 } else if ((c & 0xFE) == 0xFC) {
422 count = 5;
423 } else {
424 // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 leading byte", c, int(c));
425 return false; // no UTF-8
426 }
427
428 // following chars
429 size_t pos = firstpos + 1;
430 while ((pos < str.length()) && (0 < count)) {
431 c = str.at(pos);
432 if ((c & 0xC0) != 0x80) {
433 // pdebug("UTF-8 test: char '%c' (%d) is not a valid UTF-8 following byte", c, int(c));
434 return false; // no UTF-8
435 }
436 --count;
437 ++pos;
438 }
439
440 // true if the sequence is complete
441 return (0 == count);
442}
443
444void t_html_generator::detect_input_encoding(std::string const& str, size_t firstpos) {
445 if (is_utf8_sequence(str, firstpos)) {
446 pdebug("Input seems to be already UTF-8 encoded");
447 input_type_ = INPUT_UTF8;
448 return;
449 }
450
451 // fallback
452 pwarning(1, "Input is not UTF-8, treating as plain ANSI");
453 input_type_ = INPUT_PLAIN;
454}
455
456void t_html_generator::init_allowed__markup() {
457 allowed_markup.clear();
458 // standalone tags
459 allowed_markup["br"] = 1;
460 allowed_markup["br/"] = 1;
461 allowed_markup["img"] = 1;
462 // paired tags
463 allowed_markup["b"] = 1;
464 allowed_markup["/b"] = 1;
465 allowed_markup["u"] = 1;
466 allowed_markup["/u"] = 1;
467 allowed_markup["i"] = 1;
468 allowed_markup["/i"] = 1;
469 allowed_markup["s"] = 1;
470 allowed_markup["/s"] = 1;
471 allowed_markup["big"] = 1;
472 allowed_markup["/big"] = 1;
473 allowed_markup["small"] = 1;
474 allowed_markup["/small"] = 1;
475 allowed_markup["sup"] = 1;
476 allowed_markup["/sup"] = 1;
477 allowed_markup["sub"] = 1;
478 allowed_markup["/sub"] = 1;
479 allowed_markup["pre"] = 1;
480 allowed_markup["/pre"] = 1;
481 allowed_markup["tt"] = 1;
482 allowed_markup["/tt"] = 1;
483 allowed_markup["ul"] = 1;
484 allowed_markup["/ul"] = 1;
485 allowed_markup["ol"] = 1;
486 allowed_markup["/ol"] = 1;
487 allowed_markup["li"] = 1;
488 allowed_markup["/li"] = 1;
489 allowed_markup["a"] = 1;
490 allowed_markup["/a"] = 1;
491 allowed_markup["p"] = 1;
492 allowed_markup["/p"] = 1;
493 allowed_markup["code"] = 1;
494 allowed_markup["/code"] = 1;
495 allowed_markup["dl"] = 1;
496 allowed_markup["/dl"] = 1;
497 allowed_markup["dt"] = 1;
498 allowed_markup["/dt"] = 1;
499 allowed_markup["dd"] = 1;
500 allowed_markup["/dd"] = 1;
501 allowed_markup["h1"] = 1;
502 allowed_markup["/h1"] = 1;
503 allowed_markup["h2"] = 1;
504 allowed_markup["/h2"] = 1;
505 allowed_markup["h3"] = 1;
506 allowed_markup["/h3"] = 1;
507 allowed_markup["h4"] = 1;
508 allowed_markup["/h4"] = 1;
509 allowed_markup["h5"] = 1;
510 allowed_markup["/h5"] = 1;
511 allowed_markup["h6"] = 1;
512 allowed_markup["/h6"] = 1;
513}
514
515std::string t_html_generator::escape_html_tags(std::string const& str) {
516 std::ostringstream result;
517
518 unsigned char c = '?';
519 size_t lastpos;
520 size_t firstpos = 0;
521 while (firstpos < str.length()) {
522
523 // look for non-ASCII char
524 lastpos = firstpos;
525 while (lastpos < str.length()) {
526 c = str.at(lastpos);
527 if (('<' == c) || ('>' == c)) {
528 break;
529 }
530 ++lastpos;
531 }
532
533 // copy what we got so far
534 if (lastpos > firstpos) {
535 result << str.substr(firstpos, lastpos - firstpos);
536 firstpos = lastpos;
537 }
538
539 // reached the end?
540 if (firstpos >= str.length()) {
541 break;
542 }
543
544 // tag end without corresponding begin
545 ++firstpos;
546 if ('>' == c) {
547 result << "&gt;";
548 continue;
549 }
550
551 // extract the tag
552 std::ostringstream tagstream;
553 while (firstpos < str.length()) {
554 c = str.at(firstpos);
555 ++firstpos;
556 if ('<' == c) {
557 tagstream << "&lt;"; // nested begin?
558 } else if ('>' == c) {
559 break;
560 } else {
561 tagstream << c; // not very efficient, but tags should be quite short
562 }
563 }
564
565 // we allow for several markup in docstrings, all else will become escaped
566 string tag_content = tagstream.str();
567 string tag_key = tag_content;
568 size_t first_white = tag_key.find_first_of(" \t\f\v\n\r");
569 if (first_white != string::npos) {
570 tag_key.erase(first_white);
571 }
572 for (char & i : tag_key) {
573 i = tolower(i);
574 }
575 if (allowed_markup.find(tag_key) != allowed_markup.end()) {
576 result << "<" << tag_content << ">";
577 } else {
578 result << "&lt;" << tagstream.str() << "&gt;";
579 pverbose("illegal markup <%s> in doc-comment\n", tag_key.c_str());
580 }
581 }
582
583 return result.str();
584}
585
586std::string t_html_generator::escape_html(std::string const& str) {
587 // the generated HTML header says it is UTF-8 encoded
588 // if UTF-8 input has been detected before, we don't need to change anything
589 if (input_type_ == INPUT_UTF8) {
590 return escape_html_tags(str);
591 }
592
593 // convert unsafe chars to their &#<num>; equivalent
594 std::ostringstream result;
595 unsigned char c = '?';
596 unsigned int ic = 0;
597 size_t lastpos;
598 size_t firstpos = 0;
599 while (firstpos < str.length()) {
600
601 // look for non-ASCII char
602 lastpos = firstpos;
603 while (lastpos < str.length()) {
604 c = str.at(lastpos);
605 ic = c;
606 if ((32 > ic) || (127 < ic)) {
607 break;
608 }
609 ++lastpos;
610 }
611
612 // copy what we got so far
613 if (lastpos > firstpos) {
614 result << str.substr(firstpos, lastpos - firstpos);
615 firstpos = lastpos;
616 }
617
618 // reached the end?
619 if (firstpos >= str.length()) {
620 break;
621 }
622
623 // some control code?
624 if (ic <= 31) {
625 switch (c) {
626 case '\r':
627 case '\n':
628 case '\t':
629 result << c;
630 break;
631 default: // silently consume all other ctrl chars
632 break;
633 }
634 ++firstpos;
635 continue;
636 }
637
638 // reached the end?
639 if (firstpos >= str.length()) {
640 break;
641 }
642
643 // try to detect input encoding
644 if (input_type_ == INPUT_UNKNOWN) {
645 detect_input_encoding(str, firstpos);
646 if (input_type_ == INPUT_UTF8) {
647 lastpos = str.length();
648 result << str.substr(firstpos, lastpos - firstpos);
649 break;
650 }
651 }
652
653 // convert the character to something useful based on the detected encoding
654 switch (input_type_) {
655 case INPUT_PLAIN:
656 result << "&#" << ic << ";";
657 ++firstpos;
658 break;
659 default:
660 throw "Unexpected or unrecognized input encoding";
661 }
662 }
663
664 return escape_html_tags(result.str());
665}
666
667/**
668 * Prints out the provided type in HTML
669 */
670int t_html_generator::print_type(t_type* ttype) {
671 std::string::size_type len = 0;
672 f_out_ << "<code>";
673 if (ttype->is_container()) {
674 if (ttype->is_list()) {
675 f_out_ << "list&lt;";
676 len = 6 + print_type(((t_list*)ttype)->get_elem_type());
677 f_out_ << "&gt;";
678 } else if (ttype->is_set()) {
679 f_out_ << "set&lt;";
680 len = 5 + print_type(((t_set*)ttype)->get_elem_type());
681 f_out_ << "&gt;";
682 } else if (ttype->is_map()) {
683 f_out_ << "map&lt;";
684 len = 5 + print_type(((t_map*)ttype)->get_key_type());
685 f_out_ << ", ";
686 len += print_type(((t_map*)ttype)->get_val_type());
687 f_out_ << "&gt;";
688 }
689 } else if (ttype->is_base_type()) {
690 f_out_ << (ttype->is_binary() ? "binary" : ttype->get_name());
691 len = ttype->get_name().size();
692 } else {
693 string prog_name = ttype->get_program()->get_name();
694 string type_name = ttype->get_name();
695 f_out_ << "<a href=\"" << make_file_link(prog_name + ".html") << "#";
696 if (ttype->is_typedef()) {
697 f_out_ << "Struct_";
698 } else if (ttype->is_struct() || ttype->is_xception()) {
699 f_out_ << "Struct_";
700 } else if (ttype->is_enum()) {
701 f_out_ << "Enum_";
702 } else if (ttype->is_service()) {
703 f_out_ << "Svc_";
704 }
705 f_out_ << type_name << "\">";
706 len = type_name.size();
707 if (ttype->get_program() != program_) {
708 f_out_ << prog_name << ".";
709 len += prog_name.size() + 1;
710 }
711 f_out_ << type_name << "</a>";
712 }
713 f_out_ << "</code>";
714 return (int)len;
715}
716
717/**
718 * Prints out an HTML representation of the provided constant value
719 */
720void t_html_generator::print_const_value(t_type* type, t_const_value* tvalue) {
721
722 // if tvalue is an identifier, the constant content is already shown elsewhere
723 if (tvalue->get_type() == t_const_value::CV_IDENTIFIER) {
724 string fname = program_->get_name() + ".html";
725 string name = escape_html(tvalue->get_identifier());
726 f_out_ << "<code><a href=\"" + make_file_link(fname) + "#Const_" + name + "\">" + name
727 + "</a></code>";
728 return;
729 }
730
731 t_type* truetype = type;
732 while (truetype->is_typedef()) {
733 truetype = ((t_typedef*)truetype)->get_type();
734 }
735
736 bool first = true;
737 if (truetype->is_base_type()) {
738 t_base_type::t_base tbase = ((t_base_type*)truetype)->get_base();
739 switch (tbase) {
740 case t_base_type::TYPE_STRING:
741 f_out_ << '"' << escape_html(get_escaped_string(tvalue)) << '"';
742 break;
743 case t_base_type::TYPE_BOOL:
744 f_out_ << ((tvalue->get_integer() != 0) ? "true" : "false");
745 break;
746 case t_base_type::TYPE_I8:
747 f_out_ << tvalue->get_integer();
748 break;
749 case t_base_type::TYPE_I16:
750 f_out_ << tvalue->get_integer();
751 break;
752 case t_base_type::TYPE_I32:
753 f_out_ << tvalue->get_integer();
754 break;
755 case t_base_type::TYPE_I64:
756 f_out_ << tvalue->get_integer();
757 break;
758 case t_base_type::TYPE_DOUBLE:
759 if (tvalue->get_type() == t_const_value::CV_INTEGER) {
760 f_out_ << tvalue->get_integer();
761 } else {
762 f_out_ << tvalue->get_double();
763 }
764 break;
765 default:
766 f_out_ << "UNKNOWN BASE TYPE";
767 break;
768 }
769 } else if (truetype->is_enum()) {
770 f_out_ << escape_html(truetype->get_name()) << "."
771 << escape_html(tvalue->get_identifier_name());
772 } else if (truetype->is_struct() || truetype->is_xception()) {
773 f_out_ << "{ ";
774 const vector<t_field*>& fields = ((t_struct*)truetype)->get_members();
775 vector<t_field*>::const_iterator f_iter;
776 const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = tvalue->get_map();
777 map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
778 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
779 t_type* field_type = NULL;
780 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
781 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
782 field_type = (*f_iter)->get_type();
783 }
784 }
785 if (field_type == NULL) {
786 throw "type error: " + truetype->get_name() + " has no field "
787 + v_iter->first->get_string();
788 }
789 if (!first) {
790 f_out_ << ", ";
791 }
792 first = false;
793 f_out_ << escape_html(v_iter->first->get_string()) << " = ";
794 print_const_value(field_type, v_iter->second);
795 }
796 f_out_ << " }";
797 } else if (truetype->is_map()) {
798 f_out_ << "{ ";
799 map<t_const_value*, t_const_value*, t_const_value::value_compare> map_elems = tvalue->get_map();
800 map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator map_iter;
801 for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) {
802 if (!first) {
803 f_out_ << ", ";
804 }
805 first = false;
806 print_const_value(((t_map*)truetype)->get_key_type(), map_iter->first);
807 f_out_ << " = ";
808 print_const_value(((t_map*)truetype)->get_val_type(), map_iter->second);
809 }
810 f_out_ << " }";
811 } else if (truetype->is_list()) {
812 f_out_ << "{ ";
813 vector<t_const_value*> list_elems = tvalue->get_list();
814 ;
815 vector<t_const_value*>::iterator list_iter;
816 for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
817 if (!first) {
818 f_out_ << ", ";
819 }
820 first = false;
821 print_const_value(((t_list*)truetype)->get_elem_type(), *list_iter);
822 }
823 f_out_ << " }";
824 } else if (truetype->is_set()) {
825 f_out_ << "{ ";
826 vector<t_const_value*> list_elems = tvalue->get_list();
827 ;
828 vector<t_const_value*>::iterator list_iter;
829 for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
830 if (!first) {
831 f_out_ << ", ";
832 }
833 first = false;
834 print_const_value(((t_set*)truetype)->get_elem_type(), *list_iter);
835 }
836 f_out_ << " }";
837 } else {
838 f_out_ << "UNKNOWN TYPE";
839 }
840}
841
842/**
843 * Prints out documentation for arguments/exceptions of a function, if any documentation has been
844 * supplied.
845 */
846void t_html_generator::print_fn_args_doc(t_function* tfunction) {
847 bool has_docs = false;
848 vector<t_field*> args = tfunction->get_arglist()->get_members();
849 vector<t_field*>::iterator arg_iter = args.begin();
850 if (arg_iter != args.end()) {
851 for (; arg_iter != args.end(); arg_iter++) {
852 if ((*arg_iter)->has_doc() && !(*arg_iter)->get_doc().empty())
853 has_docs = true;
854 }
855 if (has_docs) {
856 arg_iter = args.begin();
857 f_out_ << "<br/><h4 id=\"Parameters_" << service_name_ << "_" << tfunction->get_name()
858 << "\">Parameters</h4>" << endl;
859 f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
860 f_out_ << "<thead><tr><th>Name</th><th>Description</th></tr></thead><tbody>";
861 for (; arg_iter != args.end(); arg_iter++) {
862 f_out_ << "<tr><td>" << (*arg_iter)->get_name();
863 f_out_ << "</td><td>";
864 f_out_ << escape_html((*arg_iter)->get_doc());
865 f_out_ << "</td></tr>" << endl;
866 }
867 f_out_ << "</tbody></table>";
868 }
869 }
870
871 has_docs = false;
872 vector<t_field*> excepts = tfunction->get_xceptions()->get_members();
873 vector<t_field*>::iterator ex_iter = excepts.begin();
874 if (ex_iter != excepts.end()) {
875 for (; ex_iter != excepts.end(); ex_iter++) {
876 if ((*ex_iter)->has_doc() && !(*ex_iter)->get_doc().empty())
877 has_docs = true;
878 }
879 if (has_docs) {
880 ex_iter = excepts.begin();
881 f_out_ << "<br/><h4 id=\"Exceptions_" << service_name_ << "_" << tfunction->get_name()
882 << "\">Exceptions</h4>" << endl;
883 f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
884 f_out_ << "<thead><tr><th>Type</th><th>Description</th></tr></thead><tbody>";
885 for (; ex_iter != excepts.end(); ex_iter++) {
886 f_out_ << "<tr><td>" << (*ex_iter)->get_type()->get_name();
887 f_out_ << "</td><td>";
888 f_out_ << escape_html((*ex_iter)->get_doc());
889 f_out_ << "</td></tr>" << endl;
890 }
891 f_out_ << "</tbody></table>";
892 }
893 }
894}
895
896/**
897 * Generates a typedef.
898 *
899 * @param ttypedef The type definition
900 */
901void t_html_generator::generate_typedef(t_typedef* ttypedef) {
902 string name = ttypedef->get_name();
903 f_out_ << "<div class=\"definition\">";
904 f_out_ << "<h3 id=\"Typedef_" << name << "\">Typedef: " << name << "</h3>" << endl;
905 f_out_ << "<p><strong>Base type:</strong>&nbsp;";
906 print_type(ttypedef->get_type());
907 f_out_ << "</p>" << endl;
908 print_doc(ttypedef);
909 f_out_ << "</div>" << endl;
910}
911
912/**
913 * Generates code for an enumerated type.
914 *
915 * @param tenum The enumeration
916 */
917void t_html_generator::generate_enum(t_enum* tenum) {
918 string name = tenum->get_name();
919 f_out_ << "<div class=\"definition\">";
920 f_out_ << "<h3 id=\"Enum_" << name << "\">Enumeration: " << name << "</h3>" << endl;
921 print_doc(tenum);
922 vector<t_enum_value*> values = tenum->get_constants();
923 vector<t_enum_value*>::iterator val_iter;
924 f_out_ << "<br/><table class=\"table-bordered table-striped table-condensed\">" << endl;
925 for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
926 f_out_ << "<tr><td><code>";
927 f_out_ << (*val_iter)->get_name();
928 f_out_ << "</code></td><td><code>";
929 f_out_ << (*val_iter)->get_value();
930 f_out_ << "</code></td><td>" << endl;
931 print_doc((*val_iter));
932 f_out_ << "</td></tr>" << endl;
933 }
934 f_out_ << "</table></div>" << endl;
935}
936
937/**
938 * Generates a constant value
939 */
940void t_html_generator::generate_const(t_const* tconst) {
941 string name = tconst->get_name();
942 f_out_ << "<tr id=\"Const_" << name << "\"><td><code>" << name << "</code></td><td>";
943 print_type(tconst->get_type());
944 f_out_ << "</td><td><code>";
945 print_const_value(tconst->get_type(), tconst->get_value());
946 f_out_ << "</code></td></tr>";
947 if (tconst->has_doc()) {
948 f_out_ << "<tr><td colspan=\"3\"><blockquote>";
949 print_doc(tconst);
950 f_out_ << "</blockquote></td></tr>";
951 }
952}
953
954/**
955 * Generates a struct definition for a thrift data type.
956 *
957 * @param tstruct The struct definition
958 */
959void t_html_generator::generate_struct(t_struct* tstruct) {
960 string name = tstruct->get_name();
961 f_out_ << "<div class=\"definition\">";
962 f_out_ << "<h3 id=\"Struct_" << name << "\">";
963 if (tstruct->is_xception()) {
964 f_out_ << "Exception: ";
965 } else if (tstruct->is_union()) {
966 f_out_ << "Union: ";
967 } else {
968 f_out_ << "Struct: ";
969 }
970 f_out_ << name << "</h3>" << endl;
971 vector<t_field*> members = tstruct->get_members();
972 vector<t_field*>::iterator mem_iter = members.begin();
973 f_out_ << "<table class=\"table-bordered table-striped table-condensed\">";
974 f_out_ << "<thead><tr><th>Key</th><th>Field</th><th>Type</th><th>Description</th><th>Requiredness</"
975 "th><th>Default value</th></tr></thead><tbody>" << endl;
976 for (; mem_iter != members.end(); mem_iter++) {
977 f_out_ << "<tr><td>" << (*mem_iter)->get_key() << "</td><td>";
978 f_out_ << (*mem_iter)->get_name();
979 f_out_ << "</td><td>";
980 print_type((*mem_iter)->get_type());
981 f_out_ << "</td><td>";
982 f_out_ << escape_html((*mem_iter)->get_doc());
983 f_out_ << "</td><td>";
984 if ((*mem_iter)->get_req() == t_field::T_OPTIONAL) {
985 f_out_ << "optional";
986 } else if ((*mem_iter)->get_req() == t_field::T_REQUIRED) {
987 f_out_ << "required";
988 } else {
989 f_out_ << "default";
990 }
991 f_out_ << "</td><td>";
992 t_const_value* default_val = (*mem_iter)->get_value();
993 if (default_val != NULL) {
994 f_out_ << "<code>";
995 print_const_value((*mem_iter)->get_type(), default_val);
996 f_out_ << "</code>";
997 }
998 f_out_ << "</td></tr>" << endl;
999 }
1000 f_out_ << "</tbody></table><br/>";
1001 print_doc(tstruct);
1002 f_out_ << "</div>";
1003}
1004
1005/**
1006 * Exceptions are special structs
1007 *
1008 * @param tstruct The struct definition
1009 */
1010void t_html_generator::generate_xception(t_struct* txception) {
1011 generate_struct(txception);
1012}
1013
1014/**
1015 * Generates the HTML block for a Thrift service.
1016 *
1017 * @param tservice The service definition
1018 */
1019void t_html_generator::generate_service(t_service* tservice) {
1020 f_out_ << "<h3 id=\"Svc_" << service_name_ << "\">Service: " << service_name_ << "</h3>" << endl;
1021
1022 if (tservice->get_extends()) {
1023 f_out_ << "<div class=\"extends\"><em>extends</em> ";
1024 print_type(tservice->get_extends());
1025 f_out_ << "</div>\n";
1026 }
1027 print_doc(tservice);
1028 vector<t_function*> functions = tservice->get_functions();
1029 vector<t_function*>::iterator fn_iter = functions.begin();
1030 for (; fn_iter != functions.end(); fn_iter++) {
1031 string fn_name = (*fn_iter)->get_name();
1032 f_out_ << "<div class=\"definition\">";
1033 f_out_ << "<h4 id=\"Fn_" << service_name_ << "_" << fn_name << "\">Function: " << service_name_
1034 << "." << fn_name << "</h4>" << endl;
1035 f_out_ << "<pre>";
1036 std::string::size_type offset = print_type((*fn_iter)->get_returntype());
1037 bool first = true;
1038 f_out_ << " " << fn_name << "(";
1039 offset += fn_name.size() + 2;
1040 vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
1041 vector<t_field*>::iterator arg_iter = args.begin();
1042 for (; arg_iter != args.end(); arg_iter++) {
1043 if (!first) {
1044 f_out_ << "," << endl;
1045 for (std::string::size_type i = 0; i < offset; ++i) {
1046 f_out_ << " ";
1047 }
1048 }
1049 first = false;
1050 print_type((*arg_iter)->get_type());
1051 f_out_ << " " << (*arg_iter)->get_name();
1052 if ((*arg_iter)->get_value() != NULL) {
1053 f_out_ << " = ";
1054 print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value());
1055 }
1056 }
1057 f_out_ << ")" << endl;
1058 first = true;
1059 vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members();
1060 vector<t_field*>::iterator ex_iter = excepts.begin();
1061 if (ex_iter != excepts.end()) {
1062 f_out_ << " throws ";
1063 for (; ex_iter != excepts.end(); ex_iter++) {
1064 if (!first) {
1065 f_out_ << ", ";
1066 }
1067 first = false;
1068 print_type((*ex_iter)->get_type());
1069 }
1070 f_out_ << endl;
1071 }
1072 f_out_ << "</pre>";
1073 print_doc(*fn_iter);
1074 print_fn_args_doc(*fn_iter);
1075 f_out_ << "</div>";
1076 }
1077}
1078
1079THRIFT_REGISTER_GENERATOR(
1080 html,
1081 "HTML",
1082 " standalone: Self-contained mode, includes all CSS in the HTML files.\n"
1083 " Generates no style.css file, but HTML files will be larger.\n"
1084 " noescape: Do not escape html in doc text.\n")