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_PROGRAM_H
21 #define T_PROGRAM_H
22 
23 #include <map>
24 #include <string>
25 #include <vector>
26 
27 // For program_name()
28 #include "thrift/main.h"
29 
30 #include "thrift/parse/t_doc.h"
31 #include "thrift/parse/t_scope.h"
32 #include "thrift/parse/t_base_type.h"
33 #include "thrift/parse/t_typedef.h"
34 #include "thrift/parse/t_enum.h"
35 #include "thrift/parse/t_const.h"
36 #include "thrift/parse/t_struct.h"
37 #include "thrift/parse/t_service.h"
38 #include "thrift/parse/t_list.h"
39 #include "thrift/parse/t_map.h"
40 #include "thrift/parse/t_set.h"
41 #include "thrift/generate/t_generator_registry.h"
42 //#include "thrift/parse/t_doc.h"
43 
44 /**
45  * Top level class representing an entire thrift program. A program consists
46  * fundamentally of the following:
47  *
48  *   Typedefs
49  *   Enumerations
50  *   Constants
51  *   Structs
52  *   Exceptions
53  *   Services
54  *
55  * The program module also contains the definitions of the base types.
56  *
57  */
58 class t_program : public t_doc {
59 public:
t_program(std::string path,std::string name)60   t_program(std::string path, std::string name)
61     : path_(path), name_(name), out_path_("./"), out_path_is_absolute_(false), scope_(new t_scope), recursive_(false) {}
62 
t_program(std::string path)63   t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false), recursive_(false) {
64     name_ = program_name(path);
65     scope_ = new t_scope();
66   }
67 
~t_program()68   ~t_program() override {
69     if (scope_) {
70       delete scope_;
71       scope_ = nullptr;
72     }
73   }
74 
75   // Path accessor
get_path()76   const std::string& get_path() const { return path_; }
77 
78   // Output path accessor
get_out_path()79   const std::string& get_out_path() const { return out_path_; }
80 
81   // Create gen-* dir accessor
is_out_path_absolute()82   bool is_out_path_absolute() const { return out_path_is_absolute_; }
83 
84   // Name accessor
get_name()85   const std::string& get_name() const { return name_; }
86 
87   // Namespace
get_namespace()88   const std::string& get_namespace() const { return namespace_; }
89 
90   // Include prefix accessor
get_include_prefix()91   const std::string& get_include_prefix() const { return include_prefix_; }
92 
93   // Accessors for program elements
get_typedefs()94   const std::vector<t_typedef*>& get_typedefs() const { return typedefs_; }
get_enums()95   const std::vector<t_enum*>& get_enums() const { return enums_; }
get_consts()96   const std::vector<t_const*>& get_consts() const { return consts_; }
get_structs()97   const std::vector<t_struct*>& get_structs() const { return structs_; }
get_xceptions()98   const std::vector<t_struct*>& get_xceptions() const { return xceptions_; }
get_objects()99   const std::vector<t_struct*>& get_objects() const { return objects_; }
get_services()100   const std::vector<t_service*>& get_services() const { return services_; }
get_namespaces()101   const std::map<std::string, std::string>& get_namespaces() const { return namespaces_; }
102 
103   // Program elements
add_typedef(t_typedef * td)104   void add_typedef(t_typedef* td) { typedefs_.push_back(td); }
add_enum(t_enum * te)105   void add_enum(t_enum* te) { enums_.push_back(te); }
add_const(t_const * tc)106   void add_const(t_const* tc) { consts_.push_back(tc); }
add_struct(t_struct * ts)107   void add_struct(t_struct* ts) {
108     objects_.push_back(ts);
109     structs_.push_back(ts);
110   }
add_xception(t_struct * tx)111   void add_xception(t_struct* tx) {
112     objects_.push_back(tx);
113     xceptions_.push_back(tx);
114   }
add_service(t_service * ts)115   void add_service(t_service* ts) {
116     ts->validate_unique_members();
117     services_.push_back(ts);
118   }
119 
120   // Programs to include
get_includes()121   std::vector<t_program*>& get_includes() { return includes_; }
122 
get_includes()123   const std::vector<t_program*>& get_includes() const { return includes_; }
124 
set_out_path(std::string out_path,bool out_path_is_absolute)125   void set_out_path(std::string out_path, bool out_path_is_absolute) {
126     out_path_ = out_path;
127     out_path_is_absolute_ = out_path_is_absolute;
128     // Ensure that it ends with a trailing '/' (or '\' for windows machines)
129     char c = out_path_.at(out_path_.size() - 1);
130     if (!(c == '/' || c == '\\')) {
131       out_path_.push_back('/');
132     }
133   }
134 
135   // Typename collision detection
136   /**
137    * Search for typename collisions
138    * @param t    the type to test for collisions
139    * @return     true if a certain collision was found, otherwise false
140    */
is_unique_typename(const t_type * t)141   bool is_unique_typename(const t_type* t) const {
142     int occurrences = program_typename_count(this, t);
143     for (auto it = includes_.cbegin(); it != includes_.cend(); ++it) {
144       occurrences += program_typename_count(*it, t);
145     }
146     return 0 == occurrences;
147   }
148 
149   /**
150    * Search all type collections for duplicate typenames
151    * @param prog the program to search
152    * @param t    the type to test for collisions
153    * @return     the number of certain typename collisions
154    */
program_typename_count(const t_program * prog,const t_type * t)155   int program_typename_count(const t_program* prog, const t_type* t) const {
156     int occurrences = 0;
157     occurrences += collection_typename_count(prog, prog->typedefs_, t);
158     occurrences += collection_typename_count(prog, prog->enums_, t);
159     occurrences += collection_typename_count(prog, prog->objects_, t);
160     occurrences += collection_typename_count(prog, prog->services_, t);
161     return occurrences;
162   }
163 
164   /**
165    * Search a type collection for duplicate typenames
166    * @param prog            the program to search
167    * @param type_collection the type collection to search
168    * @param t               the type to test for collisions
169    * @return                the number of certain typename collisions
170    */
171   template <class T>
collection_typename_count(const t_program * prog,const T type_collection,const t_type * t)172   int collection_typename_count(const t_program* prog, const T type_collection, const t_type* t) const {
173     int occurrences = 0;
174     for (auto it = type_collection.cbegin(); it != type_collection.cend(); ++it)
175       if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t))
176         ++occurrences;
177     return occurrences;
178   }
179 
180   /**
181    * Determine whether identical typenames will collide based on namespaces.
182    *
183    * Because we do not know which languages the user will generate code for,
184    * collisions within programs (IDL files) having namespace declarations can be
185    * difficult to determine. Only guaranteed collisions return true (cause an error).
186    * Possible collisions involving explicit namespace declarations produce a warning.
187    * Other possible collisions go unreported.
188    * @param prog the program containing the preexisting typename
189    * @param t    the type containing the typename match
190    * @return     true if a collision within namespaces is found, otherwise false
191    */
is_common_namespace(const t_program * prog,const t_type * t)192   bool is_common_namespace(const t_program* prog, const t_type* t) const {
193     // Case 1: Typenames are in the same program [collision]
194     if (prog == t->get_program()) {
195       pwarning(1,
196                "Duplicate typename %s found in %s",
197                t->get_name().c_str(),
198                t->get_program()->get_name().c_str());
199       return true;
200     }
201 
202     // Case 2: Both programs have identical namespace scope/name declarations [collision]
203     bool match = true;
204     for (auto it = prog->namespaces_.cbegin();
205          it != prog->namespaces_.cend();
206          ++it) {
207       if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) {
208         pwarning(1,
209                  "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
210                  t->get_name().c_str(),
211                  t->get_program()->get_name().c_str(),
212                  it->first.c_str(),
213                  it->second.c_str(),
214                  prog->get_name().c_str(),
215                  it->first.c_str(),
216                  it->second.c_str());
217       } else {
218         match = false;
219       }
220     }
221     for (auto it = t->get_program()->namespaces_.cbegin();
222          it != t->get_program()->namespaces_.cend();
223          ++it) {
224       if (0 == it->second.compare(prog->get_namespace(it->first))) {
225         pwarning(1,
226                  "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
227                  t->get_name().c_str(),
228                  t->get_program()->get_name().c_str(),
229                  it->first.c_str(),
230                  it->second.c_str(),
231                  prog->get_name().c_str(),
232                  it->first.c_str(),
233                  it->second.c_str());
234       } else {
235         match = false;
236       }
237     }
238     if (0 == prog->namespaces_.size() && 0 == t->get_program()->namespaces_.size()) {
239       pwarning(1,
240                "Duplicate typename %s found in %s and %s",
241                t->get_name().c_str(),
242                t->get_program()->get_name().c_str(),
243                prog->get_name().c_str());
244     }
245     return match;
246   }
247 
248   // Scoping and namespacing
set_namespace(std::string name)249   void set_namespace(std::string name) { namespace_ = name; }
250 
251   // Scope accessor
scope()252   t_scope* scope() { return scope_; }
253 
scope()254   const t_scope* scope() const { return scope_; }
255 
256   // Includes
257 
add_include(t_program * program)258   void add_include(t_program* program) {
259     includes_.push_back(program);
260   }
261 
add_include(std::string path,std::string include_site)262   void add_include(std::string path, std::string include_site) {
263     t_program* program = new t_program(path);
264 
265     // include prefix for this program is the site at which it was included
266     // (minus the filename)
267     std::string include_prefix;
268     std::string::size_type last_slash = std::string::npos;
269     if ((last_slash = include_site.rfind("/")) != std::string::npos) {
270       include_prefix = include_site.substr(0, last_slash);
271     }
272 
273     program->set_include_prefix(include_prefix);
274     includes_.push_back(program);
275   }
276 
set_include_prefix(std::string include_prefix)277   void set_include_prefix(std::string include_prefix) {
278     include_prefix_ = include_prefix;
279 
280     // this is intended to be a directory; add a trailing slash if necessary
281     std::string::size_type len = include_prefix_.size();
282     if (len > 0 && include_prefix_[len - 1] != '/') {
283       include_prefix_ += '/';
284     }
285   }
286 
287   // Language neutral namespace / packaging
set_namespace(std::string language,std::string name_space)288   void set_namespace(std::string language, std::string name_space) {
289     if (language != "*") {
290       size_t sub_index = language.find('.');
291       std::string base_language = language.substr(0, sub_index);
292 
293       if (base_language == "smalltalk") {
294         pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead");
295         base_language = "st";
296       }
297 
298       t_generator_registry::gen_map_t my_copy = t_generator_registry::get_generator_map();
299 
300       t_generator_registry::gen_map_t::iterator it;
301       it = my_copy.find(base_language);
302 
303       if (it == my_copy.end()) {
304         std::string warning = "No generator named '" + base_language + "' could be found!";
305         pwarning(1, warning.c_str());
306       } else {
307         if (sub_index != std::string::npos) {
308           std::string sub_namespace = language.substr(sub_index + 1);
309           if (!it->second->is_valid_namespace(sub_namespace)) {
310             std::string warning = base_language + " generator does not accept '" + sub_namespace
311                                   + "' as sub-namespace!";
312             pwarning(1, warning.c_str());
313           }
314         }
315       }
316     }
317 
318     namespaces_[language] = name_space;
319   }
320 
get_namespace(std::string language)321   std::string get_namespace(std::string language) const {
322     std::map<std::string, std::string>::const_iterator iter;
323     if ((iter = namespaces_.find(language)) != namespaces_.end()
324         || (iter = namespaces_.find("*")) != namespaces_.end()) {
325       return iter->second;
326     }
327     return std::string();
328   }
329 
get_all_namespaces()330   const std::map<std::string, std::string>& get_all_namespaces() const {
331      return namespaces_;
332   }
333 
set_namespace_annotations(std::string language,std::map<std::string,std::string> annotations)334   void set_namespace_annotations(std::string language, std::map<std::string, std::string> annotations) {
335     namespace_annotations_[language] = annotations;
336   }
337 
get_namespace_annotations(const std::string & language)338   const std::map<std::string, std::string>& get_namespace_annotations(const std::string& language) const {
339     auto it = namespace_annotations_.find(language);
340     if (namespace_annotations_.end() != it) {
341       return it->second;
342     }
343     static const std::map<std::string, std::string> emptyMap;
344     return emptyMap;
345   }
346 
get_namespace_annotations(const std::string & language)347   std::map<std::string, std::string>& get_namespace_annotations(const std::string& language) {
348     return namespace_annotations_[language];
349   }
350 
351   // Language specific namespace / packaging
352 
add_cpp_include(std::string path)353   void add_cpp_include(std::string path) { cpp_includes_.push_back(path); }
354 
get_cpp_includes()355   const std::vector<std::string>& get_cpp_includes() const { return cpp_includes_; }
356 
add_c_include(std::string path)357   void add_c_include(std::string path) { c_includes_.push_back(path); }
358 
get_c_includes()359   const std::vector<std::string>& get_c_includes() const { return c_includes_; }
360 
set_recursive(const bool recursive)361   void set_recursive(const bool recursive) { recursive_ = recursive; }
362 
get_recursive()363   bool get_recursive() const { return recursive_; }
364 
365 private:
366   // File path
367   std::string path_;
368 
369   // Name
370   std::string name_;
371 
372   // Output directory
373   std::string out_path_;
374 
375   // Output directory is absolute location for generated source (no gen-*)
376   bool out_path_is_absolute_;
377 
378   // Namespace
379   std::string namespace_;
380 
381   // Included programs
382   std::vector<t_program*> includes_;
383 
384   // Include prefix for this program, if any
385   std::string include_prefix_;
386 
387   // Identifier lookup scope
388   t_scope* scope_;
389 
390   // Components to generate code for
391   std::vector<t_typedef*> typedefs_;
392   std::vector<t_enum*> enums_;
393   std::vector<t_const*> consts_;
394   std::vector<t_struct*> objects_;
395   std::vector<t_struct*> structs_;
396   std::vector<t_struct*> xceptions_;
397   std::vector<t_service*> services_;
398 
399   // Dynamic namespaces
400   std::map<std::string, std::string> namespaces_;
401 
402   // Annotations for dynamic namespaces
403   std::map<std::string, std::map<std::string, std::string> > namespace_annotations_;
404 
405   // C++ extra includes
406   std::vector<std::string> cpp_includes_;
407 
408   // C extra includes
409   std::vector<std::string> c_includes_;
410 
411   // Recursive code generation
412   bool recursive_;
413 };
414 
415 #endif
416