]>
Commit | Line | Data |
---|---|---|
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 | ||
33 | using std::map; | |
34 | using std::ofstream; | |
35 | using std::ostringstream; | |
36 | using std::pair; | |
37 | using std::string; | |
38 | using std::stringstream; | |
39 | using std::vector; | |
40 | ||
41 | static const string endl = "\n"; // avoid ostream << std::endl flushes | |
42 | ||
43 | /** | |
44 | * Graphviz code generator | |
45 | */ | |
46 | class t_gv_generator : public t_generator { | |
47 | public: | |
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 | ||
82 | protected: | |
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 | ||
89 | private: | |
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 | */ | |
101 | void 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 | */ | |
121 | void 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 | ||
133 | void 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 | ||
145 | void 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 | ||
161 | void 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 | ||
176 | void 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 | ||
210 | void 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 | */ | |
238 | void 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 | ||
293 | void 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 | ||
342 | THRIFT_REGISTER_GENERATOR( | |
343 | gv, | |
344 | "Graphviz", | |
345 | " exceptions: Whether to draw arrows from functions to exception.\n") |