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