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) {} 62 t_program(std::string path)63 t_program(std::string path) : path_(path), out_path_("./"), out_path_is_absolute_(false) { 64 name_ = program_name(path); 65 scope_ = new t_scope(); 66 } 67 ~t_program()68 ~t_program() { 69 if (scope_) { 70 delete scope_; 71 scope_ = NULL; 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) { services_.push_back(ts); } 116 117 // Programs to include get_includes()118 const std::vector<t_program*>& get_includes() const { return includes_; } 119 set_out_path(std::string out_path,bool out_path_is_absolute)120 void set_out_path(std::string out_path, bool out_path_is_absolute) { 121 out_path_ = out_path; 122 out_path_is_absolute_ = out_path_is_absolute; 123 // Ensure that it ends with a trailing '/' (or '\' for windows machines) 124 char c = out_path_.at(out_path_.size() - 1); 125 if (!(c == '/' || c == '\\')) { 126 out_path_.push_back('/'); 127 } 128 } 129 130 // Typename collision detection 131 /** 132 * Search for typename collisions 133 * @param t the type to test for collisions 134 * @return true if a certain collision was found, otherwise false 135 */ is_unique_typename(t_type * t)136 bool is_unique_typename(t_type* t) { 137 int occurrences = program_typename_count(this, t); 138 for (std::vector<t_program*>::iterator it = includes_.begin(); it != includes_.end(); ++it) { 139 occurrences += program_typename_count(*it, t); 140 } 141 return 0 == occurrences; 142 } 143 144 /** 145 * Search all type collections for duplicate typenames 146 * @param prog the program to search 147 * @param t the type to test for collisions 148 * @return the number of certain typename collisions 149 */ program_typename_count(t_program * prog,t_type * t)150 int program_typename_count(t_program* prog, t_type* t) { 151 int occurrences = 0; 152 occurrences += collection_typename_count(prog, prog->typedefs_, t); 153 occurrences += collection_typename_count(prog, prog->enums_, t); 154 occurrences += collection_typename_count(prog, prog->objects_, t); 155 occurrences += collection_typename_count(prog, prog->services_, t); 156 return occurrences; 157 } 158 159 /** 160 * Search a type collection for duplicate typenames 161 * @param prog the program to search 162 * @param type_collection the type collection to search 163 * @param t the type to test for collisions 164 * @return the number of certain typename collisions 165 */ 166 template <class T> collection_typename_count(t_program * prog,T type_collection,t_type * t)167 int collection_typename_count(t_program* prog, T type_collection, t_type* t) { 168 int occurrences = 0; 169 for (typename T::iterator it = type_collection.begin(); it != type_collection.end(); ++it) 170 if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t)) 171 ++occurrences; 172 return occurrences; 173 } 174 175 /** 176 * Determine whether identical typenames will collide based on namespaces. 177 * 178 * Because we do not know which languages the user will generate code for, 179 * collisions within programs (IDL files) having namespace declarations can be 180 * difficult to determine. Only guaranteed collisions return true (cause an error). 181 * Possible collisions involving explicit namespace declarations produce a warning. 182 * Other possible collisions go unreported. 183 * @param prog the program containing the preexisting typename 184 * @param t the type containing the typename match 185 * @return true if a collision within namespaces is found, otherwise false 186 */ is_common_namespace(t_program * prog,t_type * t)187 bool is_common_namespace(t_program* prog, t_type* t) { 188 // Case 1: Typenames are in the same program [collision] 189 if (prog == t->get_program()) { 190 pwarning(1, 191 "Duplicate typename %s found in %s", 192 t->get_name().c_str(), 193 t->get_program()->get_name().c_str()); 194 return true; 195 } 196 197 // Case 2: Both programs have identical namespace scope/name declarations [collision] 198 bool match = true; 199 for (std::map<std::string, std::string>::iterator it = prog->namespaces_.begin(); 200 it != prog->namespaces_.end(); 201 ++it) { 202 if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) { 203 pwarning(1, 204 "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]", 205 t->get_name().c_str(), 206 t->get_program()->get_name().c_str(), 207 it->first.c_str(), 208 it->second.c_str(), 209 prog->get_name().c_str(), 210 it->first.c_str(), 211 it->second.c_str()); 212 } else { 213 match = false; 214 } 215 } 216 for (std::map<std::string, std::string>::iterator it = t->get_program()->namespaces_.begin(); 217 it != t->get_program()->namespaces_.end(); 218 ++it) { 219 if (0 == it->second.compare(prog->get_namespace(it->first))) { 220 pwarning(1, 221 "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]", 222 t->get_name().c_str(), 223 t->get_program()->get_name().c_str(), 224 it->first.c_str(), 225 it->second.c_str(), 226 prog->get_name().c_str(), 227 it->first.c_str(), 228 it->second.c_str()); 229 } else { 230 match = false; 231 } 232 } 233 if (0 == prog->namespaces_.size() && 0 == t->get_program()->namespaces_.size()) { 234 pwarning(1, 235 "Duplicate typename %s found in %s and %s", 236 t->get_name().c_str(), 237 t->get_program()->get_name().c_str(), 238 prog->get_name().c_str()); 239 } 240 return match; 241 } 242 243 // Scoping and namespacing set_namespace(std::string name)244 void set_namespace(std::string name) { namespace_ = name; } 245 246 // Scope accessor scope()247 t_scope* scope() const { return scope_; } 248 249 // Includes 250 add_include(t_program * program)251 void add_include(t_program* program) { 252 includes_.push_back(program); 253 } 254 add_include(std::string path,std::string include_site)255 void add_include(std::string path, std::string include_site) { 256 t_program* program = new t_program(path); 257 258 // include prefix for this program is the site at which it was included 259 // (minus the filename) 260 std::string include_prefix; 261 std::string::size_type last_slash = std::string::npos; 262 if ((last_slash = include_site.rfind("/")) != std::string::npos) { 263 include_prefix = include_site.substr(0, last_slash); 264 } 265 266 program->set_include_prefix(include_prefix); 267 includes_.push_back(program); 268 } 269 get_includes()270 std::vector<t_program*>& get_includes() { return includes_; } 271 set_include_prefix(std::string include_prefix)272 void set_include_prefix(std::string include_prefix) { 273 include_prefix_ = include_prefix; 274 275 // this is intended to be a directory; add a trailing slash if necessary 276 std::string::size_type len = include_prefix_.size(); 277 if (len > 0 && include_prefix_[len - 1] != '/') { 278 include_prefix_ += '/'; 279 } 280 } 281 282 // Language neutral namespace / packaging set_namespace(std::string language,std::string name_space)283 void set_namespace(std::string language, std::string name_space) { 284 if (language != "*") { 285 size_t sub_index = language.find('.'); 286 std::string base_language = language.substr(0, sub_index); 287 std::string sub_namespace; 288 289 if (base_language == "smalltalk") { 290 pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead"); 291 base_language = "st"; 292 } 293 294 t_generator_registry::gen_map_t my_copy = t_generator_registry::get_generator_map(); 295 296 t_generator_registry::gen_map_t::iterator it; 297 it = my_copy.find(base_language); 298 299 if (it == my_copy.end()) { 300 std::string warning = "No generator named '" + base_language + "' could be found!"; 301 pwarning(1, warning.c_str()); 302 } else { 303 if (sub_index != std::string::npos) { 304 std::string sub_namespace = language.substr(sub_index + 1); 305 if (!it->second->is_valid_namespace(sub_namespace)) { 306 std::string warning = base_language + " generator does not accept '" + sub_namespace 307 + "' as sub-namespace!"; 308 pwarning(1, warning.c_str()); 309 } 310 } 311 } 312 } 313 314 namespaces_[language] = name_space; 315 } 316 get_namespace(std::string language)317 std::string get_namespace(std::string language) const { 318 std::map<std::string, std::string>::const_iterator iter; 319 if ((iter = namespaces_.find(language)) != namespaces_.end() 320 || (iter = namespaces_.find("*")) != namespaces_.end()) { 321 return iter->second; 322 } 323 return std::string(); 324 } 325 get_all_namespaces()326 const std::map<std::string, std::string>& get_all_namespaces(){ 327 return namespaces_; 328 } 329 set_namespace_annotations(std::string language,std::map<std::string,std::string> annotations)330 void set_namespace_annotations(std::string language, std::map<std::string, std::string> annotations) { 331 namespace_annotations_[language] = annotations; 332 } 333 get_namespace_annotations(std::string language)334 const std::map<std::string, std::string>& get_namespace_annotations(std::string language) { 335 return namespace_annotations_[language]; 336 } 337 338 // Language specific namespace / packaging 339 add_cpp_include(std::string path)340 void add_cpp_include(std::string path) { cpp_includes_.push_back(path); } 341 get_cpp_includes()342 const std::vector<std::string>& get_cpp_includes() { return cpp_includes_; } 343 add_c_include(std::string path)344 void add_c_include(std::string path) { c_includes_.push_back(path); } 345 get_c_includes()346 const std::vector<std::string>& get_c_includes() { return c_includes_; } 347 348 private: 349 // File path 350 std::string path_; 351 352 // Name 353 std::string name_; 354 355 // Output directory 356 std::string out_path_; 357 358 // Output directory is absolute location for generated source (no gen-*) 359 bool out_path_is_absolute_; 360 361 // Namespace 362 std::string namespace_; 363 364 // Included programs 365 std::vector<t_program*> includes_; 366 367 // Include prefix for this program, if any 368 std::string include_prefix_; 369 370 // Identifier lookup scope 371 t_scope* scope_; 372 373 // Components to generate code for 374 std::vector<t_typedef*> typedefs_; 375 std::vector<t_enum*> enums_; 376 std::vector<t_const*> consts_; 377 std::vector<t_struct*> objects_; 378 std::vector<t_struct*> structs_; 379 std::vector<t_struct*> xceptions_; 380 std::vector<t_service*> services_; 381 382 // Dynamic namespaces 383 std::map<std::string, std::string> namespaces_; 384 385 // Annotations for dynamic namespaces 386 std::map<std::string, std::map<std::string, std::string> > namespace_annotations_; 387 388 // C++ extra includes 389 std::vector<std::string> cpp_includes_; 390 391 // C extra includes 392 std::vector<std::string> c_includes_; 393 }; 394 395 #endif 396