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::vector<std::string>> annotations)334 void set_namespace_annotations(std::string language, std::map<std::string, std::vector<std::string>> annotations) { 335 namespace_annotations_[language] = annotations; 336 } 337 get_namespace_annotations(const std::string & language)338 const std::map<std::string, std::vector<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::vector<std::string>> emptyMap; 344 return emptyMap; 345 } 346 get_namespace_annotations(const std::string & language)347 std::map<std::string, std::vector<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::vector<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