]>
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 <sstream> | |
21 | #include <string> | |
22 | #include <fstream> | |
23 | #include <iostream> | |
24 | #include <vector> | |
25 | #include <cctype> | |
26 | ||
27 | #include <sys/stat.h> | |
28 | #include <stdexcept> | |
29 | ||
30 | #include "thrift/platform.h" | |
31 | #include "thrift/generate/t_oop_generator.h" | |
32 | ||
33 | using std::map; | |
34 | using std::ostream; | |
35 | using std::ostringstream; | |
36 | using std::string; | |
37 | using std::stringstream; | |
38 | using std::vector; | |
39 | ||
40 | static const string endl = "\n"; // avoid ostream << std::endl flushes | |
41 | ||
42 | /** | |
43 | * AS3 code generator. | |
44 | * | |
45 | */ | |
46 | class t_as3_generator : public t_oop_generator { | |
47 | public: | |
48 | t_as3_generator(t_program* program, | |
49 | const std::map<std::string, std::string>& parsed_options, | |
50 | const std::string& option_string) | |
51 | : t_oop_generator(program) { | |
52 | (void)option_string; | |
53 | std::map<std::string, std::string>::const_iterator iter; | |
54 | ||
55 | bindable_ = false; | |
56 | for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) { | |
57 | if( iter->first.compare("bindable") == 0) { | |
58 | bindable_ = true; | |
59 | } else { | |
60 | throw "unknown option as3:" + iter->first; | |
61 | } | |
62 | } | |
63 | ||
64 | out_dir_base_ = "gen-as3"; | |
65 | } | |
66 | ||
67 | /** | |
68 | * Init and close methods | |
69 | */ | |
70 | ||
71 | void init_generator() override; | |
72 | void close_generator() override; | |
73 | ||
74 | void generate_consts(std::vector<t_const*> consts) override; | |
75 | ||
76 | /** | |
77 | * Program-level generation functions | |
78 | */ | |
79 | ||
80 | void generate_typedef(t_typedef* ttypedef) override; | |
81 | void generate_enum(t_enum* tenum) override; | |
82 | void generate_struct(t_struct* tstruct) override; | |
83 | void generate_xception(t_struct* txception) override; | |
84 | void generate_service(t_service* tservice) override; | |
85 | ||
86 | void print_const_value(std::ostream& out, | |
87 | std::string name, | |
88 | t_type* type, | |
89 | t_const_value* value, | |
90 | bool in_static, | |
91 | bool defval = false); | |
92 | std::string render_const_value(ostream& out, | |
93 | std::string name, | |
94 | t_type* type, | |
95 | t_const_value* value); | |
96 | ||
97 | /** | |
98 | * Service-level generation functions | |
99 | */ | |
100 | ||
101 | void generate_as3_struct(t_struct* tstruct, bool is_exception); | |
102 | ||
103 | void generate_as3_struct_definition(std::ostream& out, | |
104 | t_struct* tstruct, | |
105 | bool is_xception = false, | |
106 | bool in_class = false, | |
107 | bool is_result = false); | |
108 | // removed -- equality,compare_to | |
109 | void generate_as3_struct_reader(std::ostream& out, t_struct* tstruct); | |
110 | void generate_as3_validator(std::ostream& out, t_struct* tstruct); | |
111 | void generate_as3_struct_result_writer(std::ostream& out, t_struct* tstruct); | |
112 | void generate_as3_struct_writer(std::ostream& out, t_struct* tstruct); | |
113 | void generate_as3_struct_tostring(std::ostream& out, t_struct* tstruct, bool bindable); | |
114 | void generate_as3_meta_data_map(std::ostream& out, t_struct* tstruct); | |
115 | void generate_field_value_meta_data(std::ostream& out, t_type* type); | |
116 | std::string get_as3_type_string(t_type* type); | |
117 | void generate_reflection_setters(std::ostringstream& out, | |
118 | t_type* type, | |
119 | std::string field_name, | |
120 | std::string cap_name); | |
121 | void generate_reflection_getters(std::ostringstream& out, | |
122 | t_type* type, | |
123 | std::string field_name, | |
124 | std::string cap_name); | |
125 | void generate_generic_field_getters_setters(std::ostream& out, t_struct* tstruct); | |
126 | void generate_generic_isset_method(std::ostream& out, t_struct* tstruct); | |
127 | void generate_as3_bean_boilerplate(std::ostream& out, t_struct* tstruct, bool bindable); | |
128 | ||
129 | void generate_function_helpers(t_function* tfunction); | |
130 | std::string get_cap_name(std::string name); | |
131 | std::string generate_isset_check(t_field* field); | |
132 | std::string generate_isset_check(std::string field); | |
133 | void generate_isset_set(ostream& out, t_field* field); | |
134 | // removed std::string isset_field_id(t_field* field); | |
135 | ||
136 | void generate_service_interface(t_service* tservice); | |
137 | void generate_service_helpers(t_service* tservice); | |
138 | void generate_service_client(t_service* tservice); | |
139 | void generate_service_server(t_service* tservice); | |
140 | void generate_process_function(t_service* tservice, t_function* tfunction); | |
141 | ||
142 | /** | |
143 | * Serialization constructs | |
144 | */ | |
145 | ||
146 | void generate_deserialize_field(std::ostream& out, t_field* tfield, std::string prefix = ""); | |
147 | ||
148 | void generate_deserialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); | |
149 | ||
150 | void generate_deserialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); | |
151 | ||
152 | void generate_deserialize_set_element(std::ostream& out, t_set* tset, std::string prefix = ""); | |
153 | ||
154 | void generate_deserialize_map_element(std::ostream& out, t_map* tmap, std::string prefix = ""); | |
155 | ||
156 | void generate_deserialize_list_element(std::ostream& out, | |
157 | t_list* tlist, | |
158 | std::string prefix = ""); | |
159 | ||
160 | void generate_serialize_field(std::ostream& out, t_field* tfield, std::string prefix = ""); | |
161 | ||
162 | void generate_serialize_struct(std::ostream& out, t_struct* tstruct, std::string prefix = ""); | |
163 | ||
164 | void generate_serialize_container(std::ostream& out, t_type* ttype, std::string prefix = ""); | |
165 | ||
166 | void generate_serialize_map_element(std::ostream& out, | |
167 | t_map* tmap, | |
168 | std::string iter, | |
169 | std::string map); | |
170 | ||
171 | void generate_serialize_set_element(std::ostream& out, t_set* tmap, std::string iter); | |
172 | ||
173 | void generate_serialize_list_element(std::ostream& out, t_list* tlist, std::string iter); | |
174 | ||
175 | void generate_as3_doc(std::ostream& out, t_doc* tdoc); | |
176 | ||
177 | void generate_as3_doc(std::ostream& out, t_function* tdoc); | |
178 | ||
179 | /** | |
180 | * Helper rendering functions | |
181 | */ | |
182 | ||
183 | std::string as3_package(); | |
184 | std::string as3_type_imports(); | |
185 | std::string as3_thrift_imports(); | |
186 | std::string as3_thrift_gen_imports(t_struct* tstruct, string& imports); | |
187 | std::string as3_thrift_gen_imports(t_service* tservice); | |
188 | std::string type_name(t_type* ttype, bool in_container = false, bool in_init = false); | |
189 | std::string base_type_name(t_base_type* tbase, bool in_container = false); | |
190 | std::string declare_field(t_field* tfield, bool init = false); | |
191 | std::string function_signature(t_function* tfunction, std::string prefix = ""); | |
192 | std::string argument_list(t_struct* tstruct); | |
193 | std::string type_to_enum(t_type* ttype); | |
194 | std::string get_enum_class_name(t_type* type) override; | |
195 | ||
196 | bool type_can_be_null(t_type* ttype) { | |
197 | ttype = get_true_type(ttype); | |
198 | ||
199 | return ttype->is_container() || ttype->is_struct() || ttype->is_xception() | |
200 | || ttype->is_string(); | |
201 | } | |
202 | ||
203 | std::string constant_name(std::string name); | |
204 | ||
205 | private: | |
206 | /** | |
207 | * File streams | |
208 | */ | |
209 | ||
210 | std::string package_name_; | |
211 | ofstream_with_content_based_conditional_update f_service_; | |
212 | std::string package_dir_; | |
213 | ||
214 | bool bindable_; | |
215 | }; | |
216 | ||
217 | /** | |
218 | * Prepares for file generation by opening up the necessary file output | |
219 | * streams. | |
220 | * | |
221 | * @param tprogram The program to generate | |
222 | */ | |
223 | void t_as3_generator::init_generator() { | |
224 | // Make output directory | |
225 | MKDIR(get_out_dir().c_str()); | |
226 | package_name_ = program_->get_namespace("as3"); | |
227 | ||
228 | string dir = package_name_; | |
229 | string subdir = get_out_dir(); | |
230 | string::size_type loc; | |
231 | while ((loc = dir.find(".")) != string::npos) { | |
232 | subdir = subdir + "/" + dir.substr(0, loc); | |
233 | MKDIR(subdir.c_str()); | |
234 | dir = dir.substr(loc + 1); | |
235 | } | |
236 | if (dir.size() > 0) { | |
237 | subdir = subdir + "/" + dir; | |
238 | MKDIR(subdir.c_str()); | |
239 | } | |
240 | ||
241 | package_dir_ = subdir; | |
242 | } | |
243 | ||
244 | /** | |
245 | * Packages the generated file | |
246 | * | |
247 | * @return String of the package, i.e. "package org.apache.thriftdemo;" | |
248 | */ | |
249 | string t_as3_generator::as3_package() { | |
250 | if (!package_name_.empty()) { | |
251 | return string("package ") + package_name_ + " "; | |
252 | } | |
253 | return "package "; | |
254 | } | |
255 | ||
256 | /** | |
257 | * Prints standard as3 imports | |
258 | * | |
259 | * @return List of imports for As3 types that are used in here | |
260 | */ | |
261 | string t_as3_generator::as3_type_imports() { | |
262 | return string() + "import org.apache.thrift.Set;\n" + "import flash.utils.ByteArray;\n" | |
263 | + "import flash.utils.Dictionary;\n\n"; | |
264 | } | |
265 | ||
266 | /** | |
267 | * Prints standard as3 imports | |
268 | * | |
269 | * @return List of imports necessary for thrift | |
270 | */ | |
271 | string t_as3_generator::as3_thrift_imports() { | |
272 | return string() + "import org.apache.thrift.*;\n" + "import org.apache.thrift.meta_data.*;\n" | |
273 | + "import org.apache.thrift.protocol.*;\n\n"; | |
274 | } | |
275 | ||
276 | /** | |
277 | * Prints imports needed for a given type | |
278 | * | |
279 | * @return List of imports necessary for a given t_struct | |
280 | */ | |
281 | string t_as3_generator::as3_thrift_gen_imports(t_struct* tstruct, string& imports) { | |
282 | ||
283 | const vector<t_field*>& members = tstruct->get_members(); | |
284 | vector<t_field*>::const_iterator m_iter; | |
285 | ||
286 | // For each type check if it is from a differnet namespace | |
287 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
288 | t_program* program = (*m_iter)->get_type()->get_program(); | |
289 | if (program != NULL && program != program_) { | |
290 | string package = program->get_namespace("as3"); | |
291 | if (!package.empty()) { | |
292 | if (imports.find(package + "." + (*m_iter)->get_type()->get_name()) == string::npos) { | |
293 | imports.append("import " + package + "." + (*m_iter)->get_type()->get_name() + ";\n"); | |
294 | } | |
295 | } | |
296 | } | |
297 | } | |
298 | return imports; | |
299 | } | |
300 | ||
301 | /** | |
302 | * Prints imports needed for a given type | |
303 | * | |
304 | * @return List of imports necessary for a given t_service | |
305 | */ | |
306 | string t_as3_generator::as3_thrift_gen_imports(t_service* tservice) { | |
307 | string imports; | |
308 | const vector<t_function*>& functions = tservice->get_functions(); | |
309 | vector<t_function*>::const_iterator f_iter; | |
310 | ||
311 | // For each type check if it is from a differnet namespace | |
312 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
313 | t_program* program = (*f_iter)->get_returntype()->get_program(); | |
314 | if (program != NULL && program != program_) { | |
315 | string package = program->get_namespace("as3"); | |
316 | if (!package.empty()) { | |
317 | if (imports.find(package + "." + (*f_iter)->get_returntype()->get_name()) == string::npos) { | |
318 | imports.append("import " + package + "." + (*f_iter)->get_returntype()->get_name() | |
319 | + ";\n"); | |
320 | } | |
321 | } | |
322 | } | |
323 | ||
324 | as3_thrift_gen_imports((*f_iter)->get_arglist(), imports); | |
325 | as3_thrift_gen_imports((*f_iter)->get_xceptions(), imports); | |
326 | } | |
327 | ||
328 | return imports; | |
329 | } | |
330 | ||
331 | /** | |
332 | * Nothing in As3 | |
333 | */ | |
334 | void t_as3_generator::close_generator() { | |
335 | } | |
336 | ||
337 | /** | |
338 | * Generates a typedef. This is not done in As3, since it does | |
339 | * not support arbitrary name replacements, and it'd be a wacky waste | |
340 | * of overhead to make wrapper classes. | |
341 | * | |
342 | * @param ttypedef The type definition | |
343 | */ | |
344 | void t_as3_generator::generate_typedef(t_typedef* ttypedef) { | |
345 | (void)ttypedef; | |
346 | } | |
347 | ||
348 | /** | |
349 | * Enums are a class with a set of static constants. | |
350 | * | |
351 | * @param tenum The enumeration | |
352 | */ | |
353 | void t_as3_generator::generate_enum(t_enum* tenum) { | |
354 | // Make output file | |
355 | string f_enum_name = package_dir_ + "/" + (tenum->get_name()) + ".as"; | |
356 | ofstream_with_content_based_conditional_update f_enum; | |
357 | f_enum.open(f_enum_name); | |
358 | ||
359 | // Comment and package it | |
360 | f_enum << autogen_comment() << as3_package() << endl; | |
361 | ||
362 | scope_up(f_enum); | |
363 | // Add as3 imports | |
364 | f_enum << string() + "import org.apache.thrift.Set;" << endl << "import flash.utils.Dictionary;" | |
365 | << endl; | |
366 | ||
367 | indent(f_enum) << "public class " << tenum->get_name() << " "; | |
368 | scope_up(f_enum); | |
369 | ||
370 | vector<t_enum_value*> constants = tenum->get_constants(); | |
371 | vector<t_enum_value*>::iterator c_iter; | |
372 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
373 | int value = (*c_iter)->get_value(); | |
374 | indent(f_enum) << "public static const " << (*c_iter)->get_name() << ":int = " << value << ";" | |
375 | << endl; | |
376 | } | |
377 | ||
378 | // Create a static Set with all valid values for this enum | |
379 | f_enum << endl; | |
380 | ||
381 | indent(f_enum) << "public static const VALID_VALUES:Set = new Set("; | |
382 | indent_up(); | |
383 | bool firstValue = true; | |
384 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
385 | // populate set | |
386 | f_enum << (firstValue ? "" : ", ") << (*c_iter)->get_name(); | |
387 | firstValue = false; | |
388 | } | |
389 | indent_down(); | |
390 | f_enum << ");" << endl; | |
391 | ||
392 | indent(f_enum) << "public static const VALUES_TO_NAMES:Dictionary = new Dictionary();" << endl; | |
393 | ||
394 | scope_up(f_enum); | |
395 | for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { | |
396 | indent(f_enum) << "VALUES_TO_NAMES[" << (*c_iter)->get_name() << "] = \"" | |
397 | << (*c_iter)->get_name() << "\";" << endl; | |
398 | } | |
399 | f_enum << endl; | |
400 | ||
401 | scope_down(f_enum); | |
402 | ||
403 | scope_down(f_enum); // end class | |
404 | ||
405 | scope_down(f_enum); // end package | |
406 | ||
407 | f_enum.close(); | |
408 | } | |
409 | ||
410 | /** | |
411 | * Generates a class that holds all the constants. | |
412 | */ | |
413 | void t_as3_generator::generate_consts(std::vector<t_const*> consts) { | |
414 | if (consts.empty()) { | |
415 | return; | |
416 | } | |
417 | ||
418 | string f_consts_name = package_dir_ + "/" + program_name_ + "Constants.as"; | |
419 | ofstream_with_content_based_conditional_update f_consts; | |
420 | f_consts.open(f_consts_name); | |
421 | ||
422 | // Print header | |
423 | f_consts << autogen_comment() << as3_package(); | |
424 | ||
425 | scope_up(f_consts); | |
426 | f_consts << endl; | |
427 | ||
428 | f_consts << as3_type_imports(); | |
429 | ||
430 | indent(f_consts) << "public class " << program_name_ << "Constants {" << endl << endl; | |
431 | indent_up(); | |
432 | vector<t_const*>::iterator c_iter; | |
433 | for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) { | |
434 | print_const_value(f_consts, | |
435 | (*c_iter)->get_name(), | |
436 | (*c_iter)->get_type(), | |
437 | (*c_iter)->get_value(), | |
438 | false); | |
439 | } | |
440 | indent_down(); | |
441 | indent(f_consts) << "}" << endl; | |
442 | scope_down(f_consts); | |
443 | f_consts.close(); | |
444 | } | |
445 | ||
446 | void t_as3_generator::print_const_value(std::ostream& out, | |
447 | string name, | |
448 | t_type* type, | |
449 | t_const_value* value, | |
450 | bool in_static, | |
451 | bool defval) { | |
452 | type = get_true_type(type); | |
453 | ||
454 | indent(out); | |
455 | if (!defval) { | |
456 | out << (in_static ? "var " : "public static const "); | |
457 | } | |
458 | if (type->is_base_type()) { | |
459 | string v2 = render_const_value(out, name, type, value); | |
460 | out << name; | |
461 | if (!defval) { | |
462 | out << ":" << type_name(type); | |
463 | } | |
464 | out << " = " << v2 << ";" << endl << endl; | |
465 | } else if (type->is_enum()) { | |
466 | out << name; | |
467 | if (!defval) { | |
468 | out << ":" << type_name(type); | |
469 | } | |
470 | out << " = " << value->get_integer() << ";" << endl << endl; | |
471 | } else if (type->is_struct() || type->is_xception()) { | |
472 | const vector<t_field*>& fields = ((t_struct*)type)->get_members(); | |
473 | vector<t_field*>::const_iterator f_iter; | |
474 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
475 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
476 | out << name << ":" << type_name(type) << " = new " << type_name(type, false, true) << "();" | |
477 | << endl; | |
478 | if (!in_static) { | |
479 | indent(out) << "{" << endl; | |
480 | indent_up(); | |
481 | indent(out) << "new function():void {" << endl; | |
482 | indent_up(); | |
483 | } | |
484 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
485 | t_type* field_type = NULL; | |
486 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
487 | if ((*f_iter)->get_name() == v_iter->first->get_string()) { | |
488 | field_type = (*f_iter)->get_type(); | |
489 | } | |
490 | } | |
491 | if (field_type == NULL) { | |
492 | throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); | |
493 | } | |
494 | string val = render_const_value(out, name, field_type, v_iter->second); | |
495 | indent(out) << name << "."; | |
496 | out << v_iter->first->get_string() << " = " << val << ";" << endl; | |
497 | } | |
498 | if (!in_static) { | |
499 | indent_down(); | |
500 | indent(out) << "}();" << endl; | |
501 | indent_down(); | |
502 | indent(out) << "}" << endl; | |
503 | } | |
504 | out << endl; | |
505 | } else if (type->is_map()) { | |
506 | out << name; | |
507 | if (!defval) { | |
508 | out << ":" << type_name(type); | |
509 | } | |
510 | out << " = new " << type_name(type, false, true) << "();" << endl; | |
511 | if (!in_static) { | |
512 | indent(out) << "{" << endl; | |
513 | indent_up(); | |
514 | indent(out) << "new function():void {" << endl; | |
515 | indent_up(); | |
516 | } | |
517 | t_type* ktype = ((t_map*)type)->get_key_type(); | |
518 | t_type* vtype = ((t_map*)type)->get_val_type(); | |
519 | const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map(); | |
520 | map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter; | |
521 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
522 | string key = render_const_value(out, name, ktype, v_iter->first); | |
523 | string val = render_const_value(out, name, vtype, v_iter->second); | |
524 | indent(out) << name << "[" << key << "] = " << val << ";" << endl; | |
525 | } | |
526 | if (!in_static) { | |
527 | indent_down(); | |
528 | indent(out) << "}();" << endl; | |
529 | indent_down(); | |
530 | indent(out) << "}" << endl; | |
531 | } | |
532 | out << endl; | |
533 | } else if (type->is_list() || type->is_set()) { | |
534 | out << name; | |
535 | if (!defval) { | |
536 | out << ":" << type_name(type); | |
537 | } | |
538 | out << " = new " << type_name(type, false, true) << "();" << endl; | |
539 | if (!in_static) { | |
540 | indent(out) << "{" << endl; | |
541 | indent_up(); | |
542 | indent(out) << "new function():void {" << endl; | |
543 | indent_up(); | |
544 | } | |
545 | t_type* etype; | |
546 | if (type->is_list()) { | |
547 | etype = ((t_list*)type)->get_elem_type(); | |
548 | } else { | |
549 | etype = ((t_set*)type)->get_elem_type(); | |
550 | } | |
551 | const vector<t_const_value*>& val = value->get_list(); | |
552 | vector<t_const_value*>::const_iterator v_iter; | |
553 | for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { | |
554 | string val = render_const_value(out, name, etype, *v_iter); | |
555 | indent(out) << name << "." << (type->is_list() ? "push" : "add") << "(" << val << ");" | |
556 | << endl; | |
557 | } | |
558 | if (!in_static) { | |
559 | indent_down(); | |
560 | indent(out) << "}();" << endl; | |
561 | indent_down(); | |
562 | indent(out) << "}" << endl; | |
563 | } | |
564 | out << endl; | |
565 | } else { | |
566 | throw "compiler error: no const of type " + type->get_name(); | |
567 | } | |
568 | } | |
569 | ||
570 | string t_as3_generator::render_const_value(ostream& out, | |
571 | string name, | |
572 | t_type* type, | |
573 | t_const_value* value) { | |
574 | (void)name; | |
575 | type = get_true_type(type); | |
576 | std::ostringstream render; | |
577 | ||
578 | if (type->is_base_type()) { | |
579 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
580 | switch (tbase) { | |
581 | case t_base_type::TYPE_STRING: | |
582 | render << '"' << get_escaped_string(value) << '"'; | |
583 | break; | |
584 | case t_base_type::TYPE_BOOL: | |
585 | render << ((value->get_integer() > 0) ? "true" : "false"); | |
586 | break; | |
587 | case t_base_type::TYPE_I8: | |
588 | render << "(byte)" << value->get_integer(); | |
589 | break; | |
590 | case t_base_type::TYPE_I16: | |
591 | render << "(short)" << value->get_integer(); | |
592 | break; | |
593 | case t_base_type::TYPE_I32: | |
594 | render << value->get_integer(); | |
595 | break; | |
596 | case t_base_type::TYPE_I64: | |
597 | render << value->get_integer() << "L"; | |
598 | break; | |
599 | case t_base_type::TYPE_DOUBLE: | |
600 | if (value->get_type() == t_const_value::CV_INTEGER) { | |
601 | render << "(double)" << value->get_integer(); | |
602 | } else { | |
603 | render << value->get_double(); | |
604 | } | |
605 | break; | |
606 | default: | |
607 | throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); | |
608 | } | |
609 | } else if (type->is_enum()) { | |
610 | render << value->get_integer(); | |
611 | } else { | |
612 | string t = tmp("tmp"); | |
613 | print_const_value(out, t, type, value, true); | |
614 | render << t; | |
615 | } | |
616 | ||
617 | return render.str(); | |
618 | } | |
619 | ||
620 | /** | |
621 | * Generates a struct definition for a thrift data type. This is a class | |
622 | * with data members, read(), write(), and an inner Isset class. | |
623 | * | |
624 | * @param tstruct The struct definition | |
625 | */ | |
626 | void t_as3_generator::generate_struct(t_struct* tstruct) { | |
627 | generate_as3_struct(tstruct, false); | |
628 | } | |
629 | ||
630 | /** | |
631 | * Exceptions are structs, but they inherit from Exception | |
632 | * | |
633 | * @param tstruct The struct definition | |
634 | */ | |
635 | void t_as3_generator::generate_xception(t_struct* txception) { | |
636 | generate_as3_struct(txception, true); | |
637 | } | |
638 | ||
639 | /** | |
640 | * As3 struct definition. | |
641 | * | |
642 | * @param tstruct The struct definition | |
643 | */ | |
644 | void t_as3_generator::generate_as3_struct(t_struct* tstruct, bool is_exception) { | |
645 | // Make output file | |
646 | string f_struct_name = package_dir_ + "/" + (tstruct->get_name()) + ".as"; | |
647 | ofstream_with_content_based_conditional_update f_struct; | |
648 | f_struct.open(f_struct_name.c_str()); | |
649 | ||
650 | f_struct << autogen_comment() << as3_package(); | |
651 | ||
652 | scope_up(f_struct); | |
653 | f_struct << endl; | |
654 | ||
655 | string imports; | |
656 | ||
657 | f_struct << as3_type_imports() << as3_thrift_imports() << as3_thrift_gen_imports(tstruct, imports) | |
658 | << endl; | |
659 | ||
660 | if (bindable_ && !is_exception) { | |
661 | f_struct << "import flash.events.Event;" << endl << "import flash.events.EventDispatcher;" | |
662 | << endl << "import mx.events.PropertyChangeEvent;" << endl; | |
663 | } | |
664 | ||
665 | generate_as3_struct_definition(f_struct, tstruct, is_exception); | |
666 | ||
667 | scope_down(f_struct); // end of package | |
668 | f_struct.close(); | |
669 | } | |
670 | ||
671 | /** | |
672 | * As3 struct definition. This has various parameters, as it could be | |
673 | * generated standalone or inside another class as a helper. If it | |
674 | * is a helper than it is a static class. | |
675 | * | |
676 | * @param tstruct The struct definition | |
677 | * @param is_exception Is this an exception? | |
678 | * @param in_class If inside a class, needs to be static class | |
679 | * @param is_result If this is a result it needs a different writer | |
680 | */ | |
681 | void t_as3_generator::generate_as3_struct_definition(ostream& out, | |
682 | t_struct* tstruct, | |
683 | bool is_exception, | |
684 | bool in_class, | |
685 | bool is_result) { | |
686 | generate_as3_doc(out, tstruct); | |
687 | ||
688 | bool is_final = (tstruct->annotations_.find("final") != tstruct->annotations_.end()); | |
689 | bool bindable = !is_exception && !in_class && bindable_; | |
690 | ||
691 | indent(out) << (in_class ? "" : "public ") << (is_final ? "final " : "") << "class " | |
692 | << tstruct->get_name() << " "; | |
693 | ||
694 | if (is_exception) { | |
695 | out << "extends Error "; | |
696 | } else if (bindable) { | |
697 | out << "extends EventDispatcher "; | |
698 | } | |
699 | out << "implements TBase "; | |
700 | ||
701 | scope_up(out); | |
702 | ||
703 | indent(out) << "private static const STRUCT_DESC:TStruct = new TStruct(\"" << tstruct->get_name() | |
704 | << "\");" << endl; | |
705 | ||
706 | // Members are public for -as3, private for -as3bean | |
707 | const vector<t_field*>& members = tstruct->get_members(); | |
708 | vector<t_field*>::const_iterator m_iter; | |
709 | ||
710 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
711 | indent(out) << "private static const " << constant_name((*m_iter)->get_name()) | |
712 | << "_FIELD_DESC:TField = new TField(\"" << (*m_iter)->get_name() << "\", " | |
713 | << type_to_enum((*m_iter)->get_type()) << ", " << (*m_iter)->get_key() << ");" | |
714 | << endl; | |
715 | } | |
716 | ||
717 | out << endl; | |
718 | ||
719 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
720 | generate_as3_doc(out, *m_iter); | |
721 | indent(out) << "private var _" << (*m_iter)->get_name() + ":" + type_name((*m_iter)->get_type()) | |
722 | << ";" << endl; | |
723 | ||
724 | indent(out) << "public static const " << upcase_string((*m_iter)->get_name()) | |
725 | << ":int = " << (*m_iter)->get_key() << ";" << endl; | |
726 | } | |
727 | ||
728 | out << endl; | |
729 | ||
730 | // Inner Isset class | |
731 | if (members.size() > 0) { | |
732 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
733 | if (!type_can_be_null((*m_iter)->get_type())) { | |
734 | indent(out) << "private var __isset_" << (*m_iter)->get_name() << ":Boolean = false;" | |
735 | << endl; | |
736 | } | |
737 | } | |
738 | } | |
739 | ||
740 | out << endl; | |
741 | ||
742 | generate_as3_meta_data_map(out, tstruct); | |
743 | ||
744 | // Static initializer to populate global class to struct metadata map | |
745 | indent(out) << "{" << endl; | |
746 | indent_up(); | |
747 | indent(out) << "FieldMetaData.addStructMetaDataMap(" << type_name(tstruct) << ", metaDataMap);" | |
748 | << endl; | |
749 | indent_down(); | |
750 | indent(out) << "}" << endl << endl; | |
751 | ||
752 | // Default constructor | |
753 | indent(out) << "public function " << tstruct->get_name() << "() {" << endl; | |
754 | indent_up(); | |
755 | for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { | |
756 | if ((*m_iter)->get_value() != NULL) { | |
757 | indent(out) << "this._" << (*m_iter)->get_name() << " = " | |
758 | << (*m_iter)->get_value()->get_integer() << ";" << endl; | |
759 | } | |
760 | } | |
761 | indent_down(); | |
762 | indent(out) << "}" << endl << endl; | |
763 | ||
764 | generate_as3_bean_boilerplate(out, tstruct, bindable); | |
765 | generate_generic_field_getters_setters(out, tstruct); | |
766 | generate_generic_isset_method(out, tstruct); | |
767 | ||
768 | generate_as3_struct_reader(out, tstruct); | |
769 | if (is_result) { | |
770 | generate_as3_struct_result_writer(out, tstruct); | |
771 | } else { | |
772 | generate_as3_struct_writer(out, tstruct); | |
773 | } | |
774 | generate_as3_struct_tostring(out, tstruct, bindable); | |
775 | generate_as3_validator(out, tstruct); | |
776 | scope_down(out); | |
777 | out << endl; | |
778 | } | |
779 | ||
780 | /** | |
781 | * Generates a function to read all the fields of the struct. | |
782 | * | |
783 | * @param tstruct The struct definition | |
784 | */ | |
785 | void t_as3_generator::generate_as3_struct_reader(ostream& out, t_struct* tstruct) { | |
786 | out << indent() << "public function read(iprot:TProtocol):void {" << endl; | |
787 | indent_up(); | |
788 | ||
789 | const vector<t_field*>& fields = tstruct->get_members(); | |
790 | vector<t_field*>::const_iterator f_iter; | |
791 | ||
792 | // Declare stack tmp variables and read struct header | |
793 | out << indent() << "var field:TField;" << endl << indent() << "iprot.readStructBegin();" << endl; | |
794 | ||
795 | // Loop over reading in fields | |
796 | indent(out) << "while (true)" << endl; | |
797 | scope_up(out); | |
798 | ||
799 | // Read beginning field marker | |
800 | indent(out) << "field = iprot.readFieldBegin();" << endl; | |
801 | ||
802 | // Check for field STOP marker and break | |
803 | indent(out) << "if (field.type == TType.STOP) { " << endl; | |
804 | indent_up(); | |
805 | indent(out) << "break;" << endl; | |
806 | indent_down(); | |
807 | indent(out) << "}" << endl; | |
808 | ||
809 | // Switch statement on the field we are reading | |
810 | indent(out) << "switch (field.id)" << endl; | |
811 | ||
812 | scope_up(out); | |
813 | ||
814 | // Generate deserialization code for known cases | |
815 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
816 | indent(out) << "case " << upcase_string((*f_iter)->get_name()) << ":" << endl; | |
817 | indent_up(); | |
818 | indent(out) << "if (field.type == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl; | |
819 | indent_up(); | |
820 | ||
821 | generate_deserialize_field(out, *f_iter, "this."); | |
822 | generate_isset_set(out, *f_iter); | |
823 | indent_down(); | |
824 | out << indent() << "} else { " << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" | |
825 | << endl << indent() << "}" << endl << indent() << "break;" << endl; | |
826 | indent_down(); | |
827 | } | |
828 | ||
829 | // In the default case we skip the field | |
830 | out << indent() << "default:" << endl << indent() << " TProtocolUtil.skip(iprot, field.type);" | |
831 | << endl << indent() << " break;" << endl; | |
832 | ||
833 | scope_down(out); | |
834 | ||
835 | // Read field end marker | |
836 | indent(out) << "iprot.readFieldEnd();" << endl; | |
837 | ||
838 | scope_down(out); | |
839 | ||
840 | out << indent() << "iprot.readStructEnd();" << endl << endl; | |
841 | ||
842 | // in non-beans style, check for required fields of primitive type | |
843 | // (which can be checked here but not in the general validate method) | |
844 | out << endl << indent() << "// check for required fields of primitive type, which can't be " | |
845 | "checked in the validate method" << endl; | |
846 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
847 | if ((*f_iter)->get_req() == t_field::T_REQUIRED && !type_can_be_null((*f_iter)->get_type())) { | |
848 | out << indent() << "if (!__isset_" << (*f_iter)->get_name() << ") {" << endl << indent() | |
849 | << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '" | |
850 | << (*f_iter)->get_name() | |
851 | << "' was not found in serialized data! Struct: \" + toString());" << endl << indent() | |
852 | << "}" << endl; | |
853 | } | |
854 | } | |
855 | ||
856 | // performs various checks (e.g. check that all required fields are set) | |
857 | indent(out) << "validate();" << endl; | |
858 | ||
859 | indent_down(); | |
860 | out << indent() << "}" << endl << endl; | |
861 | } | |
862 | ||
863 | // generates as3 method to perform various checks | |
864 | // (e.g. check that all required fields are set) | |
865 | void t_as3_generator::generate_as3_validator(ostream& out, t_struct* tstruct) { | |
866 | indent(out) << "public function validate():void {" << endl; | |
867 | indent_up(); | |
868 | ||
869 | const vector<t_field*>& fields = tstruct->get_members(); | |
870 | vector<t_field*>::const_iterator f_iter; | |
871 | ||
872 | out << indent() << "// check for required fields" << endl; | |
873 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
874 | if ((*f_iter)->get_req() == t_field::T_REQUIRED) { | |
875 | if (type_can_be_null((*f_iter)->get_type())) { | |
876 | indent(out) << "if (" << (*f_iter)->get_name() << " == null) {" << endl; | |
877 | indent(out) << " throw new TProtocolError(TProtocolError.UNKNOWN, \"Required field '" | |
878 | << (*f_iter)->get_name() << "' was not present! Struct: \" + toString());" | |
879 | << endl; | |
880 | indent(out) << "}" << endl; | |
881 | } else { | |
882 | indent(out) << "// alas, we cannot check '" << (*f_iter)->get_name() | |
883 | << "' because it's a primitive and you chose the non-beans generator." << endl; | |
884 | } | |
885 | } | |
886 | } | |
887 | ||
888 | // check that fields of type enum have valid values | |
889 | out << indent() << "// check that fields of type enum have valid values" << endl; | |
890 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
891 | t_field* field = (*f_iter); | |
892 | t_type* type = field->get_type(); | |
893 | // if field is an enum, check that its value is valid | |
894 | if (type->is_enum()) { | |
895 | indent(out) << "if (" << generate_isset_check(field) << " && !" << get_enum_class_name(type) | |
896 | << ".VALID_VALUES.contains(" << field->get_name() << ")){" << endl; | |
897 | indent_up(); | |
898 | indent(out) << "throw new TProtocolError(TProtocolError.UNKNOWN, \"The field '" | |
899 | << field->get_name() << "' has been assigned the invalid value \" + " | |
900 | << field->get_name() << ");" << endl; | |
901 | indent_down(); | |
902 | indent(out) << "}" << endl; | |
903 | } | |
904 | } | |
905 | ||
906 | indent_down(); | |
907 | indent(out) << "}" << endl << endl; | |
908 | } | |
909 | ||
910 | /** | |
911 | * Generates a function to write all the fields of the struct | |
912 | * | |
913 | * @param tstruct The struct definition | |
914 | */ | |
915 | void t_as3_generator::generate_as3_struct_writer(ostream& out, t_struct* tstruct) { | |
916 | out << indent() << "public function write(oprot:TProtocol):void {" << endl; | |
917 | indent_up(); | |
918 | ||
919 | string name = tstruct->get_name(); | |
920 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
921 | vector<t_field*>::const_iterator f_iter; | |
922 | ||
923 | // performs various checks (e.g. check that all required fields are set) | |
924 | indent(out) << "validate();" << endl << endl; | |
925 | ||
926 | indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; | |
927 | ||
928 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
929 | bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; | |
930 | if (could_be_unset) { | |
931 | indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; | |
932 | indent_up(); | |
933 | } | |
934 | bool null_allowed = type_can_be_null((*f_iter)->get_type()); | |
935 | if (null_allowed) { | |
936 | out << indent() << "if (this." << (*f_iter)->get_name() << " != null) {" << endl; | |
937 | indent_up(); | |
938 | } | |
939 | ||
940 | indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) | |
941 | << "_FIELD_DESC);" << endl; | |
942 | ||
943 | // Write field contents | |
944 | generate_serialize_field(out, *f_iter, "this."); | |
945 | ||
946 | // Write field closer | |
947 | indent(out) << "oprot.writeFieldEnd();" << endl; | |
948 | ||
949 | if (null_allowed) { | |
950 | indent_down(); | |
951 | indent(out) << "}" << endl; | |
952 | } | |
953 | if (could_be_unset) { | |
954 | indent_down(); | |
955 | indent(out) << "}" << endl; | |
956 | } | |
957 | } | |
958 | // Write the struct map | |
959 | out << indent() << "oprot.writeFieldStop();" << endl << indent() << "oprot.writeStructEnd();" | |
960 | << endl; | |
961 | ||
962 | indent_down(); | |
963 | out << indent() << "}" << endl << endl; | |
964 | } | |
965 | ||
966 | /** | |
967 | * Generates a function to write all the fields of the struct, | |
968 | * which is a function result. These fields are only written | |
969 | * if they are set in the Isset array, and only one of them | |
970 | * can be set at a time. | |
971 | * | |
972 | * @param tstruct The struct definition | |
973 | */ | |
974 | void t_as3_generator::generate_as3_struct_result_writer(ostream& out, t_struct* tstruct) { | |
975 | out << indent() << "public function write(oprot:TProtocol):void {" << endl; | |
976 | indent_up(); | |
977 | ||
978 | string name = tstruct->get_name(); | |
979 | const vector<t_field*>& fields = tstruct->get_sorted_members(); | |
980 | vector<t_field*>::const_iterator f_iter; | |
981 | ||
982 | indent(out) << "oprot.writeStructBegin(STRUCT_DESC);" << endl; | |
983 | ||
984 | bool first = true; | |
985 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
986 | if (first) { | |
987 | first = false; | |
988 | out << endl << indent() << "if "; | |
989 | } else { | |
990 | out << " else if "; | |
991 | } | |
992 | ||
993 | out << "(this." << generate_isset_check(*f_iter) << ") {" << endl; | |
994 | ||
995 | indent_up(); | |
996 | ||
997 | indent(out) << "oprot.writeFieldBegin(" << constant_name((*f_iter)->get_name()) | |
998 | << "_FIELD_DESC);" << endl; | |
999 | ||
1000 | // Write field contents | |
1001 | generate_serialize_field(out, *f_iter, "this."); | |
1002 | ||
1003 | // Write field closer | |
1004 | indent(out) << "oprot.writeFieldEnd();" << endl; | |
1005 | ||
1006 | indent_down(); | |
1007 | indent(out) << "}"; | |
1008 | } | |
1009 | // Write the struct map | |
1010 | out << endl << indent() << "oprot.writeFieldStop();" << endl << indent() | |
1011 | << "oprot.writeStructEnd();" << endl; | |
1012 | ||
1013 | indent_down(); | |
1014 | out << indent() << "}" << endl << endl; | |
1015 | } | |
1016 | ||
1017 | void t_as3_generator::generate_reflection_getters(ostringstream& out, | |
1018 | t_type* type, | |
1019 | string field_name, | |
1020 | string cap_name) { | |
1021 | (void)type; | |
1022 | (void)cap_name; | |
1023 | indent(out) << "case " << upcase_string(field_name) << ":" << endl; | |
1024 | indent_up(); | |
1025 | indent(out) << "return this." << field_name << ";" << endl; | |
1026 | indent_down(); | |
1027 | } | |
1028 | ||
1029 | void t_as3_generator::generate_reflection_setters(ostringstream& out, | |
1030 | t_type* type, | |
1031 | string field_name, | |
1032 | string cap_name) { | |
1033 | (void)type; | |
1034 | (void)cap_name; | |
1035 | indent(out) << "case " << upcase_string(field_name) << ":" << endl; | |
1036 | indent_up(); | |
1037 | indent(out) << "if (value == null) {" << endl; | |
1038 | indent(out) << " unset" << get_cap_name(field_name) << "();" << endl; | |
1039 | indent(out) << "} else {" << endl; | |
1040 | indent(out) << " this." << field_name << " = value;" << endl; | |
1041 | indent(out) << "}" << endl; | |
1042 | indent(out) << "break;" << endl << endl; | |
1043 | ||
1044 | indent_down(); | |
1045 | } | |
1046 | ||
1047 | void t_as3_generator::generate_generic_field_getters_setters(std::ostream& out, | |
1048 | t_struct* tstruct) { | |
1049 | ||
1050 | std::ostringstream getter_stream; | |
1051 | std::ostringstream setter_stream; | |
1052 | ||
1053 | // build up the bodies of both the getter and setter at once | |
1054 | const vector<t_field*>& fields = tstruct->get_members(); | |
1055 | vector<t_field*>::const_iterator f_iter; | |
1056 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1057 | t_field* field = *f_iter; | |
1058 | t_type* type = get_true_type(field->get_type()); | |
1059 | std::string field_name = field->get_name(); | |
1060 | std::string cap_name = get_cap_name(field_name); | |
1061 | ||
1062 | indent_up(); | |
1063 | generate_reflection_setters(setter_stream, type, field_name, cap_name); | |
1064 | generate_reflection_getters(getter_stream, type, field_name, cap_name); | |
1065 | indent_down(); | |
1066 | } | |
1067 | ||
1068 | // create the setter | |
1069 | indent(out) << "public function setFieldValue(fieldID:int, value:*):void {" << endl; | |
1070 | indent_up(); | |
1071 | ||
1072 | indent(out) << "switch (fieldID) {" << endl; | |
1073 | ||
1074 | out << setter_stream.str(); | |
1075 | ||
1076 | indent(out) << "default:" << endl; | |
1077 | indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; | |
1078 | ||
1079 | indent(out) << "}" << endl; | |
1080 | ||
1081 | indent_down(); | |
1082 | indent(out) << "}" << endl << endl; | |
1083 | ||
1084 | // create the getter | |
1085 | indent(out) << "public function getFieldValue(fieldID:int):* {" << endl; | |
1086 | indent_up(); | |
1087 | ||
1088 | indent(out) << "switch (fieldID) {" << endl; | |
1089 | ||
1090 | out << getter_stream.str(); | |
1091 | ||
1092 | indent(out) << "default:" << endl; | |
1093 | indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; | |
1094 | ||
1095 | indent(out) << "}" << endl; | |
1096 | ||
1097 | indent_down(); | |
1098 | ||
1099 | indent(out) << "}" << endl << endl; | |
1100 | } | |
1101 | ||
1102 | // Creates a generic isSet method that takes the field number as argument | |
1103 | void t_as3_generator::generate_generic_isset_method(std::ostream& out, t_struct* tstruct) { | |
1104 | const vector<t_field*>& fields = tstruct->get_members(); | |
1105 | vector<t_field*>::const_iterator f_iter; | |
1106 | ||
1107 | // create the isSet method | |
1108 | indent(out) << "// Returns true if field corresponding to fieldID is set (has been assigned a " | |
1109 | "value) and false otherwise" << endl; | |
1110 | indent(out) << "public function isSet(fieldID:int):Boolean {" << endl; | |
1111 | indent_up(); | |
1112 | indent(out) << "switch (fieldID) {" << endl; | |
1113 | ||
1114 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1115 | t_field* field = *f_iter; | |
1116 | indent(out) << "case " << upcase_string(field->get_name()) << ":" << endl; | |
1117 | indent_up(); | |
1118 | indent(out) << "return " << generate_isset_check(field) << ";" << endl; | |
1119 | indent_down(); | |
1120 | } | |
1121 | ||
1122 | indent(out) << "default:" << endl; | |
1123 | indent(out) << " throw new ArgumentError(\"Field \" + fieldID + \" doesn't exist!\");" << endl; | |
1124 | ||
1125 | indent(out) << "}" << endl; | |
1126 | ||
1127 | indent_down(); | |
1128 | indent(out) << "}" << endl << endl; | |
1129 | } | |
1130 | ||
1131 | /** | |
1132 | * Generates a set of As3 Bean boilerplate functions (setters, getters, etc.) | |
1133 | * for the given struct. | |
1134 | * | |
1135 | * @param tstruct The struct definition | |
1136 | */ | |
1137 | void t_as3_generator::generate_as3_bean_boilerplate(ostream& out, | |
1138 | t_struct* tstruct, | |
1139 | bool bindable) { | |
1140 | const vector<t_field*>& fields = tstruct->get_members(); | |
1141 | vector<t_field*>::const_iterator f_iter; | |
1142 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1143 | t_field* field = *f_iter; | |
1144 | t_type* type = get_true_type(field->get_type()); | |
1145 | std::string field_name = field->get_name(); | |
1146 | std::string cap_name = get_cap_name(field_name); | |
1147 | ||
1148 | // Simple getter | |
1149 | generate_as3_doc(out, field); | |
1150 | indent(out) << "public function get " << field_name << "():" << type_name(type) << " {" << endl; | |
1151 | indent_up(); | |
1152 | indent(out) << "return this._" << field_name << ";" << endl; | |
1153 | indent_down(); | |
1154 | indent(out) << "}" << endl << endl; | |
1155 | ||
1156 | // Simple setter | |
1157 | generate_as3_doc(out, field); | |
1158 | std::string propName = tmp("thriftPropertyChange"); | |
1159 | if (bindable) { | |
1160 | indent(out) << "[Bindable(event=\"" << propName << "\")]" << endl; | |
1161 | } | |
1162 | indent(out) << "public function set " << field_name << "(" << field_name << ":" | |
1163 | << type_name(type) << "):void {" << endl; | |
1164 | indent_up(); | |
1165 | indent(out) << "this._" << field_name << " = " << field_name << ";" << endl; | |
1166 | generate_isset_set(out, field); | |
1167 | ||
1168 | if (bindable) { | |
1169 | // We have to use a custom event rather than the default, because if you use the default, | |
1170 | // the setter only gets called if the value has changed - this means calling | |
1171 | // foo.setIntValue(0) | |
1172 | // will not cause foo.isIntValueSet() to return true since the value of foo._intValue wasn't | |
1173 | // changed | |
1174 | // so the setter was never called. | |
1175 | indent(out) << "dispatchEvent(new Event(\"" << propName << "\"));" << endl; | |
1176 | ||
1177 | // However, if you just use a custom event, then collections won't be able to detect when | |
1178 | // elements | |
1179 | // in the collections have changed since they listed for PropertyChangeEvents. So, we | |
1180 | // dispatch both. | |
1181 | indent(out) << "dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE));" | |
1182 | << endl; | |
1183 | } | |
1184 | indent_down(); | |
1185 | indent(out) << "}" << endl << endl; | |
1186 | ||
1187 | // Unsetter | |
1188 | indent(out) << "public function unset" << cap_name << "():void {" << endl; | |
1189 | indent_up(); | |
1190 | if (type_can_be_null(type)) { | |
1191 | indent(out) << "this." << field_name << " = null;" << endl; | |
1192 | } else { | |
1193 | indent(out) << "this.__isset_" << field_name << " = false;" << endl; | |
1194 | } | |
1195 | indent_down(); | |
1196 | indent(out) << "}" << endl << endl; | |
1197 | ||
1198 | // isSet method | |
1199 | indent(out) << "// Returns true if field " << field_name | |
1200 | << " is set (has been assigned a value) and false otherwise" << endl; | |
1201 | indent(out) << "public function is" << get_cap_name("set") << cap_name << "():Boolean {" | |
1202 | << endl; | |
1203 | indent_up(); | |
1204 | if (type_can_be_null(type)) { | |
1205 | indent(out) << "return this." << field_name << " != null;" << endl; | |
1206 | } else { | |
1207 | indent(out) << "return this.__isset_" << field_name << ";" << endl; | |
1208 | } | |
1209 | indent_down(); | |
1210 | indent(out) << "}" << endl << endl; | |
1211 | } | |
1212 | } | |
1213 | ||
1214 | /** | |
1215 | * Generates a toString() method for the given struct | |
1216 | * | |
1217 | * @param tstruct The struct definition | |
1218 | */ | |
1219 | void t_as3_generator::generate_as3_struct_tostring(ostream& out, | |
1220 | t_struct* tstruct, | |
1221 | bool bindable) { | |
1222 | // If it's bindable, it extends EventDispatcher so toString is an override. | |
1223 | out << indent() << "public " << (bindable ? "override " : "") << "function toString():String {" | |
1224 | << endl; | |
1225 | indent_up(); | |
1226 | ||
1227 | out << indent() << "var ret:String = new String(\"" << tstruct->get_name() << "(\");" << endl; | |
1228 | out << indent() << "var first:Boolean = true;" << endl << endl; | |
1229 | ||
1230 | const vector<t_field*>& fields = tstruct->get_members(); | |
1231 | vector<t_field*>::const_iterator f_iter; | |
1232 | bool first = true; | |
1233 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1234 | bool could_be_unset = (*f_iter)->get_req() == t_field::T_OPTIONAL; | |
1235 | if (could_be_unset) { | |
1236 | indent(out) << "if (" << generate_isset_check(*f_iter) << ") {" << endl; | |
1237 | indent_up(); | |
1238 | } | |
1239 | ||
1240 | t_field* field = (*f_iter); | |
1241 | ||
1242 | if (!first) { | |
1243 | indent(out) << "if (!first) ret += \", \";" << endl; | |
1244 | } | |
1245 | indent(out) << "ret += \"" << (*f_iter)->get_name() << ":\";" << endl; | |
1246 | bool can_be_null = type_can_be_null(field->get_type()); | |
1247 | if (can_be_null) { | |
1248 | indent(out) << "if (this." << (*f_iter)->get_name() << " == null) {" << endl; | |
1249 | indent(out) << " ret += \"null\";" << endl; | |
1250 | indent(out) << "} else {" << endl; | |
1251 | indent_up(); | |
1252 | } | |
1253 | ||
1254 | if (field->get_type()->is_binary()) { | |
1255 | indent(out) << " ret += \"BINARY\";" << endl; | |
1256 | } else if (field->get_type()->is_enum()) { | |
1257 | indent(out) << "var " << field->get_name() | |
1258 | << "_name:String = " << get_enum_class_name(field->get_type()) | |
1259 | << ".VALUES_TO_NAMES[this." << (*f_iter)->get_name() << "];" << endl; | |
1260 | indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; | |
1261 | indent(out) << " ret += " << field->get_name() << "_name;" << endl; | |
1262 | indent(out) << " ret += \" (\";" << endl; | |
1263 | indent(out) << "}" << endl; | |
1264 | indent(out) << "ret += this." << field->get_name() << ";" << endl; | |
1265 | indent(out) << "if (" << field->get_name() << "_name != null) {" << endl; | |
1266 | indent(out) << " ret += \")\";" << endl; | |
1267 | indent(out) << "}" << endl; | |
1268 | } else { | |
1269 | indent(out) << "ret += this." << (*f_iter)->get_name() << ";" << endl; | |
1270 | } | |
1271 | ||
1272 | if (can_be_null) { | |
1273 | indent_down(); | |
1274 | indent(out) << "}" << endl; | |
1275 | } | |
1276 | indent(out) << "first = false;" << endl; | |
1277 | ||
1278 | if (could_be_unset) { | |
1279 | indent_down(); | |
1280 | indent(out) << "}" << endl; | |
1281 | } | |
1282 | first = false; | |
1283 | } | |
1284 | out << indent() << "ret += \")\";" << endl << indent() << "return ret;" << endl; | |
1285 | ||
1286 | indent_down(); | |
1287 | indent(out) << "}" << endl << endl; | |
1288 | } | |
1289 | ||
1290 | /** | |
1291 | * Generates a static map with meta data to store information such as fieldID to | |
1292 | * fieldName mapping | |
1293 | * | |
1294 | * @param tstruct The struct definition | |
1295 | */ | |
1296 | void t_as3_generator::generate_as3_meta_data_map(ostream& out, t_struct* tstruct) { | |
1297 | const vector<t_field*>& fields = tstruct->get_members(); | |
1298 | vector<t_field*>::const_iterator f_iter; | |
1299 | ||
1300 | // Static Map with fieldID -> FieldMetaData mappings | |
1301 | indent(out) << "public static const metaDataMap:Dictionary = new Dictionary();" << endl; | |
1302 | ||
1303 | if (fields.size() > 0) { | |
1304 | // Populate map | |
1305 | scope_up(out); | |
1306 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1307 | t_field* field = *f_iter; | |
1308 | std::string field_name = field->get_name(); | |
1309 | indent(out) << "metaDataMap[" << upcase_string(field_name) << "] = new FieldMetaData(\"" | |
1310 | << field_name << "\", "; | |
1311 | ||
1312 | // Set field requirement type (required, optional, etc.) | |
1313 | if (field->get_req() == t_field::T_REQUIRED) { | |
1314 | out << "TFieldRequirementType.REQUIRED, "; | |
1315 | } else if (field->get_req() == t_field::T_OPTIONAL) { | |
1316 | out << "TFieldRequirementType.OPTIONAL, "; | |
1317 | } else { | |
1318 | out << "TFieldRequirementType.DEFAULT, "; | |
1319 | } | |
1320 | ||
1321 | // Create value meta data | |
1322 | generate_field_value_meta_data(out, field->get_type()); | |
1323 | out << ");" << endl; | |
1324 | } | |
1325 | scope_down(out); | |
1326 | } | |
1327 | } | |
1328 | ||
1329 | /** | |
1330 | * Returns a string with the as3 representation of the given thrift type | |
1331 | * (e.g. for the type struct it returns "TType.STRUCT") | |
1332 | */ | |
1333 | std::string t_as3_generator::get_as3_type_string(t_type* type) { | |
1334 | if (type->is_list()) { | |
1335 | return "TType.LIST"; | |
1336 | } else if (type->is_map()) { | |
1337 | return "TType.MAP"; | |
1338 | } else if (type->is_set()) { | |
1339 | return "TType.SET"; | |
1340 | } else if (type->is_struct() || type->is_xception()) { | |
1341 | return "TType.STRUCT"; | |
1342 | } else if (type->is_enum()) { | |
1343 | return "TType.I32"; | |
1344 | } else if (type->is_typedef()) { | |
1345 | return get_as3_type_string(((t_typedef*)type)->get_type()); | |
1346 | } else if (type->is_base_type()) { | |
1347 | switch (((t_base_type*)type)->get_base()) { | |
1348 | case t_base_type::TYPE_VOID: | |
1349 | return "TType.VOID"; | |
1350 | break; | |
1351 | case t_base_type::TYPE_STRING: | |
1352 | return "TType.STRING"; | |
1353 | break; | |
1354 | case t_base_type::TYPE_BOOL: | |
1355 | return "TType.BOOL"; | |
1356 | break; | |
1357 | case t_base_type::TYPE_I8: | |
1358 | return "TType.BYTE"; | |
1359 | break; | |
1360 | case t_base_type::TYPE_I16: | |
1361 | return "TType.I16"; | |
1362 | break; | |
1363 | case t_base_type::TYPE_I32: | |
1364 | return "TType.I32"; | |
1365 | break; | |
1366 | case t_base_type::TYPE_I64: | |
1367 | return "TType.I64"; | |
1368 | break; | |
1369 | case t_base_type::TYPE_DOUBLE: | |
1370 | return "TType.DOUBLE"; | |
1371 | break; | |
1372 | default: | |
1373 | throw std::runtime_error("Unknown thrift type \"" + type->get_name() | |
1374 | + "\" passed to t_as3_generator::get_as3_type_string!"); | |
1375 | break; // This should never happen! | |
1376 | } | |
1377 | } else { | |
1378 | throw std::runtime_error( | |
1379 | "Unknown thrift type \"" + type->get_name() | |
1380 | + "\" passed to t_as3_generator::get_as3_type_string!"); // This should never happen! | |
1381 | } | |
1382 | } | |
1383 | ||
1384 | void t_as3_generator::generate_field_value_meta_data(std::ostream& out, t_type* type) { | |
1385 | out << endl; | |
1386 | indent_up(); | |
1387 | indent_up(); | |
1388 | if (type->is_struct() || type->is_xception()) { | |
1389 | indent(out) << "new StructMetaData(TType.STRUCT, " << type_name(type); | |
1390 | } else if (type->is_container()) { | |
1391 | if (type->is_list()) { | |
1392 | indent(out) << "new ListMetaData(TType.LIST, "; | |
1393 | t_type* elem_type = ((t_list*)type)->get_elem_type(); | |
1394 | generate_field_value_meta_data(out, elem_type); | |
1395 | } else if (type->is_set()) { | |
1396 | indent(out) << "new SetMetaData(TType.SET, "; | |
1397 | t_type* elem_type = ((t_list*)type)->get_elem_type(); | |
1398 | generate_field_value_meta_data(out, elem_type); | |
1399 | } else { // map | |
1400 | indent(out) << "new MapMetaData(TType.MAP, "; | |
1401 | t_type* key_type = ((t_map*)type)->get_key_type(); | |
1402 | t_type* val_type = ((t_map*)type)->get_val_type(); | |
1403 | generate_field_value_meta_data(out, key_type); | |
1404 | out << ", "; | |
1405 | generate_field_value_meta_data(out, val_type); | |
1406 | } | |
1407 | } else { | |
1408 | indent(out) << "new FieldValueMetaData(" << get_as3_type_string(type); | |
1409 | } | |
1410 | out << ")"; | |
1411 | indent_down(); | |
1412 | indent_down(); | |
1413 | } | |
1414 | ||
1415 | /** | |
1416 | * Generates a thrift service. In C++, this comprises an entirely separate | |
1417 | * header and source file. The header file defines the methods and includes | |
1418 | * the data types defined in the main header file, and the implementation | |
1419 | * file contains implementations of the basic printer and default interfaces. | |
1420 | * | |
1421 | * @param tservice The service definition | |
1422 | */ | |
1423 | void t_as3_generator::generate_service(t_service* tservice) { | |
1424 | // Make interface file | |
1425 | string f_service_name = package_dir_ + "/" + service_name_ + ".as"; | |
1426 | f_service_.open(f_service_name.c_str()); | |
1427 | ||
1428 | f_service_ << autogen_comment() << as3_package(); | |
1429 | ||
1430 | scope_up(f_service_); | |
1431 | ||
1432 | f_service_ << endl << as3_type_imports() << as3_thrift_imports() | |
1433 | << as3_thrift_gen_imports(tservice); | |
1434 | ||
1435 | if (tservice->get_extends() != NULL) { | |
1436 | t_type* parent = tservice->get_extends(); | |
1437 | string parent_namespace = parent->get_program()->get_namespace("as3"); | |
1438 | if (!parent_namespace.empty() && parent_namespace != package_name_) { | |
1439 | f_service_ << "import " << type_name(parent) << ";" << endl; | |
1440 | } | |
1441 | } | |
1442 | ||
1443 | f_service_ << endl; | |
1444 | ||
1445 | generate_service_interface(tservice); | |
1446 | ||
1447 | scope_down(f_service_); | |
1448 | f_service_.close(); | |
1449 | ||
1450 | // Now make the implementation/client file | |
1451 | f_service_name = package_dir_ + "/" + service_name_ + "Impl.as"; | |
1452 | f_service_.open(f_service_name.c_str()); | |
1453 | ||
1454 | f_service_ << autogen_comment() << as3_package(); | |
1455 | ||
1456 | scope_up(f_service_); | |
1457 | ||
1458 | f_service_ << endl << as3_type_imports() << as3_thrift_imports() | |
1459 | << as3_thrift_gen_imports(tservice); | |
1460 | ||
1461 | if (tservice->get_extends() != NULL) { | |
1462 | t_type* parent = tservice->get_extends(); | |
1463 | string parent_namespace = parent->get_program()->get_namespace("as3"); | |
1464 | if (!parent_namespace.empty() && parent_namespace != package_name_) { | |
1465 | f_service_ << "import " << type_name(parent) << "Impl;" << endl; | |
1466 | } | |
1467 | } | |
1468 | ||
1469 | f_service_ << endl; | |
1470 | ||
1471 | generate_service_client(tservice); | |
1472 | scope_down(f_service_); | |
1473 | ||
1474 | f_service_ << as3_type_imports(); | |
1475 | f_service_ << as3_thrift_imports(); | |
1476 | f_service_ << as3_thrift_gen_imports(tservice); | |
1477 | if (!package_name_.empty()) { | |
1478 | f_service_ << "import " << package_name_ << ".*;" << endl; | |
1479 | } | |
1480 | ||
1481 | generate_service_helpers(tservice); | |
1482 | ||
1483 | f_service_.close(); | |
1484 | ||
1485 | // Now make the processor/server file | |
1486 | f_service_name = package_dir_ + "/" + service_name_ + "Processor.as"; | |
1487 | f_service_.open(f_service_name.c_str()); | |
1488 | ||
1489 | f_service_ << autogen_comment() << as3_package(); | |
1490 | ||
1491 | scope_up(f_service_); | |
1492 | ||
1493 | f_service_ << endl << as3_type_imports() << as3_thrift_imports() | |
1494 | << as3_thrift_gen_imports(tservice) << endl; | |
1495 | ||
1496 | generate_service_server(tservice); | |
1497 | scope_down(f_service_); | |
1498 | ||
1499 | f_service_ << as3_type_imports(); | |
1500 | f_service_ << as3_thrift_imports(); | |
1501 | f_service_ << as3_thrift_gen_imports(tservice) << endl; | |
1502 | if (!package_name_.empty()) { | |
1503 | f_service_ << "import " << package_name_ << ".*;" << endl; | |
1504 | } | |
1505 | ||
1506 | generate_service_helpers(tservice); | |
1507 | ||
1508 | f_service_.close(); | |
1509 | } | |
1510 | ||
1511 | /** | |
1512 | * Generates a service interface definition. | |
1513 | * | |
1514 | * @param tservice The service to generate a header definition for | |
1515 | */ | |
1516 | void t_as3_generator::generate_service_interface(t_service* tservice) { | |
1517 | string extends_iface = ""; | |
1518 | if (tservice->get_extends() != NULL) { | |
1519 | extends_iface = " extends " + tservice->get_extends()->get_name(); | |
1520 | } | |
1521 | ||
1522 | generate_as3_doc(f_service_, tservice); | |
1523 | f_service_ << indent() << "public interface " << service_name_ << extends_iface << " {" << endl | |
1524 | << endl; | |
1525 | indent_up(); | |
1526 | vector<t_function*> functions = tservice->get_functions(); | |
1527 | vector<t_function*>::iterator f_iter; | |
1528 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1529 | generate_as3_doc(f_service_, *f_iter); | |
1530 | if (!(*f_iter)->is_oneway()) { | |
1531 | if ((*f_iter)->get_returntype()->is_void()) { | |
1532 | indent(f_service_) << "//function onError(Error):void;" << endl; | |
1533 | indent(f_service_) << "//function onSuccess():void;" << endl; | |
1534 | } else { | |
1535 | indent(f_service_) << "//function onError(Error):void;" << endl; | |
1536 | indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype()) | |
1537 | << "):void;" << endl; | |
1538 | } | |
1539 | } | |
1540 | indent(f_service_) << function_signature(*f_iter) << ";" << endl << endl; | |
1541 | } | |
1542 | indent_down(); | |
1543 | f_service_ << indent() << "}" << endl << endl; | |
1544 | } | |
1545 | ||
1546 | /** | |
1547 | * Generates structs for all the service args and return types | |
1548 | * | |
1549 | * @param tservice The service | |
1550 | */ | |
1551 | void t_as3_generator::generate_service_helpers(t_service* tservice) { | |
1552 | vector<t_function*> functions = tservice->get_functions(); | |
1553 | vector<t_function*>::iterator f_iter; | |
1554 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1555 | t_struct* ts = (*f_iter)->get_arglist(); | |
1556 | generate_as3_struct_definition(f_service_, ts, false, true); | |
1557 | generate_function_helpers(*f_iter); | |
1558 | } | |
1559 | } | |
1560 | ||
1561 | /** | |
1562 | * Generates a service client definition. | |
1563 | * | |
1564 | * @param tservice The service to generate a server for. | |
1565 | */ | |
1566 | void t_as3_generator::generate_service_client(t_service* tservice) { | |
1567 | string extends = ""; | |
1568 | string extends_client = ""; | |
1569 | if (tservice->get_extends() != NULL) { | |
1570 | extends = tservice->get_extends()->get_name(); | |
1571 | extends_client = " extends " + extends + "Impl"; | |
1572 | } | |
1573 | ||
1574 | indent(f_service_) << "public class " << service_name_ << "Impl" << extends_client | |
1575 | << " implements " << service_name_ << " {" << endl; | |
1576 | indent_up(); | |
1577 | ||
1578 | indent(f_service_) << "public function " << service_name_ << "Impl" | |
1579 | << "(iprot:TProtocol, oprot:TProtocol=null)" << endl; | |
1580 | scope_up(f_service_); | |
1581 | if (extends.empty()) { | |
1582 | f_service_ << indent() << "iprot_ = iprot;" << endl; | |
1583 | f_service_ << indent() << "if (oprot == null) {" << endl; | |
1584 | indent_up(); | |
1585 | f_service_ << indent() << "oprot_ = iprot;" << endl; | |
1586 | indent_down(); | |
1587 | f_service_ << indent() << "} else {" << endl; | |
1588 | indent_up(); | |
1589 | f_service_ << indent() << "oprot_ = oprot;" << endl; | |
1590 | indent_down(); | |
1591 | f_service_ << indent() << "}"; | |
1592 | } else { | |
1593 | f_service_ << indent() << "super(iprot, oprot);" << endl; | |
1594 | } | |
1595 | scope_down(f_service_); | |
1596 | f_service_ << endl; | |
1597 | ||
1598 | if (extends.empty()) { | |
1599 | f_service_ << indent() << "protected var iprot_:TProtocol;" << endl << indent() | |
1600 | << "protected var oprot_:TProtocol;" << endl << endl << indent() | |
1601 | << "protected var seqid_:int;" << endl << endl; | |
1602 | ||
1603 | indent(f_service_) << "public function getInputProtocol():TProtocol" << endl; | |
1604 | scope_up(f_service_); | |
1605 | indent(f_service_) << "return this.iprot_;" << endl; | |
1606 | scope_down(f_service_); | |
1607 | f_service_ << endl; | |
1608 | ||
1609 | indent(f_service_) << "public function getOutputProtocol():TProtocol" << endl; | |
1610 | scope_up(f_service_); | |
1611 | indent(f_service_) << "return this.oprot_;" << endl; | |
1612 | scope_down(f_service_); | |
1613 | f_service_ << endl; | |
1614 | } | |
1615 | ||
1616 | // Generate client method implementations | |
1617 | vector<t_function*> functions = tservice->get_functions(); | |
1618 | vector<t_function*>::const_iterator f_iter; | |
1619 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1620 | string funname = (*f_iter)->get_name(); | |
1621 | ||
1622 | // Open function | |
1623 | if (!(*f_iter)->is_oneway()) { | |
1624 | if ((*f_iter)->get_returntype()->is_void()) { | |
1625 | indent(f_service_) << "//function onError(Error):void;" << endl; | |
1626 | indent(f_service_) << "//function onSuccess():void;" << endl; | |
1627 | } else { | |
1628 | indent(f_service_) << "//function onError(Error):void;" << endl; | |
1629 | indent(f_service_) << "//function onSuccess(" << type_name((*f_iter)->get_returntype()) | |
1630 | << "):void;" << endl; | |
1631 | } | |
1632 | } | |
1633 | indent(f_service_) << "public " << function_signature(*f_iter) << endl; | |
1634 | scope_up(f_service_); | |
1635 | ||
1636 | // Get the struct of function call params | |
1637 | t_struct* arg_struct = (*f_iter)->get_arglist(); | |
1638 | ||
1639 | string argsname = (*f_iter)->get_name() + "_args"; | |
1640 | vector<t_field*>::const_iterator fld_iter; | |
1641 | const vector<t_field*>& fields = arg_struct->get_members(); | |
1642 | ||
1643 | // Serialize the request | |
1644 | f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", " | |
1645 | << ((*f_iter)->is_oneway() ? "TMessageType.ONEWAY" : "TMessageType.CALL") | |
1646 | << ", seqid_));" << endl << indent() << "var args:" << argsname << " = new " | |
1647 | << argsname << "();" << endl; | |
1648 | ||
1649 | for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { | |
1650 | f_service_ << indent() << "args." << (*fld_iter)->get_name() << " = " | |
1651 | << (*fld_iter)->get_name() << ";" << endl; | |
1652 | } | |
1653 | ||
1654 | f_service_ << indent() << "args.write(oprot_);" << endl << indent() | |
1655 | << "oprot_.writeMessageEnd();" << endl; | |
1656 | ||
1657 | if ((*f_iter)->is_oneway()) { | |
1658 | f_service_ << indent() << "oprot_.getTransport().flush();" << endl; | |
1659 | } else { | |
1660 | f_service_ << indent() << "oprot_.getTransport().flush(function(error:Error):void {" << endl; | |
1661 | indent_up(); | |
1662 | f_service_ << indent() << "try {" << endl; | |
1663 | indent_up(); | |
1664 | string resultname = (*f_iter)->get_name() + "_result"; | |
1665 | f_service_ << indent() << "if (error != null) {" << endl << indent() | |
1666 | << " if (onError != null) onError(error);" << endl << indent() << " return;" | |
1667 | << endl << indent() << "}" << endl << indent() | |
1668 | << "var msg:TMessage = iprot_.readMessageBegin();" << endl << indent() | |
1669 | << "if (msg.type == TMessageType.EXCEPTION) {" << endl << indent() | |
1670 | << " var x:TApplicationError = TApplicationError.read(iprot_);" << endl | |
1671 | << indent() << " iprot_.readMessageEnd();" << endl << indent() | |
1672 | << " if (onError != null) onError(x);" << endl << indent() << " return;" << endl | |
1673 | << indent() << "}" << endl << indent() << "var result :" << resultname << " = new " | |
1674 | << resultname << "();" << endl << indent() << "result.read(iprot_);" << endl | |
1675 | << indent() << "iprot_.readMessageEnd();" << endl; | |
1676 | ||
1677 | // Careful, only return _result if not a void function | |
1678 | if (!(*f_iter)->get_returntype()->is_void()) { | |
1679 | f_service_ << indent() << "if (result." << generate_isset_check("success") << ") {" << endl | |
1680 | << indent() << " if (onSuccess != null) onSuccess(result.success);" << endl | |
1681 | << indent() << " return;" << endl << indent() << "}" << endl; | |
1682 | } | |
1683 | ||
1684 | t_struct* xs = (*f_iter)->get_xceptions(); | |
1685 | const std::vector<t_field*>& xceptions = xs->get_members(); | |
1686 | vector<t_field*>::const_iterator x_iter; | |
1687 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
1688 | f_service_ << indent() << "if (result." << (*x_iter)->get_name() << " != null) {" << endl | |
1689 | << indent() << " if (onError != null) onError(result." << (*x_iter)->get_name() | |
1690 | << ");" << endl << indent() << " return;" << endl << indent() << "}" << endl; | |
1691 | } | |
1692 | ||
1693 | // If you get here it's an exception, unless a void function | |
1694 | if ((*f_iter)->get_returntype()->is_void()) { | |
1695 | f_service_ << indent() << "if (onSuccess != null) onSuccess();" << endl << indent() | |
1696 | << "return;" << endl; | |
1697 | } else { | |
1698 | ||
1699 | f_service_ << indent() << "if (onError != null) onError(new " | |
1700 | "TApplicationError(TApplicationError.MISSING_RESULT, \"" | |
1701 | << (*f_iter)->get_name() << " failed: unknown result\"));" << endl; | |
1702 | } | |
1703 | indent_down(); | |
1704 | f_service_ << indent() << "} catch (e:TError) {" << endl << indent() | |
1705 | << " if (onError != null) onError(e);" << endl << indent() << "}" << endl; | |
1706 | ||
1707 | indent_down(); | |
1708 | indent(f_service_) << "});" << endl; | |
1709 | } | |
1710 | // Close function | |
1711 | scope_down(f_service_); | |
1712 | f_service_ << endl; | |
1713 | } | |
1714 | ||
1715 | indent_down(); | |
1716 | indent(f_service_) << "}" << endl; | |
1717 | } | |
1718 | ||
1719 | /** | |
1720 | * Generates a service server definition. | |
1721 | * | |
1722 | * @param tservice The service to generate a server for. | |
1723 | */ | |
1724 | void t_as3_generator::generate_service_server(t_service* tservice) { | |
1725 | // Generate the dispatch methods | |
1726 | vector<t_function*> functions = tservice->get_functions(); | |
1727 | vector<t_function*>::iterator f_iter; | |
1728 | ||
1729 | // Extends stuff | |
1730 | string extends = ""; | |
1731 | string extends_processor = ""; | |
1732 | if (tservice->get_extends() != NULL) { | |
1733 | extends = type_name(tservice->get_extends()); | |
1734 | extends_processor = " extends " + extends + "Processor"; | |
1735 | } | |
1736 | ||
1737 | // Generate the header portion | |
1738 | indent(f_service_) << "public class " << service_name_ << "Processor" << extends_processor | |
1739 | << " implements TProcessor {" << endl; | |
1740 | indent_up(); | |
1741 | ||
1742 | indent(f_service_) << "public function " << service_name_ << "Processor(iface:" << service_name_ | |
1743 | << ")" << endl; | |
1744 | scope_up(f_service_); | |
1745 | if (!extends.empty()) { | |
1746 | f_service_ << indent() << "super(iface);" << endl; | |
1747 | } | |
1748 | f_service_ << indent() << "iface_ = iface;" << endl; | |
1749 | ||
1750 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1751 | f_service_ << indent() << "PROCESS_MAP[\"" << (*f_iter)->get_name() | |
1752 | << "\"] = " << (*f_iter)->get_name() << "();" << endl; | |
1753 | } | |
1754 | ||
1755 | scope_down(f_service_); | |
1756 | f_service_ << endl; | |
1757 | ||
1758 | f_service_ << indent() << "private var iface_:" << service_name_ << ";" << endl; | |
1759 | ||
1760 | if (extends.empty()) { | |
1761 | f_service_ << indent() << "protected const PROCESS_MAP:Dictionary = new Dictionary();" << endl; | |
1762 | } | |
1763 | ||
1764 | f_service_ << endl; | |
1765 | ||
1766 | // Generate the server implementation | |
1767 | string override = ""; | |
1768 | if (tservice->get_extends() != NULL) { | |
1769 | override = "override "; | |
1770 | } | |
1771 | indent(f_service_) << override | |
1772 | << "public function process(iprot:TProtocol, oprot:TProtocol):Boolean" << endl; | |
1773 | scope_up(f_service_); | |
1774 | ||
1775 | f_service_ << indent() << "var msg:TMessage = iprot.readMessageBegin();" << endl; | |
1776 | ||
1777 | // TODO(mcslee): validate message, was the seqid etc. legit? | |
1778 | // AS- If all method is oneway: | |
1779 | // do you have an oprot? | |
1780 | // do you you need nullcheck? | |
1781 | f_service_ | |
1782 | << indent() << "var fn:Function = PROCESS_MAP[msg.name];" << endl << indent() | |
1783 | << "if (fn == null) {" << endl << indent() << " TProtocolUtil.skip(iprot, TType.STRUCT);" | |
1784 | << endl << indent() << " iprot.readMessageEnd();" << endl << indent() | |
1785 | << " var x:TApplicationError = new TApplicationError(TApplicationError.UNKNOWN_METHOD, " | |
1786 | "\"Invalid method name: '\"+msg.name+\"'\");" << endl << indent() | |
1787 | << " oprot.writeMessageBegin(new TMessage(msg.name, TMessageType.EXCEPTION, msg.seqid));" | |
1788 | << endl << indent() << " x.write(oprot);" << endl << indent() << " oprot.writeMessageEnd();" | |
1789 | << endl << indent() << " oprot.getTransport().flush();" << endl << indent() | |
1790 | << " return true;" << endl << indent() << "}" << endl << indent() | |
1791 | << "fn.call(this,msg.seqid, iprot, oprot);" << endl; | |
1792 | ||
1793 | f_service_ << indent() << "return true;" << endl; | |
1794 | ||
1795 | scope_down(f_service_); | |
1796 | f_service_ << endl; | |
1797 | ||
1798 | // Generate the process subfunctions | |
1799 | for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { | |
1800 | generate_process_function(tservice, *f_iter); | |
1801 | } | |
1802 | ||
1803 | indent_down(); | |
1804 | indent(f_service_) << "}" << endl << endl; | |
1805 | } | |
1806 | ||
1807 | /** | |
1808 | * Generates a struct and helpers for a function. | |
1809 | * | |
1810 | * @param tfunction The function | |
1811 | */ | |
1812 | void t_as3_generator::generate_function_helpers(t_function* tfunction) { | |
1813 | if (tfunction->is_oneway()) { | |
1814 | return; | |
1815 | } | |
1816 | ||
1817 | t_struct result(program_, tfunction->get_name() + "_result"); | |
1818 | t_field success(tfunction->get_returntype(), "success", 0); | |
1819 | if (!tfunction->get_returntype()->is_void()) { | |
1820 | result.append(&success); | |
1821 | } | |
1822 | ||
1823 | t_struct* xs = tfunction->get_xceptions(); | |
1824 | const vector<t_field*>& fields = xs->get_members(); | |
1825 | vector<t_field*>::const_iterator f_iter; | |
1826 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1827 | result.append(*f_iter); | |
1828 | } | |
1829 | ||
1830 | generate_as3_struct_definition(f_service_, &result, false, true, true); | |
1831 | } | |
1832 | ||
1833 | /** | |
1834 | * Generates a process function definition. | |
1835 | * | |
1836 | * @param tfunction The function to write a dispatcher for | |
1837 | */ | |
1838 | void t_as3_generator::generate_process_function(t_service* tservice, t_function* tfunction) { | |
1839 | (void)tservice; | |
1840 | // Open class | |
1841 | indent(f_service_) << "private function " << tfunction->get_name() << "():Function {" << endl; | |
1842 | indent_up(); | |
1843 | ||
1844 | // Open function | |
1845 | indent(f_service_) << "return function(seqid:int, iprot:TProtocol, oprot:TProtocol):void" << endl; | |
1846 | scope_up(f_service_); | |
1847 | ||
1848 | string argsname = tfunction->get_name() + "_args"; | |
1849 | string resultname = tfunction->get_name() + "_result"; | |
1850 | ||
1851 | f_service_ << indent() << "var args:" << argsname << " = new " << argsname << "();" << endl | |
1852 | << indent() << "args.read(iprot);" << endl << indent() << "iprot.readMessageEnd();" | |
1853 | << endl; | |
1854 | ||
1855 | t_struct* xs = tfunction->get_xceptions(); | |
1856 | const std::vector<t_field*>& xceptions = xs->get_members(); | |
1857 | vector<t_field*>::const_iterator x_iter; | |
1858 | ||
1859 | // Declare result for non oneway function | |
1860 | if (!tfunction->is_oneway()) { | |
1861 | f_service_ << indent() << "var result:" << resultname << " = new " << resultname << "();" | |
1862 | << endl; | |
1863 | } | |
1864 | ||
1865 | // Try block for a function with exceptions | |
1866 | if (xceptions.size() > 0) { | |
1867 | f_service_ << indent() << "try {" << endl; | |
1868 | indent_up(); | |
1869 | } | |
1870 | ||
1871 | // Generate the function call | |
1872 | t_struct* arg_struct = tfunction->get_arglist(); | |
1873 | const std::vector<t_field*>& fields = arg_struct->get_members(); | |
1874 | vector<t_field*>::const_iterator f_iter; | |
1875 | ||
1876 | f_service_ << indent(); | |
1877 | if (tfunction->is_oneway()) { | |
1878 | f_service_ << "iface_." << tfunction->get_name() << "("; | |
1879 | bool first = true; | |
1880 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
1881 | if (first) { | |
1882 | first = false; | |
1883 | } else { | |
1884 | f_service_ << ", "; | |
1885 | } | |
1886 | f_service_ << "args." << (*f_iter)->get_name(); | |
1887 | } | |
1888 | f_service_ << ");" << endl; | |
1889 | } else { | |
1890 | f_service_ << "// sorry this operation is not supported yet" << endl; | |
1891 | f_service_ << indent() << "throw new Error(\"This is not yet supported\");" << endl; | |
1892 | } | |
1893 | ||
1894 | // Set isset on success field | |
1895 | if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void() | |
1896 | && !type_can_be_null(tfunction->get_returntype())) { | |
1897 | f_service_ << indent() << "result.set" << get_cap_name("success") << get_cap_name("isSet") | |
1898 | << "(true);" << endl; | |
1899 | } | |
1900 | ||
1901 | if (!tfunction->is_oneway() && xceptions.size() > 0) { | |
1902 | indent_down(); | |
1903 | f_service_ << indent() << "}"; | |
1904 | for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { | |
1905 | f_service_ << " catch (" << (*x_iter)->get_name() << ":" | |
1906 | << type_name((*x_iter)->get_type(), false, false) << ") {" << endl; | |
1907 | if (!tfunction->is_oneway()) { | |
1908 | indent_up(); | |
1909 | f_service_ << indent() << "result." << (*x_iter)->get_name() << " = " | |
1910 | << (*x_iter)->get_name() << ";" << endl; | |
1911 | indent_down(); | |
1912 | f_service_ << indent() << "}"; | |
1913 | } else { | |
1914 | f_service_ << "}"; | |
1915 | } | |
1916 | } | |
1917 | f_service_ << " catch (th:Error) {" << endl; | |
1918 | indent_up(); | |
1919 | f_service_ << indent() << "trace(\"Internal error processing " << tfunction->get_name() | |
1920 | << "\", th);" << endl << indent() | |
1921 | << "var x:TApplicationError = new " | |
1922 | "TApplicationError(TApplicationError.INTERNAL_ERROR, \"Internal error processing " | |
1923 | << tfunction->get_name() << "\");" << endl << indent() | |
1924 | << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() | |
1925 | << "\", TMessageType.EXCEPTION, seqid));" << endl << indent() << "x.write(oprot);" | |
1926 | << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() | |
1927 | << "oprot.getTransport().flush();" << endl << indent() << "return;" << endl; | |
1928 | indent_down(); | |
1929 | f_service_ << indent() << "}" << endl; | |
1930 | } | |
1931 | ||
1932 | // Shortcut out here for oneway functions | |
1933 | if (tfunction->is_oneway()) { | |
1934 | f_service_ << indent() << "return;" << endl; | |
1935 | scope_down(f_service_); | |
1936 | ||
1937 | // Close class | |
1938 | indent_down(); | |
1939 | f_service_ << indent() << "}" << endl << endl; | |
1940 | return; | |
1941 | } | |
1942 | ||
1943 | f_service_ << indent() << "oprot.writeMessageBegin(new TMessage(\"" << tfunction->get_name() | |
1944 | << "\", TMessageType.REPLY, seqid));" << endl << indent() << "result.write(oprot);" | |
1945 | << endl << indent() << "oprot.writeMessageEnd();" << endl << indent() | |
1946 | << "oprot.getTransport().flush();" << endl; | |
1947 | ||
1948 | // Close function | |
1949 | scope_down(f_service_); | |
1950 | f_service_ << endl; | |
1951 | ||
1952 | // Close class | |
1953 | indent_down(); | |
1954 | f_service_ << indent() << "}" << endl << endl; | |
1955 | } | |
1956 | ||
1957 | /** | |
1958 | * Deserializes a field of any type. | |
1959 | * | |
1960 | * @param tfield The field | |
1961 | * @param prefix The variable name or container for this field | |
1962 | */ | |
1963 | void t_as3_generator::generate_deserialize_field(ostream& out, t_field* tfield, string prefix) { | |
1964 | t_type* type = get_true_type(tfield->get_type()); | |
1965 | ||
1966 | if (type->is_void()) { | |
1967 | throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); | |
1968 | } | |
1969 | ||
1970 | string name = prefix + tfield->get_name(); | |
1971 | ||
1972 | if (type->is_struct() || type->is_xception()) { | |
1973 | generate_deserialize_struct(out, (t_struct*)type, name); | |
1974 | } else if (type->is_container()) { | |
1975 | generate_deserialize_container(out, type, name); | |
1976 | } else if (type->is_base_type() || type->is_enum()) { | |
1977 | ||
1978 | indent(out) << name << " = iprot."; | |
1979 | ||
1980 | if (type->is_base_type()) { | |
1981 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
1982 | switch (tbase) { | |
1983 | case t_base_type::TYPE_VOID: | |
1984 | throw "compiler error: cannot serialize void field in a struct: " + name; | |
1985 | break; | |
1986 | case t_base_type::TYPE_STRING: | |
1987 | if (type->is_binary()) { | |
1988 | out << "readBinary();"; | |
1989 | } else { | |
1990 | out << "readString();"; | |
1991 | } | |
1992 | break; | |
1993 | case t_base_type::TYPE_BOOL: | |
1994 | out << "readBool();"; | |
1995 | break; | |
1996 | case t_base_type::TYPE_I8: | |
1997 | out << "readByte();"; | |
1998 | break; | |
1999 | case t_base_type::TYPE_I16: | |
2000 | out << "readI16();"; | |
2001 | break; | |
2002 | case t_base_type::TYPE_I32: | |
2003 | out << "readI32();"; | |
2004 | break; | |
2005 | case t_base_type::TYPE_I64: | |
2006 | out << "readI64();"; | |
2007 | break; | |
2008 | case t_base_type::TYPE_DOUBLE: | |
2009 | out << "readDouble();"; | |
2010 | break; | |
2011 | default: | |
2012 | throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase); | |
2013 | } | |
2014 | } else if (type->is_enum()) { | |
2015 | out << "readI32();"; | |
2016 | } | |
2017 | out << endl; | |
2018 | } else { | |
2019 | printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n", | |
2020 | tfield->get_name().c_str(), | |
2021 | type_name(type).c_str()); | |
2022 | } | |
2023 | } | |
2024 | ||
2025 | /** | |
2026 | * Generates an unserializer for a struct, invokes read() | |
2027 | */ | |
2028 | void t_as3_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) { | |
2029 | out << indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << indent() | |
2030 | << prefix << ".read(iprot);" << endl; | |
2031 | } | |
2032 | ||
2033 | /** | |
2034 | * Deserializes a container by reading its size and then iterating | |
2035 | */ | |
2036 | void t_as3_generator::generate_deserialize_container(ostream& out, t_type* ttype, string prefix) { | |
2037 | scope_up(out); | |
2038 | ||
2039 | string obj; | |
2040 | ||
2041 | if (ttype->is_map()) { | |
2042 | obj = tmp("_map"); | |
2043 | } else if (ttype->is_set()) { | |
2044 | obj = tmp("_set"); | |
2045 | } else if (ttype->is_list()) { | |
2046 | obj = tmp("_list"); | |
2047 | } | |
2048 | ||
2049 | // Declare variables, read header | |
2050 | if (ttype->is_map()) { | |
2051 | indent(out) << "var " << obj << ":TMap = iprot.readMapBegin();" << endl; | |
2052 | } else if (ttype->is_set()) { | |
2053 | indent(out) << "var " << obj << ":TSet = iprot.readSetBegin();" << endl; | |
2054 | } else if (ttype->is_list()) { | |
2055 | indent(out) << "var " << obj << ":TList = iprot.readListBegin();" << endl; | |
2056 | } | |
2057 | ||
2058 | indent(out) << prefix << " = new " << type_name(ttype, false, true) | |
2059 | // size the collection correctly | |
2060 | << "(" | |
2061 | << ");" << endl; | |
2062 | ||
2063 | // For loop iterates over elements | |
2064 | string i = tmp("_i"); | |
2065 | indent(out) << "for (var " << i << ":int = 0; " << i << " < " << obj << ".size" | |
2066 | << "; " | |
2067 | << "++" << i << ")" << endl; | |
2068 | ||
2069 | scope_up(out); | |
2070 | ||
2071 | if (ttype->is_map()) { | |
2072 | generate_deserialize_map_element(out, (t_map*)ttype, prefix); | |
2073 | } else if (ttype->is_set()) { | |
2074 | generate_deserialize_set_element(out, (t_set*)ttype, prefix); | |
2075 | } else if (ttype->is_list()) { | |
2076 | generate_deserialize_list_element(out, (t_list*)ttype, prefix); | |
2077 | } | |
2078 | ||
2079 | scope_down(out); | |
2080 | ||
2081 | // Read container end | |
2082 | if (ttype->is_map()) { | |
2083 | indent(out) << "iprot.readMapEnd();" << endl; | |
2084 | } else if (ttype->is_set()) { | |
2085 | indent(out) << "iprot.readSetEnd();" << endl; | |
2086 | } else if (ttype->is_list()) { | |
2087 | indent(out) << "iprot.readListEnd();" << endl; | |
2088 | } | |
2089 | ||
2090 | scope_down(out); | |
2091 | } | |
2092 | ||
2093 | /** | |
2094 | * Generates code to deserialize a map | |
2095 | */ | |
2096 | void t_as3_generator::generate_deserialize_map_element(ostream& out, t_map* tmap, string prefix) { | |
2097 | string key = tmp("_key"); | |
2098 | string val = tmp("_val"); | |
2099 | t_field fkey(tmap->get_key_type(), key); | |
2100 | t_field fval(tmap->get_val_type(), val); | |
2101 | ||
2102 | indent(out) << declare_field(&fkey) << endl; | |
2103 | indent(out) << declare_field(&fval) << endl; | |
2104 | ||
2105 | generate_deserialize_field(out, &fkey); | |
2106 | generate_deserialize_field(out, &fval); | |
2107 | ||
2108 | indent(out) << prefix << "[" << key << "] = " << val << ";" << endl; | |
2109 | } | |
2110 | ||
2111 | /** | |
2112 | * Deserializes a set element | |
2113 | */ | |
2114 | void t_as3_generator::generate_deserialize_set_element(ostream& out, t_set* tset, string prefix) { | |
2115 | string elem = tmp("_elem"); | |
2116 | t_field felem(tset->get_elem_type(), elem); | |
2117 | ||
2118 | indent(out) << declare_field(&felem) << endl; | |
2119 | ||
2120 | generate_deserialize_field(out, &felem); | |
2121 | ||
2122 | indent(out) << prefix << ".add(" << elem << ");" << endl; | |
2123 | } | |
2124 | ||
2125 | /** | |
2126 | * Deserializes a list element | |
2127 | */ | |
2128 | void t_as3_generator::generate_deserialize_list_element(ostream& out, | |
2129 | t_list* tlist, | |
2130 | string prefix) { | |
2131 | string elem = tmp("_elem"); | |
2132 | t_field felem(tlist->get_elem_type(), elem); | |
2133 | ||
2134 | indent(out) << declare_field(&felem) << endl; | |
2135 | ||
2136 | generate_deserialize_field(out, &felem); | |
2137 | ||
2138 | indent(out) << prefix << ".push(" << elem << ");" << endl; | |
2139 | } | |
2140 | ||
2141 | /** | |
2142 | * Serializes a field of any type. | |
2143 | * | |
2144 | * @param tfield The field to serialize | |
2145 | * @param prefix Name to prepend to field name | |
2146 | */ | |
2147 | void t_as3_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) { | |
2148 | t_type* type = get_true_type(tfield->get_type()); | |
2149 | ||
2150 | // Do nothing for void types | |
2151 | if (type->is_void()) { | |
2152 | throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " + prefix + tfield->get_name(); | |
2153 | } | |
2154 | ||
2155 | if (type->is_struct() || type->is_xception()) { | |
2156 | generate_serialize_struct(out, (t_struct*)type, prefix + tfield->get_name()); | |
2157 | } else if (type->is_container()) { | |
2158 | generate_serialize_container(out, type, prefix + tfield->get_name()); | |
2159 | } else if (type->is_base_type() || type->is_enum()) { | |
2160 | ||
2161 | string name = prefix + tfield->get_name(); | |
2162 | indent(out) << "oprot."; | |
2163 | ||
2164 | if (type->is_base_type()) { | |
2165 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
2166 | switch (tbase) { | |
2167 | case t_base_type::TYPE_VOID: | |
2168 | throw "compiler error: cannot serialize void field in a struct: " + name; | |
2169 | break; | |
2170 | case t_base_type::TYPE_STRING: | |
2171 | if (type->is_binary()) { | |
2172 | out << "writeBinary(" << name << ");"; | |
2173 | } else { | |
2174 | out << "writeString(" << name << ");"; | |
2175 | } | |
2176 | break; | |
2177 | case t_base_type::TYPE_BOOL: | |
2178 | out << "writeBool(" << name << ");"; | |
2179 | break; | |
2180 | case t_base_type::TYPE_I8: | |
2181 | out << "writeByte(" << name << ");"; | |
2182 | break; | |
2183 | case t_base_type::TYPE_I16: | |
2184 | out << "writeI16(" << name << ");"; | |
2185 | break; | |
2186 | case t_base_type::TYPE_I32: | |
2187 | out << "writeI32(" << name << ");"; | |
2188 | break; | |
2189 | case t_base_type::TYPE_I64: | |
2190 | out << "writeI64(" << name << ");"; | |
2191 | break; | |
2192 | case t_base_type::TYPE_DOUBLE: | |
2193 | out << "writeDouble(" << name << ");"; | |
2194 | break; | |
2195 | default: | |
2196 | throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase); | |
2197 | } | |
2198 | } else if (type->is_enum()) { | |
2199 | out << "writeI32(" << name << ");"; | |
2200 | } | |
2201 | out << endl; | |
2202 | } else { | |
2203 | printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s%s' TYPE '%s'\n", | |
2204 | prefix.c_str(), | |
2205 | tfield->get_name().c_str(), | |
2206 | type_name(type).c_str()); | |
2207 | } | |
2208 | } | |
2209 | ||
2210 | /** | |
2211 | * Serializes all the members of a struct. | |
2212 | * | |
2213 | * @param tstruct The struct to serialize | |
2214 | * @param prefix String prefix to attach to all fields | |
2215 | */ | |
2216 | void t_as3_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) { | |
2217 | (void)tstruct; | |
2218 | out << indent() << prefix << ".write(oprot);" << endl; | |
2219 | } | |
2220 | ||
2221 | /** | |
2222 | * Serializes a container by writing its size then the elements. | |
2223 | * | |
2224 | * @param ttype The type of container | |
2225 | * @param prefix String prefix for fields | |
2226 | */ | |
2227 | void t_as3_generator::generate_serialize_container(ostream& out, t_type* ttype, string prefix) { | |
2228 | scope_up(out); | |
2229 | ||
2230 | if (ttype->is_map()) { | |
2231 | string iter = tmp("_key"); | |
2232 | string counter = tmp("_sizeCounter"); | |
2233 | indent(out) << "var " << counter << ":int = 0;" << endl; | |
2234 | indent(out) << "for (var " << iter << ":* in " << prefix << ") {" << endl; | |
2235 | indent(out) << " " << counter << +"++;" << endl; | |
2236 | indent(out) << "}" << endl; | |
2237 | ||
2238 | indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type()) | |
2239 | << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << counter << "));" | |
2240 | << endl; | |
2241 | } else if (ttype->is_set()) { | |
2242 | indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type()) | |
2243 | << ", " << prefix << ".size));" << endl; | |
2244 | } else if (ttype->is_list()) { | |
2245 | indent(out) << "oprot.writeListBegin(new TList(" | |
2246 | << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));" | |
2247 | << endl; | |
2248 | } | |
2249 | ||
2250 | string iter = tmp("elem"); | |
2251 | if (ttype->is_map()) { | |
2252 | indent(out) << "for (var " << iter << ":* in " << prefix << ")"; | |
2253 | } else if (ttype->is_set()) { | |
2254 | indent(out) << "for each (var " << iter << ":* in " << prefix << ".toArray())"; | |
2255 | } else if (ttype->is_list()) { | |
2256 | indent(out) << "for each (var " << iter << ":* in " << prefix << ")"; | |
2257 | } | |
2258 | ||
2259 | scope_up(out); | |
2260 | ||
2261 | if (ttype->is_map()) { | |
2262 | generate_serialize_map_element(out, (t_map*)ttype, iter, prefix); | |
2263 | } else if (ttype->is_set()) { | |
2264 | generate_serialize_set_element(out, (t_set*)ttype, iter); | |
2265 | } else if (ttype->is_list()) { | |
2266 | generate_serialize_list_element(out, (t_list*)ttype, iter); | |
2267 | } | |
2268 | ||
2269 | scope_down(out); | |
2270 | ||
2271 | if (ttype->is_map()) { | |
2272 | indent(out) << "oprot.writeMapEnd();" << endl; | |
2273 | } else if (ttype->is_set()) { | |
2274 | indent(out) << "oprot.writeSetEnd();" << endl; | |
2275 | } else if (ttype->is_list()) { | |
2276 | indent(out) << "oprot.writeListEnd();" << endl; | |
2277 | } | |
2278 | ||
2279 | scope_down(out); | |
2280 | } | |
2281 | ||
2282 | /** | |
2283 | * Serializes the members of a map. | |
2284 | */ | |
2285 | void t_as3_generator::generate_serialize_map_element(ostream& out, | |
2286 | t_map* tmap, | |
2287 | string iter, | |
2288 | string map) { | |
2289 | t_field kfield(tmap->get_key_type(), iter); | |
2290 | generate_serialize_field(out, &kfield, ""); | |
2291 | t_field vfield(tmap->get_val_type(), map + "[" + iter + "]"); | |
2292 | generate_serialize_field(out, &vfield, ""); | |
2293 | } | |
2294 | ||
2295 | /** | |
2296 | * Serializes the members of a set. | |
2297 | */ | |
2298 | void t_as3_generator::generate_serialize_set_element(ostream& out, t_set* tset, string iter) { | |
2299 | t_field efield(tset->get_elem_type(), iter); | |
2300 | generate_serialize_field(out, &efield, ""); | |
2301 | } | |
2302 | ||
2303 | /** | |
2304 | * Serializes the members of a list. | |
2305 | */ | |
2306 | void t_as3_generator::generate_serialize_list_element(ostream& out, t_list* tlist, string iter) { | |
2307 | t_field efield(tlist->get_elem_type(), iter); | |
2308 | generate_serialize_field(out, &efield, ""); | |
2309 | } | |
2310 | ||
2311 | /** | |
2312 | * Returns a As3 type name | |
2313 | * | |
2314 | * @param ttype The type | |
2315 | * @param container Is the type going inside a container? | |
2316 | * @return As3 type name, i.e. HashMap<Key,Value> | |
2317 | */ | |
2318 | string t_as3_generator::type_name(t_type* ttype, bool in_container, bool in_init) { | |
2319 | (void)in_init; | |
2320 | // In As3 typedefs are just resolved to their real type | |
2321 | ttype = get_true_type(ttype); | |
2322 | string prefix; | |
2323 | ||
2324 | if (ttype->is_base_type()) { | |
2325 | return base_type_name((t_base_type*)ttype, in_container); | |
2326 | } else if (ttype->is_enum()) { | |
2327 | return "int"; | |
2328 | } else if (ttype->is_map()) { | |
2329 | return "Dictionary"; | |
2330 | } else if (ttype->is_set()) { | |
2331 | return "Set"; | |
2332 | } else if (ttype->is_list()) { | |
2333 | return "Array"; | |
2334 | } | |
2335 | ||
2336 | // Check for namespacing | |
2337 | t_program* program = ttype->get_program(); | |
2338 | if (program != NULL && program != program_) { | |
2339 | string package = program->get_namespace("as3"); | |
2340 | if (!package.empty()) { | |
2341 | return package + "." + ttype->get_name(); | |
2342 | } | |
2343 | } | |
2344 | ||
2345 | return ttype->get_name(); | |
2346 | } | |
2347 | ||
2348 | /** | |
2349 | * Returns the AS3 type that corresponds to the thrift type. | |
2350 | * | |
2351 | * @param tbase The base type | |
2352 | * @param container Is it going in a As3 container? | |
2353 | */ | |
2354 | string t_as3_generator::base_type_name(t_base_type* type, bool in_container) { | |
2355 | (void)in_container; | |
2356 | t_base_type::t_base tbase = type->get_base(); | |
2357 | ||
2358 | switch (tbase) { | |
2359 | case t_base_type::TYPE_VOID: | |
2360 | return "void"; | |
2361 | case t_base_type::TYPE_STRING: | |
2362 | if (type->is_binary()) { | |
2363 | return "ByteArray"; | |
2364 | } else { | |
2365 | return "String"; | |
2366 | } | |
2367 | case t_base_type::TYPE_BOOL: | |
2368 | return "Boolean"; | |
2369 | case t_base_type::TYPE_I8: | |
2370 | case t_base_type::TYPE_I16: | |
2371 | case t_base_type::TYPE_I32: | |
2372 | return "int"; | |
2373 | case t_base_type::TYPE_I64: | |
2374 | throw "i64 is not yet supported in as3"; | |
2375 | case t_base_type::TYPE_DOUBLE: | |
2376 | return "Number"; | |
2377 | default: | |
2378 | throw "compiler error: no As3 name for base type " + t_base_type::t_base_name(tbase); | |
2379 | } | |
2380 | } | |
2381 | ||
2382 | /** | |
2383 | * Declares a field, which may include initialization as necessary. | |
2384 | * | |
2385 | * @param ttype The type | |
2386 | */ | |
2387 | string t_as3_generator::declare_field(t_field* tfield, bool init) { | |
2388 | // TODO(mcslee): do we ever need to initialize the field? | |
2389 | string result = "var " + tfield->get_name() + ":" + type_name(tfield->get_type()); | |
2390 | if (init) { | |
2391 | t_type* ttype = get_true_type(tfield->get_type()); | |
2392 | if (ttype->is_base_type() && tfield->get_value() != NULL) { | |
2393 | std::ofstream dummy; | |
2394 | result += " = " + render_const_value(dummy, tfield->get_name(), ttype, tfield->get_value()); | |
2395 | } else if (ttype->is_base_type()) { | |
2396 | t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); | |
2397 | switch (tbase) { | |
2398 | case t_base_type::TYPE_VOID: | |
2399 | throw "NO T_VOID CONSTRUCT"; | |
2400 | case t_base_type::TYPE_STRING: | |
2401 | result += " = null"; | |
2402 | break; | |
2403 | case t_base_type::TYPE_BOOL: | |
2404 | result += " = false"; | |
2405 | break; | |
2406 | case t_base_type::TYPE_I8: | |
2407 | case t_base_type::TYPE_I16: | |
2408 | case t_base_type::TYPE_I32: | |
2409 | case t_base_type::TYPE_I64: | |
2410 | result += " = 0"; | |
2411 | break; | |
2412 | case t_base_type::TYPE_DOUBLE: | |
2413 | result += " = (double)0"; | |
2414 | break; | |
2415 | } | |
2416 | ||
2417 | } else if (ttype->is_enum()) { | |
2418 | result += " = 0"; | |
2419 | } else if (ttype->is_container()) { | |
2420 | result += " = new " + type_name(ttype, false, true) + "()"; | |
2421 | } else { | |
2422 | result += " = new " + type_name(ttype, false, true) + "()"; | |
2423 | ; | |
2424 | } | |
2425 | } | |
2426 | return result + ";"; | |
2427 | } | |
2428 | ||
2429 | /** | |
2430 | * Renders a function signature of the form 'type name(args)' | |
2431 | * | |
2432 | * @param tfunction Function definition | |
2433 | * @return String of rendered function definition | |
2434 | */ | |
2435 | string t_as3_generator::function_signature(t_function* tfunction, string prefix) { | |
2436 | std::string arguments = argument_list(tfunction->get_arglist()); | |
2437 | if (!tfunction->is_oneway()) { | |
2438 | if (arguments != "") { | |
2439 | arguments += ", "; | |
2440 | } | |
2441 | arguments += "onError:Function, onSuccess:Function"; | |
2442 | } | |
2443 | ||
2444 | std::string result = "function " + prefix + tfunction->get_name() + "(" + arguments + "):void"; | |
2445 | return result; | |
2446 | } | |
2447 | ||
2448 | /** | |
2449 | * Renders a comma separated field list, with type names | |
2450 | */ | |
2451 | string t_as3_generator::argument_list(t_struct* tstruct) { | |
2452 | string result = ""; | |
2453 | ||
2454 | const vector<t_field*>& fields = tstruct->get_members(); | |
2455 | vector<t_field*>::const_iterator f_iter; | |
2456 | bool first = true; | |
2457 | for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { | |
2458 | if (first) { | |
2459 | first = false; | |
2460 | } else { | |
2461 | result += ", "; | |
2462 | } | |
2463 | result += (*f_iter)->get_name() + ":" + type_name((*f_iter)->get_type()); | |
2464 | } | |
2465 | return result; | |
2466 | } | |
2467 | ||
2468 | /** | |
2469 | * Converts the parse type to a C++ enum string for the given type. | |
2470 | */ | |
2471 | string t_as3_generator::type_to_enum(t_type* type) { | |
2472 | type = get_true_type(type); | |
2473 | ||
2474 | if (type->is_base_type()) { | |
2475 | t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); | |
2476 | switch (tbase) { | |
2477 | case t_base_type::TYPE_VOID: | |
2478 | throw "NO T_VOID CONSTRUCT"; | |
2479 | case t_base_type::TYPE_STRING: | |
2480 | return "TType.STRING"; | |
2481 | case t_base_type::TYPE_BOOL: | |
2482 | return "TType.BOOL"; | |
2483 | case t_base_type::TYPE_I8: | |
2484 | return "TType.BYTE"; | |
2485 | case t_base_type::TYPE_I16: | |
2486 | return "TType.I16"; | |
2487 | case t_base_type::TYPE_I32: | |
2488 | return "TType.I32"; | |
2489 | case t_base_type::TYPE_I64: | |
2490 | return "TType.I64"; | |
2491 | case t_base_type::TYPE_DOUBLE: | |
2492 | return "TType.DOUBLE"; | |
2493 | } | |
2494 | } else if (type->is_enum()) { | |
2495 | return "TType.I32"; | |
2496 | } else if (type->is_struct() || type->is_xception()) { | |
2497 | return "TType.STRUCT"; | |
2498 | } else if (type->is_map()) { | |
2499 | return "TType.MAP"; | |
2500 | } else if (type->is_set()) { | |
2501 | return "TType.SET"; | |
2502 | } else if (type->is_list()) { | |
2503 | return "TType.LIST"; | |
2504 | } | |
2505 | ||
2506 | throw "INVALID TYPE IN type_to_enum: " + type->get_name(); | |
2507 | } | |
2508 | ||
2509 | /** | |
2510 | * Applies the correct style to a string based on the value of nocamel_style_ | |
2511 | */ | |
2512 | std::string t_as3_generator::get_cap_name(std::string name) { | |
2513 | name[0] = toupper(name[0]); | |
2514 | return name; | |
2515 | } | |
2516 | ||
2517 | string t_as3_generator::constant_name(string name) { | |
2518 | string constant_name; | |
2519 | ||
2520 | bool is_first = true; | |
2521 | bool was_previous_char_upper = false; | |
2522 | for (char character : name) { | |
2523 | bool is_upper = isupper(character); | |
2524 | ||
2525 | if (is_upper && !is_first && !was_previous_char_upper) { | |
2526 | constant_name += '_'; | |
2527 | } | |
2528 | constant_name += toupper(character); | |
2529 | ||
2530 | is_first = false; | |
2531 | was_previous_char_upper = is_upper; | |
2532 | } | |
2533 | ||
2534 | return constant_name; | |
2535 | } | |
2536 | ||
2537 | /** | |
2538 | * Emits a As3Doc comment if the provided object has a doc in Thrift | |
2539 | */ | |
2540 | void t_as3_generator::generate_as3_doc(ostream& out, t_doc* tdoc) { | |
2541 | if (tdoc->has_doc()) { | |
2542 | generate_docstring_comment(out, "/**\n", " * ", tdoc->get_doc(), " */\n"); | |
2543 | } | |
2544 | } | |
2545 | ||
2546 | /** | |
2547 | * Emits a As3Doc comment if the provided function object has a doc in Thrift | |
2548 | */ | |
2549 | void t_as3_generator::generate_as3_doc(ostream& out, t_function* tfunction) { | |
2550 | if (tfunction->has_doc()) { | |
2551 | stringstream ss; | |
2552 | ss << tfunction->get_doc(); | |
2553 | const vector<t_field*>& fields = tfunction->get_arglist()->get_members(); | |
2554 | vector<t_field*>::const_iterator p_iter; | |
2555 | for (p_iter = fields.begin(); p_iter != fields.end(); ++p_iter) { | |
2556 | t_field* p = *p_iter; | |
2557 | ss << "\n@param " << p->get_name(); | |
2558 | if (p->has_doc()) { | |
2559 | ss << " " << p->get_doc(); | |
2560 | } | |
2561 | } | |
2562 | generate_docstring_comment(out, "/**\n", " * ", ss.str(), " */\n"); | |
2563 | } | |
2564 | } | |
2565 | ||
2566 | std::string t_as3_generator::generate_isset_check(t_field* field) { | |
2567 | return generate_isset_check(field->get_name()); | |
2568 | } | |
2569 | ||
2570 | std::string t_as3_generator::generate_isset_check(std::string field_name) { | |
2571 | return "is" + get_cap_name("set") + get_cap_name(field_name) + "()"; | |
2572 | } | |
2573 | ||
2574 | void t_as3_generator::generate_isset_set(ostream& out, t_field* field) { | |
2575 | if (!type_can_be_null(field->get_type())) { | |
2576 | indent(out) << "this.__isset_" << field->get_name() << " = true;" << endl; | |
2577 | } | |
2578 | } | |
2579 | ||
2580 | std::string t_as3_generator::get_enum_class_name(t_type* type) { | |
2581 | string package = ""; | |
2582 | t_program* program = type->get_program(); | |
2583 | if (program != NULL && program != program_) { | |
2584 | package = program->get_namespace("as3") + "."; | |
2585 | } | |
2586 | return package + type->get_name(); | |
2587 | } | |
2588 | ||
2589 | THRIFT_REGISTER_GENERATOR( | |
2590 | as3, | |
2591 | "AS3", | |
2592 | " bindable: Add [bindable] metadata to all the struct classes.\n") |