Imported Upstream version 1.21.0
[platform/upstream/grpc.git] / src / compiler / objective_c_generator.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 #include <map>
20 #include <set>
21 #include <sstream>
22
23 #include "src/compiler/config.h"
24 #include "src/compiler/objective_c_generator.h"
25 #include "src/compiler/objective_c_generator_helpers.h"
26
27 #include <google/protobuf/compiler/objectivec/objectivec_helpers.h>
28
29 using ::google::protobuf::compiler::objectivec::ClassName;
30 using ::grpc::protobuf::FileDescriptor;
31 using ::grpc::protobuf::FileDescriptor;
32 using ::grpc::protobuf::MethodDescriptor;
33 using ::grpc::protobuf::ServiceDescriptor;
34 using ::grpc::protobuf::io::Printer;
35 using ::std::map;
36 using ::std::set;
37
38 namespace grpc_objective_c_generator {
39 namespace {
40
41 void PrintProtoRpcDeclarationAsPragma(
42     Printer* printer, const MethodDescriptor* method,
43     map< ::grpc::string, ::grpc::string> vars) {
44   vars["client_stream"] = method->client_streaming() ? "stream " : "";
45   vars["server_stream"] = method->server_streaming() ? "stream " : "";
46
47   printer->Print(vars,
48                  "#pragma mark $method_name$($client_stream$$request_type$)"
49                  " returns ($server_stream$$response_type$)\n\n");
50 }
51
52 template <typename DescriptorType>
53 static void PrintAllComments(const DescriptorType* desc, Printer* printer,
54                              bool deprecated = false) {
55   std::vector<grpc::string> comments;
56   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING_DETACHED,
57                              &comments);
58   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_LEADING,
59                              &comments);
60   grpc_generator::GetComment(desc, grpc_generator::COMMENTTYPE_TRAILING,
61                              &comments);
62   if (comments.empty()) {
63     return;
64   }
65   printer->Print("/**\n");
66   for (auto it = comments.begin(); it != comments.end(); ++it) {
67     printer->Print(" * ");
68     size_t start_pos = it->find_first_not_of(' ');
69     if (start_pos != grpc::string::npos) {
70       printer->PrintRaw(it->c_str() + start_pos);
71     }
72     printer->Print("\n");
73   }
74   if (deprecated) {
75     printer->Print(" *\n");
76     printer->Print(
77         " * This method belongs to a set of APIs that have been deprecated. "
78         "Using"
79         " the v2 API is recommended.\n");
80   }
81   printer->Print(" */\n");
82 }
83
84 void PrintMethodSignature(Printer* printer, const MethodDescriptor* method,
85                           const map< ::grpc::string, ::grpc::string>& vars) {
86   // Print comment
87   PrintAllComments(method, printer, true);
88
89   printer->Print(vars, "- ($return_type$)$method_name$With");
90   if (method->client_streaming()) {
91     printer->Print("RequestsWriter:(GRXWriter *)requestWriter");
92   } else {
93     printer->Print(vars, "Request:($request_class$ *)request");
94   }
95
96   // TODO(jcanizales): Put this on a new line and align colons.
97   if (method->server_streaming()) {
98     printer->Print(vars,
99                    " eventHandler:(void(^)(BOOL done, "
100                    "$response_class$ *_Nullable response, NSError *_Nullable "
101                    "error))eventHandler");
102   } else {
103     printer->Print(vars,
104                    " handler:(void(^)($response_class$ *_Nullable response, "
105                    "NSError *_Nullable error))handler");
106   }
107 }
108
109 void PrintSimpleSignature(Printer* printer, const MethodDescriptor* method,
110                           map< ::grpc::string, ::grpc::string> vars) {
111   vars["method_name"] =
112       grpc_generator::LowercaseFirstLetter(vars["method_name"]);
113   vars["return_type"] = "void";
114   PrintMethodSignature(printer, method, vars);
115 }
116
117 void PrintAdvancedSignature(Printer* printer, const MethodDescriptor* method,
118                             map< ::grpc::string, ::grpc::string> vars) {
119   vars["method_name"] = "RPCTo" + vars["method_name"];
120   vars["return_type"] = "GRPCProtoCall *";
121   PrintMethodSignature(printer, method, vars);
122 }
123
124 void PrintV2Signature(Printer* printer, const MethodDescriptor* method,
125                       map< ::grpc::string, ::grpc::string> vars) {
126   if (method->client_streaming()) {
127     vars["return_type"] = "GRPCStreamingProtoCall *";
128   } else {
129     vars["return_type"] = "GRPCUnaryProtoCall *";
130   }
131   vars["method_name"] =
132       grpc_generator::LowercaseFirstLetter(vars["method_name"]);
133
134   PrintAllComments(method, printer);
135
136   printer->Print(vars, "- ($return_type$)$method_name$With");
137   if (method->client_streaming()) {
138     printer->Print("ResponseHandler:(id<GRPCProtoResponseHandler>)handler");
139   } else {
140     printer->Print(vars,
141                    "Message:($request_class$ *)message "
142                    "responseHandler:(id<GRPCProtoResponseHandler>)handler");
143   }
144   printer->Print(" callOptions:(GRPCCallOptions *_Nullable)callOptions");
145 }
146
147 inline map< ::grpc::string, ::grpc::string> GetMethodVars(
148     const MethodDescriptor* method) {
149   map< ::grpc::string, ::grpc::string> res;
150   res["method_name"] = method->name();
151   res["request_type"] = method->input_type()->name();
152   res["response_type"] = method->output_type()->name();
153   res["request_class"] = ClassName(method->input_type());
154   res["response_class"] = ClassName(method->output_type());
155   return res;
156 }
157
158 void PrintMethodDeclarations(Printer* printer, const MethodDescriptor* method) {
159   map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
160
161   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
162
163   PrintSimpleSignature(printer, method, vars);
164   printer->Print(";\n\n");
165   PrintAdvancedSignature(printer, method, vars);
166   printer->Print(";\n\n\n");
167 }
168
169 void PrintV2MethodDeclarations(Printer* printer,
170                                const MethodDescriptor* method) {
171   map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
172
173   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
174
175   PrintV2Signature(printer, method, vars);
176   printer->Print(";\n\n");
177 }
178
179 void PrintSimpleImplementation(Printer* printer, const MethodDescriptor* method,
180                                map< ::grpc::string, ::grpc::string> vars) {
181   printer->Print("{\n");
182   printer->Print(vars, "  [[self RPCTo$method_name$With");
183   if (method->client_streaming()) {
184     printer->Print("RequestsWriter:requestWriter");
185   } else {
186     printer->Print("Request:request");
187   }
188   if (method->server_streaming()) {
189     printer->Print(" eventHandler:eventHandler] start];\n");
190   } else {
191     printer->Print(" handler:handler] start];\n");
192   }
193   printer->Print("}\n");
194 }
195
196 void PrintAdvancedImplementation(Printer* printer,
197                                  const MethodDescriptor* method,
198                                  map< ::grpc::string, ::grpc::string> vars) {
199   printer->Print("{\n");
200   printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
201
202   printer->Print("            requestsWriter:");
203   if (method->client_streaming()) {
204     printer->Print("requestWriter\n");
205   } else {
206     printer->Print("[GRXWriter writerWithValue:request]\n");
207   }
208
209   printer->Print(vars, "             responseClass:[$response_class$ class]\n");
210
211   printer->Print("        responsesWriteable:[GRXWriteable ");
212   if (method->server_streaming()) {
213     printer->Print("writeableWithEventHandler:eventHandler]];\n");
214   } else {
215     printer->Print("writeableWithSingleHandler:handler]];\n");
216   }
217
218   printer->Print("}\n");
219 }
220
221 void PrintV2Implementation(Printer* printer, const MethodDescriptor* method,
222                            map< ::grpc::string, ::grpc::string> vars) {
223   printer->Print(" {\n");
224   if (method->client_streaming()) {
225     printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
226     printer->Print("           responseHandler:handler\n");
227     printer->Print("               callOptions:callOptions\n");
228     printer->Print(
229         vars, "             responseClass:[$response_class$ class]];\n}\n\n");
230   } else {
231     printer->Print(vars, "  return [self RPCToMethod:@\"$method_name$\"\n");
232     printer->Print("                   message:message\n");
233     printer->Print("           responseHandler:handler\n");
234     printer->Print("               callOptions:callOptions\n");
235     printer->Print(
236         vars, "             responseClass:[$response_class$ class]];\n}\n\n");
237   }
238 }
239
240 void PrintMethodImplementations(Printer* printer,
241                                 const MethodDescriptor* method) {
242   map< ::grpc::string, ::grpc::string> vars = GetMethodVars(method);
243
244   PrintProtoRpcDeclarationAsPragma(printer, method, vars);
245
246   // TODO(jcanizales): Print documentation from the method.
247   printer->Print("// Deprecated methods.\n");
248   PrintSimpleSignature(printer, method, vars);
249   PrintSimpleImplementation(printer, method, vars);
250
251   printer->Print("// Returns a not-yet-started RPC object.\n");
252   PrintAdvancedSignature(printer, method, vars);
253   PrintAdvancedImplementation(printer, method, vars);
254
255   PrintV2Signature(printer, method, vars);
256   PrintV2Implementation(printer, method, vars);
257 }
258
259 }  // namespace
260
261 ::grpc::string GetAllMessageClasses(const FileDescriptor* file) {
262   ::grpc::string output;
263   set< ::grpc::string> classes;
264   for (int i = 0; i < file->service_count(); i++) {
265     const auto service = file->service(i);
266     for (int i = 0; i < service->method_count(); i++) {
267       const auto method = service->method(i);
268       classes.insert(ClassName(method->input_type()));
269       classes.insert(ClassName(method->output_type()));
270     }
271   }
272   for (auto one_class : classes) {
273     output += "@class " + one_class + ";\n";
274   }
275
276   return output;
277 }
278
279 ::grpc::string GetProtocol(const ServiceDescriptor* service) {
280   ::grpc::string output;
281
282   // Scope the output stream so it closes and finalizes output to the string.
283   grpc::protobuf::io::StringOutputStream output_stream(&output);
284   Printer printer(&output_stream, '$');
285
286   map< ::grpc::string, ::grpc::string> vars = {
287       {"service_class", ServiceClassName(service)}};
288
289   printer.Print(vars,
290                 "/**\n"
291                 " * The methods in this protocol belong to a set of old APIs "
292                 "that have been deprecated. They do not\n"
293                 " * recognize call options provided in the initializer. Using "
294                 "the v2 protocol is recommended.\n"
295                 " */\n");
296   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
297   for (int i = 0; i < service->method_count(); i++) {
298     PrintMethodDeclarations(&printer, service->method(i));
299   }
300   printer.Print("@end\n\n");
301
302   return output;
303 }
304
305 ::grpc::string GetV2Protocol(const ServiceDescriptor* service) {
306   ::grpc::string output;
307
308   // Scope the output stream so it closes and finalizes output to the string.
309   grpc::protobuf::io::StringOutputStream output_stream(&output);
310   Printer printer(&output_stream, '$');
311
312   map< ::grpc::string, ::grpc::string> vars = {
313       {"service_class", ServiceClassName(service) + "2"}};
314
315   printer.Print(vars, "@protocol $service_class$ <NSObject>\n\n");
316   for (int i = 0; i < service->method_count(); i++) {
317     PrintV2MethodDeclarations(&printer, service->method(i));
318   }
319   printer.Print("@end\n\n");
320
321   return output;
322 }
323
324 ::grpc::string GetInterface(const ServiceDescriptor* service) {
325   ::grpc::string output;
326
327   // Scope the output stream so it closes and finalizes output to the string.
328   grpc::protobuf::io::StringOutputStream output_stream(&output);
329   Printer printer(&output_stream, '$');
330
331   map< ::grpc::string, ::grpc::string> vars = {
332       {"service_class", ServiceClassName(service)}};
333
334   printer.Print(vars,
335                 "/**\n"
336                 " * Basic service implementation, over gRPC, that only does\n"
337                 " * marshalling and parsing.\n"
338                 " */\n");
339   printer.Print(vars,
340                 "@interface $service_class$ :"
341                 " GRPCProtoService<$service_class$, $service_class$2>\n");
342   printer.Print(
343       "- (instancetype)initWithHost:(NSString *)host "
344       "callOptions:(GRPCCallOptions "
345       "*_Nullable)callOptions"
346       " NS_DESIGNATED_INITIALIZER;\n");
347   printer.Print(
348       "+ (instancetype)serviceWithHost:(NSString *)host "
349       "callOptions:(GRPCCallOptions *_Nullable)callOptions;\n");
350   printer.Print(
351       "// The following methods belong to a set of old APIs that have been "
352       "deprecated.\n");
353   printer.Print("- (instancetype)initWithHost:(NSString *)host;\n");
354   printer.Print("+ (instancetype)serviceWithHost:(NSString *)host;\n");
355   printer.Print("@end\n");
356
357   return output;
358 }
359
360 ::grpc::string GetSource(const ServiceDescriptor* service) {
361   ::grpc::string output;
362   {
363     // Scope the output stream so it closes and finalizes output to the string.
364     grpc::protobuf::io::StringOutputStream output_stream(&output);
365     Printer printer(&output_stream, '$');
366
367     map< ::grpc::string, ::grpc::string> vars = {
368         {"service_name", service->name()},
369         {"service_class", ServiceClassName(service)},
370         {"package", service->file()->package()}};
371
372     printer.Print(vars,
373                   "@implementation $service_class$\n\n"
374                   "#pragma clang diagnostic push\n"
375                   "#pragma clang diagnostic ignored "
376                   "\"-Wobjc-designated-initializers\"\n\n"
377                   "// Designated initializer\n"
378                   "- (instancetype)initWithHost:(NSString *)host "
379                   "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
380                   "  return [super initWithHost:host\n"
381                   "                 packageName:@\"$package$\"\n"
382                   "                 serviceName:@\"$service_name$\"\n"
383                   "                 callOptions:callOptions];\n"
384                   "}\n\n"
385                   "- (instancetype)initWithHost:(NSString *)host {\n"
386                   "  return [super initWithHost:host\n"
387                   "                 packageName:@\"$package$\"\n"
388                   "                 serviceName:@\"$service_name$\"];\n"
389                   "}\n\n"
390                   "#pragma clang diagnostic pop\n\n");
391
392     printer.Print(
393         "// Override superclass initializer to disallow different"
394         " package and service names.\n"
395         "- (instancetype)initWithHost:(NSString *)host\n"
396         "                 packageName:(NSString *)packageName\n"
397         "                 serviceName:(NSString *)serviceName {\n"
398         "  return [self initWithHost:host];\n"
399         "}\n\n"
400         "- (instancetype)initWithHost:(NSString *)host\n"
401         "                 packageName:(NSString *)packageName\n"
402         "                 serviceName:(NSString *)serviceName\n"
403         "                 callOptions:(GRPCCallOptions *)callOptions {\n"
404         "  return [self initWithHost:host callOptions:callOptions];\n"
405         "}\n\n");
406
407     printer.Print(
408         "#pragma mark - Class Methods\n\n"
409         "+ (instancetype)serviceWithHost:(NSString *)host {\n"
410         "  return [[self alloc] initWithHost:host];\n"
411         "}\n\n"
412         "+ (instancetype)serviceWithHost:(NSString *)host "
413         "callOptions:(GRPCCallOptions *_Nullable)callOptions {\n"
414         "  return [[self alloc] initWithHost:host callOptions:callOptions];\n"
415         "}\n\n");
416
417     printer.Print("#pragma mark - Method Implementations\n\n");
418
419     for (int i = 0; i < service->method_count(); i++) {
420       PrintMethodImplementations(&printer, service->method(i));
421     }
422
423     printer.Print("@end\n");
424   }
425   return output;
426 }
427
428 }  // namespace grpc_objective_c_generator