blob: 26537ac1558135037d6eba944b532ff84c6b931a [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
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 */
Mark Slee7e9eea42007-09-10 21:00:23 +000019
David Reisse21ce4c2008-03-27 21:40:59 +000020#include <string>
21#include <fstream>
22#include <iostream>
23#include <vector>
24
Mark Slee7e9eea42007-09-10 21:00:23 +000025#include <stdlib.h>
26#include <sys/stat.h>
27#include <sstream>
David Reisse21ce4c2008-03-27 21:40:59 +000028#include "t_oop_generator.h"
David Reiss204420f2008-01-11 20:59:03 +000029#include "platform.h"
Mark Slee7e9eea42007-09-10 21:00:23 +000030using namespace std;
31
David Reisse21ce4c2008-03-27 21:40:59 +000032
33/**
34 * Objective-C code generator.
35 *
David Reisse21ce4c2008-03-27 21:40:59 +000036 * mostly copy/pasting/tweaking from mcslee's work.
37 */
38class t_cocoa_generator : public t_oop_generator {
39 public:
40 t_cocoa_generator(
41 t_program* program,
42 const std::map<std::string, std::string>& parsed_options,
43 const std::string& option_string)
44 : t_oop_generator(program)
45 {
Roger Meier3b771a12010-11-17 22:11:26 +000046 (void) option_string;
Andrew McGeachie645d7b82009-07-21 15:30:16 +000047 std::map<std::string, std::string>::const_iterator iter;
48
49 iter = parsed_options.find("log_unexpected");
50 log_unexpected_ = (iter != parsed_options.end());
51
David Reisse21ce4c2008-03-27 21:40:59 +000052 out_dir_base_ = "gen-cocoa";
53 }
54
55 /**
56 * Init and close methods
57 */
58
59 void init_generator();
60 void close_generator();
61
62 void generate_consts(std::vector<t_const*> consts);
63
64 /**
65 * Program-level generation functions
66 */
67
68 void generate_typedef (t_typedef* ttypedef);
69 void generate_enum (t_enum* tenum);
70 void generate_struct (t_struct* tstruct);
71 void generate_xception(t_struct* txception);
72 void generate_service (t_service* tservice);
73
74 void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value);
75 std::string render_const_value(std::string name, t_type* type, t_const_value* value,
76 bool containerize_it=false);
77
78 void generate_cocoa_struct(t_struct* tstruct, bool is_exception);
79 void generate_cocoa_struct_interface(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
80 void generate_cocoa_struct_implementation(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false);
81 void generate_cocoa_struct_initializer_signature(std::ofstream& out,
82 t_struct* tstruct);
Andrew McGeachie3533dcb2009-11-03 18:52:15 +000083 void generate_cocoa_struct_init_with_coder_method(ofstream &out,
84 t_struct* tstruct,
85 bool is_exception);
86 void generate_cocoa_struct_encode_with_coder_method(ofstream &out,
87 t_struct* tstruct,
88 bool is_exception);
David Reisse21ce4c2008-03-27 21:40:59 +000089 void generate_cocoa_struct_field_accessor_declarations(std::ofstream& out,
90 t_struct* tstruct,
91 bool is_exception);
92 void generate_cocoa_struct_field_accessor_implementations(std::ofstream& out,
93 t_struct* tstruct,
94 bool is_exception);
95 void generate_cocoa_struct_reader(std::ofstream& out, t_struct* tstruct);
96 void generate_cocoa_struct_result_writer(std::ofstream& out, t_struct* tstruct);
97 void generate_cocoa_struct_writer(std::ofstream& out, t_struct* tstruct);
98 void generate_cocoa_struct_description(std::ofstream& out, t_struct* tstruct);
99
100 std::string function_result_helper_struct_type(t_function* tfunction);
Andrew McGeachie0c895712009-07-21 21:14:19 +0000101 std::string function_args_helper_struct_type(t_function* tfunction);
David Reisse21ce4c2008-03-27 21:40:59 +0000102 void generate_function_helpers(t_function* tfunction);
103
104 /**
105 * Service-level generation functions
106 */
107
108 void generate_cocoa_service_protocol (std::ofstream& out, t_service* tservice);
109 void generate_cocoa_service_client_interface (std::ofstream& out, t_service* tservice);
110 void generate_cocoa_service_client_implementation (std::ofstream& out, t_service* tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +0000111 void generate_cocoa_service_server_interface (std::ofstream& out, t_service* tservice);
112 void generate_cocoa_service_server_implementation (std::ofstream& out, t_service* tservice);
David Reisse21ce4c2008-03-27 21:40:59 +0000113 void generate_cocoa_service_helpers (t_service* tservice);
114 void generate_service_client (t_service* tservice);
115 void generate_service_server (t_service* tservice);
116 void generate_process_function (t_service* tservice, t_function* tfunction);
117
118 /**
119 * Serialization constructs
120 */
121
122 void generate_deserialize_field (std::ofstream& out,
123 t_field* tfield,
124 std::string fieldName);
125
126 void generate_deserialize_struct (std::ofstream& out,
127 t_struct* tstruct,
128 std::string prefix="");
129
130 void generate_deserialize_container (std::ofstream& out,
131 t_type* ttype,
132 std::string prefix="");
133
134 void generate_deserialize_set_element (std::ofstream& out,
135 t_set* tset,
136 std::string prefix="");
137
138 void generate_deserialize_map_element (std::ofstream& out,
139 t_map* tmap,
140 std::string prefix="");
141
142 void generate_deserialize_list_element (std::ofstream& out,
143 t_list* tlist,
144 std::string prefix="");
145
146 void generate_serialize_field (std::ofstream& out,
147 t_field* tfield,
148 std::string prefix="");
149
150 void generate_serialize_struct (std::ofstream& out,
151 t_struct* tstruct,
152 std::string fieldName="");
153
154 void generate_serialize_container (std::ofstream& out,
155 t_type* ttype,
156 std::string prefix="");
157
158 void generate_serialize_map_element (std::ofstream& out,
159 t_map* tmap,
160 std::string iter,
161 std::string map);
162
163 void generate_serialize_set_element (std::ofstream& out,
164 t_set* tmap,
165 std::string iter);
166
167 void generate_serialize_list_element (std::ofstream& out,
168 t_list* tlist,
169 std::string index,
170 std::string listName);
171
172 /**
173 * Helper rendering functions
174 */
175
176 std::string cocoa_prefix();
177 std::string cocoa_imports();
178 std::string cocoa_thrift_imports();
179 std::string type_name(t_type* ttype, bool class_ref=false);
180 std::string base_type_name(t_base_type* tbase);
181 std::string declare_field(t_field* tfield);
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000182 std::string declare_property(t_field* tfield);
David Reisse21ce4c2008-03-27 21:40:59 +0000183 std::string function_signature(t_function* tfunction);
184 std::string argument_list(t_struct* tstruct);
185 std::string type_to_enum(t_type* ttype);
186 std::string format_string_for_type(t_type* type);
187 std::string call_field_setter(t_field* tfield, std::string fieldName);
188 std::string containerize(t_type * ttype, std::string fieldName);
189 std::string decontainerize(t_field * tfield, std::string fieldName);
190
191 bool type_can_be_null(t_type* ttype) {
192 ttype = get_true_type(ttype);
193
194 return
195 ttype->is_container() ||
196 ttype->is_struct() ||
197 ttype->is_xception() ||
198 ttype->is_string();
199 }
200
201 private:
202
203 std::string cocoa_prefix_;
204 std::string constants_declarations_;
205
206 /**
207 * File streams
208 */
209
210 std::ofstream f_header_;
211 std::ofstream f_impl_;
212
Andrew McGeachie645d7b82009-07-21 15:30:16 +0000213 bool log_unexpected_;
David Reisse21ce4c2008-03-27 21:40:59 +0000214};
215
216
Mark Slee7e9eea42007-09-10 21:00:23 +0000217/**
218 * Prepares for file generation by opening up the necessary file output
219 * streams.
220 */
221void t_cocoa_generator::init_generator() {
222 // Make output directory
David Reiss204420f2008-01-11 20:59:03 +0000223 MKDIR(get_out_dir().c_str());
David Reiss54b602b2008-03-27 21:41:06 +0000224 cocoa_prefix_ = program_->get_namespace("cocoa");
Mark Slee7e9eea42007-09-10 21:00:23 +0000225
226 // we have a .h header file...
227 string f_header_name = program_name_+".h";
dweatherford65b70752007-10-31 02:18:14 +0000228 string f_header_fullname = get_out_dir()+f_header_name;
Mark Slee7e9eea42007-09-10 21:00:23 +0000229 f_header_.open(f_header_fullname.c_str());
230
Mark Slee12a28752007-11-20 23:55:33 +0000231 f_header_ <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000232 autogen_comment() <<
233 endl;
234
235 f_header_ <<
236 cocoa_imports() <<
237 cocoa_thrift_imports();
238
239 // ...and a .m implementation file
dweatherford65b70752007-10-31 02:18:14 +0000240 string f_impl_name = get_out_dir()+program_name_+".m";
Mark Slee7e9eea42007-09-10 21:00:23 +0000241 f_impl_.open(f_impl_name.c_str());
242
Mark Slee12a28752007-11-20 23:55:33 +0000243 f_impl_ <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000244 autogen_comment() <<
245 endl;
246
247 f_impl_ <<
248 cocoa_imports() <<
249 cocoa_thrift_imports() <<
250 "#import \"" << f_header_name << "\"" << endl <<
251 endl;
Mark Slee12a28752007-11-20 23:55:33 +0000252
Mark Slee7e9eea42007-09-10 21:00:23 +0000253}
254
255/**
256 * Prints standard Cocoa imports
257 *
258 * @return List of imports for Cocoa libraries
259 */
260string t_cocoa_generator::cocoa_imports() {
261 return
262 string() +
Andrew McGeachie6db89f22009-07-21 14:45:12 +0000263 "#import <Foundation/Foundation.h>\n" +
Mark Slee7e9eea42007-09-10 21:00:23 +0000264 "\n";
265}
266
267/**
268 * Prints thrift runtime imports
269 *
270 * @return List of imports necessary for thrift runtime
271 */
272string t_cocoa_generator::cocoa_thrift_imports() {
Mark Slee33a7d892007-09-14 19:44:30 +0000273 string result = string() +
Mark Slee7e9eea42007-09-10 21:00:23 +0000274 "#import <TProtocol.h>\n" +
275 "#import <TApplicationException.h>\n" +
276 "#import <TProtocolUtil.h>\n" +
Andrew McGeachie0c895712009-07-21 21:14:19 +0000277 "#import <TProcessor.h>\n" +
Mark Slee7e9eea42007-09-10 21:00:23 +0000278 "\n";
Mark Slee33a7d892007-09-14 19:44:30 +0000279
280 // Include other Thrift includes
281 const vector<t_program*>& includes = program_->get_includes();
282 for (size_t i = 0; i < includes.size(); ++i) {
283 result += "#import \"" + includes[i]->get_name() + ".h\"" + "\n";
284 }
285 result += "\n";
286
287 return result;
Mark Slee7e9eea42007-09-10 21:00:23 +0000288}
289
Mark Slee33a7d892007-09-14 19:44:30 +0000290
Mark Slee7e9eea42007-09-10 21:00:23 +0000291/**
292 * Finish up generation.
293 */
Mark Slee12a28752007-11-20 23:55:33 +0000294void t_cocoa_generator::close_generator()
Mark Slee7e9eea42007-09-10 21:00:23 +0000295{
296 // stick our constants declarations at the end of the header file
297 // since they refer to things we are defining.
David Reissbe975752008-03-27 21:40:52 +0000298 f_header_ << constants_declarations_ << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +0000299}
300
301/**
302 * Generates a typedef. This is just a simple 1-liner in objective-c
303 *
304 * @param ttypedef The type definition
305 */
306void t_cocoa_generator::generate_typedef(t_typedef* ttypedef) {
307 f_header_ <<
308 indent() << "typedef " << type_name(ttypedef->get_type()) << " " << cocoa_prefix_ << ttypedef->get_symbolic() << ";" << endl <<
309 endl;
310}
311
312/**
313 * Generates code for an enumerated type. In Objective-C, this is
314 * essentially the same as the thrift definition itself, using the
315 * enum keyword in Objective-C. For namespace purposes, the name of
316 * the enum plus an underscore is prefixed onto each element.
317 *
318 * @param tenum The enumeration
319 */
320void t_cocoa_generator::generate_enum(t_enum* tenum) {
321 f_header_ <<
322 indent() << "enum " << cocoa_prefix_ << tenum->get_name() << " {" << endl;
323 indent_up();
324
325 vector<t_enum_value*> constants = tenum->get_constants();
326 vector<t_enum_value*>::iterator c_iter;
327 bool first = true;
328 for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) {
329 if (first) {
330 first = false;
331 } else {
332 f_header_ <<
333 "," << endl;
334 }
335 f_header_ <<
336 indent() << tenum->get_name() << "_" << (*c_iter)->get_name();
Bryan Duxburya406b902010-09-27 23:37:44 +0000337 f_header_ <<
338 " = " << (*c_iter)->get_value();
Mark Slee7e9eea42007-09-10 21:00:23 +0000339 }
340
341 indent_down();
342 f_header_ <<
343 endl <<
344 "};" << endl <<
345 endl;
346}
347
348/**
349 * Generates a class that holds all the constants. Primitive values
350 * could have been placed outside this class, but I just put
351 * everything in for consistency.
352 */
353void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) {
354 std::ostringstream const_interface;
355 string constants_class_name = cocoa_prefix_ + program_name_ + "Constants";
356
Andrew McGeachie853bdfe2009-05-05 00:44:48 +0000357 const_interface << "@interface " << constants_class_name << " : NSObject ";
Mark Slee7e9eea42007-09-10 21:00:23 +0000358 scope_up(const_interface);
359 scope_down(const_interface);
360
361 // getter method for each constant defined.
362 vector<t_const*>::iterator c_iter;
363 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
364 string name = (*c_iter)->get_name();
365 t_type* type = (*c_iter)->get_type();
366 const_interface <<
367 "+ (" << type_name(type) << ") " << name << ";" << endl;
368 }
369
370 const_interface << "@end";
371
372 // this gets spit into the header file in ::close_generator
373 constants_declarations_ = const_interface.str();
374
375 // static variables in the .m hold all constant values
376 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
377 string name = (*c_iter)->get_name();
378 t_type* type = (*c_iter)->get_type();
379 f_impl_ <<
380 "static " << type_name(type) << " " << cocoa_prefix_ << name;
381 if (!type->is_container() && !type->is_struct()) {
382 f_impl_ << " = " << render_const_value(name, type, (*c_iter)->get_value());
383 }
384 f_impl_ << ";" << endl;
385 }
386 f_impl_ << endl;
387
388 f_impl_ << "@implementation " << constants_class_name << endl;
389
390 // initialize complex constants when the class is loaded
391 f_impl_ << "+ (void) initialize ";
392 scope_up(f_impl_);
393
394 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
395 if ((*c_iter)->get_type()->is_container() ||
396 (*c_iter)->get_type()->is_struct()) {
397 string name = (*c_iter)->get_name();
Andrew McGeachie75649252009-07-21 16:12:33 +0000398 f_impl_ << indent() << cocoa_prefix_ << name << " = " << render_const_value(name,
Mark Slee7e9eea42007-09-10 21:00:23 +0000399 (*c_iter)->get_type(),
400 (*c_iter)->get_value());
401 f_impl_ << ";" << endl;
402 }
403 }
404 scope_down(f_impl_);
405
406 // getter method for each constant
407 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
408 string name = (*c_iter)->get_name();
409 t_type* type = (*c_iter)->get_type();
410 f_impl_ <<
411 "+ (" << type_name(type) << ") " << name;
412 scope_up(f_impl_);
Andrew McGeachie75649252009-07-21 16:12:33 +0000413 indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +0000414 scope_down(f_impl_);
415 }
416
417 f_impl_ << "@end" << endl << endl;
418}
419
420
421/**
422 * Generates a struct definition for a thrift data type. This is a class
423 * with protected data members, read(), write(), and getters and setters.
424 *
425 * @param tstruct The struct definition
426 */
427void t_cocoa_generator::generate_struct(t_struct* tstruct) {
428 generate_cocoa_struct_interface(f_header_, tstruct, false);
429 generate_cocoa_struct_implementation(f_impl_, tstruct, false);
430}
431
432/**
433 * Exceptions are structs, but they inherit from NSException
434 *
435 * @param tstruct The struct definition
436 */
437void t_cocoa_generator::generate_xception(t_struct* txception) {
438 generate_cocoa_struct_interface(f_header_, txception, true);
439 generate_cocoa_struct_implementation(f_impl_, txception, true);
440}
441
442
443/**
444 * Generate the interface for a struct
445 *
446 * @param tstruct The struct definition
447 */
448void t_cocoa_generator::generate_cocoa_struct_interface(ofstream &out,
449 t_struct* tstruct,
450 bool is_exception) {
451 out << "@interface " << cocoa_prefix_ << tstruct->get_name() << " : ";
Mark Slee12a28752007-11-20 23:55:33 +0000452
Mark Slee7e9eea42007-09-10 21:00:23 +0000453 if (is_exception) {
454 out << "NSException ";
455 } else {
456 out << "NSObject ";
Mark Slee12a28752007-11-20 23:55:33 +0000457 }
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000458 out << "<NSCoding> ";
Mark Slee12a28752007-11-20 23:55:33 +0000459
Mark Slee7e9eea42007-09-10 21:00:23 +0000460 scope_up(out);
461
462 // members are protected. this is redundant, but explicit.
463 // f_header_ << endl << "@protected:" << endl;
464
465 const vector<t_field*>& members = tstruct->get_members();
466
467 // member varialbes
Mark Slee12a28752007-11-20 23:55:33 +0000468 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000469 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
470 out << indent() << declare_field(*m_iter) << endl;
471 }
472
473 if (members.size() > 0) {
474 out << endl;
475 // isset fields
476 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
477 indent(out) <<
478 "BOOL __" << (*m_iter)->get_name() << "_isset;" << endl;
479 }
480 }
481
482 scope_down(out);
483 out << endl;
484
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000485 // properties
486 if (members.size() > 0) {
487 out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" << endl;
488 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
489 out << indent() << declare_property(*m_iter) << endl;
490 }
491 out << "#endif" << endl << endl;
492 }
493
Mark Slee7e9eea42007-09-10 21:00:23 +0000494 // initializer for all fields
495 if (!members.empty()) {
496 generate_cocoa_struct_initializer_signature(out, tstruct);
497 out << ";" << endl;
498 }
499 out << endl;
500
Mark Slee33a7d892007-09-14 19:44:30 +0000501 // read and write
502 out << "- (void) read: (id <TProtocol>) inProtocol;" << endl;
503 out << "- (void) write: (id <TProtocol>) outProtocol;" << endl;
504 out << endl;
505
Mark Slee7e9eea42007-09-10 21:00:23 +0000506 // getters and setters
507 generate_cocoa_struct_field_accessor_declarations(out, tstruct, is_exception);
508
509 out << "@end" << endl << endl;
510}
511
512
513/**
514 * Generate signature for initializer of struct with a parameter for
515 * each field.
516 */
517void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ofstream &out,
518 t_struct* tstruct) {
519 const vector<t_field*>& members = tstruct->get_members();
Mark Slee12a28752007-11-20 23:55:33 +0000520 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000521 indent(out) << "- (id) initWith";
522 for (m_iter = members.begin(); m_iter != members.end(); ) {
523 if (m_iter == members.begin()) {
524 out << capitalize((*m_iter)->get_name());
525 } else {
526 out << (*m_iter)->get_name();
527 }
528 out << ": (" << type_name((*m_iter)->get_type()) << ") " <<
529 (*m_iter)->get_name();
530 ++m_iter;
531 if (m_iter != members.end()) {
532 out << " ";
533 }
534 }
535}
536
537
538/**
539 * Generate getter and setter declarations for all fields, plus an
540 * IsSet getter.
541 */
542void t_cocoa_generator::generate_cocoa_struct_field_accessor_declarations(ofstream &out,
543 t_struct* tstruct,
544 bool is_exception) {
Roger Meier3b771a12010-11-17 22:11:26 +0000545 (void) is_exception;
Mark Slee7e9eea42007-09-10 21:00:23 +0000546 const vector<t_field*>& members = tstruct->get_members();
Mark Slee12a28752007-11-20 23:55:33 +0000547 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000548 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
549 out << indent() << "- (" << type_name((*m_iter)->get_type()) << ") " << decapitalize((*m_iter)->get_name()) << ";" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000550 out << indent() << "- (void) set" << capitalize((*m_iter)->get_name()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000551 ": (" << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name() << ";" << endl;
552 out << indent() << "- (BOOL) " << (*m_iter)->get_name() << "IsSet;" << endl << endl;
553 }
554}
555
556
557/**
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000558 * Generate the initWithCoder method for this struct so it's compatible with
559 * the NSCoding protocol
560 */
561void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream &out,
562 t_struct* tstruct,
563 bool is_exception)
564{
565 indent(out) << "- (id) initWithCoder: (NSCoder *) decoder" << endl;
566 scope_up(out);
567 if (is_exception) {
568 // NSExceptions conform to NSCoding, so we can call super
569 out << indent() << "self = [super initWithCoder: decoder];" << endl;
570 } else {
571 out << indent() << "self = [super init];" << endl;
572 }
573
574 const vector<t_field*>& members = tstruct->get_members();
575 vector<t_field*>::const_iterator m_iter;
576
577 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
578 t_type* t = get_true_type((*m_iter)->get_type());
579 out << indent() << "if ([decoder containsValueForKey: @\""<< (*m_iter)->get_name() <<"\"])" << endl;
580 scope_up(out);
581 out << indent() << "__" << (*m_iter)->get_name() << " = ";
582 if (type_can_be_null(t))
583 {
584 out << "[[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"] retain];" << endl;
585 }
586 else if (t->is_enum())
587 {
588 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
589 }
590 else
591 {
592 t_base_type::t_base tbase = ((t_base_type *) t)->get_base();
593 switch (tbase)
594 {
595 case t_base_type::TYPE_BOOL:
596 out << "[decoder decodeBoolForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
597 break;
598 case t_base_type::TYPE_BYTE:
599 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
600 break;
601 case t_base_type::TYPE_I16:
602 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
603 break;
604 case t_base_type::TYPE_I32:
605 out << "[decoder decodeInt32ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
606 break;
607 case t_base_type::TYPE_I64:
608 out << "[decoder decodeInt64ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
609 break;
610 case t_base_type::TYPE_DOUBLE:
611 out << "[decoder decodeDoubleForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
612 break;
613 default:
614 throw "compiler error: don't know how to decode thrift type: " + t_base_type::t_base_name(tbase);
615 }
616 }
617 out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl;
618 scope_down(out);
619 }
620
621 out << indent() << "return self;" << endl;
622 scope_down(out);
623 out << endl;
624}
625
626
627/**
628 * Generate the encodeWithCoder method for this struct so it's compatible with
629 * the NSCoding protocol
630 */
631void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream &out,
632 t_struct* tstruct,
633 bool is_exception)
634{
635 indent(out) << "- (void) encodeWithCoder: (NSCoder *) encoder" << endl;
636 scope_up(out);
637 if (is_exception) {
638 // NSExceptions conform to NSCoding, so we can call super
639 out << indent() << "[super encodeWithCoder: encoder];" << endl;
640 }
641
642 const vector<t_field*>& members = tstruct->get_members();
643 vector<t_field*>::const_iterator m_iter;
644
645 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
646 t_type* t = get_true_type((*m_iter)->get_type());
647 out << indent() << "if (__"<< (*m_iter)->get_name() <<"_isset)" << endl;
648 scope_up(out);
649 //out << indent() << "__" << (*m_iter)->get_name() << " = ";
650 if (type_can_be_null(t))
651 {
652 out << indent() << "[encoder encodeObject: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
653 }
654 else if (t->is_enum())
655 {
656 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
657 }
658 else
659 {
660 t_base_type::t_base tbase = ((t_base_type *) t)->get_base();
661 switch (tbase)
662 {
663 case t_base_type::TYPE_BOOL:
664 out << indent() << "[encoder encodeBool: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
665 break;
666 case t_base_type::TYPE_BYTE:
667 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
668 break;
669 case t_base_type::TYPE_I16:
670 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
671 break;
672 case t_base_type::TYPE_I32:
673 out << indent() << "[encoder encodeInt32: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
674 break;
675 case t_base_type::TYPE_I64:
676 out << indent() << "[encoder encodeInt64: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
677 break;
678 case t_base_type::TYPE_DOUBLE:
679 out << indent() << "[encoder encodeDouble: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
680 break;
681 default:
682 throw "compiler error: don't know how to encode thrift type: " + t_base_type::t_base_name(tbase);
683 }
684 }
685 scope_down(out);
686 }
687
688 scope_down(out);
689 out << endl;
690}
691
692
693/**
Mark Slee7e9eea42007-09-10 21:00:23 +0000694 * Generate struct implementation.
695 *
696 * @param tstruct The struct definition
697 * @param is_exception Is this an exception?
698 * @param is_result If this is a result it needs a different writer
699 */
700void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream &out,
701 t_struct* tstruct,
702 bool is_exception,
703 bool is_result) {
704 indent(out) <<
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000705 "@implementation " << cocoa_prefix_ << tstruct->get_name() << endl << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +0000706
707 // exceptions need to call the designated initializer on NSException
708 if (is_exception) {
709 out << indent() << "- (id) init" << endl;
710 scope_up(out);
711 out << indent() << "return [super initWithName: @\"" << tstruct->get_name() <<
712 "\" reason: @\"unknown\" userInfo: nil];" << endl;
713 scope_down(out);
714 }
715
Mark Slee7e9eea42007-09-10 21:00:23 +0000716 const vector<t_field*>& members = tstruct->get_members();
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000717 vector<t_field*>::const_iterator m_iter;
718
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000719 // initializer with all fields as params
Mark Slee7e9eea42007-09-10 21:00:23 +0000720 if (!members.empty()) {
721 generate_cocoa_struct_initializer_signature(out, tstruct);
722 out << endl;
723 scope_up(out);
724 if (is_exception) {
725 out << indent() << "self = [self init];" << endl;
726 } else {
727 out << indent() << "self = [super init];" << endl;
728 }
729
Mark Slee7e9eea42007-09-10 21:00:23 +0000730 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
731 t_type* t = get_true_type((*m_iter)->get_type());
732 out << indent() << "__" << (*m_iter)->get_name() << " = ";
733 if (type_can_be_null(t)) {
734 out << "[" << (*m_iter)->get_name() << " retain];" << endl;
735 } else {
736 out << (*m_iter)->get_name() << ";" << endl;
737 }
738 out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl;
739 }
740
741 out << indent() << "return self;" << endl;
742 scope_down(out);
743 out << endl;
744 }
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000745
746 // initWithCoder for NSCoding
747 generate_cocoa_struct_init_with_coder_method(out, tstruct, is_exception);
748 // encodeWithCoder for NSCoding
749 generate_cocoa_struct_encode_with_coder_method(out, tstruct, is_exception);
Mark Slee7e9eea42007-09-10 21:00:23 +0000750
751 // dealloc
752 if (!members.empty()) {
753 out << "- (void) dealloc" << endl;
754 scope_up(out);
755
Mark Slee7e9eea42007-09-10 21:00:23 +0000756 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
757 t_type* t = get_true_type((*m_iter)->get_type());
758 if (type_can_be_null(t)) {
759 indent(out) << "[__" << (*m_iter)->get_name() << " release];" << endl;
760 }
761 }
762
763 out << indent() << "[super dealloc];" << endl;
764 scope_down(out);
765 out << endl;
766 }
767
768 // the rest of the methods
769 generate_cocoa_struct_field_accessor_implementations(out, tstruct, is_exception);
770 generate_cocoa_struct_reader(out, tstruct);
771 if (is_result) {
772 generate_cocoa_struct_result_writer(out, tstruct);
773 } else {
774 generate_cocoa_struct_writer(out, tstruct);
775 }
776 generate_cocoa_struct_description(out, tstruct);
777
778 out << "@end" << endl << endl;
779}
780
781
782/**
783 * Generates a function to read all the fields of the struct.
784 *
785 * @param tstruct The struct definition
786 */
787void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out,
788 t_struct* tstruct) {
789 out <<
790 "- (void) read: (id <TProtocol>) inProtocol" << endl;
791 scope_up(out);
792
793 const vector<t_field*>& fields = tstruct->get_members();
794 vector<t_field*>::const_iterator f_iter;
795
796 // Declare stack tmp variables
797 indent(out) << "NSString * fieldName;" << endl;
798 indent(out) << "int fieldType;" << endl;
799 indent(out) << "int fieldID;" << endl;
800 out << endl;
801
Mark Slee33a7d892007-09-14 19:44:30 +0000802 indent(out) << "[inProtocol readStructBeginReturningName: NULL];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000803
Mark Slee7e9eea42007-09-10 21:00:23 +0000804 // Loop over reading in fields
805 indent(out) <<
806 "while (true)" << endl;
807 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +0000808
Mark Slee7e9eea42007-09-10 21:00:23 +0000809 // Read beginning field marker
810 indent(out) <<
Mark Slee33a7d892007-09-14 19:44:30 +0000811 "[inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000812
Mark Slee7e9eea42007-09-10 21:00:23 +0000813 // Check for field STOP marker and break
814 indent(out) <<
815 "if (fieldType == TType_STOP) { " << endl;
816 indent_up();
817 indent(out) <<
818 "break;" << endl;
819 indent_down();
820 indent(out) <<
821 "}" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000822
Mark Slee7e9eea42007-09-10 21:00:23 +0000823 // Switch statement on the field we are reading
824 indent(out) <<
825 "switch (fieldID)" << endl;
826
827 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +0000828
Mark Slee7e9eea42007-09-10 21:00:23 +0000829 // Generate deserialization code for known cases
830 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
831 indent(out) <<
832 "case " << (*f_iter)->get_key() << ":" << endl;
833 indent_up();
834 indent(out) <<
835 "if (fieldType == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
836 indent_up();
837
838 generate_deserialize_field(out, *f_iter, "fieldValue");
839 indent(out) << call_field_setter(*f_iter, "fieldValue") << endl;
840 // if this is an allocated field, release it since the struct
841 // is now retaining it
842 if (type_can_be_null((*f_iter)->get_type())) {
Mark Slee12a28752007-11-20 23:55:33 +0000843 // deserialized strings are autorelease, so don't release them
Mark Sleea9387af2007-11-21 22:05:50 +0000844 if (!(get_true_type((*f_iter)->get_type())->is_string())) {
Mark Slee12a28752007-11-20 23:55:33 +0000845 indent(out) << "[fieldValue release];" << endl;
846 }
Mark Slee7e9eea42007-09-10 21:00:23 +0000847 }
848
849 indent_down();
Andrew McGeachie645d7b82009-07-21 15:30:16 +0000850 out << indent() << "} else { " << endl;
851 if (log_unexpected_) {
852 out << indent() << " NSLog(@\"%s: field ID %i has unexpected type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl;
853 }
854 out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000855 indent() << "}" << endl <<
856 indent() << "break;" << endl;
857 indent_down();
858 }
Mark Slee12a28752007-11-20 23:55:33 +0000859
Mark Slee7e9eea42007-09-10 21:00:23 +0000860 // In the default case we skip the field
Andrew McGeachie645d7b82009-07-21 15:30:16 +0000861 out << indent() << "default:" << endl;
862 if (log_unexpected_) {
863 out << indent() << " NSLog(@\"%s: unexpected field ID %i with type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl;
864 }
865 out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000866 indent() << " break;" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000867
Mark Slee7e9eea42007-09-10 21:00:23 +0000868 scope_down(out);
869
870 // Read field end marker
871 indent(out) <<
872 "[inProtocol readFieldEnd];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000873
Mark Slee7e9eea42007-09-10 21:00:23 +0000874 scope_down(out);
Mark Slee12a28752007-11-20 23:55:33 +0000875
Mark Slee7e9eea42007-09-10 21:00:23 +0000876 out <<
877 indent() << "[inProtocol readStructEnd];" << endl;
878
879 indent_down();
880 out <<
881 indent() << "}" << endl <<
882 endl;
883}
884
885/**
886 * Generates a function to write all the fields of the struct
887 *
888 * @param tstruct The struct definition
889 */
890void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out,
891 t_struct* tstruct) {
892 out <<
893 indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl;
894 indent_up();
895
896 string name = tstruct->get_name();
897 const vector<t_field*>& fields = tstruct->get_members();
898 vector<t_field*>::const_iterator f_iter;
899
900 out <<
901 indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl;
902
903 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
Mark Slee33a7d892007-09-14 19:44:30 +0000904 out <<
905 indent() << "if (__" << (*f_iter)->get_name() << "_isset) {" << endl;
906 indent_up();
Mark Slee7e9eea42007-09-10 21:00:23 +0000907 bool null_allowed = type_can_be_null((*f_iter)->get_type());
908 if (null_allowed) {
909 out <<
910 indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl;
911 indent_up();
912 }
913
Mark Slee12a28752007-11-20 23:55:33 +0000914 indent(out) << "[outProtocol writeFieldBeginWithName: @\"" <<
915 (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000916 " fieldID: " << (*f_iter)->get_key() << "];" << endl;
917
918 // Write field contents
919 generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name());
920
921 // Write field closer
922 indent(out) <<
923 "[outProtocol writeFieldEnd];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000924
Mark Slee7e9eea42007-09-10 21:00:23 +0000925 if (null_allowed) {
Mark Slee33a7d892007-09-14 19:44:30 +0000926 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +0000927 }
Mark Slee33a7d892007-09-14 19:44:30 +0000928 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +0000929 }
930 // Write the struct map
931 out <<
932 indent() << "[outProtocol writeFieldStop];" << endl <<
933 indent() << "[outProtocol writeStructEnd];" << endl;
934
935 indent_down();
936 out <<
937 indent() << "}" << endl <<
938 endl;
939}
940
941/**
942 * Generates a function to write all the fields of the struct, which
943 * is a function result. These fields are only written if they are
944 * set, and only one of them can be set at a time.
945 *
946 * @param tstruct The struct definition
947 */
948void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out,
949 t_struct* tstruct) {
950 out <<
951 indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl;
952 indent_up();
953
954 string name = tstruct->get_name();
955 const vector<t_field*>& fields = tstruct->get_members();
956 vector<t_field*>::const_iterator f_iter;
957
958 out <<
959 indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl;
960
961 bool first = true;
962 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
963 if (first) {
964 first = false;
965 out <<
966 endl <<
967 indent() << "if ";
968 } else {
969 out <<
970 " else if ";
971 }
972
973 out <<
974 "(__" << (*f_iter)->get_name() << "_isset) {" << endl;
975 indent_up();
976
977 bool null_allowed = type_can_be_null((*f_iter)->get_type());
978 if (null_allowed) {
979 out <<
980 indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl;
981 indent_up();
982 }
983
984 indent(out) << "[outProtocol writeFieldBeginWithName: @\"" <<
Mark Slee12a28752007-11-20 23:55:33 +0000985 (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000986 " fieldID: " << (*f_iter)->get_key() << "];" << endl;
987
988 // Write field contents
989 generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name());
990
991 // Write field closer
992 indent(out) <<
993 "[outProtocol writeFieldEnd];" << endl;
994
995 if (null_allowed) {
996 indent_down();
997 indent(out) << "}" << endl;
998 }
999
1000 indent_down();
1001 indent(out) << "}";
1002 }
1003 // Write the struct map
1004 out <<
1005 endl <<
1006 indent() << "[outProtocol writeFieldStop];" << endl <<
1007 indent() << "[outProtocol writeStructEnd];" << endl;
1008
1009 indent_down();
1010 out <<
1011 indent() << "}" << endl <<
1012 endl;
1013}
1014
1015/**
1016 * Generate property accessor methods for all fields in the struct.
1017 * getter, setter, isset getter.
1018 *
1019 * @param tstruct The struct definition
1020 */
1021void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofstream& out,
1022 t_struct* tstruct,
1023 bool is_exception) {
Roger Meier3b771a12010-11-17 22:11:26 +00001024 (void) is_exception;
Mark Slee7e9eea42007-09-10 21:00:23 +00001025 const vector<t_field*>& fields = tstruct->get_members();
1026 vector<t_field*>::const_iterator f_iter;
1027 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1028 t_field* field = *f_iter;
1029 t_type* type = get_true_type(field->get_type());
1030 std::string field_name = field->get_name();
1031 std::string cap_name = field_name;
1032 cap_name[0] = toupper(cap_name[0]);
1033
1034 // Simple getter
1035 indent(out) << "- (" << type_name(type) << ") ";
1036 out << field_name << " {" << endl;
1037 indent_up();
1038 if (!type_can_be_null(type)) {
1039 indent(out) << "return __" << field_name << ";" << endl;
1040 } else {
1041 indent(out) << "return [[__" << field_name << " retain] autorelease];" << endl;
1042 }
1043 indent_down();
1044 indent(out) << "}" << endl << endl;
1045
1046 // Simple setter
1047 indent(out) << "- (void) set" << cap_name << ": (" << type_name(type) <<
1048 ") " << field_name << " {" << endl;
1049 indent_up();
1050 if (!type_can_be_null(type)) {
1051 indent(out) << "__" << field_name << " = " << field_name << ";" << endl;
1052 } else {
1053 indent(out) << "[" << field_name << " retain];" << endl;
1054 indent(out) << "[__" << field_name << " release];" << endl;
1055 indent(out) << "__" << field_name << " = " << field_name << ";" << endl;
1056 }
1057 indent(out) << "__" << field_name << "_isset = YES;" << endl;
1058 indent_down();
1059 indent(out) << "}" << endl << endl;
1060
1061 // IsSet
1062 indent(out) << "- (BOOL) " << field_name << "IsSet {" << endl;
1063 indent_up();
1064 indent(out) << "return __" << field_name << "_isset;" << endl;
1065 indent_down();
1066 indent(out) << "}" << endl << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001067
Mark Slee7e9eea42007-09-10 21:00:23 +00001068 // Unsetter - do we need this?
1069 indent(out) << "- (void) unset" << cap_name << " {" << endl;
1070 indent_up();
1071 if (type_can_be_null(type)) {
1072 indent(out) << "[__" << field_name << " release];" << endl;
1073 indent(out) << "__" << field_name << " = nil;" << endl;
1074 }
1075 indent(out) << "__" << field_name << "_isset = NO;" << endl;
1076 indent_down();
1077 indent(out) << "}" << endl << endl;
1078 }
1079}
1080
1081/**
1082 * Generates a description method for the given struct
1083 *
1084 * @param tstruct The struct definition
1085 */
1086void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out,
1087 t_struct* tstruct) {
1088 out <<
1089 indent() << "- (NSString *) description {" << endl;
1090 indent_up();
1091
1092 out <<
Mark Slee12a28752007-11-20 23:55:33 +00001093 indent() << "NSMutableString * ms = [NSMutableString stringWithString: @\"" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001094 tstruct->get_name() << "(\"];" << endl;
1095
1096 const vector<t_field*>& fields = tstruct->get_members();
1097 vector<t_field*>::const_iterator f_iter;
1098 bool first = true;
1099 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1100 if (first) {
1101 first = false;
1102 indent(out) << "[ms appendString: @\"" << (*f_iter)->get_name() << ":\"];" << endl;
1103 } else {
1104 indent(out) << "[ms appendString: @\"," << (*f_iter)->get_name() << ":\"];" << endl;
1105 }
1106 t_type* ttype = (*f_iter)->get_type();
1107 indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", __" <<
1108 (*f_iter)->get_name() << "];" << endl;
1109 }
1110 out <<
1111 indent() << "[ms appendString: @\")\"];" << endl <<
Andrew McGeachie38e1a102009-07-21 17:22:03 +00001112 indent() << "return [NSString stringWithString: ms];" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +00001113
1114 indent_down();
1115 indent(out) << "}" << endl <<
1116 endl;
1117}
1118
1119
1120/**
1121 * Generates a thrift service. In Objective-C this consists of a
1122 * protocol definition, a client interface and a client implementation.
1123 *
1124 * @param tservice The service definition
1125 */
1126void t_cocoa_generator::generate_service(t_service* tservice) {
1127 generate_cocoa_service_protocol(f_header_, tservice);
1128 generate_cocoa_service_client_interface(f_header_, tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001129 generate_cocoa_service_server_interface(f_header_, tservice);
Mark Slee7e9eea42007-09-10 21:00:23 +00001130 generate_cocoa_service_helpers(tservice);
1131 generate_cocoa_service_client_implementation(f_impl_, tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001132 generate_cocoa_service_server_implementation(f_impl_, tservice);
Mark Slee7e9eea42007-09-10 21:00:23 +00001133}
1134
1135
1136/**
1137 * Generates structs for all the service return types
1138 *
1139 * @param tservice The service
1140 */
1141void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) {
1142 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001143 vector<t_function*>::iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001144 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
Andrew McGeachie0c895712009-07-21 21:14:19 +00001145 t_struct* ts = (*f_iter)->get_arglist();
1146 generate_cocoa_struct_interface(f_impl_, ts, false);
1147 generate_cocoa_struct_implementation(f_impl_, ts, false, false);
Mark Slee7e9eea42007-09-10 21:00:23 +00001148 generate_function_helpers(*f_iter);
1149 }
1150}
1151
1152string t_cocoa_generator::function_result_helper_struct_type(t_function* tfunction) {
Bryan Duxbury773b8db2010-09-02 00:43:16 +00001153 if (tfunction->is_oneway()) {
1154 return capitalize(tfunction->get_name());
1155 } else {
1156 return capitalize(tfunction->get_name()) + "_result";
1157 }
Andrew McGeachie0c895712009-07-21 21:14:19 +00001158}
1159
1160
1161string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction) {
1162 return tfunction->get_name() + "_args";
Mark Slee7e9eea42007-09-10 21:00:23 +00001163}
1164
1165
1166/**
1167 * Generates a struct and helpers for a function.
1168 *
1169 * @param tfunction The function
1170 */
1171void t_cocoa_generator::generate_function_helpers(t_function* tfunction) {
David Reiss47329252009-03-24 20:01:02 +00001172 if (tfunction->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001173 return;
1174 }
1175
1176 // create a result struct with a success field of the return type,
1177 // and a field for each type of exception thrown
1178 t_struct result(program_, function_result_helper_struct_type(tfunction));
1179 t_field success(tfunction->get_returntype(), "success", 0);
1180 if (!tfunction->get_returntype()->is_void()) {
1181 result.append(&success);
1182 }
1183
1184 t_struct* xs = tfunction->get_xceptions();
1185 const vector<t_field*>& fields = xs->get_members();
1186 vector<t_field*>::const_iterator f_iter;
1187 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1188 result.append(*f_iter);
1189 }
1190
1191 // generate the result struct
1192 generate_cocoa_struct_interface(f_impl_, &result, false);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001193 generate_cocoa_struct_implementation(f_impl_, &result, false, true);
Mark Slee7e9eea42007-09-10 21:00:23 +00001194}
1195
Andrew McGeachie0c895712009-07-21 21:14:19 +00001196
Mark Slee7e9eea42007-09-10 21:00:23 +00001197/**
1198 * Generates a service protocol definition.
1199 *
1200 * @param tservice The service to generate a protocol definition for
1201 */
1202void t_cocoa_generator::generate_cocoa_service_protocol(ofstream& out,
1203 t_service* tservice) {
1204 out << "@protocol " << cocoa_prefix_ << tservice->get_name() << " <NSObject>" << endl;
1205
1206 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001207 vector<t_function*>::iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001208 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
Mark Slee12a28752007-11-20 23:55:33 +00001209 out << "- " << function_signature(*f_iter) << ";" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001210 " // throws ";
1211 t_struct* xs = (*f_iter)->get_xceptions();
1212 const std::vector<t_field*>& xceptions = xs->get_members();
1213 vector<t_field*>::const_iterator x_iter;
1214 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1215 out << type_name((*x_iter)->get_type()) + ", ";
1216 }
1217 out << "TException" << endl;
1218 }
1219 out << "@end" << endl << endl;
1220}
1221
1222
1223/**
1224 * Generates a service client interface definition.
1225 *
1226 * @param tservice The service to generate a client interface definition for
1227 */
1228void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out,
1229 t_service* tservice) {
Mark Slee12a28752007-11-20 23:55:33 +00001230 out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Client : NSObject <" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001231 cocoa_prefix_ << tservice->get_name() << "> ";
1232
1233 scope_up(out);
1234 out << indent() << "id <TProtocol> inProtocol;" << endl;
1235 out << indent() << "id <TProtocol> outProtocol;" << endl;
1236 scope_down(out);
1237
1238 out << "- (id) initWithProtocol: (id <TProtocol>) protocol;" << endl;
1239 out << "- (id) initWithInProtocol: (id <TProtocol>) inProtocol outProtocol: (id <TProtocol>) outProtocol;" << endl;
1240 out << "@end" << endl << endl;
1241}
1242
1243
1244/**
Andrew McGeachie0c895712009-07-21 21:14:19 +00001245 * Generates a service server interface definition. In other words, the TProcess implementation for the
1246 * service definition.
1247 *
1248 * @param tservice The service to generate a client interface definition for
1249 */
1250void t_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out,
1251 t_service* tservice) {
1252 out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Processor : NSObject <TProcessor> ";
1253
1254 scope_up(out);
1255 out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() <<"> mService;" << endl;
1256 out << indent() << "NSDictionary * mMethodMap;" << endl;
1257 scope_down(out);
1258
1259 out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
1260 out << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
1261
1262 out << "@end" << endl << endl;
1263}
1264
1265
1266/**
Mark Slee7e9eea42007-09-10 21:00:23 +00001267 * Generates a service client implementation.
1268 *
1269 * @param tservice The service to generate an implementation for
1270 */
1271void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& out,
1272 t_service* tservice) {
1273 out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Client" << endl;
1274
1275 // initializers
1276 out << "- (id) initWithProtocol: (id <TProtocol>) protocol" << endl;
1277 scope_up(out);
1278 out << indent() << "return [self initWithInProtocol: protocol outProtocol: protocol];" << endl;
1279 scope_down(out);
1280 out << endl;
1281
1282 out << "- (id) initWithInProtocol: (id <TProtocol>) anInProtocol outProtocol: (id <TProtocol>) anOutProtocol" << endl;
1283 scope_up(out);
1284 out << indent() << "[super init];" << endl;
1285 out << indent() << "inProtocol = [anInProtocol retain];" << endl;
1286 out << indent() << "outProtocol = [anOutProtocol retain];" << endl;
1287 out << indent() << "return self;" << endl;
1288 scope_down(out);
1289 out << endl;
1290
1291 // dealloc
1292 out << "- (void) dealloc" << endl;
1293 scope_up(out);
1294 out << indent() << "[inProtocol release];" << endl;
1295 out << indent() << "[outProtocol release];" << endl;
1296 out << indent() << "[super dealloc];" << endl;
1297 scope_down(out);
1298 out << endl;
1299
1300 // generate client method implementations
1301 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001302 vector<t_function*>::const_iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001303 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1304 string funname = (*f_iter)->get_name();
1305
1306 t_function send_function(g_type_void,
1307 string("send_") + (*f_iter)->get_name(),
1308 (*f_iter)->get_arglist());
1309
1310 string argsname = (*f_iter)->get_name() + "_args";
1311
1312 // Open function
1313 indent(out) <<
1314 "- " << function_signature(&send_function) << endl;
1315 scope_up(out);
1316
1317 // Serialize the request
1318 out <<
1319 indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" <<
1320 " type: TMessageType_CALL" <<
1321 " sequenceID: 0];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001322
Mark Slee7e9eea42007-09-10 21:00:23 +00001323 out <<
1324 indent() << "[outProtocol writeStructBeginWithName: @\"" << argsname << "\"];" << endl;
1325
1326 // write out function parameters
1327 t_struct* arg_struct = (*f_iter)->get_arglist();
1328 const vector<t_field*>& fields = arg_struct->get_members();
1329 vector<t_field*>::const_iterator fld_iter;
1330 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001331 string fieldName = (*fld_iter)->get_name();
1332 if (type_can_be_null((*fld_iter)->get_type())) {
1333 out << indent() << "if (" << fieldName << " != nil)";
1334 scope_up(out);
1335 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001336 out <<
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001337 indent() << "[outProtocol writeFieldBeginWithName: @\"" << fieldName << "\""
Mark Slee7e9eea42007-09-10 21:00:23 +00001338 " type: " << type_to_enum((*fld_iter)->get_type()) <<
1339 " fieldID: " << (*fld_iter)->get_key() << "];" << endl;
1340
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001341 generate_serialize_field(out, *fld_iter, fieldName);
Mark Slee7e9eea42007-09-10 21:00:23 +00001342
1343 out <<
1344 indent() << "[outProtocol writeFieldEnd];" << endl;
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001345
1346 if (type_can_be_null((*fld_iter)->get_type())) {
1347 scope_down(out);
1348 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001349 }
1350
1351 out <<
1352 indent() << "[outProtocol writeFieldStop];" << endl;
1353 out <<
1354 indent() << "[outProtocol writeStructEnd];" << endl;
1355
1356 out <<
1357 indent() << "[outProtocol writeMessageEnd];" << endl <<
1358 indent() << "[[outProtocol transport] flush];" << endl;
1359
1360 scope_down(out);
1361 out << endl;
1362
David Reiss47329252009-03-24 20:01:02 +00001363 if (!(*f_iter)->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001364 t_struct noargs(program_);
1365 t_function recv_function((*f_iter)->get_returntype(),
1366 string("recv_") + (*f_iter)->get_name(),
1367 &noargs,
1368 (*f_iter)->get_xceptions());
1369 // Open function
1370 indent(out) <<
1371 "- " << function_signature(&recv_function) << endl;
1372 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001373
Mark Slee7e9eea42007-09-10 21:00:23 +00001374 // TODO(mcslee): Message validation here, was the seqid etc ok?
1375
1376 // check for an exception
1377 out <<
1378 indent() << "int msgType = 0;" << endl <<
Mark Slee33a7d892007-09-14 19:44:30 +00001379 indent() << "[inProtocol readMessageBeginReturningName: nil type: &msgType sequenceID: NULL];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001380 indent() << "if (msgType == TMessageType_EXCEPTION) {" << endl <<
1381 indent() << " TApplicationException * x = [TApplicationException read: inProtocol];" << endl <<
1382 indent() << " [inProtocol readMessageEnd];" << endl <<
1383 indent() << " @throw x;" << endl <<
1384 indent() << "}" << endl;
1385
1386 // FIXME - could optimize here to reduce creation of temporary objects.
1387 string resultname = function_result_helper_struct_type(*f_iter);
1388 out <<
Mark Slee12a28752007-11-20 23:55:33 +00001389 indent() << cocoa_prefix_ << resultname << " * result = [[[" << cocoa_prefix_ <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001390 resultname << " alloc] init] autorelease];" << endl;
1391 indent(out) << "[result read: inProtocol];" << endl;
1392 indent(out) << "[inProtocol readMessageEnd];" << endl;
1393
1394 // Careful, only return _result if not a void function
1395 if (!(*f_iter)->get_returntype()->is_void()) {
1396 out <<
1397 indent() << "if ([result successIsSet]) {" << endl <<
1398 indent() << " return [result success];" << endl <<
1399 indent() << "}" << endl;
1400 }
1401
1402 t_struct* xs = (*f_iter)->get_xceptions();
1403 const std::vector<t_field*>& xceptions = xs->get_members();
1404 vector<t_field*>::const_iterator x_iter;
1405 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1406 out <<
1407 indent() << "if ([result " << (*x_iter)->get_name() << "IsSet]) {" << endl <<
1408 indent() << " @throw [result " << (*x_iter)->get_name() << "];" << endl <<
1409 indent() << "}" << endl;
1410 }
1411
1412 // If you get here it's an exception, unless a void function
1413 if ((*f_iter)->get_returntype()->is_void()) {
1414 indent(out) <<
1415 "return;" << endl;
1416 } else {
1417 out <<
1418 indent() << "@throw [TApplicationException exceptionWithType: TApplicationException_MISSING_RESULT" << endl <<
Mark Slee33a7d892007-09-14 19:44:30 +00001419 indent() << " reason: @\"" << (*f_iter)->get_name() << " failed: unknown result\"];" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +00001420 }
Mark Slee12a28752007-11-20 23:55:33 +00001421
Mark Slee7e9eea42007-09-10 21:00:23 +00001422 // Close function
1423 scope_down(out);
1424 out << endl;
1425 }
1426
1427 // Open function
1428 indent(out) <<
1429 "- " << function_signature(*f_iter) << endl;
1430 scope_up(out);
1431 indent(out) <<
1432 "[self send_" << funname;
1433
1434 // Declare the function arguments
1435 bool first = true;
1436 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1437 if (first) {
1438 first = false;
1439 } else {
1440 out << " ";
1441 }
1442 out << ": " << (*fld_iter)->get_name();
1443 }
1444 out << "];" << endl;
1445
David Reiss47329252009-03-24 20:01:02 +00001446 if (!(*f_iter)->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001447 out << indent();
1448 if (!(*f_iter)->get_returntype()->is_void()) {
1449 out << "return ";
1450 }
1451 out <<
1452 "[self recv_" << funname << "];" << endl;
1453 }
1454 scope_down(out);
1455 out << endl;
1456 }
1457
1458 indent_down();
1459
1460 out << "@end" << endl << endl;
1461}
1462
1463
1464/**
Andrew McGeachie0c895712009-07-21 21:14:19 +00001465 * Generates a service server implementation. In other words the actual TProcessor implementation
1466 * for the service.
1467 *
1468 * @param tservice The service to generate an implementation for
1469 */
1470void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& out,
1471 t_service* tservice) {
1472 out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Processor" << endl;
1473 indent_up();
1474
1475 // initializer
1476 out << endl;
1477 out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
1478 scope_up(out);
1479 out << indent() << "self = [super init];" << endl;
1480 out << indent() << "if (!self) {" << endl;
1481 out << indent() << " return nil;" << endl;
1482 out << indent() << "}" << endl;
1483 out << indent() << "mService = [service retain];" << endl;
1484 out << indent() << "mMethodMap = [[NSMutableDictionary dictionary] retain];" << endl;
1485
1486 // generate method map for routing incoming calls
1487 vector<t_function*> functions = tservice->get_functions();
1488 vector<t_function*>::const_iterator f_iter;
1489 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1490 string funname = (*f_iter)->get_name();
1491 scope_up(out);
1492 out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:);" << endl;
1493 out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl;
1494 out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl;
1495 out << indent() << "[invocation setSelector: s];" << endl;
1496 out << indent() << "[invocation retainArguments];" << endl;
1497 out << indent() << "[mMethodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl;
1498 scope_down(out);
1499 }
1500 out << indent() << "return self;" << endl;
1501 scope_down(out);
1502
1503 // implementation of the 'service' method which returns the service associated with this
1504 // processor
1505 out << endl;
1506 out << indent() << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
1507 out << indent() << "{" << endl;
1508 out << indent() << " return [[mService retain] autorelease];" << endl;
1509 out << indent() << "}" << endl;
1510
1511 // implementation of the TProcess method, which dispatches the incoming call using the method map
1512 out << endl;
1513 out << indent() << "- (BOOL) processOnInputProtocol: (id <TProtocol>) inProtocol" << endl;
1514 out << indent() << " outputProtocol: (id <TProtocol>) outProtocol" <<endl;
1515 out << indent() << "{" << endl;
1516 out << indent() << " NSString * messageName;" << endl;
1517 out << indent() << " int messageType;" << endl;
1518 out << indent() << " int seqID;" << endl;
1519 out << indent() << " [inProtocol readMessageBeginReturningName: &messageName" << endl;
1520 out << indent() << " type: &messageType" << endl;
1521 out << indent() << " sequenceID: &seqID];" << endl;
1522 out << indent() << " NSInvocation * invocation = [mMethodMap valueForKey: messageName];" << endl;
1523 out << indent() << " if (invocation == nil) {" << endl;
1524 out << indent() << " [TProtocolUtil skipType: TType_STRUCT onProtocol: inProtocol];" << endl;
1525 out << indent() << " [inProtocol readMessageEnd];" << endl;
1526 out << indent() << " TApplicationException * x = [TApplicationException exceptionWithType: TApplicationException_UNKNOWN_METHOD reason: [NSString stringWithFormat: @\"Invalid method name: '%@'\", messageName]];" << endl;
1527 out << indent() << " [outProtocol writeMessageBeginWithName: messageName" << endl;
1528 out << indent() << " type: TMessageType_EXCEPTION" << endl;
1529 out << indent() << " sequenceID: seqID];" << endl;
1530 out << indent() << " [x write: outProtocol];" << endl;
1531 out << indent() << " [outProtocol writeMessageEnd];" << endl;
1532 out << indent() << " [[outProtocol transport] flush];" << endl;
1533 out << indent() << " return YES;" << endl;
1534 out << indent() << " }" << endl;
1535 out << indent() << " // NSInvocation does not conform to NSCopying protocol" << endl;
1536 out << indent() << " NSInvocation * i = [NSInvocation invocationWithMethodSignature: [invocation methodSignature]];" << endl;
1537 out << indent() << " [i setSelector: [invocation selector]];" << endl;
1538 out << indent() << " [i setArgument: &seqID atIndex: 2];" << endl;
1539 out << indent() << " [i setArgument: &inProtocol atIndex: 3];" << endl;
1540 out << indent() << " [i setArgument: &outProtocol atIndex: 4];" << endl;
1541 out << indent() << " [i setTarget: self];" << endl;
1542 out << indent() << " [i invoke];" << endl;
1543 out << indent() << " return YES;" << endl;
1544 out << indent() << "}" << endl;
1545
1546 // generate a process_XXXX method for each service function, which reads args, calls the service, and writes results
1547 functions = tservice->get_functions();
1548 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
Bryan Duxbury773b8db2010-09-02 00:43:16 +00001549 if ((*f_iter)->is_oneway()) {
1550 continue;
1551 }
Andrew McGeachie0c895712009-07-21 21:14:19 +00001552 out << endl;
1553 string funname = (*f_iter)->get_name();
1554 out << indent() << "- (void) process_" << funname << "_withSequenceID: (int32_t) seqID inProtocol: (id<TProtocol>) inProtocol outProtocol: (id<TProtocol>) outProtocol" << endl;
1555 scope_up(out);
1556 string argstype = cocoa_prefix_ + function_args_helper_struct_type(*f_iter);
1557 out << indent() << argstype << " * args = [[" << argstype << " alloc] init];" << endl;
1558 out << indent() << "[args read: inProtocol];" << endl;
1559 out << indent() << "[inProtocol readMessageEnd];" << endl;
1560
1561 string resulttype = cocoa_prefix_ + function_result_helper_struct_type(*f_iter);
1562 out << indent() << resulttype << " * result = [[" << resulttype << " alloc] init];" << endl;
1563
1564 // make the call to the actual service object
1565 out << indent();
1566 if (!(*f_iter)->get_returntype()->is_void()) {
1567 out << "[result setSuccess: ";
1568 }
1569 out << "[mService " << funname;
1570 // supplying arguments
1571 t_struct* arg_struct = (*f_iter)->get_arglist();
1572 const vector<t_field*>& fields = arg_struct->get_members();
1573 vector<t_field*>::const_iterator fld_iter;
1574 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1575 string fieldName = (*fld_iter)->get_name();
1576 out << ": [args " << fieldName << "]";
1577 }
1578 out << "]";
1579 if (!(*f_iter)->get_returntype()->is_void()) {
1580 out << "]";
1581 }
1582 out << ";" << endl;
1583
1584 // write out the result
1585 out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << endl;
1586 out << indent() << " type: TMessageType_REPLY" << endl;
1587 out << indent() << " sequenceID: seqID];" << endl;
1588 out << indent() << "[result write: outProtocol];" << endl;
1589 out << indent() << "[outProtocol writeMessageEnd];" << endl;
1590 out << indent() << "[[outProtocol transport] flush];" << endl;
1591 out << indent() << "[result release];" << endl;
1592 out << indent() << "[args release];" << endl;
1593
1594 scope_down(out);
1595 }
1596
1597 // dealloc
1598 out << endl;
1599 out << "- (void) dealloc" << endl;
1600 scope_up(out);
1601 out << indent() << "[mService release];" << endl;
1602 out << indent() << "[mMethodMap release];" << endl;
1603 out << indent() << "[super dealloc];" << endl;
1604 scope_down(out);
1605 out << endl;
1606
1607 indent_down();
1608
1609 out << "@end" << endl << endl;
1610}
1611
1612
1613/**
Mark Slee7e9eea42007-09-10 21:00:23 +00001614 * Deserializes a field of any type.
1615 *
1616 * @param tfield The field
1617 * @param fieldName The variable name for this field
1618 */
1619void t_cocoa_generator::generate_deserialize_field(ofstream& out,
1620 t_field* tfield,
1621 string fieldName) {
1622 t_type* type = get_true_type(tfield->get_type());
1623
1624 if (type->is_void()) {
1625 throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " +
1626 tfield->get_name();
1627 }
1628
1629 if (type->is_struct() || type->is_xception()) {
1630 generate_deserialize_struct(out,
1631 (t_struct*)type,
1632 fieldName);
1633 } else if (type->is_container()) {
1634 generate_deserialize_container(out, type, fieldName);
1635 } else if (type->is_base_type() || type->is_enum()) {
1636 indent(out) <<
1637 type_name(type) << " " << fieldName << " = [inProtocol ";
Mark Slee12a28752007-11-20 23:55:33 +00001638
Mark Slee7e9eea42007-09-10 21:00:23 +00001639 if (type->is_base_type()) {
1640 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1641 switch (tbase) {
1642 case t_base_type::TYPE_VOID:
1643 throw "compiler error: cannot serialize void field in a struct: " +
1644 tfield->get_name();
1645 break;
Mark Slee12a28752007-11-20 23:55:33 +00001646 case t_base_type::TYPE_STRING:
Mark Slee7e9eea42007-09-10 21:00:23 +00001647 if (((t_base_type*)type)->is_binary()) {
1648 out << "readBinary];";
1649 } else {
1650 out << "readString];";
1651 }
1652 break;
1653 case t_base_type::TYPE_BOOL:
1654 out << "readBool];";
1655 break;
1656 case t_base_type::TYPE_BYTE:
1657 out << "readByte];";
1658 break;
1659 case t_base_type::TYPE_I16:
1660 out << "readI16];";
1661 break;
1662 case t_base_type::TYPE_I32:
1663 out << "readI32];";
1664 break;
1665 case t_base_type::TYPE_I64:
1666 out << "readI64];";
1667 break;
1668 case t_base_type::TYPE_DOUBLE:
1669 out << "readDouble];";
1670 break;
1671 default:
1672 throw "compiler error: no Objective-C name for base type " + t_base_type::t_base_name(tbase);
1673 }
1674 } else if (type->is_enum()) {
1675 out << "readI32];";
1676 }
1677 out <<
1678 endl;
1679 } else {
1680 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
1681 tfield->get_name().c_str(), type_name(type).c_str());
1682 }
1683}
1684
1685/**
1686 * Generates an unserializer for a struct, allocates the struct and invokes read:
1687 */
1688void t_cocoa_generator::generate_deserialize_struct(ofstream& out,
1689 t_struct* tstruct,
1690 string fieldName) {
Mark Slee12a28752007-11-20 23:55:33 +00001691 indent(out) << type_name(tstruct) << fieldName << " = [[" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001692 type_name(tstruct, true) << " alloc] init];" << endl;
1693 indent(out) << "[" << fieldName << " read: inProtocol];" << endl;
1694}
1695
1696/**
1697 * Deserializes a container by reading its size and then iterating
1698 */
1699void t_cocoa_generator::generate_deserialize_container(ofstream& out,
1700 t_type* ttype,
1701 string fieldName) {
1702 string size = tmp("_size");
1703 indent(out) << "int " << size << ";" << endl;
1704
1705 // Declare variables, read header
1706 if (ttype->is_map()) {
Mark Slee12a28752007-11-20 23:55:33 +00001707 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001708 << "[inProtocol readMapBeginReturningKeyType: NULL valueType: NULL size: &" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001709 size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001710 indent(out) << "NSMutableDictionary * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001711 " = [[NSMutableDictionary alloc] initWithCapacity: " << size << "];" << endl;
1712 } else if (ttype->is_set()) {
Mark Slee12a28752007-11-20 23:55:33 +00001713 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001714 << "[inProtocol readSetBeginReturningElementType: NULL size: &" << size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001715 indent(out) << "NSMutableSet * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001716 " = [[NSMutableSet alloc] initWithCapacity: " << size << "];" << endl;
1717 } else if (ttype->is_list()) {
Mark Slee12a28752007-11-20 23:55:33 +00001718 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001719 << "[inProtocol readListBeginReturningElementType: NULL size: &" << size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001720 indent(out) << "NSMutableArray * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001721 " = [[NSMutableArray alloc] initWithCapacity: " << size << "];" << endl;
1722 }
1723 // FIXME - the code above does not verify that the element types of
1724 // the containers being read match the element types of the
1725 // containers we are reading into. Does that matter?
1726
1727 // For loop iterates over elements
1728 string i = tmp("_i");
1729 indent(out) << "int " << i << ";" << endl <<
1730 indent() << "for (" << i << " = 0; " <<
1731 i << " < " << size << "; " <<
1732 "++" << i << ")" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001733
Mark Slee7e9eea42007-09-10 21:00:23 +00001734 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001735
Mark Slee7e9eea42007-09-10 21:00:23 +00001736 if (ttype->is_map()) {
1737 generate_deserialize_map_element(out, (t_map*)ttype, fieldName);
1738 } else if (ttype->is_set()) {
1739 generate_deserialize_set_element(out, (t_set*)ttype, fieldName);
1740 } else if (ttype->is_list()) {
1741 generate_deserialize_list_element(out, (t_list*)ttype, fieldName);
1742 }
Mark Slee12a28752007-11-20 23:55:33 +00001743
Mark Slee7e9eea42007-09-10 21:00:23 +00001744 scope_down(out);
1745
1746 // Read container end
1747 if (ttype->is_map()) {
1748 indent(out) << "[inProtocol readMapEnd];" << endl;
1749 } else if (ttype->is_set()) {
1750 indent(out) << "[inProtocol readSetEnd];" << endl;
1751 } else if (ttype->is_list()) {
1752 indent(out) << "[inProtocol readListEnd];" << endl;
1753 }
1754
1755}
1756
1757
1758/**
1759 * Take a variable of a given type and wrap it in code to make it
1760 * suitable for putting into a container, if necessary. Basically,
1761 * wrap scaler primitives in NSNumber objects.
1762 */
1763string t_cocoa_generator::containerize(t_type * ttype,
1764 string fieldName)
1765{
1766 // FIXME - optimize here to avoid autorelease pool?
1767 ttype = get_true_type(ttype);
1768 if (ttype->is_enum()) {
1769 return "[NSNumber numberWithInt: " + fieldName + "]";
1770 } else if (ttype->is_base_type()) {
1771 t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
1772 switch (tbase) {
1773 case t_base_type::TYPE_VOID:
1774 throw "can't containerize void";
1775 case t_base_type::TYPE_BOOL:
1776 return "[NSNumber numberWithBool: " + fieldName + "]";
1777 case t_base_type::TYPE_BYTE:
1778 return "[NSNumber numberWithUnsignedChar: " + fieldName + "]";
1779 case t_base_type::TYPE_I16:
1780 return "[NSNumber numberWithShort: " + fieldName + "]";
1781 case t_base_type::TYPE_I32:
1782 return "[NSNumber numberWithLong: " + fieldName + "]";
1783 case t_base_type::TYPE_I64:
1784 return "[NSNumber numberWithLongLong: " + fieldName + "]";
1785 case t_base_type::TYPE_DOUBLE:
1786 return "[NSNumber numberWithDouble: " + fieldName + "]";
1787 default:
1788 break;
1789 }
1790 }
1791
1792 // do nothing
1793 return fieldName;
1794}
1795
1796
1797/**
1798 * Generates code to deserialize a map element
1799 */
1800void t_cocoa_generator::generate_deserialize_map_element(ofstream& out,
1801 t_map* tmap,
1802 string fieldName) {
1803 string key = tmp("_key");
1804 string val = tmp("_val");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001805 t_type* keyType = tmap->get_key_type();
1806 t_type* valType = tmap->get_val_type();
1807 t_field fkey(keyType, key);
1808 t_field fval(valType, val);
Mark Slee7e9eea42007-09-10 21:00:23 +00001809
1810 generate_deserialize_field(out, &fkey, key);
1811 generate_deserialize_field(out, &fval, val);
1812
1813 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001814 "[" << fieldName << " setObject: " << containerize(valType, val) <<
1815 " forKey: " << containerize(keyType, key) << "];" << endl;
1816
1817 if (type_can_be_null(keyType)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001818 if (!(get_true_type(keyType)->is_string())) {
1819 indent(out) << "[" << containerize(keyType, key) << " release];" << endl;
1820 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001821 }
1822
1823 if (type_can_be_null(valType)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001824 if (!(get_true_type(valType)->is_string())) {
1825 indent(out) << "[" << containerize(valType, val) << " release];" << endl;
1826 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001827 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001828}
1829
1830/**
1831 * Deserializes a set element
1832 */
1833void t_cocoa_generator::generate_deserialize_set_element(ofstream& out,
1834 t_set* tset,
1835 string fieldName) {
1836 string elem = tmp("_elem");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001837 t_type* type = tset->get_elem_type();
1838 t_field felem(type, elem);
Mark Slee7e9eea42007-09-10 21:00:23 +00001839
1840 generate_deserialize_field(out, &felem, elem);
1841
1842 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001843 "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl;
1844
1845 if (type_can_be_null(type)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001846 // deserialized strings are autorelease, so don't release them
1847 if (!(get_true_type(type)->is_string())) {
1848 indent(out) << "[" << containerize(type, elem) << " release];" << endl;
1849 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001850 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001851}
1852
1853/**
1854 * Deserializes a list element
1855 */
1856void t_cocoa_generator::generate_deserialize_list_element(ofstream& out,
1857 t_list* tlist,
1858 string fieldName) {
1859 string elem = tmp("_elem");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001860 t_type* type = tlist->get_elem_type();
1861 t_field felem(type, elem);
Mark Slee7e9eea42007-09-10 21:00:23 +00001862
1863 generate_deserialize_field(out, &felem, elem);
1864
1865 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001866 "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl;
1867
1868 if (type_can_be_null(type)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001869 if (!(get_true_type(type)->is_string())) {
1870 indent(out) << "[" << containerize(type, elem) << " release];" << endl;
1871 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001872 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001873}
1874
1875
1876/**
1877 * Serializes a field of any type.
1878 *
1879 * @param tfield The field to serialize
1880 * @param fieldName Name to of the variable holding the field
1881 */
1882void t_cocoa_generator::generate_serialize_field(ofstream& out,
1883 t_field* tfield,
1884 string fieldName) {
1885 t_type* type = get_true_type(tfield->get_type());
1886
1887 // Do nothing for void types
1888 if (type->is_void()) {
1889 throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
1890 tfield->get_name();
1891 }
Mark Slee12a28752007-11-20 23:55:33 +00001892
Mark Slee7e9eea42007-09-10 21:00:23 +00001893 if (type->is_struct() || type->is_xception()) {
1894 generate_serialize_struct(out,
1895 (t_struct*)type,
1896 fieldName);
1897 } else if (type->is_container()) {
1898 generate_serialize_container(out,
1899 type,
1900 fieldName);
1901 } else if (type->is_base_type() || type->is_enum()) {
1902 indent(out) <<
1903 "[outProtocol ";
Mark Slee12a28752007-11-20 23:55:33 +00001904
Mark Slee7e9eea42007-09-10 21:00:23 +00001905 if (type->is_base_type()) {
1906 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1907 switch (tbase) {
1908 case t_base_type::TYPE_VOID:
1909 throw
1910 "compiler error: cannot serialize void field in a struct: " + fieldName;
1911 break;
1912 case t_base_type::TYPE_STRING:
1913 if (((t_base_type*)type)->is_binary()) {
1914 out << "writeBinary: " << fieldName << "];";
1915 } else {
1916 out << "writeString: " << fieldName << "];";
1917 }
1918 break;
1919 case t_base_type::TYPE_BOOL:
1920 out << "writeBool: " << fieldName << "];";
1921 break;
1922 case t_base_type::TYPE_BYTE:
1923 out << "writeByte: " << fieldName << "];";
1924 break;
1925 case t_base_type::TYPE_I16:
1926 out << "writeI16: " << fieldName << "];";
1927 break;
1928 case t_base_type::TYPE_I32:
1929 out << "writeI32: " << fieldName << "];";
1930 break;
1931 case t_base_type::TYPE_I64:
1932 out << "writeI64: " << fieldName << "];";
1933 break;
1934 case t_base_type::TYPE_DOUBLE:
1935 out << "writeDouble: " << fieldName << "];";
1936 break;
1937 default:
1938 throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
1939 }
1940 } else if (type->is_enum()) {
1941 out << "writeI32: " << fieldName << "];";
1942 }
1943 out << endl;
1944 } else {
1945 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
1946 tfield->get_name().c_str(),
1947 type_name(type).c_str());
1948 }
1949}
1950
1951/**
1952 * Serialize a struct.
1953 *
1954 * @param tstruct The struct to serialize
1955 * @param fieldName Name of variable holding struct
1956 */
1957void t_cocoa_generator::generate_serialize_struct(ofstream& out,
1958 t_struct* tstruct,
1959 string fieldName) {
Roger Meier3b771a12010-11-17 22:11:26 +00001960 (void) tstruct;
Mark Slee7e9eea42007-09-10 21:00:23 +00001961 out <<
1962 indent() << "[" << fieldName << " write: outProtocol];" << endl;
1963}
1964
1965/**
1966 * Serializes a container by writing its size then the elements.
1967 *
1968 * @param ttype The type of container
1969 * @param fieldName Name of variable holding container
1970 */
1971void t_cocoa_generator::generate_serialize_container(ofstream& out,
1972 t_type* ttype,
1973 string fieldName) {
1974 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001975
Mark Slee7e9eea42007-09-10 21:00:23 +00001976 if (ttype->is_map()) {
1977 indent(out) <<
1978 "[outProtocol writeMapBeginWithKeyType: " <<
1979 type_to_enum(((t_map*)ttype)->get_key_type()) << " valueType: " <<
1980 type_to_enum(((t_map*)ttype)->get_val_type()) << " size: [" <<
1981 fieldName << " count]];" << endl;
1982 } else if (ttype->is_set()) {
1983 indent(out) <<
1984 "[outProtocol writeSetBeginWithElementType: " <<
1985 type_to_enum(((t_set*)ttype)->get_elem_type()) << " size: [" <<
1986 fieldName << " count]];" << endl;
1987 } else if (ttype->is_list()) {
1988 indent(out) <<
1989 "[outProtocol writeListBeginWithElementType: " <<
1990 type_to_enum(((t_list*)ttype)->get_elem_type()) << " size: [" <<
1991 fieldName << " count]];" << endl;
1992 }
1993
1994 string iter = tmp("_iter");
1995 string key;
1996 if (ttype->is_map()) {
1997 key = tmp("key");
1998 indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " keyEnumerator];" << endl;
1999 indent(out) << "id " << key << ";" << endl;
2000 indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl;
2001 } else if (ttype->is_set()) {
2002 key = tmp("obj");
2003 indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " objectEnumerator];" << endl;
2004 indent(out) << "id " << key << ";" << endl;
2005 indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl;
2006 } else if (ttype->is_list()) {
2007 key = tmp("i");
2008 indent(out) << "int " << key << ";" << endl;
2009 indent(out) <<
2010 "for (" << key << " = 0; " << key << " < [" << fieldName << " count]; " << key << "++)" << endl;
2011 }
2012
2013 scope_up(out);
2014
2015 if (ttype->is_map()) {
2016 generate_serialize_map_element(out, (t_map*)ttype, key, fieldName);
2017 } else if (ttype->is_set()) {
2018 generate_serialize_set_element(out, (t_set*)ttype, key);
2019 } else if (ttype->is_list()) {
2020 generate_serialize_list_element(out, (t_list*)ttype, key, fieldName);
2021 }
Mark Slee12a28752007-11-20 23:55:33 +00002022
Mark Slee7e9eea42007-09-10 21:00:23 +00002023 scope_down(out);
Mark Slee12a28752007-11-20 23:55:33 +00002024
Mark Slee7e9eea42007-09-10 21:00:23 +00002025 if (ttype->is_map()) {
2026 indent(out) <<
2027 "[outProtocol writeMapEnd];" << endl;
2028 } else if (ttype->is_set()) {
2029 indent(out) <<
2030 "[outProtocol writeSetEnd];" << endl;
2031 } else if (ttype->is_list()) {
2032 indent(out) <<
2033 "[outProtocol writeListEnd];" << endl;
2034 }
Mark Slee12a28752007-11-20 23:55:33 +00002035
2036 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +00002037}
2038
2039/**
2040 * Given a field variable name, wrap it in code that converts it to a
2041 * primitive type, if necessary.
2042 */
2043string t_cocoa_generator::decontainerize(t_field * tfield,
2044 string fieldName)
2045{
2046 t_type * ttype = get_true_type(tfield->get_type());
2047 if (ttype->is_enum()) {
2048 return "[" + fieldName + " intValue]";
2049 } else if (ttype->is_base_type()) {
2050 t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
2051 switch (tbase) {
2052 case t_base_type::TYPE_VOID:
2053 throw "can't decontainerize void";
2054 case t_base_type::TYPE_BOOL:
2055 return "[" + fieldName + " boolValue]";
2056 case t_base_type::TYPE_BYTE:
2057 return "[" + fieldName + " unsignedCharValue]";
2058 case t_base_type::TYPE_I16:
2059 return "[" + fieldName + " shortValue]";
2060 case t_base_type::TYPE_I32:
2061 return "[" + fieldName + " longValue]";
2062 case t_base_type::TYPE_I64:
2063 return "[" + fieldName + " longLongValue]";
2064 case t_base_type::TYPE_DOUBLE:
2065 return "[" + fieldName + " doubleValue]";
2066 default:
2067 break;
2068 }
2069 }
2070
2071 // do nothing
2072 return fieldName;
2073}
2074
2075
2076/**
2077 * Serializes the members of a map.
Mark Slee12a28752007-11-20 23:55:33 +00002078 */
Mark Slee7e9eea42007-09-10 21:00:23 +00002079void t_cocoa_generator::generate_serialize_map_element(ofstream& out,
2080 t_map* tmap,
2081 string key,
2082 string mapName) {
2083 t_field kfield(tmap->get_key_type(), key);
2084 generate_serialize_field(out, &kfield, decontainerize(&kfield, key));
2085 t_field vfield(tmap->get_val_type(), "[" + mapName + " objectForKey: " + key + "]");
2086 generate_serialize_field(out, &vfield, decontainerize(&vfield, vfield.get_name()));
2087}
2088
2089/**
2090 * Serializes the members of a set.
2091 */
2092void t_cocoa_generator::generate_serialize_set_element(ofstream& out,
2093 t_set* tset,
2094 string elementName) {
2095 t_field efield(tset->get_elem_type(), elementName);
2096 generate_serialize_field(out, &efield, decontainerize(&efield, elementName));
2097}
2098
2099/**
2100 * Serializes the members of a list.
2101 */
2102void t_cocoa_generator::generate_serialize_list_element(ofstream& out,
2103 t_list* tlist,
2104 string index,
2105 string listName) {
2106 t_field efield(tlist->get_elem_type(), "[" + listName + " objectAtIndex: " + index + "]");
2107 generate_serialize_field(out, &efield, decontainerize(&efield, efield.get_name()));
2108}
2109
2110
2111/**
2112 * Returns an Objective-C name
2113 *
2114 * @param ttype The type
2115 * @param class_ref Do we want a Class reference istead of a type reference?
2116 * @return Java type name, i.e. HashMap<Key,Value>
2117 */
2118string t_cocoa_generator::type_name(t_type* ttype, bool class_ref) {
2119 if (ttype->is_typedef()) {
2120 return cocoa_prefix_ + ttype->get_name();
2121 }
2122
2123 string result;
2124 if (ttype->is_base_type()) {
2125 return base_type_name((t_base_type*)ttype);
2126 } else if (ttype->is_enum()) {
2127 return "int";
2128 } else if (ttype->is_map()) {
2129 result = "NSDictionary";
2130 } else if (ttype->is_set()) {
2131 result = "NSSet";
2132 } else if (ttype->is_list()) {
2133 result = "NSArray";
2134 } else {
2135 // Check for prefix
2136 t_program* program = ttype->get_program();
2137 if (program != NULL) {
David Reiss54b602b2008-03-27 21:41:06 +00002138 result = program->get_namespace("cocoa") + ttype->get_name();
Mark Slee7e9eea42007-09-10 21:00:23 +00002139 } else {
2140 result = ttype->get_name();
2141 }
2142 }
2143
2144 if (!class_ref) {
2145 result += " *";
2146 }
2147 return result;
2148}
2149
2150/**
2151 * Returns the Objective-C type that corresponds to the thrift type.
2152 *
2153 * @param tbase The base type
2154 */
2155string t_cocoa_generator::base_type_name(t_base_type* type) {
2156 t_base_type::t_base tbase = type->get_base();
2157
2158 switch (tbase) {
2159 case t_base_type::TYPE_VOID:
2160 return "void";
2161 case t_base_type::TYPE_STRING:
2162 if (type->is_binary()) {
2163 return "NSData *";
2164 } else {
2165 return "NSString *";
2166 }
2167 case t_base_type::TYPE_BOOL:
2168 return "BOOL";
2169 case t_base_type::TYPE_BYTE:
2170 return "uint8_t";
2171 case t_base_type::TYPE_I16:
2172 return"int16_t";
2173 case t_base_type::TYPE_I32:
2174 return "int32_t";
2175 case t_base_type::TYPE_I64:
2176 return"int64_t";
2177 case t_base_type::TYPE_DOUBLE:
2178 return "double";
2179 default:
2180 throw "compiler error: no objective-c name for base type " + t_base_type::t_base_name(tbase);
2181 }
2182}
2183
2184
2185/**
2186 * Spit out code that evaluates to the specified constant value.
2187 */
Mark Slee12a28752007-11-20 23:55:33 +00002188string t_cocoa_generator::render_const_value(string name,
2189 t_type* type,
Mark Slee7e9eea42007-09-10 21:00:23 +00002190 t_const_value* value,
2191 bool containerize_it) {
David Reiss9a4edfa2008-05-01 05:52:50 +00002192 type = get_true_type(type);
Mark Slee7e9eea42007-09-10 21:00:23 +00002193 std::ostringstream render;
2194
2195 if (type->is_base_type()) {
2196 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2197 switch (tbase) {
2198 case t_base_type::TYPE_STRING:
David Reiss82e6fc02009-03-26 23:32:36 +00002199 render << "@\"" << get_escaped_string(value) << '"';
Mark Slee7e9eea42007-09-10 21:00:23 +00002200 break;
2201 case t_base_type::TYPE_BOOL:
2202 render << ((value->get_integer() > 0) ? "YES" : "NO");
2203 break;
2204 case t_base_type::TYPE_BYTE:
2205 case t_base_type::TYPE_I16:
2206 case t_base_type::TYPE_I32:
2207 case t_base_type::TYPE_I64:
2208 render << value->get_integer();
2209 break;
2210 case t_base_type::TYPE_DOUBLE:
2211 if (value->get_type() == t_const_value::CV_INTEGER) {
2212 render << value->get_integer();
2213 } else {
2214 render << value->get_double();
2215 }
2216 break;
2217 default:
2218 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
2219 }
2220 } else if (type->is_enum()) {
2221 render << value->get_integer();
2222 } else if (type->is_struct() || type->is_xception()) {
2223 render << "[[" << type_name(type, true) << " alloc] initWith";
2224 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
2225 vector<t_field*>::const_iterator f_iter;
2226 const map<t_const_value*, t_const_value*>& val = value->get_map();
2227 map<t_const_value*, t_const_value*>::const_iterator v_iter;
2228 bool first = true;
2229 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2230 t_type* field_type = NULL;
2231 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2232 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
2233 field_type = (*f_iter)->get_type();
2234 }
2235 }
2236 if (field_type == NULL) {
2237 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
2238 }
2239 if (first) {
2240 render << capitalize(v_iter->first->get_string());
2241 first = false;
2242 } else {
2243 render << " " << v_iter->first->get_string();
2244 }
2245 render << ": " << render_const_value(name, field_type, v_iter->second);
2246 }
2247 render << "]";
2248 } else if (type->is_map()) {
2249 render << "[[NSDictionary alloc] initWithObjectsAndKeys: ";
2250 t_type* ktype = ((t_map*)type)->get_key_type();
2251 t_type* vtype = ((t_map*)type)->get_val_type();
2252 const map<t_const_value*, t_const_value*>& val = value->get_map();
2253 map<t_const_value*, t_const_value*>::const_iterator v_iter;
2254 bool first = true;
2255 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2256 string key = render_const_value(name, ktype, v_iter->first, true);
2257 string val = render_const_value(name, vtype, v_iter->second, true);
2258 if (first) {
2259 first = false;
2260 } else {
2261 render << ", ";
2262 }
2263 render << val << ", " << key;
2264 }
2265 render << ", nil]";
2266 } else if (type->is_list()) {
2267 render << "[[NSArray alloc] initWithObjects: ";
2268 t_type * etype = ((t_list*)type)->get_elem_type();
2269 const vector<t_const_value*>& val = value->get_list();
2270 bool first = true;
2271 vector<t_const_value*>::const_iterator v_iter;
2272 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2273 if (first) {
2274 first = false;
2275 } else {
2276 render << ", ";
2277 }
2278 render << render_const_value(name, etype, *v_iter, true);
2279 }
2280 render << ", nil]";
2281 } else if (type->is_set()) {
2282 render << "[[NSSet alloc] initWithObjects: ";
2283 t_type * etype = ((t_set*)type)->get_elem_type();
2284 const vector<t_const_value*>& val = value->get_list();
2285 bool first = true;
2286 vector<t_const_value*>::const_iterator v_iter;
2287 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2288 if (first) {
2289 first = false;
2290 } else {
2291 render << ", ";
2292 }
2293 render << render_const_value(name, etype, *v_iter, true);
2294 }
2295 render << ", nil]";
2296 } else {
2297 throw "don't know how to render constant for type: " + type->get_name();
2298 }
2299
2300 if (containerize_it) {
2301 return containerize(type, render.str());
2302 }
2303
2304 return render.str();
2305}
2306
2307
2308/**
2309 * Declares a field.
2310 *
2311 * @param ttype The type
2312 */
2313string t_cocoa_generator::declare_field(t_field* tfield) {
2314 return type_name(tfield->get_type()) + " __" + tfield->get_name() + ";";
2315}
2316
2317/**
Andrew McGeachief2e03ba2009-07-21 16:51:49 +00002318 * Declares an Objective-C 2.0 property.
2319 *
2320 * @param tfield The field to declare a property for
2321 */
2322string t_cocoa_generator::declare_property(t_field* tfield) {
2323 std::ostringstream render;
2324 render << "@property (nonatomic, ";
2325
2326 if (type_can_be_null(tfield->get_type()))
2327 render << "retain, ";
2328
2329 render << "getter=" << decapitalize(tfield->get_name()) <<
2330 ", setter=set" << capitalize(tfield->get_name()) + ":) " <<
2331 type_name(tfield->get_type()) << " " << tfield->get_name() << ";";
2332
2333 return render.str();
2334}
2335
2336/**
Mark Slee7e9eea42007-09-10 21:00:23 +00002337 * Renders a function signature
2338 *
2339 * @param tfunction Function definition
2340 * @return String of rendered function definition
2341 */
2342string t_cocoa_generator::function_signature(t_function* tfunction) {
2343 t_type* ttype = tfunction->get_returntype();
2344 std::string result =
2345 "(" + type_name(ttype) + ") " + tfunction->get_name() + argument_list(tfunction->get_arglist());
2346 return result;
2347}
2348
2349
2350/**
2351 * Renders a colon separated list of types and names, suitable for an
2352 * objective-c parameter list
2353 */
2354string t_cocoa_generator::argument_list(t_struct* tstruct) {
2355 string result = "";
2356
2357 const vector<t_field*>& fields = tstruct->get_members();
2358 vector<t_field*>::const_iterator f_iter;
2359 bool first = true;
2360 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2361 if (first) {
2362 first = false;
2363 } else {
2364 result += " ";
2365 }
2366 result += ": (" + type_name((*f_iter)->get_type()) + ") " + (*f_iter)->get_name();
2367 }
2368 return result;
2369}
2370
2371
2372/**
2373 * Converts the parse type to an Objective-C enum string for the given type.
2374 */
2375string t_cocoa_generator::type_to_enum(t_type* type) {
2376 type = get_true_type(type);
Mark Slee12a28752007-11-20 23:55:33 +00002377
Mark Slee7e9eea42007-09-10 21:00:23 +00002378 if (type->is_base_type()) {
2379 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2380 switch (tbase) {
2381 case t_base_type::TYPE_VOID:
2382 throw "NO T_VOID CONSTRUCT";
2383 case t_base_type::TYPE_STRING:
2384 return "TType_STRING";
2385 case t_base_type::TYPE_BOOL:
2386 return "TType_BOOL";
2387 case t_base_type::TYPE_BYTE:
2388 return "TType_BYTE";
2389 case t_base_type::TYPE_I16:
2390 return "TType_I16";
2391 case t_base_type::TYPE_I32:
2392 return "TType_I32";
2393 case t_base_type::TYPE_I64:
2394 return "TType_I64";
2395 case t_base_type::TYPE_DOUBLE:
2396 return "TType_DOUBLE";
2397 }
2398 } else if (type->is_enum()) {
2399 return "TType_I32";
2400 } else if (type->is_struct() || type->is_xception()) {
2401 return "TType_STRUCT";
2402 } else if (type->is_map()) {
2403 return "TType_MAP";
2404 } else if (type->is_set()) {
2405 return "TType_SET";
2406 } else if (type->is_list()) {
2407 return "TType_LIST";
2408 }
2409
2410 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2411}
2412
2413
2414/**
2415 * Returns a format string specifier for the supplied parse type.
2416 */
2417string t_cocoa_generator::format_string_for_type(t_type* type) {
2418 type = get_true_type(type);
Mark Slee12a28752007-11-20 23:55:33 +00002419
Mark Slee7e9eea42007-09-10 21:00:23 +00002420 if (type->is_base_type()) {
2421 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2422 switch (tbase) {
2423 case t_base_type::TYPE_VOID:
2424 throw "NO T_VOID CONSTRUCT";
2425 case t_base_type::TYPE_STRING:
2426 return "\\\"%@\\\"";
2427 case t_base_type::TYPE_BOOL:
2428 return "%i";
2429 case t_base_type::TYPE_BYTE:
2430 return "%i";
2431 case t_base_type::TYPE_I16:
2432 return "%hi";
2433 case t_base_type::TYPE_I32:
2434 return "%i";
2435 case t_base_type::TYPE_I64:
2436 return "%qi";
2437 case t_base_type::TYPE_DOUBLE:
2438 return "%f";
2439 }
2440 } else if (type->is_enum()) {
2441 return "%i";
2442 } else if (type->is_struct() || type->is_xception()) {
2443 return "%@";
2444 } else if (type->is_map()) {
2445 return "%@";
2446 } else if (type->is_set()) {
2447 return "%@";
2448 } else if (type->is_list()) {
2449 return "%@";
2450 }
2451
2452 throw "INVALID TYPE IN format_string_for_type: " + type->get_name();
2453}
2454
2455/**
2456 * Generate a call to a field's setter.
2457 *
2458 * @param tfield Field the setter is being called on
2459 * @param fieldName Name of variable to pass to setter
2460 */
2461
2462string t_cocoa_generator::call_field_setter(t_field* tfield, string fieldName) {
2463 return "[self set" + capitalize(tfield->get_name()) + ": " + fieldName + "];";
2464}
David Reissd01c64d2008-03-27 21:40:55 +00002465
2466
Andrew McGeachie645d7b82009-07-21 15:30:16 +00002467THRIFT_REGISTER_GENERATOR(cocoa, "Cocoa",
2468" log_unexpected: Log every time an unexpected field ID or type is encountered.\n"
Roger Meier0069cc42010-10-13 18:10:18 +00002469)
2470