1 /*
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  */
19 
20 #ifndef T_GENERATOR_H
21 #define T_GENERATOR_H
22 #define MSC_2015_VER 1900
23 
24 #include <cstring>
25 #include <string>
26 #include <iomanip>
27 #include <iostream>
28 #include <fstream>
29 #include <limits>
30 #include <sstream>
31 #include "thrift/common.h"
32 #include "thrift/logging.h"
33 #include "thrift/version.h"
34 #include "thrift/generate/t_generator_registry.h"
35 #include "thrift/parse/t_program.h"
36 
37 /**
38  * Base class for a thrift code generator. This class defines the basic
39  * routines for code generation and contains the top level method that
40  * dispatches code generation across various components.
41  *
42  */
43 class t_generator {
44 public:
t_generator(t_program * program)45   t_generator(t_program* program) {
46     update_keywords();
47 
48     tmp_ = 0;
49     indent_ = 0;
50     program_ = program;
51     program_name_ = get_program_name(program);
52     escape_['\n'] = "\\n";
53     escape_['\r'] = "\\r";
54     escape_['\t'] = "\\t";
55     escape_['"'] = "\\\"";
56     escape_['\\'] = "\\\\";
57   }
58 
~t_generator()59   virtual ~t_generator() {}
60 
61   /**
62    * Framework generator method that iterates over all the parts of a program
63    * and performs general actions. This is implemented by the base class and
64    * should not normally be overwritten in the subclasses.
65    */
66   virtual void generate_program();
67 
get_program()68   const t_program* get_program() const { return program_; }
69 
70   void generate_docstring_comment(std::ostream& out,
71                                   const std::string& comment_start,
72                                   const std::string& line_prefix,
73                                   const std::string& contents,
74                                   const std::string& comment_end);
75 
76   static void parse_options(const std::string& options, std::string& language,
77                      std::map<std::string, std::string>& parsed_options);
78 
79   /**
80    * check whether sub-namespace declaraction is used by generator.
81    * e.g. allow
82    * namespace py.twisted bar
83    * to specify namespace to use when -gen py:twisted is specified.
84    * Will be called with subnamespace, i.e. is_valid_namespace("twisted")
85    * will be called for the above example.
86    */
is_valid_namespace(const std::string & sub_namespace)87   static bool is_valid_namespace(const std::string& sub_namespace) {
88     (void)sub_namespace;
89     return false;
90   }
91 
92   /**
93    * Escape string to use one in generated sources.
94    */
95   virtual std::string escape_string(const std::string& in) const;
96 
get_escaped_string(t_const_value * constval)97   std::string get_escaped_string(t_const_value* constval) {
98     return escape_string(constval->get_string());
99   }
100 
101   /**
102    * Check if all identifiers are valid for the target language
103    * See update_keywords()
104    */
105   virtual void validate_input() const;
106 
107 protected:
108   virtual std::set<std::string> lang_keywords() const;
109 
110   /**
111    * Call this from constructor if you implement lang_keywords()
112    */
update_keywords()113   void update_keywords() {
114     keywords_ = lang_keywords();
115   }
116 
117   /**
118    * A list of reserved words that cannot be used as identifiers.
119    */
120   std::set<std::string> keywords_;
121 
122   virtual void validate_id(const std::string& id) const;
123 
124   virtual void validate(t_enum const* en) const;
125   virtual void validate(t_enum_value const* en_val) const;
126   virtual void validate(t_typedef const* td) const;
127   virtual void validate(t_const const* c) const;
128   virtual void validate(t_service const* s) const;
129   virtual void validate(t_struct const* c) const;
130   virtual void validate(t_field const* f) const;
131   virtual void validate(t_function const* f) const;
132 
133   template <typename T>
134   void validate(const std::vector<T>& list) const;
135 
136   /**
137    * Optional methods that may be implemented by subclasses to take necessary
138    * steps at the beginning or end of code generation.
139    */
140 
init_generator()141   virtual void init_generator() {}
close_generator()142   virtual void close_generator() {}
143 
144   virtual void generate_consts(std::vector<t_const*> consts);
145 
146   /**
147    * Pure virtual methods implemented by the generator subclasses.
148    */
149 
150   virtual void generate_typedef(t_typedef* ttypedef) = 0;
151   virtual void generate_enum(t_enum* tenum) = 0;
generate_const(t_const * tconst)152   virtual void generate_const(t_const* tconst) { (void)tconst; }
153   virtual void generate_struct(t_struct* tstruct) = 0;
154   virtual void generate_service(t_service* tservice) = 0;
generate_forward_declaration(t_struct *)155   virtual void generate_forward_declaration(t_struct*) {}
generate_xception(t_struct * txception)156   virtual void generate_xception(t_struct* txception) {
157     // By default exceptions are the same as structs
158     generate_struct(txception);
159   }
160 
161   /**
162    * Method to get the program name, may be overridden
163    */
get_program_name(t_program * tprogram)164   virtual std::string get_program_name(t_program* tprogram) { return tprogram->get_name(); }
165 
166   /**
167    * Method to get the service name, may be overridden
168    */
get_service_name(t_service * tservice)169   virtual std::string get_service_name(t_service* tservice) { return tservice->get_name(); }
170 
171   /**
172    * Get the current output directory
173    */
get_out_dir()174   virtual std::string get_out_dir() const {
175     if (program_->is_out_path_absolute()) {
176       return program_->get_out_path() + "/";
177     }
178 
179     return program_->get_out_path() + out_dir_base_ + "/";
180   }
181 
182   /**
183    * Creates a unique temporary variable name, which is just "name" with a
184    * number appended to it (i.e. name35)
185    */
tmp(std::string name)186   std::string tmp(std::string name) {
187     std::ostringstream out;
188     out << name << tmp_++;
189     return out.str();
190   }
191 
192   /**
193    * Generates a comment about this code being autogenerated, using C++ style
194    * comments, which are also fair game in Java / PHP, yay!
195    *
196    * @return C-style comment mentioning that this file is autogenerated.
197    */
autogen_comment()198   virtual std::string autogen_comment() {
199     return std::string("/**\n") + " * " + autogen_summary() + "\n" + " *\n"
200            + " * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING\n"
201            + " *  @generated\n" + " */\n";
202   }
203 
autogen_summary()204   virtual std::string autogen_summary() {
205     return std::string("Autogenerated by Thrift Compiler (") + THRIFT_VERSION + ")";
206   }
207 
208   /**
209    * Indentation level modifiers
210    */
211 
indent_up()212   void indent_up() { ++indent_; }
213 
indent_down()214   void indent_down() { --indent_; }
215 
216   /**
217   * Indentation validation helper
218   */
indent_count()219   int indent_count() { return indent_; }
220 
indent_validate(int expected,const char * func_name)221   void indent_validate( int expected, const char * func_name) {
222     if (indent_ != expected) {
223       pverbose("Wrong indent count in %s: difference = %i \n", func_name, (expected - indent_));
224     }
225   }
226 
227   /**
228    * Indentation print function
229    */
indent()230   std::string indent() {
231     std::string ind = "";
232     int i;
233     for (i = 0; i < indent_; ++i) {
234       ind += indent_str();
235     }
236     return ind;
237   }
238 
239   /**
240    * Indentation utility wrapper
241    */
indent(std::ostream & os)242   std::ostream& indent(std::ostream& os) { return os << indent(); }
243 
244   /**
245    * Capitalization helpers
246    */
capitalize(std::string in)247   std::string capitalize(std::string in) {
248     in[0] = toupper(in[0]);
249     return in;
250   }
decapitalize(std::string in)251   std::string decapitalize(std::string in) {
252     in[0] = tolower(in[0]);
253     return in;
254   }
lowercase(std::string in)255   static std::string lowercase(std::string in) {
256     for (size_t i = 0; i < in.size(); ++i) {
257       in[i] = tolower(in[i]);
258     }
259     return in;
260   }
uppercase(std::string in)261   static std::string uppercase(std::string in) {
262     for (size_t i = 0; i < in.size(); ++i) {
263       in[i] = toupper(in[i]);
264     }
265     return in;
266   }
267 
268   /**
269    * Transforms a camel case string to an equivalent one separated by underscores
270    * e.g. aMultiWord -> a_multi_word
271    *      someName   -> some_name
272    *      CamelCase  -> camel_case
273    *      name       -> name
274    *      Name       -> name
275    */
underscore(std::string in)276   std::string underscore(std::string in) {
277     in[0] = tolower(in[0]);
278     for (size_t i = 1; i < in.size(); ++i) {
279       if (isupper(in[i])) {
280         in[i] = tolower(in[i]);
281         in.insert(i, "_");
282       }
283     }
284     return in;
285   }
286 
287   /**
288     * Transforms a string with words separated by underscores to a camel case equivalent
289     * e.g. a_multi_word -> aMultiWord
290     *      some_name    ->  someName
291     *      name         ->  name
292     */
camelcase(std::string in)293   std::string camelcase(std::string in) {
294     std::ostringstream out;
295     bool underscore = false;
296 
297     for (size_t i = 0; i < in.size(); i++) {
298       if (in[i] == '_') {
299         underscore = true;
300         continue;
301       }
302       if (underscore) {
303         out << (char)toupper(in[i]);
304         underscore = false;
305         continue;
306       }
307       out << in[i];
308     }
309 
310     return out.str();
311   }
312 
emit_double_as_string(const double value)313   const std::string emit_double_as_string(const double value) {
314       std::stringstream double_output_stream;
315       // sets the maximum precision: http://en.cppreference.com/w/cpp/io/manip/setprecision
316       // sets the output format to fixed: http://en.cppreference.com/w/cpp/io/manip/fixed (not in scientific notation)
317       double_output_stream << std::setprecision(std::numeric_limits<double>::digits10 + 1);
318 
319       #ifdef _MSC_VER
320           // strtod is broken in MSVC compilers older than 2015, so std::fixed fails to format a double literal.
321           // more details: https://blogs.msdn.microsoft.com/vcblog/2014/06/18/
322           //               c-runtime-crt-features-fixes-and-breaking-changes-in-visual-studio-14-ctp1/
323           //               and
324           //               http://www.exploringbinary.com/visual-c-plus-plus-strtod-still-broken/
325           #if _MSC_VER >= MSC_2015_VER
326               double_output_stream << std::fixed;
327           #endif
328       #else
329           double_output_stream << std::fixed;
330       #endif
331 
332       double_output_stream << value;
333 
334       return double_output_stream.str();
335   }
336 
337 public:
338   /**
339    * Get the true type behind a series of typedefs.
340    */
get_true_type(const t_type * type)341   static const t_type* get_true_type(const t_type* type) { return type->get_true_type(); }
get_true_type(t_type * type)342   static t_type* get_true_type(t_type* type) { return type->get_true_type(); }
343 
344 protected:
345   /**
346    * The program being generated
347    */
348   t_program* program_;
349 
350   /**
351    * Quick accessor for formatted program name that is currently being
352    * generated.
353    */
354   std::string program_name_;
355 
356   /**
357    * Quick accessor for formatted service name that is currently being
358    * generated.
359    */
360   std::string service_name_;
361 
362   /**
363    * Output type-specifc directory name ("gen-*")
364    */
365   std::string out_dir_base_;
366 
367   /**
368    * Map of characters to escape in string literals.
369    */
370   std::map<char, std::string> escape_;
371 
indent_str()372   virtual std::string indent_str() const {
373     return "  ";
374   }
375 
376 private:
377   /**
378    * Current code indentation level
379    */
380   int indent_;
381 
382   /**
383    * Temporary variable counter, for making unique variable names
384    */
385   int tmp_;
386 };
387 
388 template<typename _CharT, typename _Traits = std::char_traits<_CharT> >
389 class template_ofstream_with_content_based_conditional_update : public std::ostringstream {
390 public:
template_ofstream_with_content_based_conditional_update()391   template_ofstream_with_content_based_conditional_update(): contents_written(false) {}
392 
template_ofstream_with_content_based_conditional_update(std::string const & output_file_path_)393   template_ofstream_with_content_based_conditional_update(std::string const& output_file_path_)
394   : output_file_path(output_file_path_), contents_written(false) {}
395 
~template_ofstream_with_content_based_conditional_update()396   ~template_ofstream_with_content_based_conditional_update() {
397     if (!contents_written) {
398       close();
399     }
400   }
401 
open(std::string const & output_file_path_)402   void open(std::string const& output_file_path_) {
403     output_file_path = output_file_path_;
404     clear_buf();
405     contents_written = false;
406   }
407 
close()408   void close() {
409     if (contents_written || output_file_path == "")
410       return;
411 
412     if (!is_readable(output_file_path)) {
413       dump();
414       return;
415     }
416 
417     std::ifstream old_file;
418     old_file.exceptions(old_file.exceptions() | std::ifstream::badbit | std::ifstream::failbit);
419     old_file.open(output_file_path.c_str(), std::ios::in);
420 
421     if (old_file) {
422       std::string const old_file_contents(static_cast<std::ostringstream const&>(std::ostringstream() << old_file.rdbuf()).str());
423       old_file.close();
424 
425       if (old_file_contents != str()) {
426         dump();
427       }
428     }
429   }
430 
431 protected:
dump()432   void dump() {
433     std::ofstream out_file;
434     out_file.exceptions(out_file.exceptions() | std::ofstream::badbit | std::ofstream::failbit);
435     try {
436       out_file.open(output_file_path.c_str(), std::ios::out);
437     }
438     catch (const std::ios_base::failure& e) {
439       ::failure("failed to write the output to the file '%s', details: '%s'", output_file_path.c_str(), e.what());
440     }
441     out_file << str();
442     out_file.close();
443     clear_buf();
444     contents_written = true;
445   }
446 
clear_buf()447   void clear_buf() {
448     str(std::string());
449   }
450 
is_readable(std::string const & file_name)451   static bool is_readable(std::string const& file_name) {
452     return static_cast<bool>(std::ifstream(file_name.c_str()));
453   }
454 
455 private:
456   std::string output_file_path;
457   bool contents_written;
458 };
459 typedef template_ofstream_with_content_based_conditional_update<char> ofstream_with_content_based_conditional_update;
460 
461 #endif
462