blob: 9e24b5289c69d696842f183e451610a14d740c3d [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 {
Andrew McGeachie645d7b82009-07-21 15:30:16 +000046 std::map<std::string, std::string>::const_iterator iter;
47
48 iter = parsed_options.find("log_unexpected");
49 log_unexpected_ = (iter != parsed_options.end());
50
David Reisse21ce4c2008-03-27 21:40:59 +000051 out_dir_base_ = "gen-cocoa";
52 }
53
54 /**
55 * Init and close methods
56 */
57
58 void init_generator();
59 void close_generator();
60
61 void generate_consts(std::vector<t_const*> consts);
62
63 /**
64 * Program-level generation functions
65 */
66
67 void generate_typedef (t_typedef* ttypedef);
68 void generate_enum (t_enum* tenum);
69 void generate_struct (t_struct* tstruct);
70 void generate_xception(t_struct* txception);
71 void generate_service (t_service* tservice);
72
73 void print_const_value(std::ofstream& out, std::string name, t_type* type, t_const_value* value);
74 std::string render_const_value(std::string name, t_type* type, t_const_value* value,
75 bool containerize_it=false);
76
77 void generate_cocoa_struct(t_struct* tstruct, bool is_exception);
78 void generate_cocoa_struct_interface(std::ofstream& out, t_struct* tstruct, bool is_xception=false);
79 void generate_cocoa_struct_implementation(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false);
80 void generate_cocoa_struct_initializer_signature(std::ofstream& out,
81 t_struct* tstruct);
Andrew McGeachie3533dcb2009-11-03 18:52:15 +000082 void generate_cocoa_struct_init_with_coder_method(ofstream &out,
83 t_struct* tstruct,
84 bool is_exception);
85 void generate_cocoa_struct_encode_with_coder_method(ofstream &out,
86 t_struct* tstruct,
87 bool is_exception);
David Reisse21ce4c2008-03-27 21:40:59 +000088 void generate_cocoa_struct_field_accessor_declarations(std::ofstream& out,
89 t_struct* tstruct,
90 bool is_exception);
91 void generate_cocoa_struct_field_accessor_implementations(std::ofstream& out,
92 t_struct* tstruct,
93 bool is_exception);
94 void generate_cocoa_struct_reader(std::ofstream& out, t_struct* tstruct);
95 void generate_cocoa_struct_result_writer(std::ofstream& out, t_struct* tstruct);
96 void generate_cocoa_struct_writer(std::ofstream& out, t_struct* tstruct);
97 void generate_cocoa_struct_description(std::ofstream& out, t_struct* tstruct);
98
99 std::string function_result_helper_struct_type(t_function* tfunction);
Andrew McGeachie0c895712009-07-21 21:14:19 +0000100 std::string function_args_helper_struct_type(t_function* tfunction);
David Reisse21ce4c2008-03-27 21:40:59 +0000101 void generate_function_helpers(t_function* tfunction);
102
103 /**
104 * Service-level generation functions
105 */
106
107 void generate_cocoa_service_protocol (std::ofstream& out, t_service* tservice);
108 void generate_cocoa_service_client_interface (std::ofstream& out, t_service* tservice);
109 void generate_cocoa_service_client_implementation (std::ofstream& out, t_service* tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +0000110 void generate_cocoa_service_server_interface (std::ofstream& out, t_service* tservice);
111 void generate_cocoa_service_server_implementation (std::ofstream& out, t_service* tservice);
David Reisse21ce4c2008-03-27 21:40:59 +0000112 void generate_cocoa_service_helpers (t_service* tservice);
113 void generate_service_client (t_service* tservice);
114 void generate_service_server (t_service* tservice);
115 void generate_process_function (t_service* tservice, t_function* tfunction);
116
117 /**
118 * Serialization constructs
119 */
120
121 void generate_deserialize_field (std::ofstream& out,
122 t_field* tfield,
123 std::string fieldName);
124
125 void generate_deserialize_struct (std::ofstream& out,
126 t_struct* tstruct,
127 std::string prefix="");
128
129 void generate_deserialize_container (std::ofstream& out,
130 t_type* ttype,
131 std::string prefix="");
132
133 void generate_deserialize_set_element (std::ofstream& out,
134 t_set* tset,
135 std::string prefix="");
136
137 void generate_deserialize_map_element (std::ofstream& out,
138 t_map* tmap,
139 std::string prefix="");
140
141 void generate_deserialize_list_element (std::ofstream& out,
142 t_list* tlist,
143 std::string prefix="");
144
145 void generate_serialize_field (std::ofstream& out,
146 t_field* tfield,
147 std::string prefix="");
148
149 void generate_serialize_struct (std::ofstream& out,
150 t_struct* tstruct,
151 std::string fieldName="");
152
153 void generate_serialize_container (std::ofstream& out,
154 t_type* ttype,
155 std::string prefix="");
156
157 void generate_serialize_map_element (std::ofstream& out,
158 t_map* tmap,
159 std::string iter,
160 std::string map);
161
162 void generate_serialize_set_element (std::ofstream& out,
163 t_set* tmap,
164 std::string iter);
165
166 void generate_serialize_list_element (std::ofstream& out,
167 t_list* tlist,
168 std::string index,
169 std::string listName);
170
171 /**
172 * Helper rendering functions
173 */
174
175 std::string cocoa_prefix();
176 std::string cocoa_imports();
177 std::string cocoa_thrift_imports();
178 std::string type_name(t_type* ttype, bool class_ref=false);
179 std::string base_type_name(t_base_type* tbase);
180 std::string declare_field(t_field* tfield);
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000181 std::string declare_property(t_field* tfield);
Andrew McGeachie72751722009-10-27 20:23:02 +0000182 std::string dynamic_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();
337 if ((*c_iter)->has_value()) {
338 f_header_ <<
339 " = " << (*c_iter)->get_value();
340 }
341 }
342
343 indent_down();
344 f_header_ <<
345 endl <<
346 "};" << endl <<
347 endl;
348}
349
350/**
351 * Generates a class that holds all the constants. Primitive values
352 * could have been placed outside this class, but I just put
353 * everything in for consistency.
354 */
355void t_cocoa_generator::generate_consts(std::vector<t_const*> consts) {
356 std::ostringstream const_interface;
357 string constants_class_name = cocoa_prefix_ + program_name_ + "Constants";
358
Andrew McGeachie853bdfe2009-05-05 00:44:48 +0000359 const_interface << "@interface " << constants_class_name << " : NSObject ";
Mark Slee7e9eea42007-09-10 21:00:23 +0000360 scope_up(const_interface);
361 scope_down(const_interface);
362
363 // getter method for each constant defined.
364 vector<t_const*>::iterator c_iter;
365 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
366 string name = (*c_iter)->get_name();
367 t_type* type = (*c_iter)->get_type();
368 const_interface <<
369 "+ (" << type_name(type) << ") " << name << ";" << endl;
370 }
371
372 const_interface << "@end";
373
374 // this gets spit into the header file in ::close_generator
375 constants_declarations_ = const_interface.str();
376
377 // static variables in the .m hold all constant values
378 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
379 string name = (*c_iter)->get_name();
380 t_type* type = (*c_iter)->get_type();
381 f_impl_ <<
382 "static " << type_name(type) << " " << cocoa_prefix_ << name;
383 if (!type->is_container() && !type->is_struct()) {
384 f_impl_ << " = " << render_const_value(name, type, (*c_iter)->get_value());
385 }
386 f_impl_ << ";" << endl;
387 }
388 f_impl_ << endl;
389
390 f_impl_ << "@implementation " << constants_class_name << endl;
391
392 // initialize complex constants when the class is loaded
393 f_impl_ << "+ (void) initialize ";
394 scope_up(f_impl_);
395
396 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
397 if ((*c_iter)->get_type()->is_container() ||
398 (*c_iter)->get_type()->is_struct()) {
399 string name = (*c_iter)->get_name();
Andrew McGeachie75649252009-07-21 16:12:33 +0000400 f_impl_ << indent() << cocoa_prefix_ << name << " = " << render_const_value(name,
Mark Slee7e9eea42007-09-10 21:00:23 +0000401 (*c_iter)->get_type(),
402 (*c_iter)->get_value());
403 f_impl_ << ";" << endl;
404 }
405 }
406 scope_down(f_impl_);
407
408 // getter method for each constant
409 for (c_iter = consts.begin(); c_iter != consts.end(); ++c_iter) {
410 string name = (*c_iter)->get_name();
411 t_type* type = (*c_iter)->get_type();
412 f_impl_ <<
413 "+ (" << type_name(type) << ") " << name;
414 scope_up(f_impl_);
Andrew McGeachie75649252009-07-21 16:12:33 +0000415 indent(f_impl_) << "return " << cocoa_prefix_ << name << ";" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +0000416 scope_down(f_impl_);
417 }
418
419 f_impl_ << "@end" << endl << endl;
420}
421
422
423/**
424 * Generates a struct definition for a thrift data type. This is a class
425 * with protected data members, read(), write(), and getters and setters.
426 *
427 * @param tstruct The struct definition
428 */
429void t_cocoa_generator::generate_struct(t_struct* tstruct) {
430 generate_cocoa_struct_interface(f_header_, tstruct, false);
431 generate_cocoa_struct_implementation(f_impl_, tstruct, false);
432}
433
434/**
435 * Exceptions are structs, but they inherit from NSException
436 *
437 * @param tstruct The struct definition
438 */
439void t_cocoa_generator::generate_xception(t_struct* txception) {
440 generate_cocoa_struct_interface(f_header_, txception, true);
441 generate_cocoa_struct_implementation(f_impl_, txception, true);
442}
443
444
445/**
446 * Generate the interface for a struct
447 *
448 * @param tstruct The struct definition
449 */
450void t_cocoa_generator::generate_cocoa_struct_interface(ofstream &out,
451 t_struct* tstruct,
452 bool is_exception) {
453 out << "@interface " << cocoa_prefix_ << tstruct->get_name() << " : ";
Mark Slee12a28752007-11-20 23:55:33 +0000454
Mark Slee7e9eea42007-09-10 21:00:23 +0000455 if (is_exception) {
456 out << "NSException ";
457 } else {
458 out << "NSObject ";
Mark Slee12a28752007-11-20 23:55:33 +0000459 }
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000460 out << "<NSCoding> ";
Mark Slee12a28752007-11-20 23:55:33 +0000461
Mark Slee7e9eea42007-09-10 21:00:23 +0000462 scope_up(out);
463
464 // members are protected. this is redundant, but explicit.
465 // f_header_ << endl << "@protected:" << endl;
466
467 const vector<t_field*>& members = tstruct->get_members();
468
469 // member varialbes
Mark Slee12a28752007-11-20 23:55:33 +0000470 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000471 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
472 out << indent() << declare_field(*m_iter) << endl;
473 }
474
475 if (members.size() > 0) {
476 out << endl;
477 // isset fields
478 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
479 indent(out) <<
480 "BOOL __" << (*m_iter)->get_name() << "_isset;" << endl;
481 }
482 }
483
484 scope_down(out);
485 out << endl;
486
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000487 // properties
488 if (members.size() > 0) {
489 out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" << endl;
490 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
491 out << indent() << declare_property(*m_iter) << endl;
492 }
493 out << "#endif" << endl << endl;
494 }
495
Mark Slee7e9eea42007-09-10 21:00:23 +0000496 // initializer for all fields
497 if (!members.empty()) {
498 generate_cocoa_struct_initializer_signature(out, tstruct);
499 out << ";" << endl;
500 }
501 out << endl;
502
Mark Slee33a7d892007-09-14 19:44:30 +0000503 // read and write
504 out << "- (void) read: (id <TProtocol>) inProtocol;" << endl;
505 out << "- (void) write: (id <TProtocol>) outProtocol;" << endl;
506 out << endl;
507
Mark Slee7e9eea42007-09-10 21:00:23 +0000508 // getters and setters
509 generate_cocoa_struct_field_accessor_declarations(out, tstruct, is_exception);
510
511 out << "@end" << endl << endl;
512}
513
514
515/**
516 * Generate signature for initializer of struct with a parameter for
517 * each field.
518 */
519void t_cocoa_generator::generate_cocoa_struct_initializer_signature(ofstream &out,
520 t_struct* tstruct) {
521 const vector<t_field*>& members = tstruct->get_members();
Mark Slee12a28752007-11-20 23:55:33 +0000522 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000523 indent(out) << "- (id) initWith";
524 for (m_iter = members.begin(); m_iter != members.end(); ) {
525 if (m_iter == members.begin()) {
526 out << capitalize((*m_iter)->get_name());
527 } else {
528 out << (*m_iter)->get_name();
529 }
530 out << ": (" << type_name((*m_iter)->get_type()) << ") " <<
531 (*m_iter)->get_name();
532 ++m_iter;
533 if (m_iter != members.end()) {
534 out << " ";
535 }
536 }
537}
538
539
540/**
541 * Generate getter and setter declarations for all fields, plus an
542 * IsSet getter.
543 */
544void t_cocoa_generator::generate_cocoa_struct_field_accessor_declarations(ofstream &out,
545 t_struct* tstruct,
546 bool is_exception) {
547 const vector<t_field*>& members = tstruct->get_members();
Mark Slee12a28752007-11-20 23:55:33 +0000548 vector<t_field*>::const_iterator m_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +0000549 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
550 out << indent() << "- (" << type_name((*m_iter)->get_type()) << ") " << decapitalize((*m_iter)->get_name()) << ";" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000551 out << indent() << "- (void) set" << capitalize((*m_iter)->get_name()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000552 ": (" << type_name((*m_iter)->get_type()) << ") " << (*m_iter)->get_name() << ";" << endl;
553 out << indent() << "- (BOOL) " << (*m_iter)->get_name() << "IsSet;" << endl << endl;
554 }
555}
556
557
558/**
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000559 * Generate the initWithCoder method for this struct so it's compatible with
560 * the NSCoding protocol
561 */
562void t_cocoa_generator::generate_cocoa_struct_init_with_coder_method(ofstream &out,
563 t_struct* tstruct,
564 bool is_exception)
565{
566 indent(out) << "- (id) initWithCoder: (NSCoder *) decoder" << endl;
567 scope_up(out);
568 if (is_exception) {
569 // NSExceptions conform to NSCoding, so we can call super
570 out << indent() << "self = [super initWithCoder: decoder];" << endl;
571 } else {
572 out << indent() << "self = [super init];" << endl;
573 }
574
575 const vector<t_field*>& members = tstruct->get_members();
576 vector<t_field*>::const_iterator m_iter;
577
578 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
579 t_type* t = get_true_type((*m_iter)->get_type());
580 out << indent() << "if ([decoder containsValueForKey: @\""<< (*m_iter)->get_name() <<"\"])" << endl;
581 scope_up(out);
582 out << indent() << "__" << (*m_iter)->get_name() << " = ";
583 if (type_can_be_null(t))
584 {
585 out << "[[decoder decodeObjectForKey: @\"" << (*m_iter)->get_name() << "\"] retain];" << endl;
586 }
587 else if (t->is_enum())
588 {
589 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
590 }
591 else
592 {
593 t_base_type::t_base tbase = ((t_base_type *) t)->get_base();
594 switch (tbase)
595 {
596 case t_base_type::TYPE_BOOL:
597 out << "[decoder decodeBoolForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
598 break;
599 case t_base_type::TYPE_BYTE:
600 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
601 break;
602 case t_base_type::TYPE_I16:
603 out << "[decoder decodeIntForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
604 break;
605 case t_base_type::TYPE_I32:
606 out << "[decoder decodeInt32ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
607 break;
608 case t_base_type::TYPE_I64:
609 out << "[decoder decodeInt64ForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
610 break;
611 case t_base_type::TYPE_DOUBLE:
612 out << "[decoder decodeDoubleForKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
613 break;
614 default:
615 throw "compiler error: don't know how to decode thrift type: " + t_base_type::t_base_name(tbase);
616 }
617 }
618 out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl;
619 scope_down(out);
620 }
621
622 out << indent() << "return self;" << endl;
623 scope_down(out);
624 out << endl;
625}
626
627
628/**
629 * Generate the encodeWithCoder method for this struct so it's compatible with
630 * the NSCoding protocol
631 */
632void t_cocoa_generator::generate_cocoa_struct_encode_with_coder_method(ofstream &out,
633 t_struct* tstruct,
634 bool is_exception)
635{
636 indent(out) << "- (void) encodeWithCoder: (NSCoder *) encoder" << endl;
637 scope_up(out);
638 if (is_exception) {
639 // NSExceptions conform to NSCoding, so we can call super
640 out << indent() << "[super encodeWithCoder: encoder];" << endl;
641 }
642
643 const vector<t_field*>& members = tstruct->get_members();
644 vector<t_field*>::const_iterator m_iter;
645
646 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
647 t_type* t = get_true_type((*m_iter)->get_type());
648 out << indent() << "if (__"<< (*m_iter)->get_name() <<"_isset)" << endl;
649 scope_up(out);
650 //out << indent() << "__" << (*m_iter)->get_name() << " = ";
651 if (type_can_be_null(t))
652 {
653 out << indent() << "[encoder encodeObject: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
654 }
655 else if (t->is_enum())
656 {
657 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
658 }
659 else
660 {
661 t_base_type::t_base tbase = ((t_base_type *) t)->get_base();
662 switch (tbase)
663 {
664 case t_base_type::TYPE_BOOL:
665 out << indent() << "[encoder encodeBool: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
666 break;
667 case t_base_type::TYPE_BYTE:
668 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
669 break;
670 case t_base_type::TYPE_I16:
671 out << indent() << "[encoder encodeInt: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
672 break;
673 case t_base_type::TYPE_I32:
674 out << indent() << "[encoder encodeInt32: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
675 break;
676 case t_base_type::TYPE_I64:
677 out << indent() << "[encoder encodeInt64: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
678 break;
679 case t_base_type::TYPE_DOUBLE:
680 out << indent() << "[encoder encodeDouble: __" << (*m_iter)->get_name() << " forKey: @\"" << (*m_iter)->get_name() << "\"];" << endl;
681 break;
682 default:
683 throw "compiler error: don't know how to encode thrift type: " + t_base_type::t_base_name(tbase);
684 }
685 }
686 scope_down(out);
687 }
688
689 scope_down(out);
690 out << endl;
691}
692
693
694/**
Mark Slee7e9eea42007-09-10 21:00:23 +0000695 * Generate struct implementation.
696 *
697 * @param tstruct The struct definition
698 * @param is_exception Is this an exception?
699 * @param is_result If this is a result it needs a different writer
700 */
701void t_cocoa_generator::generate_cocoa_struct_implementation(ofstream &out,
702 t_struct* tstruct,
703 bool is_exception,
704 bool is_result) {
705 indent(out) <<
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000706 "@implementation " << cocoa_prefix_ << tstruct->get_name() << endl << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +0000707
708 // exceptions need to call the designated initializer on NSException
709 if (is_exception) {
710 out << indent() << "- (id) init" << endl;
711 scope_up(out);
712 out << indent() << "return [super initWithName: @\"" << tstruct->get_name() <<
713 "\" reason: @\"unknown\" userInfo: nil];" << endl;
714 scope_down(out);
715 }
716
Mark Slee7e9eea42007-09-10 21:00:23 +0000717 const vector<t_field*>& members = tstruct->get_members();
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000718 vector<t_field*>::const_iterator m_iter;
719
Andrew McGeachie72751722009-10-27 20:23:02 +0000720 // @dynamic property declarations
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000721 if (!members.empty()) {
722 out << "#if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)" << endl;
723 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
Andrew McGeachie72751722009-10-27 20:23:02 +0000724 out << indent() << dynamic_property(*m_iter) << endl;
Andrew McGeachief2e03ba2009-07-21 16:51:49 +0000725 }
726 out << "#endif" << endl << endl;
727 }
728
729 // initializer with all fields as params
Mark Slee7e9eea42007-09-10 21:00:23 +0000730 if (!members.empty()) {
731 generate_cocoa_struct_initializer_signature(out, tstruct);
732 out << endl;
733 scope_up(out);
734 if (is_exception) {
735 out << indent() << "self = [self init];" << endl;
736 } else {
737 out << indent() << "self = [super init];" << endl;
738 }
739
Mark Slee7e9eea42007-09-10 21:00:23 +0000740 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
741 t_type* t = get_true_type((*m_iter)->get_type());
742 out << indent() << "__" << (*m_iter)->get_name() << " = ";
743 if (type_can_be_null(t)) {
744 out << "[" << (*m_iter)->get_name() << " retain];" << endl;
745 } else {
746 out << (*m_iter)->get_name() << ";" << endl;
747 }
748 out << indent() << "__" << (*m_iter)->get_name() << "_isset = YES;" << endl;
749 }
750
751 out << indent() << "return self;" << endl;
752 scope_down(out);
753 out << endl;
754 }
Andrew McGeachie3533dcb2009-11-03 18:52:15 +0000755
756 // initWithCoder for NSCoding
757 generate_cocoa_struct_init_with_coder_method(out, tstruct, is_exception);
758 // encodeWithCoder for NSCoding
759 generate_cocoa_struct_encode_with_coder_method(out, tstruct, is_exception);
Mark Slee7e9eea42007-09-10 21:00:23 +0000760
761 // dealloc
762 if (!members.empty()) {
763 out << "- (void) dealloc" << endl;
764 scope_up(out);
765
Mark Slee7e9eea42007-09-10 21:00:23 +0000766 for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
767 t_type* t = get_true_type((*m_iter)->get_type());
768 if (type_can_be_null(t)) {
769 indent(out) << "[__" << (*m_iter)->get_name() << " release];" << endl;
770 }
771 }
772
773 out << indent() << "[super dealloc];" << endl;
774 scope_down(out);
775 out << endl;
776 }
777
778 // the rest of the methods
779 generate_cocoa_struct_field_accessor_implementations(out, tstruct, is_exception);
780 generate_cocoa_struct_reader(out, tstruct);
781 if (is_result) {
782 generate_cocoa_struct_result_writer(out, tstruct);
783 } else {
784 generate_cocoa_struct_writer(out, tstruct);
785 }
786 generate_cocoa_struct_description(out, tstruct);
787
788 out << "@end" << endl << endl;
789}
790
791
792/**
793 * Generates a function to read all the fields of the struct.
794 *
795 * @param tstruct The struct definition
796 */
797void t_cocoa_generator::generate_cocoa_struct_reader(ofstream& out,
798 t_struct* tstruct) {
799 out <<
800 "- (void) read: (id <TProtocol>) inProtocol" << endl;
801 scope_up(out);
802
803 const vector<t_field*>& fields = tstruct->get_members();
804 vector<t_field*>::const_iterator f_iter;
805
806 // Declare stack tmp variables
807 indent(out) << "NSString * fieldName;" << endl;
808 indent(out) << "int fieldType;" << endl;
809 indent(out) << "int fieldID;" << endl;
810 out << endl;
811
Mark Slee33a7d892007-09-14 19:44:30 +0000812 indent(out) << "[inProtocol readStructBeginReturningName: NULL];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000813
Mark Slee7e9eea42007-09-10 21:00:23 +0000814 // Loop over reading in fields
815 indent(out) <<
816 "while (true)" << endl;
817 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +0000818
Mark Slee7e9eea42007-09-10 21:00:23 +0000819 // Read beginning field marker
820 indent(out) <<
Mark Slee33a7d892007-09-14 19:44:30 +0000821 "[inProtocol readFieldBeginReturningName: &fieldName type: &fieldType fieldID: &fieldID];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000822
Mark Slee7e9eea42007-09-10 21:00:23 +0000823 // Check for field STOP marker and break
824 indent(out) <<
825 "if (fieldType == TType_STOP) { " << endl;
826 indent_up();
827 indent(out) <<
828 "break;" << endl;
829 indent_down();
830 indent(out) <<
831 "}" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000832
Mark Slee7e9eea42007-09-10 21:00:23 +0000833 // Switch statement on the field we are reading
834 indent(out) <<
835 "switch (fieldID)" << endl;
836
837 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +0000838
Mark Slee7e9eea42007-09-10 21:00:23 +0000839 // Generate deserialization code for known cases
840 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
841 indent(out) <<
842 "case " << (*f_iter)->get_key() << ":" << endl;
843 indent_up();
844 indent(out) <<
845 "if (fieldType == " << type_to_enum((*f_iter)->get_type()) << ") {" << endl;
846 indent_up();
847
848 generate_deserialize_field(out, *f_iter, "fieldValue");
849 indent(out) << call_field_setter(*f_iter, "fieldValue") << endl;
850 // if this is an allocated field, release it since the struct
851 // is now retaining it
852 if (type_can_be_null((*f_iter)->get_type())) {
Mark Slee12a28752007-11-20 23:55:33 +0000853 // deserialized strings are autorelease, so don't release them
Mark Sleea9387af2007-11-21 22:05:50 +0000854 if (!(get_true_type((*f_iter)->get_type())->is_string())) {
Mark Slee12a28752007-11-20 23:55:33 +0000855 indent(out) << "[fieldValue release];" << endl;
856 }
Mark Slee7e9eea42007-09-10 21:00:23 +0000857 }
858
859 indent_down();
Andrew McGeachie645d7b82009-07-21 15:30:16 +0000860 out << indent() << "} else { " << endl;
861 if (log_unexpected_) {
862 out << indent() << " NSLog(@\"%s: field ID %i has unexpected type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl;
863 }
864 out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000865 indent() << "}" << endl <<
866 indent() << "break;" << endl;
867 indent_down();
868 }
Mark Slee12a28752007-11-20 23:55:33 +0000869
Mark Slee7e9eea42007-09-10 21:00:23 +0000870 // In the default case we skip the field
Andrew McGeachie645d7b82009-07-21 15:30:16 +0000871 out << indent() << "default:" << endl;
872 if (log_unexpected_) {
873 out << indent() << " NSLog(@\"%s: unexpected field ID %i with type %i. Skipping.\", __PRETTY_FUNCTION__, fieldID, fieldType);" << endl;
874 }
875 out << indent() << " [TProtocolUtil skipType: fieldType onProtocol: inProtocol];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000876 indent() << " break;" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000877
Mark Slee7e9eea42007-09-10 21:00:23 +0000878 scope_down(out);
879
880 // Read field end marker
881 indent(out) <<
882 "[inProtocol readFieldEnd];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000883
Mark Slee7e9eea42007-09-10 21:00:23 +0000884 scope_down(out);
Mark Slee12a28752007-11-20 23:55:33 +0000885
Mark Slee7e9eea42007-09-10 21:00:23 +0000886 out <<
887 indent() << "[inProtocol readStructEnd];" << endl;
888
889 indent_down();
890 out <<
891 indent() << "}" << endl <<
892 endl;
893}
894
895/**
896 * Generates a function to write all the fields of the struct
897 *
898 * @param tstruct The struct definition
899 */
900void t_cocoa_generator::generate_cocoa_struct_writer(ofstream& out,
901 t_struct* tstruct) {
902 out <<
903 indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl;
904 indent_up();
905
906 string name = tstruct->get_name();
907 const vector<t_field*>& fields = tstruct->get_members();
908 vector<t_field*>::const_iterator f_iter;
909
910 out <<
911 indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl;
912
913 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
Mark Slee33a7d892007-09-14 19:44:30 +0000914 out <<
915 indent() << "if (__" << (*f_iter)->get_name() << "_isset) {" << endl;
916 indent_up();
Mark Slee7e9eea42007-09-10 21:00:23 +0000917 bool null_allowed = type_can_be_null((*f_iter)->get_type());
918 if (null_allowed) {
919 out <<
920 indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl;
921 indent_up();
922 }
923
Mark Slee12a28752007-11-20 23:55:33 +0000924 indent(out) << "[outProtocol writeFieldBeginWithName: @\"" <<
925 (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000926 " fieldID: " << (*f_iter)->get_key() << "];" << endl;
927
928 // Write field contents
929 generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name());
930
931 // Write field closer
932 indent(out) <<
933 "[outProtocol writeFieldEnd];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +0000934
Mark Slee7e9eea42007-09-10 21:00:23 +0000935 if (null_allowed) {
Mark Slee33a7d892007-09-14 19:44:30 +0000936 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +0000937 }
Mark Slee33a7d892007-09-14 19:44:30 +0000938 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +0000939 }
940 // Write the struct map
941 out <<
942 indent() << "[outProtocol writeFieldStop];" << endl <<
943 indent() << "[outProtocol writeStructEnd];" << endl;
944
945 indent_down();
946 out <<
947 indent() << "}" << endl <<
948 endl;
949}
950
951/**
952 * Generates a function to write all the fields of the struct, which
953 * is a function result. These fields are only written if they are
954 * set, and only one of them can be set at a time.
955 *
956 * @param tstruct The struct definition
957 */
958void t_cocoa_generator::generate_cocoa_struct_result_writer(ofstream& out,
959 t_struct* tstruct) {
960 out <<
961 indent() << "- (void) write: (id <TProtocol>) outProtocol {" << endl;
962 indent_up();
963
964 string name = tstruct->get_name();
965 const vector<t_field*>& fields = tstruct->get_members();
966 vector<t_field*>::const_iterator f_iter;
967
968 out <<
969 indent() << "[outProtocol writeStructBeginWithName: @\"" << name << "\"];" << endl;
970
971 bool first = true;
972 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
973 if (first) {
974 first = false;
975 out <<
976 endl <<
977 indent() << "if ";
978 } else {
979 out <<
980 " else if ";
981 }
982
983 out <<
984 "(__" << (*f_iter)->get_name() << "_isset) {" << endl;
985 indent_up();
986
987 bool null_allowed = type_can_be_null((*f_iter)->get_type());
988 if (null_allowed) {
989 out <<
990 indent() << "if (__" << (*f_iter)->get_name() << " != nil) {" << endl;
991 indent_up();
992 }
993
994 indent(out) << "[outProtocol writeFieldBeginWithName: @\"" <<
Mark Slee12a28752007-11-20 23:55:33 +0000995 (*f_iter)->get_name() << "\" type: " << type_to_enum((*f_iter)->get_type()) <<
Mark Slee7e9eea42007-09-10 21:00:23 +0000996 " fieldID: " << (*f_iter)->get_key() << "];" << endl;
997
998 // Write field contents
999 generate_serialize_field(out, *f_iter, "__"+(*f_iter)->get_name());
1000
1001 // Write field closer
1002 indent(out) <<
1003 "[outProtocol writeFieldEnd];" << endl;
1004
1005 if (null_allowed) {
1006 indent_down();
1007 indent(out) << "}" << endl;
1008 }
1009
1010 indent_down();
1011 indent(out) << "}";
1012 }
1013 // Write the struct map
1014 out <<
1015 endl <<
1016 indent() << "[outProtocol writeFieldStop];" << endl <<
1017 indent() << "[outProtocol writeStructEnd];" << endl;
1018
1019 indent_down();
1020 out <<
1021 indent() << "}" << endl <<
1022 endl;
1023}
1024
1025/**
1026 * Generate property accessor methods for all fields in the struct.
1027 * getter, setter, isset getter.
1028 *
1029 * @param tstruct The struct definition
1030 */
1031void t_cocoa_generator::generate_cocoa_struct_field_accessor_implementations(ofstream& out,
1032 t_struct* tstruct,
1033 bool is_exception) {
1034 const vector<t_field*>& fields = tstruct->get_members();
1035 vector<t_field*>::const_iterator f_iter;
1036 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1037 t_field* field = *f_iter;
1038 t_type* type = get_true_type(field->get_type());
1039 std::string field_name = field->get_name();
1040 std::string cap_name = field_name;
1041 cap_name[0] = toupper(cap_name[0]);
1042
1043 // Simple getter
1044 indent(out) << "- (" << type_name(type) << ") ";
1045 out << field_name << " {" << endl;
1046 indent_up();
1047 if (!type_can_be_null(type)) {
1048 indent(out) << "return __" << field_name << ";" << endl;
1049 } else {
1050 indent(out) << "return [[__" << field_name << " retain] autorelease];" << endl;
1051 }
1052 indent_down();
1053 indent(out) << "}" << endl << endl;
1054
1055 // Simple setter
1056 indent(out) << "- (void) set" << cap_name << ": (" << type_name(type) <<
1057 ") " << field_name << " {" << endl;
1058 indent_up();
1059 if (!type_can_be_null(type)) {
1060 indent(out) << "__" << field_name << " = " << field_name << ";" << endl;
1061 } else {
1062 indent(out) << "[" << field_name << " retain];" << endl;
1063 indent(out) << "[__" << field_name << " release];" << endl;
1064 indent(out) << "__" << field_name << " = " << field_name << ";" << endl;
1065 }
1066 indent(out) << "__" << field_name << "_isset = YES;" << endl;
1067 indent_down();
1068 indent(out) << "}" << endl << endl;
1069
1070 // IsSet
1071 indent(out) << "- (BOOL) " << field_name << "IsSet {" << endl;
1072 indent_up();
1073 indent(out) << "return __" << field_name << "_isset;" << endl;
1074 indent_down();
1075 indent(out) << "}" << endl << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001076
Mark Slee7e9eea42007-09-10 21:00:23 +00001077 // Unsetter - do we need this?
1078 indent(out) << "- (void) unset" << cap_name << " {" << endl;
1079 indent_up();
1080 if (type_can_be_null(type)) {
1081 indent(out) << "[__" << field_name << " release];" << endl;
1082 indent(out) << "__" << field_name << " = nil;" << endl;
1083 }
1084 indent(out) << "__" << field_name << "_isset = NO;" << endl;
1085 indent_down();
1086 indent(out) << "}" << endl << endl;
1087 }
1088}
1089
1090/**
1091 * Generates a description method for the given struct
1092 *
1093 * @param tstruct The struct definition
1094 */
1095void t_cocoa_generator::generate_cocoa_struct_description(ofstream& out,
1096 t_struct* tstruct) {
1097 out <<
1098 indent() << "- (NSString *) description {" << endl;
1099 indent_up();
1100
1101 out <<
Mark Slee12a28752007-11-20 23:55:33 +00001102 indent() << "NSMutableString * ms = [NSMutableString stringWithString: @\"" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001103 tstruct->get_name() << "(\"];" << endl;
1104
1105 const vector<t_field*>& fields = tstruct->get_members();
1106 vector<t_field*>::const_iterator f_iter;
1107 bool first = true;
1108 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1109 if (first) {
1110 first = false;
1111 indent(out) << "[ms appendString: @\"" << (*f_iter)->get_name() << ":\"];" << endl;
1112 } else {
1113 indent(out) << "[ms appendString: @\"," << (*f_iter)->get_name() << ":\"];" << endl;
1114 }
1115 t_type* ttype = (*f_iter)->get_type();
1116 indent(out) << "[ms appendFormat: @\"" << format_string_for_type(ttype) << "\", __" <<
1117 (*f_iter)->get_name() << "];" << endl;
1118 }
1119 out <<
1120 indent() << "[ms appendString: @\")\"];" << endl <<
Andrew McGeachie38e1a102009-07-21 17:22:03 +00001121 indent() << "return [NSString stringWithString: ms];" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +00001122
1123 indent_down();
1124 indent(out) << "}" << endl <<
1125 endl;
1126}
1127
1128
1129/**
1130 * Generates a thrift service. In Objective-C this consists of a
1131 * protocol definition, a client interface and a client implementation.
1132 *
1133 * @param tservice The service definition
1134 */
1135void t_cocoa_generator::generate_service(t_service* tservice) {
1136 generate_cocoa_service_protocol(f_header_, tservice);
1137 generate_cocoa_service_client_interface(f_header_, tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001138 generate_cocoa_service_server_interface(f_header_, tservice);
Mark Slee7e9eea42007-09-10 21:00:23 +00001139 generate_cocoa_service_helpers(tservice);
1140 generate_cocoa_service_client_implementation(f_impl_, tservice);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001141 generate_cocoa_service_server_implementation(f_impl_, tservice);
Mark Slee7e9eea42007-09-10 21:00:23 +00001142}
1143
1144
1145/**
1146 * Generates structs for all the service return types
1147 *
1148 * @param tservice The service
1149 */
1150void t_cocoa_generator::generate_cocoa_service_helpers(t_service* tservice) {
1151 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001152 vector<t_function*>::iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001153 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
Andrew McGeachie0c895712009-07-21 21:14:19 +00001154 t_struct* ts = (*f_iter)->get_arglist();
1155 generate_cocoa_struct_interface(f_impl_, ts, false);
1156 generate_cocoa_struct_implementation(f_impl_, ts, false, false);
Mark Slee7e9eea42007-09-10 21:00:23 +00001157 generate_function_helpers(*f_iter);
1158 }
1159}
1160
1161string t_cocoa_generator::function_result_helper_struct_type(t_function* tfunction) {
Andrew McGeachie0c895712009-07-21 21:14:19 +00001162 return capitalize(tfunction->get_name()) + "_result";
1163}
1164
1165
1166string t_cocoa_generator::function_args_helper_struct_type(t_function* tfunction) {
1167 return tfunction->get_name() + "_args";
Mark Slee7e9eea42007-09-10 21:00:23 +00001168}
1169
1170
1171/**
1172 * Generates a struct and helpers for a function.
1173 *
1174 * @param tfunction The function
1175 */
1176void t_cocoa_generator::generate_function_helpers(t_function* tfunction) {
David Reiss47329252009-03-24 20:01:02 +00001177 if (tfunction->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001178 return;
1179 }
1180
1181 // create a result struct with a success field of the return type,
1182 // and a field for each type of exception thrown
1183 t_struct result(program_, function_result_helper_struct_type(tfunction));
1184 t_field success(tfunction->get_returntype(), "success", 0);
1185 if (!tfunction->get_returntype()->is_void()) {
1186 result.append(&success);
1187 }
1188
1189 t_struct* xs = tfunction->get_xceptions();
1190 const vector<t_field*>& fields = xs->get_members();
1191 vector<t_field*>::const_iterator f_iter;
1192 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
1193 result.append(*f_iter);
1194 }
1195
1196 // generate the result struct
1197 generate_cocoa_struct_interface(f_impl_, &result, false);
Andrew McGeachie0c895712009-07-21 21:14:19 +00001198 generate_cocoa_struct_implementation(f_impl_, &result, false, true);
Mark Slee7e9eea42007-09-10 21:00:23 +00001199}
1200
Andrew McGeachie0c895712009-07-21 21:14:19 +00001201
Mark Slee7e9eea42007-09-10 21:00:23 +00001202/**
1203 * Generates a service protocol definition.
1204 *
1205 * @param tservice The service to generate a protocol definition for
1206 */
1207void t_cocoa_generator::generate_cocoa_service_protocol(ofstream& out,
1208 t_service* tservice) {
1209 out << "@protocol " << cocoa_prefix_ << tservice->get_name() << " <NSObject>" << endl;
1210
1211 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001212 vector<t_function*>::iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001213 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
Mark Slee12a28752007-11-20 23:55:33 +00001214 out << "- " << function_signature(*f_iter) << ";" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001215 " // throws ";
1216 t_struct* xs = (*f_iter)->get_xceptions();
1217 const std::vector<t_field*>& xceptions = xs->get_members();
1218 vector<t_field*>::const_iterator x_iter;
1219 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1220 out << type_name((*x_iter)->get_type()) + ", ";
1221 }
1222 out << "TException" << endl;
1223 }
1224 out << "@end" << endl << endl;
1225}
1226
1227
1228/**
1229 * Generates a service client interface definition.
1230 *
1231 * @param tservice The service to generate a client interface definition for
1232 */
1233void t_cocoa_generator::generate_cocoa_service_client_interface(ofstream& out,
1234 t_service* tservice) {
Mark Slee12a28752007-11-20 23:55:33 +00001235 out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Client : NSObject <" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001236 cocoa_prefix_ << tservice->get_name() << "> ";
1237
1238 scope_up(out);
1239 out << indent() << "id <TProtocol> inProtocol;" << endl;
1240 out << indent() << "id <TProtocol> outProtocol;" << endl;
1241 scope_down(out);
1242
1243 out << "- (id) initWithProtocol: (id <TProtocol>) protocol;" << endl;
1244 out << "- (id) initWithInProtocol: (id <TProtocol>) inProtocol outProtocol: (id <TProtocol>) outProtocol;" << endl;
1245 out << "@end" << endl << endl;
1246}
1247
1248
1249/**
Andrew McGeachie0c895712009-07-21 21:14:19 +00001250 * Generates a service server interface definition. In other words, the TProcess implementation for the
1251 * service definition.
1252 *
1253 * @param tservice The service to generate a client interface definition for
1254 */
1255void t_cocoa_generator::generate_cocoa_service_server_interface(ofstream& out,
1256 t_service* tservice) {
1257 out << "@interface " << cocoa_prefix_ << tservice->get_name() << "Processor : NSObject <TProcessor> ";
1258
1259 scope_up(out);
1260 out << indent() << "id <" << cocoa_prefix_ << tservice->get_name() <<"> mService;" << endl;
1261 out << indent() << "NSDictionary * mMethodMap;" << endl;
1262 scope_down(out);
1263
1264 out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
1265 out << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service;" << endl;
1266
1267 out << "@end" << endl << endl;
1268}
1269
1270
1271/**
Mark Slee7e9eea42007-09-10 21:00:23 +00001272 * Generates a service client implementation.
1273 *
1274 * @param tservice The service to generate an implementation for
1275 */
1276void t_cocoa_generator::generate_cocoa_service_client_implementation(ofstream& out,
1277 t_service* tservice) {
1278 out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Client" << endl;
1279
1280 // initializers
1281 out << "- (id) initWithProtocol: (id <TProtocol>) protocol" << endl;
1282 scope_up(out);
1283 out << indent() << "return [self initWithInProtocol: protocol outProtocol: protocol];" << endl;
1284 scope_down(out);
1285 out << endl;
1286
1287 out << "- (id) initWithInProtocol: (id <TProtocol>) anInProtocol outProtocol: (id <TProtocol>) anOutProtocol" << endl;
1288 scope_up(out);
1289 out << indent() << "[super init];" << endl;
1290 out << indent() << "inProtocol = [anInProtocol retain];" << endl;
1291 out << indent() << "outProtocol = [anOutProtocol retain];" << endl;
1292 out << indent() << "return self;" << endl;
1293 scope_down(out);
1294 out << endl;
1295
1296 // dealloc
1297 out << "- (void) dealloc" << endl;
1298 scope_up(out);
1299 out << indent() << "[inProtocol release];" << endl;
1300 out << indent() << "[outProtocol release];" << endl;
1301 out << indent() << "[super dealloc];" << endl;
1302 scope_down(out);
1303 out << endl;
1304
1305 // generate client method implementations
1306 vector<t_function*> functions = tservice->get_functions();
Mark Slee12a28752007-11-20 23:55:33 +00001307 vector<t_function*>::const_iterator f_iter;
Mark Slee7e9eea42007-09-10 21:00:23 +00001308 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1309 string funname = (*f_iter)->get_name();
1310
1311 t_function send_function(g_type_void,
1312 string("send_") + (*f_iter)->get_name(),
1313 (*f_iter)->get_arglist());
1314
1315 string argsname = (*f_iter)->get_name() + "_args";
1316
1317 // Open function
1318 indent(out) <<
1319 "- " << function_signature(&send_function) << endl;
1320 scope_up(out);
1321
1322 // Serialize the request
1323 out <<
1324 indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" <<
1325 " type: TMessageType_CALL" <<
1326 " sequenceID: 0];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001327
Mark Slee7e9eea42007-09-10 21:00:23 +00001328 out <<
1329 indent() << "[outProtocol writeStructBeginWithName: @\"" << argsname << "\"];" << endl;
1330
1331 // write out function parameters
1332 t_struct* arg_struct = (*f_iter)->get_arglist();
1333 const vector<t_field*>& fields = arg_struct->get_members();
1334 vector<t_field*>::const_iterator fld_iter;
1335 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001336 string fieldName = (*fld_iter)->get_name();
1337 if (type_can_be_null((*fld_iter)->get_type())) {
1338 out << indent() << "if (" << fieldName << " != nil)";
1339 scope_up(out);
1340 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001341 out <<
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001342 indent() << "[outProtocol writeFieldBeginWithName: @\"" << fieldName << "\""
Mark Slee7e9eea42007-09-10 21:00:23 +00001343 " type: " << type_to_enum((*fld_iter)->get_type()) <<
1344 " fieldID: " << (*fld_iter)->get_key() << "];" << endl;
1345
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001346 generate_serialize_field(out, *fld_iter, fieldName);
Mark Slee7e9eea42007-09-10 21:00:23 +00001347
1348 out <<
1349 indent() << "[outProtocol writeFieldEnd];" << endl;
Mark Sleeaa3c5a82007-09-19 21:12:52 +00001350
1351 if (type_can_be_null((*fld_iter)->get_type())) {
1352 scope_down(out);
1353 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001354 }
1355
1356 out <<
1357 indent() << "[outProtocol writeFieldStop];" << endl;
1358 out <<
1359 indent() << "[outProtocol writeStructEnd];" << endl;
1360
1361 out <<
1362 indent() << "[outProtocol writeMessageEnd];" << endl <<
1363 indent() << "[[outProtocol transport] flush];" << endl;
1364
1365 scope_down(out);
1366 out << endl;
1367
David Reiss47329252009-03-24 20:01:02 +00001368 if (!(*f_iter)->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001369 t_struct noargs(program_);
1370 t_function recv_function((*f_iter)->get_returntype(),
1371 string("recv_") + (*f_iter)->get_name(),
1372 &noargs,
1373 (*f_iter)->get_xceptions());
1374 // Open function
1375 indent(out) <<
1376 "- " << function_signature(&recv_function) << endl;
1377 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001378
Mark Slee7e9eea42007-09-10 21:00:23 +00001379 // TODO(mcslee): Message validation here, was the seqid etc ok?
1380
1381 // check for an exception
1382 out <<
1383 indent() << "int msgType = 0;" << endl <<
Mark Slee33a7d892007-09-14 19:44:30 +00001384 indent() << "[inProtocol readMessageBeginReturningName: nil type: &msgType sequenceID: NULL];" << endl <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001385 indent() << "if (msgType == TMessageType_EXCEPTION) {" << endl <<
1386 indent() << " TApplicationException * x = [TApplicationException read: inProtocol];" << endl <<
1387 indent() << " [inProtocol readMessageEnd];" << endl <<
1388 indent() << " @throw x;" << endl <<
1389 indent() << "}" << endl;
1390
1391 // FIXME - could optimize here to reduce creation of temporary objects.
1392 string resultname = function_result_helper_struct_type(*f_iter);
1393 out <<
Mark Slee12a28752007-11-20 23:55:33 +00001394 indent() << cocoa_prefix_ << resultname << " * result = [[[" << cocoa_prefix_ <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001395 resultname << " alloc] init] autorelease];" << endl;
1396 indent(out) << "[result read: inProtocol];" << endl;
1397 indent(out) << "[inProtocol readMessageEnd];" << endl;
1398
1399 // Careful, only return _result if not a void function
1400 if (!(*f_iter)->get_returntype()->is_void()) {
1401 out <<
1402 indent() << "if ([result successIsSet]) {" << endl <<
1403 indent() << " return [result success];" << endl <<
1404 indent() << "}" << endl;
1405 }
1406
1407 t_struct* xs = (*f_iter)->get_xceptions();
1408 const std::vector<t_field*>& xceptions = xs->get_members();
1409 vector<t_field*>::const_iterator x_iter;
1410 for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) {
1411 out <<
1412 indent() << "if ([result " << (*x_iter)->get_name() << "IsSet]) {" << endl <<
1413 indent() << " @throw [result " << (*x_iter)->get_name() << "];" << endl <<
1414 indent() << "}" << endl;
1415 }
1416
1417 // If you get here it's an exception, unless a void function
1418 if ((*f_iter)->get_returntype()->is_void()) {
1419 indent(out) <<
1420 "return;" << endl;
1421 } else {
1422 out <<
1423 indent() << "@throw [TApplicationException exceptionWithType: TApplicationException_MISSING_RESULT" << endl <<
Mark Slee33a7d892007-09-14 19:44:30 +00001424 indent() << " reason: @\"" << (*f_iter)->get_name() << " failed: unknown result\"];" << endl;
Mark Slee7e9eea42007-09-10 21:00:23 +00001425 }
Mark Slee12a28752007-11-20 23:55:33 +00001426
Mark Slee7e9eea42007-09-10 21:00:23 +00001427 // Close function
1428 scope_down(out);
1429 out << endl;
1430 }
1431
1432 // Open function
1433 indent(out) <<
1434 "- " << function_signature(*f_iter) << endl;
1435 scope_up(out);
1436 indent(out) <<
1437 "[self send_" << funname;
1438
1439 // Declare the function arguments
1440 bool first = true;
1441 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1442 if (first) {
1443 first = false;
1444 } else {
1445 out << " ";
1446 }
1447 out << ": " << (*fld_iter)->get_name();
1448 }
1449 out << "];" << endl;
1450
David Reiss47329252009-03-24 20:01:02 +00001451 if (!(*f_iter)->is_oneway()) {
Mark Slee7e9eea42007-09-10 21:00:23 +00001452 out << indent();
1453 if (!(*f_iter)->get_returntype()->is_void()) {
1454 out << "return ";
1455 }
1456 out <<
1457 "[self recv_" << funname << "];" << endl;
1458 }
1459 scope_down(out);
1460 out << endl;
1461 }
1462
1463 indent_down();
1464
1465 out << "@end" << endl << endl;
1466}
1467
1468
1469/**
Andrew McGeachie0c895712009-07-21 21:14:19 +00001470 * Generates a service server implementation. In other words the actual TProcessor implementation
1471 * for the service.
1472 *
1473 * @param tservice The service to generate an implementation for
1474 */
1475void t_cocoa_generator::generate_cocoa_service_server_implementation(ofstream& out,
1476 t_service* tservice) {
1477 out << "@implementation " << cocoa_prefix_ << tservice->get_name() << "Processor" << endl;
1478 indent_up();
1479
1480 // initializer
1481 out << endl;
1482 out << "- (id) initWith" << tservice->get_name() << ": (id <" << cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
1483 scope_up(out);
1484 out << indent() << "self = [super init];" << endl;
1485 out << indent() << "if (!self) {" << endl;
1486 out << indent() << " return nil;" << endl;
1487 out << indent() << "}" << endl;
1488 out << indent() << "mService = [service retain];" << endl;
1489 out << indent() << "mMethodMap = [[NSMutableDictionary dictionary] retain];" << endl;
1490
1491 // generate method map for routing incoming calls
1492 vector<t_function*> functions = tservice->get_functions();
1493 vector<t_function*>::const_iterator f_iter;
1494 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1495 string funname = (*f_iter)->get_name();
1496 scope_up(out);
1497 out << indent() << "SEL s = @selector(process_" << funname << "_withSequenceID:inProtocol:outProtocol:);" << endl;
1498 out << indent() << "NSMethodSignature * sig = [self methodSignatureForSelector: s];" << endl;
1499 out << indent() << "NSInvocation * invocation = [NSInvocation invocationWithMethodSignature: sig];" << endl;
1500 out << indent() << "[invocation setSelector: s];" << endl;
1501 out << indent() << "[invocation retainArguments];" << endl;
1502 out << indent() << "[mMethodMap setValue: invocation forKey: @\"" << funname << "\"];" << endl;
1503 scope_down(out);
1504 }
1505 out << indent() << "return self;" << endl;
1506 scope_down(out);
1507
1508 // implementation of the 'service' method which returns the service associated with this
1509 // processor
1510 out << endl;
1511 out << indent() << "- (id<"<<cocoa_prefix_ << tservice->get_name() << ">) service" << endl;
1512 out << indent() << "{" << endl;
1513 out << indent() << " return [[mService retain] autorelease];" << endl;
1514 out << indent() << "}" << endl;
1515
1516 // implementation of the TProcess method, which dispatches the incoming call using the method map
1517 out << endl;
1518 out << indent() << "- (BOOL) processOnInputProtocol: (id <TProtocol>) inProtocol" << endl;
1519 out << indent() << " outputProtocol: (id <TProtocol>) outProtocol" <<endl;
1520 out << indent() << "{" << endl;
1521 out << indent() << " NSString * messageName;" << endl;
1522 out << indent() << " int messageType;" << endl;
1523 out << indent() << " int seqID;" << endl;
1524 out << indent() << " [inProtocol readMessageBeginReturningName: &messageName" << endl;
1525 out << indent() << " type: &messageType" << endl;
1526 out << indent() << " sequenceID: &seqID];" << endl;
1527 out << indent() << " NSInvocation * invocation = [mMethodMap valueForKey: messageName];" << endl;
1528 out << indent() << " if (invocation == nil) {" << endl;
1529 out << indent() << " [TProtocolUtil skipType: TType_STRUCT onProtocol: inProtocol];" << endl;
1530 out << indent() << " [inProtocol readMessageEnd];" << endl;
1531 out << indent() << " TApplicationException * x = [TApplicationException exceptionWithType: TApplicationException_UNKNOWN_METHOD reason: [NSString stringWithFormat: @\"Invalid method name: '%@'\", messageName]];" << endl;
1532 out << indent() << " [outProtocol writeMessageBeginWithName: messageName" << endl;
1533 out << indent() << " type: TMessageType_EXCEPTION" << endl;
1534 out << indent() << " sequenceID: seqID];" << endl;
1535 out << indent() << " [x write: outProtocol];" << endl;
1536 out << indent() << " [outProtocol writeMessageEnd];" << endl;
1537 out << indent() << " [[outProtocol transport] flush];" << endl;
1538 out << indent() << " return YES;" << endl;
1539 out << indent() << " }" << endl;
1540 out << indent() << " // NSInvocation does not conform to NSCopying protocol" << endl;
1541 out << indent() << " NSInvocation * i = [NSInvocation invocationWithMethodSignature: [invocation methodSignature]];" << endl;
1542 out << indent() << " [i setSelector: [invocation selector]];" << endl;
1543 out << indent() << " [i setArgument: &seqID atIndex: 2];" << endl;
1544 out << indent() << " [i setArgument: &inProtocol atIndex: 3];" << endl;
1545 out << indent() << " [i setArgument: &outProtocol atIndex: 4];" << endl;
1546 out << indent() << " [i setTarget: self];" << endl;
1547 out << indent() << " [i invoke];" << endl;
1548 out << indent() << " return YES;" << endl;
1549 out << indent() << "}" << endl;
1550
1551 // generate a process_XXXX method for each service function, which reads args, calls the service, and writes results
1552 functions = tservice->get_functions();
1553 for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
1554 out << endl;
1555 string funname = (*f_iter)->get_name();
1556 out << indent() << "- (void) process_" << funname << "_withSequenceID: (int32_t) seqID inProtocol: (id<TProtocol>) inProtocol outProtocol: (id<TProtocol>) outProtocol" << endl;
1557 scope_up(out);
1558 string argstype = cocoa_prefix_ + function_args_helper_struct_type(*f_iter);
1559 out << indent() << argstype << " * args = [[" << argstype << " alloc] init];" << endl;
1560 out << indent() << "[args read: inProtocol];" << endl;
1561 out << indent() << "[inProtocol readMessageEnd];" << endl;
1562
1563 string resulttype = cocoa_prefix_ + function_result_helper_struct_type(*f_iter);
1564 out << indent() << resulttype << " * result = [[" << resulttype << " alloc] init];" << endl;
1565
1566 // make the call to the actual service object
1567 out << indent();
1568 if (!(*f_iter)->get_returntype()->is_void()) {
1569 out << "[result setSuccess: ";
1570 }
1571 out << "[mService " << funname;
1572 // supplying arguments
1573 t_struct* arg_struct = (*f_iter)->get_arglist();
1574 const vector<t_field*>& fields = arg_struct->get_members();
1575 vector<t_field*>::const_iterator fld_iter;
1576 for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) {
1577 string fieldName = (*fld_iter)->get_name();
1578 out << ": [args " << fieldName << "]";
1579 }
1580 out << "]";
1581 if (!(*f_iter)->get_returntype()->is_void()) {
1582 out << "]";
1583 }
1584 out << ";" << endl;
1585
1586 // write out the result
1587 out << indent() << "[outProtocol writeMessageBeginWithName: @\"" << funname << "\"" << endl;
1588 out << indent() << " type: TMessageType_REPLY" << endl;
1589 out << indent() << " sequenceID: seqID];" << endl;
1590 out << indent() << "[result write: outProtocol];" << endl;
1591 out << indent() << "[outProtocol writeMessageEnd];" << endl;
1592 out << indent() << "[[outProtocol transport] flush];" << endl;
1593 out << indent() << "[result release];" << endl;
1594 out << indent() << "[args release];" << endl;
1595
1596 scope_down(out);
1597 }
1598
1599 // dealloc
1600 out << endl;
1601 out << "- (void) dealloc" << endl;
1602 scope_up(out);
1603 out << indent() << "[mService release];" << endl;
1604 out << indent() << "[mMethodMap release];" << endl;
1605 out << indent() << "[super dealloc];" << endl;
1606 scope_down(out);
1607 out << endl;
1608
1609 indent_down();
1610
1611 out << "@end" << endl << endl;
1612}
1613
1614
1615/**
Mark Slee7e9eea42007-09-10 21:00:23 +00001616 * Deserializes a field of any type.
1617 *
1618 * @param tfield The field
1619 * @param fieldName The variable name for this field
1620 */
1621void t_cocoa_generator::generate_deserialize_field(ofstream& out,
1622 t_field* tfield,
1623 string fieldName) {
1624 t_type* type = get_true_type(tfield->get_type());
1625
1626 if (type->is_void()) {
1627 throw "CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: " +
1628 tfield->get_name();
1629 }
1630
1631 if (type->is_struct() || type->is_xception()) {
1632 generate_deserialize_struct(out,
1633 (t_struct*)type,
1634 fieldName);
1635 } else if (type->is_container()) {
1636 generate_deserialize_container(out, type, fieldName);
1637 } else if (type->is_base_type() || type->is_enum()) {
1638 indent(out) <<
1639 type_name(type) << " " << fieldName << " = [inProtocol ";
Mark Slee12a28752007-11-20 23:55:33 +00001640
Mark Slee7e9eea42007-09-10 21:00:23 +00001641 if (type->is_base_type()) {
1642 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1643 switch (tbase) {
1644 case t_base_type::TYPE_VOID:
1645 throw "compiler error: cannot serialize void field in a struct: " +
1646 tfield->get_name();
1647 break;
Mark Slee12a28752007-11-20 23:55:33 +00001648 case t_base_type::TYPE_STRING:
Mark Slee7e9eea42007-09-10 21:00:23 +00001649 if (((t_base_type*)type)->is_binary()) {
1650 out << "readBinary];";
1651 } else {
1652 out << "readString];";
1653 }
1654 break;
1655 case t_base_type::TYPE_BOOL:
1656 out << "readBool];";
1657 break;
1658 case t_base_type::TYPE_BYTE:
1659 out << "readByte];";
1660 break;
1661 case t_base_type::TYPE_I16:
1662 out << "readI16];";
1663 break;
1664 case t_base_type::TYPE_I32:
1665 out << "readI32];";
1666 break;
1667 case t_base_type::TYPE_I64:
1668 out << "readI64];";
1669 break;
1670 case t_base_type::TYPE_DOUBLE:
1671 out << "readDouble];";
1672 break;
1673 default:
1674 throw "compiler error: no Objective-C name for base type " + t_base_type::t_base_name(tbase);
1675 }
1676 } else if (type->is_enum()) {
1677 out << "readI32];";
1678 }
1679 out <<
1680 endl;
1681 } else {
1682 printf("DO NOT KNOW HOW TO DESERIALIZE FIELD '%s' TYPE '%s'\n",
1683 tfield->get_name().c_str(), type_name(type).c_str());
1684 }
1685}
1686
1687/**
1688 * Generates an unserializer for a struct, allocates the struct and invokes read:
1689 */
1690void t_cocoa_generator::generate_deserialize_struct(ofstream& out,
1691 t_struct* tstruct,
1692 string fieldName) {
Mark Slee12a28752007-11-20 23:55:33 +00001693 indent(out) << type_name(tstruct) << fieldName << " = [[" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001694 type_name(tstruct, true) << " alloc] init];" << endl;
1695 indent(out) << "[" << fieldName << " read: inProtocol];" << endl;
1696}
1697
1698/**
1699 * Deserializes a container by reading its size and then iterating
1700 */
1701void t_cocoa_generator::generate_deserialize_container(ofstream& out,
1702 t_type* ttype,
1703 string fieldName) {
1704 string size = tmp("_size");
1705 indent(out) << "int " << size << ";" << endl;
1706
1707 // Declare variables, read header
1708 if (ttype->is_map()) {
Mark Slee12a28752007-11-20 23:55:33 +00001709 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001710 << "[inProtocol readMapBeginReturningKeyType: NULL valueType: NULL size: &" <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001711 size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001712 indent(out) << "NSMutableDictionary * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001713 " = [[NSMutableDictionary alloc] initWithCapacity: " << size << "];" << endl;
1714 } else if (ttype->is_set()) {
Mark Slee12a28752007-11-20 23:55:33 +00001715 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001716 << "[inProtocol readSetBeginReturningElementType: NULL size: &" << size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001717 indent(out) << "NSMutableSet * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001718 " = [[NSMutableSet alloc] initWithCapacity: " << size << "];" << endl;
1719 } else if (ttype->is_list()) {
Mark Slee12a28752007-11-20 23:55:33 +00001720 indent(out)
Mark Slee33a7d892007-09-14 19:44:30 +00001721 << "[inProtocol readListBeginReturningElementType: NULL size: &" << size << "];" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001722 indent(out) << "NSMutableArray * " << fieldName <<
Mark Slee7e9eea42007-09-10 21:00:23 +00001723 " = [[NSMutableArray alloc] initWithCapacity: " << size << "];" << endl;
1724 }
1725 // FIXME - the code above does not verify that the element types of
1726 // the containers being read match the element types of the
1727 // containers we are reading into. Does that matter?
1728
1729 // For loop iterates over elements
1730 string i = tmp("_i");
1731 indent(out) << "int " << i << ";" << endl <<
1732 indent() << "for (" << i << " = 0; " <<
1733 i << " < " << size << "; " <<
1734 "++" << i << ")" << endl;
Mark Slee12a28752007-11-20 23:55:33 +00001735
Mark Slee7e9eea42007-09-10 21:00:23 +00001736 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001737
Mark Slee7e9eea42007-09-10 21:00:23 +00001738 if (ttype->is_map()) {
1739 generate_deserialize_map_element(out, (t_map*)ttype, fieldName);
1740 } else if (ttype->is_set()) {
1741 generate_deserialize_set_element(out, (t_set*)ttype, fieldName);
1742 } else if (ttype->is_list()) {
1743 generate_deserialize_list_element(out, (t_list*)ttype, fieldName);
1744 }
Mark Slee12a28752007-11-20 23:55:33 +00001745
Mark Slee7e9eea42007-09-10 21:00:23 +00001746 scope_down(out);
1747
1748 // Read container end
1749 if (ttype->is_map()) {
1750 indent(out) << "[inProtocol readMapEnd];" << endl;
1751 } else if (ttype->is_set()) {
1752 indent(out) << "[inProtocol readSetEnd];" << endl;
1753 } else if (ttype->is_list()) {
1754 indent(out) << "[inProtocol readListEnd];" << endl;
1755 }
1756
1757}
1758
1759
1760/**
1761 * Take a variable of a given type and wrap it in code to make it
1762 * suitable for putting into a container, if necessary. Basically,
1763 * wrap scaler primitives in NSNumber objects.
1764 */
1765string t_cocoa_generator::containerize(t_type * ttype,
1766 string fieldName)
1767{
1768 // FIXME - optimize here to avoid autorelease pool?
1769 ttype = get_true_type(ttype);
1770 if (ttype->is_enum()) {
1771 return "[NSNumber numberWithInt: " + fieldName + "]";
1772 } else if (ttype->is_base_type()) {
1773 t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
1774 switch (tbase) {
1775 case t_base_type::TYPE_VOID:
1776 throw "can't containerize void";
1777 case t_base_type::TYPE_BOOL:
1778 return "[NSNumber numberWithBool: " + fieldName + "]";
1779 case t_base_type::TYPE_BYTE:
1780 return "[NSNumber numberWithUnsignedChar: " + fieldName + "]";
1781 case t_base_type::TYPE_I16:
1782 return "[NSNumber numberWithShort: " + fieldName + "]";
1783 case t_base_type::TYPE_I32:
1784 return "[NSNumber numberWithLong: " + fieldName + "]";
1785 case t_base_type::TYPE_I64:
1786 return "[NSNumber numberWithLongLong: " + fieldName + "]";
1787 case t_base_type::TYPE_DOUBLE:
1788 return "[NSNumber numberWithDouble: " + fieldName + "]";
1789 default:
1790 break;
1791 }
1792 }
1793
1794 // do nothing
1795 return fieldName;
1796}
1797
1798
1799/**
1800 * Generates code to deserialize a map element
1801 */
1802void t_cocoa_generator::generate_deserialize_map_element(ofstream& out,
1803 t_map* tmap,
1804 string fieldName) {
1805 string key = tmp("_key");
1806 string val = tmp("_val");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001807 t_type* keyType = tmap->get_key_type();
1808 t_type* valType = tmap->get_val_type();
1809 t_field fkey(keyType, key);
1810 t_field fval(valType, val);
Mark Slee7e9eea42007-09-10 21:00:23 +00001811
1812 generate_deserialize_field(out, &fkey, key);
1813 generate_deserialize_field(out, &fval, val);
1814
1815 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001816 "[" << fieldName << " setObject: " << containerize(valType, val) <<
1817 " forKey: " << containerize(keyType, key) << "];" << endl;
1818
1819 if (type_can_be_null(keyType)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001820 if (!(get_true_type(keyType)->is_string())) {
1821 indent(out) << "[" << containerize(keyType, key) << " release];" << endl;
1822 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001823 }
1824
1825 if (type_can_be_null(valType)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001826 if (!(get_true_type(valType)->is_string())) {
1827 indent(out) << "[" << containerize(valType, val) << " release];" << endl;
1828 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001829 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001830}
1831
1832/**
1833 * Deserializes a set element
1834 */
1835void t_cocoa_generator::generate_deserialize_set_element(ofstream& out,
1836 t_set* tset,
1837 string fieldName) {
1838 string elem = tmp("_elem");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001839 t_type* type = tset->get_elem_type();
1840 t_field felem(type, elem);
Mark Slee7e9eea42007-09-10 21:00:23 +00001841
1842 generate_deserialize_field(out, &felem, elem);
1843
1844 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001845 "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl;
1846
1847 if (type_can_be_null(type)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001848 // deserialized strings are autorelease, so don't release them
1849 if (!(get_true_type(type)->is_string())) {
1850 indent(out) << "[" << containerize(type, elem) << " release];" << endl;
1851 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001852 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001853}
1854
1855/**
1856 * Deserializes a list element
1857 */
1858void t_cocoa_generator::generate_deserialize_list_element(ofstream& out,
1859 t_list* tlist,
1860 string fieldName) {
1861 string elem = tmp("_elem");
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001862 t_type* type = tlist->get_elem_type();
1863 t_field felem(type, elem);
Mark Slee7e9eea42007-09-10 21:00:23 +00001864
1865 generate_deserialize_field(out, &felem, elem);
1866
1867 indent(out) <<
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001868 "[" << fieldName << " addObject: " << containerize(type, elem) << "];" << endl;
1869
1870 if (type_can_be_null(type)) {
Andrew McGeachie6efefc02009-07-21 20:14:31 +00001871 if (!(get_true_type(type)->is_string())) {
1872 indent(out) << "[" << containerize(type, elem) << " release];" << endl;
1873 }
Andrew McGeachie330cfc12009-07-21 14:33:17 +00001874 }
Mark Slee7e9eea42007-09-10 21:00:23 +00001875}
1876
1877
1878/**
1879 * Serializes a field of any type.
1880 *
1881 * @param tfield The field to serialize
1882 * @param fieldName Name to of the variable holding the field
1883 */
1884void t_cocoa_generator::generate_serialize_field(ofstream& out,
1885 t_field* tfield,
1886 string fieldName) {
1887 t_type* type = get_true_type(tfield->get_type());
1888
1889 // Do nothing for void types
1890 if (type->is_void()) {
1891 throw "CANNOT GENERATE SERIALIZE CODE FOR void TYPE: " +
1892 tfield->get_name();
1893 }
Mark Slee12a28752007-11-20 23:55:33 +00001894
Mark Slee7e9eea42007-09-10 21:00:23 +00001895 if (type->is_struct() || type->is_xception()) {
1896 generate_serialize_struct(out,
1897 (t_struct*)type,
1898 fieldName);
1899 } else if (type->is_container()) {
1900 generate_serialize_container(out,
1901 type,
1902 fieldName);
1903 } else if (type->is_base_type() || type->is_enum()) {
1904 indent(out) <<
1905 "[outProtocol ";
Mark Slee12a28752007-11-20 23:55:33 +00001906
Mark Slee7e9eea42007-09-10 21:00:23 +00001907 if (type->is_base_type()) {
1908 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
1909 switch (tbase) {
1910 case t_base_type::TYPE_VOID:
1911 throw
1912 "compiler error: cannot serialize void field in a struct: " + fieldName;
1913 break;
1914 case t_base_type::TYPE_STRING:
1915 if (((t_base_type*)type)->is_binary()) {
1916 out << "writeBinary: " << fieldName << "];";
1917 } else {
1918 out << "writeString: " << fieldName << "];";
1919 }
1920 break;
1921 case t_base_type::TYPE_BOOL:
1922 out << "writeBool: " << fieldName << "];";
1923 break;
1924 case t_base_type::TYPE_BYTE:
1925 out << "writeByte: " << fieldName << "];";
1926 break;
1927 case t_base_type::TYPE_I16:
1928 out << "writeI16: " << fieldName << "];";
1929 break;
1930 case t_base_type::TYPE_I32:
1931 out << "writeI32: " << fieldName << "];";
1932 break;
1933 case t_base_type::TYPE_I64:
1934 out << "writeI64: " << fieldName << "];";
1935 break;
1936 case t_base_type::TYPE_DOUBLE:
1937 out << "writeDouble: " << fieldName << "];";
1938 break;
1939 default:
1940 throw "compiler error: no Java name for base type " + t_base_type::t_base_name(tbase);
1941 }
1942 } else if (type->is_enum()) {
1943 out << "writeI32: " << fieldName << "];";
1944 }
1945 out << endl;
1946 } else {
1947 printf("DO NOT KNOW HOW TO SERIALIZE FIELD '%s' TYPE '%s'\n",
1948 tfield->get_name().c_str(),
1949 type_name(type).c_str());
1950 }
1951}
1952
1953/**
1954 * Serialize a struct.
1955 *
1956 * @param tstruct The struct to serialize
1957 * @param fieldName Name of variable holding struct
1958 */
1959void t_cocoa_generator::generate_serialize_struct(ofstream& out,
1960 t_struct* tstruct,
1961 string fieldName) {
1962 out <<
1963 indent() << "[" << fieldName << " write: outProtocol];" << endl;
1964}
1965
1966/**
1967 * Serializes a container by writing its size then the elements.
1968 *
1969 * @param ttype The type of container
1970 * @param fieldName Name of variable holding container
1971 */
1972void t_cocoa_generator::generate_serialize_container(ofstream& out,
1973 t_type* ttype,
1974 string fieldName) {
1975 scope_up(out);
Mark Slee12a28752007-11-20 23:55:33 +00001976
Mark Slee7e9eea42007-09-10 21:00:23 +00001977 if (ttype->is_map()) {
1978 indent(out) <<
1979 "[outProtocol writeMapBeginWithKeyType: " <<
1980 type_to_enum(((t_map*)ttype)->get_key_type()) << " valueType: " <<
1981 type_to_enum(((t_map*)ttype)->get_val_type()) << " size: [" <<
1982 fieldName << " count]];" << endl;
1983 } else if (ttype->is_set()) {
1984 indent(out) <<
1985 "[outProtocol writeSetBeginWithElementType: " <<
1986 type_to_enum(((t_set*)ttype)->get_elem_type()) << " size: [" <<
1987 fieldName << " count]];" << endl;
1988 } else if (ttype->is_list()) {
1989 indent(out) <<
1990 "[outProtocol writeListBeginWithElementType: " <<
1991 type_to_enum(((t_list*)ttype)->get_elem_type()) << " size: [" <<
1992 fieldName << " count]];" << endl;
1993 }
1994
1995 string iter = tmp("_iter");
1996 string key;
1997 if (ttype->is_map()) {
1998 key = tmp("key");
1999 indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " keyEnumerator];" << endl;
2000 indent(out) << "id " << key << ";" << endl;
2001 indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl;
2002 } else if (ttype->is_set()) {
2003 key = tmp("obj");
2004 indent(out) << "NSEnumerator * " << iter << " = [" << fieldName << " objectEnumerator];" << endl;
2005 indent(out) << "id " << key << ";" << endl;
2006 indent(out) << "while ((" << key << " = [" << iter << " nextObject]))" << endl;
2007 } else if (ttype->is_list()) {
2008 key = tmp("i");
2009 indent(out) << "int " << key << ";" << endl;
2010 indent(out) <<
2011 "for (" << key << " = 0; " << key << " < [" << fieldName << " count]; " << key << "++)" << endl;
2012 }
2013
2014 scope_up(out);
2015
2016 if (ttype->is_map()) {
2017 generate_serialize_map_element(out, (t_map*)ttype, key, fieldName);
2018 } else if (ttype->is_set()) {
2019 generate_serialize_set_element(out, (t_set*)ttype, key);
2020 } else if (ttype->is_list()) {
2021 generate_serialize_list_element(out, (t_list*)ttype, key, fieldName);
2022 }
Mark Slee12a28752007-11-20 23:55:33 +00002023
Mark Slee7e9eea42007-09-10 21:00:23 +00002024 scope_down(out);
Mark Slee12a28752007-11-20 23:55:33 +00002025
Mark Slee7e9eea42007-09-10 21:00:23 +00002026 if (ttype->is_map()) {
2027 indent(out) <<
2028 "[outProtocol writeMapEnd];" << endl;
2029 } else if (ttype->is_set()) {
2030 indent(out) <<
2031 "[outProtocol writeSetEnd];" << endl;
2032 } else if (ttype->is_list()) {
2033 indent(out) <<
2034 "[outProtocol writeListEnd];" << endl;
2035 }
Mark Slee12a28752007-11-20 23:55:33 +00002036
2037 scope_down(out);
Mark Slee7e9eea42007-09-10 21:00:23 +00002038}
2039
2040/**
2041 * Given a field variable name, wrap it in code that converts it to a
2042 * primitive type, if necessary.
2043 */
2044string t_cocoa_generator::decontainerize(t_field * tfield,
2045 string fieldName)
2046{
2047 t_type * ttype = get_true_type(tfield->get_type());
2048 if (ttype->is_enum()) {
2049 return "[" + fieldName + " intValue]";
2050 } else if (ttype->is_base_type()) {
2051 t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base();
2052 switch (tbase) {
2053 case t_base_type::TYPE_VOID:
2054 throw "can't decontainerize void";
2055 case t_base_type::TYPE_BOOL:
2056 return "[" + fieldName + " boolValue]";
2057 case t_base_type::TYPE_BYTE:
2058 return "[" + fieldName + " unsignedCharValue]";
2059 case t_base_type::TYPE_I16:
2060 return "[" + fieldName + " shortValue]";
2061 case t_base_type::TYPE_I32:
2062 return "[" + fieldName + " longValue]";
2063 case t_base_type::TYPE_I64:
2064 return "[" + fieldName + " longLongValue]";
2065 case t_base_type::TYPE_DOUBLE:
2066 return "[" + fieldName + " doubleValue]";
2067 default:
2068 break;
2069 }
2070 }
2071
2072 // do nothing
2073 return fieldName;
2074}
2075
2076
2077/**
2078 * Serializes the members of a map.
Mark Slee12a28752007-11-20 23:55:33 +00002079 */
Mark Slee7e9eea42007-09-10 21:00:23 +00002080void t_cocoa_generator::generate_serialize_map_element(ofstream& out,
2081 t_map* tmap,
2082 string key,
2083 string mapName) {
2084 t_field kfield(tmap->get_key_type(), key);
2085 generate_serialize_field(out, &kfield, decontainerize(&kfield, key));
2086 t_field vfield(tmap->get_val_type(), "[" + mapName + " objectForKey: " + key + "]");
2087 generate_serialize_field(out, &vfield, decontainerize(&vfield, vfield.get_name()));
2088}
2089
2090/**
2091 * Serializes the members of a set.
2092 */
2093void t_cocoa_generator::generate_serialize_set_element(ofstream& out,
2094 t_set* tset,
2095 string elementName) {
2096 t_field efield(tset->get_elem_type(), elementName);
2097 generate_serialize_field(out, &efield, decontainerize(&efield, elementName));
2098}
2099
2100/**
2101 * Serializes the members of a list.
2102 */
2103void t_cocoa_generator::generate_serialize_list_element(ofstream& out,
2104 t_list* tlist,
2105 string index,
2106 string listName) {
2107 t_field efield(tlist->get_elem_type(), "[" + listName + " objectAtIndex: " + index + "]");
2108 generate_serialize_field(out, &efield, decontainerize(&efield, efield.get_name()));
2109}
2110
2111
2112/**
2113 * Returns an Objective-C name
2114 *
2115 * @param ttype The type
2116 * @param class_ref Do we want a Class reference istead of a type reference?
2117 * @return Java type name, i.e. HashMap<Key,Value>
2118 */
2119string t_cocoa_generator::type_name(t_type* ttype, bool class_ref) {
2120 if (ttype->is_typedef()) {
2121 return cocoa_prefix_ + ttype->get_name();
2122 }
2123
2124 string result;
2125 if (ttype->is_base_type()) {
2126 return base_type_name((t_base_type*)ttype);
2127 } else if (ttype->is_enum()) {
2128 return "int";
2129 } else if (ttype->is_map()) {
2130 result = "NSDictionary";
2131 } else if (ttype->is_set()) {
2132 result = "NSSet";
2133 } else if (ttype->is_list()) {
2134 result = "NSArray";
2135 } else {
2136 // Check for prefix
2137 t_program* program = ttype->get_program();
2138 if (program != NULL) {
David Reiss54b602b2008-03-27 21:41:06 +00002139 result = program->get_namespace("cocoa") + ttype->get_name();
Mark Slee7e9eea42007-09-10 21:00:23 +00002140 } else {
2141 result = ttype->get_name();
2142 }
2143 }
2144
2145 if (!class_ref) {
2146 result += " *";
2147 }
2148 return result;
2149}
2150
2151/**
2152 * Returns the Objective-C type that corresponds to the thrift type.
2153 *
2154 * @param tbase The base type
2155 */
2156string t_cocoa_generator::base_type_name(t_base_type* type) {
2157 t_base_type::t_base tbase = type->get_base();
2158
2159 switch (tbase) {
2160 case t_base_type::TYPE_VOID:
2161 return "void";
2162 case t_base_type::TYPE_STRING:
2163 if (type->is_binary()) {
2164 return "NSData *";
2165 } else {
2166 return "NSString *";
2167 }
2168 case t_base_type::TYPE_BOOL:
2169 return "BOOL";
2170 case t_base_type::TYPE_BYTE:
2171 return "uint8_t";
2172 case t_base_type::TYPE_I16:
2173 return"int16_t";
2174 case t_base_type::TYPE_I32:
2175 return "int32_t";
2176 case t_base_type::TYPE_I64:
2177 return"int64_t";
2178 case t_base_type::TYPE_DOUBLE:
2179 return "double";
2180 default:
2181 throw "compiler error: no objective-c name for base type " + t_base_type::t_base_name(tbase);
2182 }
2183}
2184
2185
2186/**
2187 * Spit out code that evaluates to the specified constant value.
2188 */
Mark Slee12a28752007-11-20 23:55:33 +00002189string t_cocoa_generator::render_const_value(string name,
2190 t_type* type,
Mark Slee7e9eea42007-09-10 21:00:23 +00002191 t_const_value* value,
2192 bool containerize_it) {
David Reiss9a4edfa2008-05-01 05:52:50 +00002193 type = get_true_type(type);
Mark Slee7e9eea42007-09-10 21:00:23 +00002194 std::ostringstream render;
2195
2196 if (type->is_base_type()) {
2197 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2198 switch (tbase) {
2199 case t_base_type::TYPE_STRING:
David Reiss82e6fc02009-03-26 23:32:36 +00002200 render << "@\"" << get_escaped_string(value) << '"';
Mark Slee7e9eea42007-09-10 21:00:23 +00002201 break;
2202 case t_base_type::TYPE_BOOL:
2203 render << ((value->get_integer() > 0) ? "YES" : "NO");
2204 break;
2205 case t_base_type::TYPE_BYTE:
2206 case t_base_type::TYPE_I16:
2207 case t_base_type::TYPE_I32:
2208 case t_base_type::TYPE_I64:
2209 render << value->get_integer();
2210 break;
2211 case t_base_type::TYPE_DOUBLE:
2212 if (value->get_type() == t_const_value::CV_INTEGER) {
2213 render << value->get_integer();
2214 } else {
2215 render << value->get_double();
2216 }
2217 break;
2218 default:
2219 throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
2220 }
2221 } else if (type->is_enum()) {
2222 render << value->get_integer();
2223 } else if (type->is_struct() || type->is_xception()) {
2224 render << "[[" << type_name(type, true) << " alloc] initWith";
2225 const vector<t_field*>& fields = ((t_struct*)type)->get_members();
2226 vector<t_field*>::const_iterator f_iter;
2227 const map<t_const_value*, t_const_value*>& val = value->get_map();
2228 map<t_const_value*, t_const_value*>::const_iterator v_iter;
2229 bool first = true;
2230 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2231 t_type* field_type = NULL;
2232 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2233 if ((*f_iter)->get_name() == v_iter->first->get_string()) {
2234 field_type = (*f_iter)->get_type();
2235 }
2236 }
2237 if (field_type == NULL) {
2238 throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string();
2239 }
2240 if (first) {
2241 render << capitalize(v_iter->first->get_string());
2242 first = false;
2243 } else {
2244 render << " " << v_iter->first->get_string();
2245 }
2246 render << ": " << render_const_value(name, field_type, v_iter->second);
2247 }
2248 render << "]";
2249 } else if (type->is_map()) {
2250 render << "[[NSDictionary alloc] initWithObjectsAndKeys: ";
2251 t_type* ktype = ((t_map*)type)->get_key_type();
2252 t_type* vtype = ((t_map*)type)->get_val_type();
2253 const map<t_const_value*, t_const_value*>& val = value->get_map();
2254 map<t_const_value*, t_const_value*>::const_iterator v_iter;
2255 bool first = true;
2256 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2257 string key = render_const_value(name, ktype, v_iter->first, true);
2258 string val = render_const_value(name, vtype, v_iter->second, true);
2259 if (first) {
2260 first = false;
2261 } else {
2262 render << ", ";
2263 }
2264 render << val << ", " << key;
2265 }
2266 render << ", nil]";
2267 } else if (type->is_list()) {
2268 render << "[[NSArray alloc] initWithObjects: ";
2269 t_type * etype = ((t_list*)type)->get_elem_type();
2270 const vector<t_const_value*>& val = value->get_list();
2271 bool first = true;
2272 vector<t_const_value*>::const_iterator v_iter;
2273 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2274 if (first) {
2275 first = false;
2276 } else {
2277 render << ", ";
2278 }
2279 render << render_const_value(name, etype, *v_iter, true);
2280 }
2281 render << ", nil]";
2282 } else if (type->is_set()) {
2283 render << "[[NSSet alloc] initWithObjects: ";
2284 t_type * etype = ((t_set*)type)->get_elem_type();
2285 const vector<t_const_value*>& val = value->get_list();
2286 bool first = true;
2287 vector<t_const_value*>::const_iterator v_iter;
2288 for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
2289 if (first) {
2290 first = false;
2291 } else {
2292 render << ", ";
2293 }
2294 render << render_const_value(name, etype, *v_iter, true);
2295 }
2296 render << ", nil]";
2297 } else {
2298 throw "don't know how to render constant for type: " + type->get_name();
2299 }
2300
2301 if (containerize_it) {
2302 return containerize(type, render.str());
2303 }
2304
2305 return render.str();
2306}
2307
2308
2309/**
2310 * Declares a field.
2311 *
2312 * @param ttype The type
2313 */
2314string t_cocoa_generator::declare_field(t_field* tfield) {
2315 return type_name(tfield->get_type()) + " __" + tfield->get_name() + ";";
2316}
2317
2318/**
Andrew McGeachief2e03ba2009-07-21 16:51:49 +00002319 * Declares an Objective-C 2.0 property.
2320 *
2321 * @param tfield The field to declare a property for
2322 */
2323string t_cocoa_generator::declare_property(t_field* tfield) {
2324 std::ostringstream render;
2325 render << "@property (nonatomic, ";
2326
2327 if (type_can_be_null(tfield->get_type()))
2328 render << "retain, ";
2329
2330 render << "getter=" << decapitalize(tfield->get_name()) <<
2331 ", setter=set" << capitalize(tfield->get_name()) + ":) " <<
2332 type_name(tfield->get_type()) << " " << tfield->get_name() << ";";
2333
2334 return render.str();
2335}
2336
2337/**
Andrew McGeachie72751722009-10-27 20:23:02 +00002338 * Add @dynamic declaration for an Objective-C 2.0 property.
Andrew McGeachief2e03ba2009-07-21 16:51:49 +00002339 *
Andrew McGeachie72751722009-10-27 20:23:02 +00002340 * @param tfield The field for which to declare a dynamic property
Andrew McGeachief2e03ba2009-07-21 16:51:49 +00002341 */
Andrew McGeachie72751722009-10-27 20:23:02 +00002342string t_cocoa_generator::dynamic_property(t_field* tfield) {
2343 return "@dynamic " + tfield->get_name() + ";";
Andrew McGeachief2e03ba2009-07-21 16:51:49 +00002344}
2345
2346/**
Mark Slee7e9eea42007-09-10 21:00:23 +00002347 * Renders a function signature
2348 *
2349 * @param tfunction Function definition
2350 * @return String of rendered function definition
2351 */
2352string t_cocoa_generator::function_signature(t_function* tfunction) {
2353 t_type* ttype = tfunction->get_returntype();
2354 std::string result =
2355 "(" + type_name(ttype) + ") " + tfunction->get_name() + argument_list(tfunction->get_arglist());
2356 return result;
2357}
2358
2359
2360/**
2361 * Renders a colon separated list of types and names, suitable for an
2362 * objective-c parameter list
2363 */
2364string t_cocoa_generator::argument_list(t_struct* tstruct) {
2365 string result = "";
2366
2367 const vector<t_field*>& fields = tstruct->get_members();
2368 vector<t_field*>::const_iterator f_iter;
2369 bool first = true;
2370 for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
2371 if (first) {
2372 first = false;
2373 } else {
2374 result += " ";
2375 }
2376 result += ": (" + type_name((*f_iter)->get_type()) + ") " + (*f_iter)->get_name();
2377 }
2378 return result;
2379}
2380
2381
2382/**
2383 * Converts the parse type to an Objective-C enum string for the given type.
2384 */
2385string t_cocoa_generator::type_to_enum(t_type* type) {
2386 type = get_true_type(type);
Mark Slee12a28752007-11-20 23:55:33 +00002387
Mark Slee7e9eea42007-09-10 21:00:23 +00002388 if (type->is_base_type()) {
2389 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2390 switch (tbase) {
2391 case t_base_type::TYPE_VOID:
2392 throw "NO T_VOID CONSTRUCT";
2393 case t_base_type::TYPE_STRING:
2394 return "TType_STRING";
2395 case t_base_type::TYPE_BOOL:
2396 return "TType_BOOL";
2397 case t_base_type::TYPE_BYTE:
2398 return "TType_BYTE";
2399 case t_base_type::TYPE_I16:
2400 return "TType_I16";
2401 case t_base_type::TYPE_I32:
2402 return "TType_I32";
2403 case t_base_type::TYPE_I64:
2404 return "TType_I64";
2405 case t_base_type::TYPE_DOUBLE:
2406 return "TType_DOUBLE";
2407 }
2408 } else if (type->is_enum()) {
2409 return "TType_I32";
2410 } else if (type->is_struct() || type->is_xception()) {
2411 return "TType_STRUCT";
2412 } else if (type->is_map()) {
2413 return "TType_MAP";
2414 } else if (type->is_set()) {
2415 return "TType_SET";
2416 } else if (type->is_list()) {
2417 return "TType_LIST";
2418 }
2419
2420 throw "INVALID TYPE IN type_to_enum: " + type->get_name();
2421}
2422
2423
2424/**
2425 * Returns a format string specifier for the supplied parse type.
2426 */
2427string t_cocoa_generator::format_string_for_type(t_type* type) {
2428 type = get_true_type(type);
Mark Slee12a28752007-11-20 23:55:33 +00002429
Mark Slee7e9eea42007-09-10 21:00:23 +00002430 if (type->is_base_type()) {
2431 t_base_type::t_base tbase = ((t_base_type*)type)->get_base();
2432 switch (tbase) {
2433 case t_base_type::TYPE_VOID:
2434 throw "NO T_VOID CONSTRUCT";
2435 case t_base_type::TYPE_STRING:
2436 return "\\\"%@\\\"";
2437 case t_base_type::TYPE_BOOL:
2438 return "%i";
2439 case t_base_type::TYPE_BYTE:
2440 return "%i";
2441 case t_base_type::TYPE_I16:
2442 return "%hi";
2443 case t_base_type::TYPE_I32:
2444 return "%i";
2445 case t_base_type::TYPE_I64:
2446 return "%qi";
2447 case t_base_type::TYPE_DOUBLE:
2448 return "%f";
2449 }
2450 } else if (type->is_enum()) {
2451 return "%i";
2452 } else if (type->is_struct() || type->is_xception()) {
2453 return "%@";
2454 } else if (type->is_map()) {
2455 return "%@";
2456 } else if (type->is_set()) {
2457 return "%@";
2458 } else if (type->is_list()) {
2459 return "%@";
2460 }
2461
2462 throw "INVALID TYPE IN format_string_for_type: " + type->get_name();
2463}
2464
2465/**
2466 * Generate a call to a field's setter.
2467 *
2468 * @param tfield Field the setter is being called on
2469 * @param fieldName Name of variable to pass to setter
2470 */
2471
2472string t_cocoa_generator::call_field_setter(t_field* tfield, string fieldName) {
2473 return "[self set" + capitalize(tfield->get_name()) + ": " + fieldName + "];";
2474}
David Reissd01c64d2008-03-27 21:40:55 +00002475
2476
Andrew McGeachie645d7b82009-07-21 15:30:16 +00002477THRIFT_REGISTER_GENERATOR(cocoa, "Cocoa",
2478" log_unexpected: Log every time an unexpected field ID or type is encountered.\n"
2479);