1 /*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // independent from idl_parser, since this code is not needed for most clients
18
19 #include "flatbuffers/code_generators.h"
20 #include "flatbuffers/flatbuffers.h"
21 #include "flatbuffers/idl.h"
22 #include "flatbuffers/util.h"
23
24 #include "src/compiler/cpp_generator.h"
25 #include "src/compiler/go_generator.h"
26 #include "src/compiler/java_generator.h"
27
28 #if defined(_MSC_VER)
29 # pragma warning(push)
30 # pragma warning(disable : 4512) // C4512: 'class' : assignment operator could
31 // not be generated
32 #endif
33
34 namespace flatbuffers {
35
36 class FlatBufMethod : public grpc_generator::Method {
37 public:
38 enum Streaming {
39 kNone, kClient, kServer, kBiDi
40 };
41
FlatBufMethod(const RPCCall * method)42 FlatBufMethod(const RPCCall *method) : method_(method) {
43 streaming_ = kNone;
44 auto val = method_->attributes.Lookup("streaming");
45 if (val) {
46 if (val->constant == "client") streaming_ = kClient;
47 if (val->constant == "server") streaming_ = kServer;
48 if (val->constant == "bidi") streaming_ = kBiDi;
49 }
50 }
51
GetLeadingComments(const grpc::string) const52 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
53
GetTrailingComments(const grpc::string) const54 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
55
GetAllComments() const56 std::vector<grpc::string> GetAllComments() const {
57 return method_->doc_comment;
58 }
59
name() const60 std::string name() const { return method_->name; }
61
GRPCType(const StructDef & sd) const62 std::string GRPCType(const StructDef &sd) const {
63 return "flatbuffers::grpc::Message<" + sd.name + ">";
64 }
65
get_input_type_name() const66 std::string get_input_type_name() const { return (*method_->request).name; }
67
get_output_type_name() const68 std::string get_output_type_name() const { return (*method_->response).name; }
69
get_module_and_message_path_input(grpc::string *,grpc::string,bool,grpc::string) const70 bool get_module_and_message_path_input(grpc::string * /*str*/,
71 grpc::string /*generator_file_name*/,
72 bool /*generate_in_pb2_grpc*/,
73 grpc::string /*import_prefix*/) const {
74 return true;
75 }
76
get_module_and_message_path_output(grpc::string *,grpc::string,bool,grpc::string) const77 bool get_module_and_message_path_output(
78 grpc::string * /*str*/, grpc::string /*generator_file_name*/,
79 bool /*generate_in_pb2_grpc*/, grpc::string /*import_prefix*/) const {
80 return true;
81 }
82
input_type_name() const83 std::string input_type_name() const { return GRPCType(*method_->request); }
84
output_type_name() const85 std::string output_type_name() const { return GRPCType(*method_->response); }
86
NoStreaming() const87 bool NoStreaming() const { return streaming_ == kNone; }
88
ClientStreaming() const89 bool ClientStreaming() const { return streaming_ == kClient; }
90
ServerStreaming() const91 bool ServerStreaming() const { return streaming_ == kServer; }
92
BidiStreaming() const93 bool BidiStreaming() const { return streaming_ == kBiDi; }
94
95 private:
96 const RPCCall *method_;
97 Streaming streaming_;
98 };
99
100 class FlatBufService : public grpc_generator::Service {
101 public:
FlatBufService(const ServiceDef * service)102 FlatBufService(const ServiceDef *service) : service_(service) {}
103
GetLeadingComments(const grpc::string) const104 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
105
GetTrailingComments(const grpc::string) const106 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
107
GetAllComments() const108 std::vector<grpc::string> GetAllComments() const {
109 return service_->doc_comment;
110 }
111
name() const112 std::string name() const { return service_->name; }
113
method_count() const114 int method_count() const {
115 return static_cast<int>(service_->calls.vec.size());
116 }
117
method(int i) const118 std::unique_ptr<const grpc_generator::Method> method(int i) const {
119 return std::unique_ptr<const grpc_generator::Method>(
120 new FlatBufMethod(service_->calls.vec[i]));
121 }
122
123 private:
124 const ServiceDef *service_;
125 };
126
127 class FlatBufPrinter : public grpc_generator::Printer {
128 public:
FlatBufPrinter(std::string * str)129 FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {}
130
Print(const std::map<std::string,std::string> & vars,const char * string_template)131 void Print(const std::map<std::string, std::string> &vars,
132 const char *string_template) {
133 std::string s = string_template;
134 // Replace any occurrences of strings in "vars" that are surrounded
135 // by the escape character by what they're mapped to.
136 size_t pos;
137 while ((pos = s.find(escape_char_)) != std::string::npos) {
138 // Found an escape char, must also find the closing one.
139 size_t pos2 = s.find(escape_char_, pos + 1);
140 // If placeholder not closed, ignore.
141 if (pos2 == std::string::npos) break;
142 auto it = vars.find(s.substr(pos + 1, pos2 - pos - 1));
143 // If unknown placeholder, ignore.
144 if (it == vars.end()) break;
145 // Subtitute placeholder.
146 s.replace(pos, pos2 - pos + 1, it->second);
147 }
148 Print(s.c_str());
149 }
150
Print(const char * s)151 void Print(const char *s) {
152 if (s == nullptr || std::strlen(s) == 0) { return; }
153 // Add this string, but for each part separated by \n, add indentation.
154 for (;;) {
155 // Current indentation.
156 str_->insert(str_->end(), indent_ * 2, ' ');
157 // See if this contains more than one line.
158 const char *lf = strchr(s, '\n');
159 if (lf) {
160 (*str_) += std::string(s, lf + 1);
161 s = lf + 1;
162 if (!*s) break; // Only continue if there's more lines.
163 } else {
164 (*str_) += s;
165 break;
166 }
167 }
168 }
169
Indent()170 void Indent() { indent_++; }
171
Outdent()172 void Outdent() {
173 indent_--;
174 FLATBUFFERS_ASSERT(indent_ >= 0);
175 }
176
177 private:
178 std::string *str_;
179 char escape_char_;
180 int indent_;
181 };
182
183 class FlatBufFile : public grpc_generator::File {
184 public:
185 enum Language {
186 kLanguageGo, kLanguageCpp, kLanguageJava
187 };
188
FlatBufFile(const Parser & parser,const std::string & file_name,Language language)189 FlatBufFile(const Parser &parser, const std::string &file_name,
190 Language language)
191 : parser_(parser), file_name_(file_name), language_(language) {}
192
193 FlatBufFile &operator=(const FlatBufFile &);
194
GetLeadingComments(const grpc::string) const195 grpc::string GetLeadingComments(const grpc::string) const { return ""; }
196
GetTrailingComments(const grpc::string) const197 grpc::string GetTrailingComments(const grpc::string) const { return ""; }
198
GetAllComments() const199 std::vector<grpc::string> GetAllComments() const {
200 return std::vector<grpc::string>();
201 }
202
filename() const203 std::string filename() const { return file_name_; }
204
filename_without_ext() const205 std::string filename_without_ext() const {
206 return StripExtension(file_name_);
207 }
208
message_header_ext() const209 std::string message_header_ext() const { return "_generated.h"; }
210
service_header_ext() const211 std::string service_header_ext() const { return ".grpc.fb.h"; }
212
package() const213 std::string package() const {
214 return parser_.current_namespace_->GetFullyQualifiedName("");
215 }
216
package_parts() const217 std::vector<std::string> package_parts() const {
218 return parser_.current_namespace_->components;
219 }
220
additional_headers() const221 std::string additional_headers() const {
222 switch (language_) {
223 case kLanguageCpp: {
224 return "#include \"flatbuffers/grpc.h\"\n";
225 }
226 case kLanguageGo: {
227 return "import \"github.com/google/flatbuffers/go\"";
228 }
229 case kLanguageJava: {
230 return "import com.google.flatbuffers.grpc.FlatbuffersUtils;";
231 }
232 }
233 return "";
234 }
235
service_count() const236 int service_count() const {
237 return static_cast<int>(parser_.services_.vec.size());
238 }
239
service(int i) const240 std::unique_ptr<const grpc_generator::Service> service(int i) const {
241 return std::unique_ptr<const grpc_generator::Service>(
242 new FlatBufService(parser_.services_.vec[i]));
243 }
244
CreatePrinter(std::string * str) const245 std::unique_ptr<grpc_generator::Printer> CreatePrinter(
246 std::string *str) const {
247 return std::unique_ptr<grpc_generator::Printer>(new FlatBufPrinter(str));
248 }
249
250 private:
251 const Parser &parser_;
252 const std::string &file_name_;
253 const Language language_;
254 };
255
256 class GoGRPCGenerator : public flatbuffers::BaseGenerator {
257 public:
GoGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)258 GoGRPCGenerator(const Parser &parser, const std::string &path,
259 const std::string &file_name)
260 : BaseGenerator(parser, path, file_name, "", "" /*Unused*/),
261 parser_(parser),
262 path_(path),
263 file_name_(file_name) {}
264
generate()265 bool generate() {
266 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageGo);
267 grpc_go_generator::Parameters p;
268 p.custom_method_io_type = "flatbuffers.Builder";
269 for (int i = 0; i < file.service_count(); i++) {
270 auto service = file.service(i);
271 const Definition *def = parser_.services_.vec[i];
272 p.package_name = LastNamespacePart(*(def->defined_namespace));
273 p.service_prefix = def->defined_namespace->GetFullyQualifiedName(""); // file.package();
274 std::string output =
275 grpc_go_generator::GenerateServiceSource(&file, service.get(), &p);
276 std::string filename =
277 NamespaceDir(*def->defined_namespace) + def->name + "_grpc.go";
278 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
279 }
280 return true;
281 }
282
283 protected:
284 const Parser &parser_;
285 const std::string &path_, &file_name_;
286 };
287
GenerateGoGRPC(const Parser & parser,const std::string & path,const std::string & file_name)288 bool GenerateGoGRPC(const Parser &parser, const std::string &path,
289 const std::string &file_name) {
290 int nservices = 0;
291 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
292 ++it) {
293 if (!(*it)->generated) nservices++;
294 }
295 if (!nservices) return true;
296 return GoGRPCGenerator(parser, path, file_name).generate();
297 }
298
GenerateCppGRPC(const Parser & parser,const std::string & path,const std::string & file_name)299 bool GenerateCppGRPC(const Parser &parser, const std::string &path,
300 const std::string &file_name) {
301 int nservices = 0;
302 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
303 ++it) {
304 if (!(*it)->generated) nservices++;
305 }
306 if (!nservices) return true;
307
308 grpc_cpp_generator::Parameters generator_parameters;
309 // TODO(wvo): make the other parameters in this struct configurable.
310 generator_parameters.use_system_headers = true;
311
312 FlatBufFile fbfile(parser, file_name, FlatBufFile::kLanguageCpp);
313
314 std::string header_code =
315 grpc_cpp_generator::GetHeaderPrologue(&fbfile, generator_parameters) +
316 grpc_cpp_generator::GetHeaderIncludes(&fbfile, generator_parameters) +
317 grpc_cpp_generator::GetHeaderServices(&fbfile, generator_parameters) +
318 grpc_cpp_generator::GetHeaderEpilogue(&fbfile, generator_parameters);
319
320 std::string source_code =
321 grpc_cpp_generator::GetSourcePrologue(&fbfile, generator_parameters) +
322 grpc_cpp_generator::GetSourceIncludes(&fbfile, generator_parameters) +
323 grpc_cpp_generator::GetSourceServices(&fbfile, generator_parameters) +
324 grpc_cpp_generator::GetSourceEpilogue(&fbfile, generator_parameters);
325
326 return flatbuffers::SaveFile((path + file_name + ".grpc.fb.h").c_str(),
327 header_code, false) &&
328 flatbuffers::SaveFile((path + file_name + ".grpc.fb.cc").c_str(),
329 source_code, false);
330 }
331
332 class JavaGRPCGenerator : public flatbuffers::BaseGenerator {
333 public:
JavaGRPCGenerator(const Parser & parser,const std::string & path,const std::string & file_name)334 JavaGRPCGenerator(const Parser &parser, const std::string &path,
335 const std::string &file_name)
336 : BaseGenerator(parser, path, file_name, "", "." /*separator*/) {}
337
generate()338 bool generate() {
339 FlatBufFile file(parser_, file_name_, FlatBufFile::kLanguageJava);
340 grpc_java_generator::Parameters p;
341 for (int i = 0; i < file.service_count(); i++) {
342 auto service = file.service(i);
343 const Definition *def = parser_.services_.vec[i];
344 p.package_name =
345 def->defined_namespace->GetFullyQualifiedName(""); // file.package();
346 std::string output =
347 grpc_java_generator::GenerateServiceSource(&file, service.get(), &p);
348 std::string filename =
349 NamespaceDir(*def->defined_namespace) + def->name + "Grpc.java";
350 if (!flatbuffers::SaveFile(filename.c_str(), output, false)) return false;
351 }
352 return true;
353 }
354 };
355
GenerateJavaGRPC(const Parser & parser,const std::string & path,const std::string & file_name)356 bool GenerateJavaGRPC(const Parser &parser, const std::string &path,
357 const std::string &file_name) {
358 int nservices = 0;
359 for (auto it = parser.services_.vec.begin(); it != parser.services_.vec.end();
360 ++it) {
361 if (!(*it)->generated) nservices++;
362 }
363 if (!nservices) return true;
364 return JavaGRPCGenerator(parser, path, file_name).generate();
365 }
366
367 } // namespace flatbuffers
368
369 #if defined(_MSC_VER)
370 # pragma warning(pop)
371 #endif
372