diff options
Diffstat (limited to 'compiler/cpp/src/generate/t_html_generator.cc')
-rw-r--r-- | compiler/cpp/src/generate/t_html_generator.cc | 637 |
1 files changed, 637 insertions, 0 deletions
diff --git a/compiler/cpp/src/generate/t_html_generator.cc b/compiler/cpp/src/generate/t_html_generator.cc new file mode 100644 index 000000000..ad1c4cbcf --- /dev/null +++ b/compiler/cpp/src/generate/t_html_generator.cc @@ -0,0 +1,637 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <string> +#include <fstream> +#include <iostream> +#include <vector> +#include <map> + +#include <stdlib.h> +#include <sys/stat.h> +#include <sstream> +#include "t_generator.h" +#include "platform.h" +using namespace std; + + +/** + * HTML code generator + * + * mostly copy/pasting/tweaking from mcslee's work. + */ +class t_html_generator : public t_generator { + public: + t_html_generator( + t_program* program, + const std::map<std::string, std::string>& parsed_options, + const std::string& option_string) + : t_generator(program) + { + out_dir_base_ = "gen-html"; + escape_.clear(); + escape_['&'] = "&"; + escape_['<'] = "<"; + escape_['>'] = ">"; + escape_['"'] = """; + escape_['\''] = "'"; + } + + void generate_program(); + void generate_program_toc(); + void generate_program_toc_row(t_program* tprog); + void generate_program_toc_rows(t_program* tprog, + std::vector<t_program*>& finished); + void generate_index(); + void generate_css(); + + /** + * Program-level generation functions + */ + + void generate_typedef (t_typedef* ttypedef); + void generate_enum (t_enum* tenum); + void generate_const (t_const* tconst); + void generate_struct (t_struct* tstruct); + void generate_service (t_service* tservice); + void generate_xception(t_struct* txception); + + void print_doc (t_doc* tdoc); + int print_type (t_type* ttype); + void print_const_value(t_const_value* tvalue); + + std::ofstream f_out_; +}; + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_html_generator::generate_program_toc() { + f_out_ << "<table><tr><th>Module</th><th>Services</th>" + << "<th>Data types</th><th>Constants</th></tr>" << endl; + generate_program_toc_row(program_); + f_out_ << "</table>" << endl; +} + + +/** + * Recurses through from the provided program and generates a ToC row + * for each discovered program exactly once by maintaining the list of + * completed rows in 'finished' + */ +void t_html_generator::generate_program_toc_rows(t_program* tprog, + std::vector<t_program*>& finished) { + for (vector<t_program*>::iterator iter = finished.begin(); + iter != finished.end(); iter++) { + if (tprog->get_path() == (*iter)->get_path()) { + return; + } + } + finished.push_back(tprog); + generate_program_toc_row(tprog); + vector<t_program*> includes = tprog->get_includes(); + for (vector<t_program*>::iterator iter = includes.begin(); + iter != includes.end(); iter++) { + generate_program_toc_rows(*iter, finished); + } +} + +/** + * Emits the Table of Contents links at the top of the module's page + */ +void t_html_generator::generate_program_toc_row(t_program* tprog) { + string fname = tprog->get_name() + ".html"; + f_out_ << "<tr>" << endl << "<td>" << tprog->get_name() << "</td><td>"; + if (!tprog->get_services().empty()) { + vector<t_service*> services = tprog->get_services(); + vector<t_service*>::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + string name = get_service_name(*sv_iter); + f_out_ << "<a href=\"" << fname << "#Svc_" << name << "\">" << name + << "</a><br/>" << endl; + f_out_ << "<ul>" << endl; + map<string,string> fn_html; + vector<t_function*> functions = (*sv_iter)->get_functions(); + vector<t_function*>::iterator fn_iter; + for (fn_iter = functions.begin(); fn_iter != functions.end(); ++fn_iter) { + string fn_name = (*fn_iter)->get_name(); + string html = "<li><a href=\"" + fname + "#Fn_" + name + "_" + + fn_name + "\">" + fn_name + "</a></li>"; + fn_html.insert(pair<string,string>(fn_name, html)); + } + for (map<string,string>::iterator html_iter = fn_html.begin(); + html_iter != fn_html.end(); html_iter++) { + f_out_ << html_iter->second << endl; + } + f_out_ << "</ul>" << endl; + } + } + f_out_ << "</td>" << endl << "<td>"; + map<string,string> data_types; + if (!tprog->get_enums().empty()) { + vector<t_enum*> enums = tprog->get_enums(); + vector<t_enum*>::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + string name = (*en_iter)->get_name(); + // f_out_ << "<a href=\"" << fname << "#Enum_" << name << "\">" << name + // << "</a><br/>" << endl; + string html = "<a href=\"" + fname + "#Enum_" + name + "\">" + name + + "</a>"; + data_types.insert(pair<string,string>(name, html)); + } + } + if (!tprog->get_typedefs().empty()) { + vector<t_typedef*> typedefs = tprog->get_typedefs(); + vector<t_typedef*>::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + string name = (*td_iter)->get_symbolic(); + // f_out_ << "<a href=\"" << fname << "#Typedef_" << name << "\">" << name + // << "</a><br/>" << endl; + string html = "<a href=\"" + fname + "#Typedef_" + name + "\">" + name + + "</a>"; + data_types.insert(pair<string,string>(name, html)); + } + } + if (!tprog->get_objects().empty()) { + vector<t_struct*> objects = tprog->get_objects(); + vector<t_struct*>::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + string name = (*o_iter)->get_name(); + //f_out_ << "<a href=\"" << fname << "#Struct_" << name << "\">" << name + //<< "</a><br/>" << endl; + string html = "<a href=\"" + fname + "#Struct_" + name + "\">" + name + + "</a>"; + data_types.insert(pair<string,string>(name, html)); + } + } + for (map<string,string>::iterator dt_iter = data_types.begin(); + dt_iter != data_types.end(); dt_iter++) { + f_out_ << dt_iter->second << "<br/>" << endl; + } + f_out_ << "</td>" << endl << "<td><code>"; + if (!tprog->get_consts().empty()) { + map<string,string> const_html; + vector<t_const*> consts = tprog->get_consts(); + vector<t_const*>::iterator con_iter; + for (con_iter = consts.begin(); con_iter != consts.end(); ++con_iter) { + string name = (*con_iter)->get_name(); + string html ="<a href=\"" + fname + "#Const_" + name + + "\">" + name + "</a>"; + const_html.insert(pair<string,string>(name, html)); + } + for (map<string,string>::iterator con_iter = const_html.begin(); + con_iter != const_html.end(); con_iter++) { + f_out_ << con_iter->second << "<br/>" << endl; + } + } + f_out_ << "</code></td>" << endl << "</tr>"; +} + +/** + * Prepares for file generation by opening up the necessary file output + * stream. + */ +void t_html_generator::generate_program() { + // Make output directory + MKDIR(get_out_dir().c_str()); + string fname = get_out_dir() + program_->get_name() + ".html"; + f_out_.open(fname.c_str()); + f_out_ << "<html><head>" << endl; + f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>" + << endl; + f_out_ << "<title>Thrift module: " << program_->get_name() + << "</title></head><body>" << endl << "<h1>Thrift module: " + << program_->get_name() << "</h1>" << endl; + + print_doc(program_); + + generate_program_toc(); + + if (!program_->get_consts().empty()) { + f_out_ << "<hr/><h2 id=\"Constants\">Constants</h2>" << endl; + vector<t_const*> consts = program_->get_consts(); + f_out_ << "<table>"; + f_out_ << "<tr><th>Constant</th><th>Type</th><th>Value</th></tr>" << endl; + generate_consts(consts); + f_out_ << "</table>"; + } + + if (!program_->get_enums().empty()) { + f_out_ << "<hr/><h2 id=\"Enumerations\">Enumerations</h2>" << endl; + // Generate enums + vector<t_enum*> enums = program_->get_enums(); + vector<t_enum*>::iterator en_iter; + for (en_iter = enums.begin(); en_iter != enums.end(); ++en_iter) { + generate_enum(*en_iter); + } + } + + if (!program_->get_typedefs().empty()) { + f_out_ << "<hr/><h2 id=\"Typedefs\">Type declarations</h2>" << endl; + // Generate typedefs + vector<t_typedef*> typedefs = program_->get_typedefs(); + vector<t_typedef*>::iterator td_iter; + for (td_iter = typedefs.begin(); td_iter != typedefs.end(); ++td_iter) { + generate_typedef(*td_iter); + } + } + + if (!program_->get_objects().empty()) { + f_out_ << "<hr/><h2 id=\"Structs\">Data structures</h2>" << endl; + // Generate structs and exceptions in declared order + vector<t_struct*> objects = program_->get_objects(); + vector<t_struct*>::iterator o_iter; + for (o_iter = objects.begin(); o_iter != objects.end(); ++o_iter) { + if ((*o_iter)->is_xception()) { + generate_xception(*o_iter); + } else { + generate_struct(*o_iter); + } + } + } + + if (!program_->get_services().empty()) { + f_out_ << "<hr/><h2 id=\"Services\">Services</h2>" << endl; + // Generate services + vector<t_service*> services = program_->get_services(); + vector<t_service*>::iterator sv_iter; + for (sv_iter = services.begin(); sv_iter != services.end(); ++sv_iter) { + service_name_ = get_service_name(*sv_iter); + generate_service(*sv_iter); + } + } + + f_out_ << "</body></html>" << endl; + f_out_.close(); + + generate_index(); + generate_css(); +} + +/** + * Emits the index.html file for the recursive set of Thrift programs + */ +void t_html_generator::generate_index() { + string index_fname = get_out_dir() + "index.html"; + f_out_.open(index_fname.c_str()); + f_out_ << "<html><head>" << endl; + f_out_ << "<link href=\"style.css\" rel=\"stylesheet\" type=\"text/css\"/>" + << endl; + f_out_ << "<title>All Thrift declarations</title></head><body>" + << endl << "<h1>All Thrift declarations</h1>" << endl; + f_out_ << "<table><tr><th>Module</th><th>Services</th><th>Data types</th>" + << "<th>Constants</th></tr>" << endl; + vector<t_program*> programs; + generate_program_toc_rows(program_, programs); + f_out_ << "</table>" << endl; + f_out_ << "</body></html>" << endl; + f_out_.close(); +} + +void t_html_generator::generate_css() { + string css_fname = get_out_dir() + "style.css"; + f_out_.open(css_fname.c_str()); + f_out_ << "/* Auto-generated CSS for generated Thrift docs */" << endl; + f_out_ << + "body { font-family: Tahoma, sans-serif; }" << endl; + f_out_ << + "pre { background-color: #dddddd; padding: 6px; }" << endl; + f_out_ << + "h3,h4 { padding-top: 0px; margin-top: 0px; }" << endl; + f_out_ << + "div.definition { border: 1px solid gray; margin: 10px; padding: 10px; }" << endl; + f_out_ << + "div.extends { margin: -0.5em 0 1em 5em }" << endl; + f_out_ << + "table { border: 1px solid grey; border-collapse: collapse; }" << endl; + f_out_ << + "td { border: 1px solid grey; padding: 1px 6px; vertical-align: top; }" << endl; + f_out_ << + "th { border: 1px solid black; background-color: #bbbbbb;" << endl << + " text-align: left; padding: 1px 6px; }" << endl; + f_out_.close(); +} + +/** + * If the provided documentable object has documentation attached, this + * will emit it to the output stream in HTML format. + */ +void t_html_generator::print_doc(t_doc* tdoc) { + if (tdoc->has_doc()) { + string doc = tdoc->get_doc(); + size_t index; + while ((index = doc.find_first_of("\r\n")) != string::npos) { + if (index == 0) { + f_out_ << "<p/>" << endl; + } else { + f_out_ << doc.substr(0, index) << endl; + } + if (index + 1 < doc.size() && doc.at(index) != doc.at(index + 1) && + (doc.at(index + 1) == '\r' || doc.at(index + 1) == '\n')) { + index++; + } + doc = doc.substr(index + 1); + } + f_out_ << doc << "<br/>"; + } +} + +/** + * Prints out the provided type in HTML + */ +int t_html_generator::print_type(t_type* ttype) { + int len = 0; + f_out_ << "<code>"; + if (ttype->is_container()) { + if (ttype->is_list()) { + f_out_ << "list<"; + len = 6 + print_type(((t_list*)ttype)->get_elem_type()); + f_out_ << ">"; + } else if (ttype->is_set()) { + f_out_ << "set<"; + len = 5 + print_type(((t_set*)ttype)->get_elem_type()); + f_out_ << ">"; + } else if (ttype->is_map()) { + f_out_ << "map<"; + len = 5 + print_type(((t_map*)ttype)->get_key_type()); + f_out_ << ", "; + len += print_type(((t_map*)ttype)->get_val_type()); + f_out_ << ">"; + } + } else if (ttype->is_base_type()) { + f_out_ << ttype->get_name(); + len = ttype->get_name().size(); + } else { + string prog_name = ttype->get_program()->get_name(); + string type_name = ttype->get_name(); + f_out_ << "<a href=\"" << prog_name << ".html#"; + if (ttype->is_typedef()) { + f_out_ << "Typedef_"; + } else if (ttype->is_struct() || ttype->is_xception()) { + f_out_ << "Struct_"; + } else if (ttype->is_enum()) { + f_out_ << "Enum_"; + } else if (ttype->is_service()) { + f_out_ << "Svc_"; + } + f_out_ << type_name << "\">"; + len = type_name.size(); + if (ttype->get_program() != program_) { + f_out_ << prog_name << "."; + len += prog_name.size() + 1; + } + f_out_ << type_name << "</a>"; + } + f_out_ << "</code>"; + return len; +} + +/** + * Prints out an HTML representation of the provided constant value + */ +void t_html_generator::print_const_value(t_const_value* tvalue) { + bool first = true; + switch (tvalue->get_type()) { + case t_const_value::CV_INTEGER: + f_out_ << tvalue->get_integer(); + break; + case t_const_value::CV_DOUBLE: + f_out_ << tvalue->get_double(); + break; + case t_const_value::CV_STRING: + f_out_ << '"' << get_escaped_string(tvalue) << '"'; + break; + case t_const_value::CV_MAP: + { + f_out_ << "{ "; + map<t_const_value*, t_const_value*> map_elems = tvalue->get_map(); + map<t_const_value*, t_const_value*>::iterator map_iter; + for (map_iter = map_elems.begin(); map_iter != map_elems.end(); + map_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_const_value(map_iter->first); + f_out_ << " = "; + print_const_value(map_iter->second); + } + f_out_ << " }"; + } + break; + case t_const_value::CV_LIST: + { + f_out_ << "{ "; + vector<t_const_value*> list_elems = tvalue->get_list();; + vector<t_const_value*>::iterator list_iter; + for (list_iter = list_elems.begin(); list_iter != list_elems.end(); + list_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_const_value(*list_iter); + } + f_out_ << " }"; + } + break; + default: + f_out_ << "UNKNOWN"; + break; + } +} + +/** + * Generates a typedef. + * + * @param ttypedef The type definition + */ +void t_html_generator::generate_typedef(t_typedef* ttypedef) { + string name = ttypedef->get_name(); + f_out_ << "<div class=\"definition\">"; + f_out_ << "<h3 id=\"Typedef_" << name << "\">Typedef: " << name + << "</h3>" << endl; + f_out_ << "<p><strong>Base type:</strong> "; + print_type(ttypedef->get_type()); + f_out_ << "</p>" << endl; + print_doc(ttypedef); + f_out_ << "</div>" << endl; +} + +/** + * Generates code for an enumerated type. + * + * @param tenum The enumeration + */ +void t_html_generator::generate_enum(t_enum* tenum) { + string name = tenum->get_name(); + f_out_ << "<div class=\"definition\">"; + f_out_ << "<h3 id=\"Enum_" << name << "\">Enumeration: " << name + << "</h3>" << endl; + print_doc(tenum); + vector<t_enum_value*> values = tenum->get_constants(); + vector<t_enum_value*>::iterator val_iter; + f_out_ << "<br/><table>" << endl; + for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) { + f_out_ << "<tr><td><code>"; + f_out_ << (*val_iter)->get_name(); + f_out_ << "</code></td><td><code>"; + f_out_ << (*val_iter)->get_value(); + f_out_ << "</code></td></tr>" << endl; + } + f_out_ << "</table></div>" << endl; +} + +/** + * Generates a constant value + */ +void t_html_generator::generate_const(t_const* tconst) { + string name = tconst->get_name(); + f_out_ << "<tr id=\"Const_" << name << "\"><td><code>" << name + << "</code></td><td><code>"; + print_type(tconst->get_type()); + f_out_ << "</code></td><td><code>"; + print_const_value(tconst->get_value()); + f_out_ << "</code></td></tr>"; + if (tconst->has_doc()) { + f_out_ << "<tr><td colspan=\"3\"><blockquote>"; + print_doc(tconst); + f_out_ << "</blockquote></td></tr>"; + } +} + +/** + * Generates a struct definition for a thrift data type. + * + * @param tstruct The struct definition + */ +void t_html_generator::generate_struct(t_struct* tstruct) { + string name = tstruct->get_name(); + f_out_ << "<div class=\"definition\">"; + f_out_ << "<h3 id=\"Struct_" << name << "\">"; + if (tstruct->is_xception()) { + f_out_ << "Exception: "; + } else { + f_out_ << "Struct: "; + } + f_out_ << name << "</h3>" << endl; + vector<t_field*> members = tstruct->get_members(); + vector<t_field*>::iterator mem_iter = members.begin(); + f_out_ << "<table>"; + f_out_ << "<tr><th>Field</th><th>Type</th><th>Required</th><th>Default value</th></tr>" + << endl; + for ( ; mem_iter != members.end(); mem_iter++) { + f_out_ << "<tr><td>" << (*mem_iter)->get_name() << "</td><td>"; + print_type((*mem_iter)->get_type()); + f_out_ << "</td><td>"; + if ((*mem_iter)->get_req() != t_field::T_OPTIONAL) { + f_out_ << "yes"; + } else { + f_out_ << "no"; + } + f_out_ << "</td><td>"; + t_const_value* default_val = (*mem_iter)->get_value(); + if (default_val != NULL) { + print_const_value(default_val); + } + f_out_ << "</td></tr>" << endl; + } + f_out_ << "</table><br/>"; + print_doc(tstruct); + f_out_ << "</div>"; +} + +/** + * Exceptions are special structs + * + * @param tstruct The struct definition + */ +void t_html_generator::generate_xception(t_struct* txception) { + generate_struct(txception); +} + +/** + * Generates the HTML block for a Thrift service. + * + * @param tservice The service definition + */ +void t_html_generator::generate_service(t_service* tservice) { + f_out_ << "<h3 id=\"Svc_" << service_name_ << "\">Service: " + << service_name_ << "</h3>" << endl; + + if (tservice->get_extends()) { + f_out_ << "<div class=\"extends\"><em>extends</em> "; + print_type(tservice->get_extends()); + f_out_ << "</div>\n"; + } + print_doc(tservice); + vector<t_function*> functions = tservice->get_functions(); + vector<t_function*>::iterator fn_iter = functions.begin(); + for ( ; fn_iter != functions.end(); fn_iter++) { + string fn_name = (*fn_iter)->get_name(); + f_out_ << "<div class=\"definition\">"; + f_out_ << "<h4 id=\"Fn_" << service_name_ << "_" << fn_name + << "\">Function: " << service_name_ << "." << fn_name + << "</h4>" << endl; + f_out_ << "<pre>"; + int offset = print_type((*fn_iter)->get_returntype()); + bool first = true; + f_out_ << " " << fn_name << "("; + offset += fn_name.size() + 2; + vector<t_field*> args = (*fn_iter)->get_arglist()->get_members(); + vector<t_field*>::iterator arg_iter = args.begin(); + if (arg_iter != args.end()) { + for ( ; arg_iter != args.end(); arg_iter++) { + if (!first) { + f_out_ << "," << endl; + for (int i = 0; i < offset; ++i) { + f_out_ << " "; + } + } + first = false; + print_type((*arg_iter)->get_type()); + f_out_ << " " << (*arg_iter)->get_name(); + if ((*arg_iter)->get_value() != NULL) { + f_out_ << " = "; + print_const_value((*arg_iter)->get_value()); + } + } + } + f_out_ << ")" << endl; + first = true; + vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members(); + vector<t_field*>::iterator ex_iter = excepts.begin(); + if (ex_iter != excepts.end()) { + f_out_ << " throws "; + for ( ; ex_iter != excepts.end(); ex_iter++) { + if (!first) { + f_out_ << ", "; + } + first = false; + print_type((*ex_iter)->get_type()); + } + f_out_ << endl; + } + f_out_ << "</pre>"; + print_doc(*fn_iter); + f_out_ << "</div>"; + } +} + +THRIFT_REGISTER_GENERATOR(html, "HTML", ""); |