]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/thrift/compiler/cpp/src/thrift/generate/t_gv_generator.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / compiler / cpp / src / thrift / generate / t_gv_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#include <list>
26
27#include <stdlib.h>
28#include <sys/stat.h>
29#include <sstream>
30#include "thrift/platform.h"
31#include "thrift/generate/t_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
43/**
44 * Graphviz code generator
45 */
46class t_gv_generator : public t_generator {
47public:
48 t_gv_generator(t_program* program,
49 const std::map<std::string, std::string>& parsed_options,
50 const std::string& option_string)
51 : t_generator(program) {
52 (void)option_string;
53 std::map<std::string, std::string>::const_iterator iter;
54
55 exception_arrows = false;
56 for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
57 if( iter->first.compare("exceptions") == 0) {
58 exception_arrows = true;
59 } else {
60 throw "unknown option gv:" + iter->first;
61 }
62 }
63
64 out_dir_base_ = "gen-gv";
65 }
66
67 /**
68 * Init and end of generator
69 */
70 void init_generator() override;
71 void close_generator() override;
72
73 /**
74 * Program-level generation functions
75 */
76 void generate_typedef(t_typedef* ttypedef) override;
77 void generate_enum(t_enum* tenum) override;
78 void generate_const(t_const* tconst) override;
79 void generate_struct(t_struct* tstruct) override;
80 void generate_service(t_service* tservice) override;
81
82protected:
83 /**
84 * Helpers
85 */
86 void print_type(t_type* ttype, string struct_field_ref);
87 void print_const_value(t_type* type, t_const_value* tvalue);
88
89private:
90 ofstream_with_content_based_conditional_update f_out_;
91 std::list<string> edges;
92 bool exception_arrows;
93};
94
95/**
96 * Init generator:
97 * - Adds some escaping for the Graphviz domain.
98 * - Create output directory and open file for writting.
99 * - Write the file header.
100 */
101void t_gv_generator::init_generator() {
102 escape_['{'] = "\\{";
103 escape_['}'] = "\\}";
104
105 // Make output directory
106 MKDIR(get_out_dir().c_str());
107 string fname = get_out_dir() + program_->get_name() + ".gv";
108 f_out_.open(fname.c_str());
109 f_out_ << "digraph \"" << escape_string(program_name_) << "\" {" << endl;
110 f_out_ << "node [style=filled, shape=record];" << endl;
111 f_out_ << "edge [arrowsize=0.5];" << endl;
112 f_out_ << "rankdir=LR" << endl;
113}
114
115/**
116 * Closes generator:
117 * - Print accumulated nodes connections.
118 * - Print footnote.
119 * - Closes file.
120 */
121void t_gv_generator::close_generator() {
122 // Print edges
123 std::list<string>::iterator iter = edges.begin();
124 for (; iter != edges.end(); iter++) {
125 f_out_ << (*iter) << endl;
126 }
127
128 // Print graph end } and close file
129 f_out_ << "}" << endl;
130 f_out_.close();
131}
132
133void t_gv_generator::generate_typedef(t_typedef* ttypedef) {
134 string name = ttypedef->get_name();
135 f_out_ << "node [fillcolor=azure];" << endl;
136 f_out_ << name << " [label=\"";
137
138 f_out_ << escape_string(name);
139 f_out_ << " :: ";
140 print_type(ttypedef->get_type(), name);
141
142 f_out_ << "\"];" << endl;
143}
144
145void t_gv_generator::generate_enum(t_enum* tenum) {
146 string name = tenum->get_name();
147 f_out_ << "node [fillcolor=white];" << endl;
148 f_out_ << name << " [label=\"enum " << escape_string(name);
149
150 vector<t_enum_value*> values = tenum->get_constants();
151 vector<t_enum_value*>::iterator val_iter;
152 for (val_iter = values.begin(); val_iter != values.end(); ++val_iter) {
153 f_out_ << '|' << (*val_iter)->get_name();
154 f_out_ << " = ";
155 f_out_ << (*val_iter)->get_value();
156 }
157
158 f_out_ << "\"];" << endl;
159}
160
161void t_gv_generator::generate_const(t_const* tconst) {
162 string name = tconst->get_name();
163
164 f_out_ << "node [fillcolor=aliceblue];" << endl;
165 f_out_ << "const_" << name << " [label=\"";
166
167 f_out_ << escape_string(name);
168 f_out_ << " = ";
169 print_const_value(tconst->get_type(), tconst->get_value());
170 f_out_ << " :: ";
171 print_type(tconst->get_type(), "const_" + name);
172
173 f_out_ << "\"];" << endl;
174}
175
176void t_gv_generator::generate_struct(t_struct* tstruct) {
177 string name = tstruct->get_name();
178
179 if (tstruct->is_xception()) {
180 f_out_ << "node [fillcolor=lightpink];" << endl;
181 f_out_ << name << " [label=\"";
182 f_out_ << "exception " << escape_string(name);
183 } else if (tstruct->is_union()) {
184 f_out_ << "node [fillcolor=lightcyan];" << endl;
185 f_out_ << name << " [label=\"";
186 f_out_ << "union " << escape_string(name);
187 } else {
188 f_out_ << "node [fillcolor=beige];" << endl;
189 f_out_ << name << " [label=\"";
190 f_out_ << "struct " << escape_string(name);
191 }
192
193 vector<t_field*> members = tstruct->get_members();
194 vector<t_field*>::iterator mem_iter = members.begin();
195 for (; mem_iter != members.end(); mem_iter++) {
196 string field_name = (*mem_iter)->get_name();
197
198 // print port (anchor reference)
199 f_out_ << "|<field_" << field_name << '>';
200
201 // field name :: field type
202 f_out_ << (*mem_iter)->get_name();
203 f_out_ << " :: ";
204 print_type((*mem_iter)->get_type(), name + ":field_" + field_name);
205 }
206
207 f_out_ << "\"];" << endl;
208}
209
210void t_gv_generator::print_type(t_type* ttype, string struct_field_ref) {
211 if (ttype->is_container()) {
212 if (ttype->is_list()) {
213 f_out_ << "list\\<";
214 print_type(((t_list*)ttype)->get_elem_type(), struct_field_ref);
215 f_out_ << "\\>";
216 } else if (ttype->is_set()) {
217 f_out_ << "set\\<";
218 print_type(((t_set*)ttype)->get_elem_type(), struct_field_ref);
219 f_out_ << "\\>";
220 } else if (ttype->is_map()) {
221 f_out_ << "map\\<";
222 print_type(((t_map*)ttype)->get_key_type(), struct_field_ref);
223 f_out_ << ", ";
224 print_type(((t_map*)ttype)->get_val_type(), struct_field_ref);
225 f_out_ << "\\>";
226 }
227 } else if (ttype->is_base_type()) {
228 f_out_ << (ttype->is_binary() ? "binary" : ttype->get_name());
229 } else {
230 f_out_ << ttype->get_name();
231 edges.push_back(struct_field_ref + " -> " + ttype->get_name());
232 }
233}
234
235/**
236 * Prints out an string representation of the provided constant value
237 */
238void t_gv_generator::print_const_value(t_type* type, t_const_value* tvalue) {
239 bool first = true;
240 switch (tvalue->get_type()) {
241 case t_const_value::CV_INTEGER:
242 f_out_ << tvalue->get_integer();
243 break;
244 case t_const_value::CV_DOUBLE:
245 f_out_ << tvalue->get_double();
246 break;
247 case t_const_value::CV_STRING:
248 f_out_ << "\\\"" << get_escaped_string(tvalue) << "\\\"";
249 break;
250 case t_const_value::CV_MAP: {
251 f_out_ << "\\{ ";
252 map<t_const_value*, t_const_value*, t_const_value::value_compare> map_elems = tvalue->get_map();
253 map<t_const_value*, t_const_value*, t_const_value::value_compare>::iterator map_iter;
254 for (map_iter = map_elems.begin(); map_iter != map_elems.end(); map_iter++) {
255 if (!first) {
256 f_out_ << ", ";
257 }
258 first = false;
259 print_const_value(((t_map*)type)->get_key_type(), map_iter->first);
260 f_out_ << " = ";
261 print_const_value(((t_map*)type)->get_val_type(), map_iter->second);
262 }
263 f_out_ << " \\}";
264 } break;
265 case t_const_value::CV_LIST: {
266 f_out_ << "\\{ ";
267 vector<t_const_value*> list_elems = tvalue->get_list();
268 ;
269 vector<t_const_value*>::iterator list_iter;
270 for (list_iter = list_elems.begin(); list_iter != list_elems.end(); list_iter++) {
271 if (!first) {
272 f_out_ << ", ";
273 }
274 first = false;
275 if (type->is_list()) {
276 print_const_value(((t_list*)type)->get_elem_type(), *list_iter);
277 } else {
278 print_const_value(((t_set*)type)->get_elem_type(), *list_iter);
279 }
280 }
281 f_out_ << " \\}";
282 } break;
283 case t_const_value::CV_IDENTIFIER:
284 f_out_ << escape_string(type->get_name()) << "."
285 << escape_string(tvalue->get_identifier_name());
286 break;
287 default:
288 f_out_ << "UNKNOWN";
289 break;
290 }
291}
292
293void t_gv_generator::generate_service(t_service* tservice) {
294 string service_name = get_service_name(tservice);
295 f_out_ << "subgraph cluster_" << service_name << " {" << endl;
296 f_out_ << "node [fillcolor=bisque];" << endl;
297 f_out_ << "style=dashed;" << endl;
298 f_out_ << "label = \"" << escape_string(service_name) << " service\";" << endl;
299
300 // TODO: service extends
301
302 vector<t_function*> functions = tservice->get_functions();
303 vector<t_function*>::iterator fn_iter = functions.begin();
304 for (; fn_iter != functions.end(); fn_iter++) {
305 string fn_name = (*fn_iter)->get_name();
306
307 f_out_ << "function_" << service_name << fn_name;
308 f_out_ << "[label=\"<return_type>function " << escape_string(fn_name);
309 f_out_ << " :: ";
310 print_type((*fn_iter)->get_returntype(), "function_" + service_name + fn_name + ":return_type");
311
312 vector<t_field*> args = (*fn_iter)->get_arglist()->get_members();
313 vector<t_field*>::iterator arg_iter = args.begin();
314 for (; arg_iter != args.end(); arg_iter++) {
315 f_out_ << "|<param_" << (*arg_iter)->get_name() << ">";
316 f_out_ << (*arg_iter)->get_name();
317 if ((*arg_iter)->get_value() != NULL) {
318 f_out_ << " = ";
319 print_const_value((*arg_iter)->get_type(), (*arg_iter)->get_value());
320 }
321 f_out_ << " :: ";
322 print_type((*arg_iter)->get_type(),
323 "function_" + service_name + fn_name + ":param_" + (*arg_iter)->get_name());
324 }
325 // end of node
326 f_out_ << "\"];" << endl;
327
328 // Exception edges
329 if (exception_arrows) {
330 vector<t_field*> excepts = (*fn_iter)->get_xceptions()->get_members();
331 vector<t_field*>::iterator ex_iter = excepts.begin();
332 for (; ex_iter != excepts.end(); ex_iter++) {
333 edges.push_back("function_" + service_name + fn_name + " -> "
334 + (*ex_iter)->get_type()->get_name() + " [color=red]");
335 }
336 }
337 }
338
339 f_out_ << " }" << endl;
340}
341
342THRIFT_REGISTER_GENERATOR(
343 gv,
344 "Graphviz",
345 " exceptions: Whether to draw arrows from functions to exception.\n")