text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements. See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership. The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License. You may obtain a copy of the License at
   *
   *   http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  #ifndef T_PROGRAM_H
  #define T_PROGRAM_H
  
  #include <map>
  #include <string>
  #include <vector>
  
  // For program_name()
  #include "main.h"
  
  #include "t_doc.h"
  #include "t_scope.h"
  #include "t_base_type.h"
  #include "t_typedef.h"
  #include "t_enum.h"
  #include "t_const.h"
  #include "t_struct.h"
  #include "t_service.h"
  #include "t_list.h"
  #include "t_map.h"
  #include "t_set.h"
  #include "generate/t_generator_registry.h"
  //#include "t_doc.h"
  
  /**
   * Top level class representing an entire thrift program. A program consists
   * fundamentally of the following:
   *
   *   Typedefs
   *   Enumerations
   *   Constants
   *   Structs
   *   Exceptions
   *   Services
   *
   * The program module also contains the definitions of the base types.
   *
   */
  class t_program : public t_doc {
   public:
    t_program(std::string path, std::string name) :
      path_(path),
      name_(name),
      out_path_("./"),
      out_path_is_absolute_(false) {
      scope_ = new t_scope();
    }
  
    t_program(std::string path) :
      path_(path),
      out_path_("./"),
      out_path_is_absolute_(false) {
      name_ = program_name(path);
      scope_ = new t_scope();
    }
  
    ~t_program()
    {
     if(scope_)
     {
       delete scope_;
       scope_ = NULL;
     }
    }
  
    // Path accessor
    const std::string& get_path() const { return path_; }
  
    // Output path accessor
    const std::string& get_out_path() const { return out_path_; }
  
    // Create gen-* dir accessor
    bool is_out_path_absolute() const { return out_path_is_absolute_; }
  
    // Name accessor
    const std::string& get_name() const { return name_; }
  
    // Namespace
    const std::string& get_namespace() const { return namespace_; }
  
    // Include prefix accessor
    const std::string& get_include_prefix() const { return include_prefix_; }
  
    // Accessors for program elements
    const std::vector<t_typedef*>& get_typedefs()  const { return typedefs_;  }
    const std::vector<t_enum*>&    get_enums()     const { return enums_;     }
    const std::vector<t_const*>&   get_consts()    const { return consts_;    }
    const std::vector<t_struct*>&  get_structs()   const { return structs_;   }
    const std::vector<t_struct*>&  get_xceptions() const { return xceptions_; }
    const std::vector<t_struct*>&  get_objects()   const { return objects_;   }
    const std::vector<t_service*>& get_services()  const { return services_;  }
  
    // Program elements
    void add_typedef  (t_typedef* td) { typedefs_.push_back(td);  }
    void add_enum     (t_enum*    te) { enums_.push_back(te);     }
    void add_const    (t_const*   tc) { consts_.push_back(tc);    }
    void add_struct   (t_struct*  ts) { objects_.push_back(ts);
                                        structs_.push_back(ts);   }
    void add_xception (t_struct*  tx) { objects_.push_back(tx);
                                        xceptions_.push_back(tx); }
    void add_service  (t_service* ts) { services_.push_back(ts);  }
  
    // Programs to include
    const std::vector<t_program*>& get_includes() const { return includes_; }
  
    void set_out_path(std::string out_path, bool out_path_is_absolute) {
      out_path_ = out_path;
      out_path_is_absolute_ = out_path_is_absolute;
      // Ensure that it ends with a trailing '/' (or '\' for windows machines)
      char c = out_path_.at(out_path_.size() - 1);
      if (!(c == '/' || c == '\\')) {
        out_path_.push_back('/');
      }
    }
  
    // Typename collision detection
    /**
     * Search for typename collisions
     * @param t    the type to test for collisions
     * @return     true if a certain collision was found, otherwise false
     */
    bool is_unique_typename(t_type * t) {
      int occurances = program_typename_count(this, t);
      for (std::vector<t_program*>::iterator it = includes_.begin();
           it != includes_.end(); ++it) {
        occurances += program_typename_count(*it, t);
      }
      return 0 == occurances;
    }
  
    /**
     * Search all type collections for duplicate typenames
     * @param prog the program to search
     * @param t    the type to test for collisions
     * @return     the number of certain typename collisions
     */
    int program_typename_count(t_program * prog, t_type * t) {
      int occurances = 0;
      occurances += collection_typename_count(prog, prog->typedefs_, t);
      occurances += collection_typename_count(prog, prog->enums_, t);
      occurances += collection_typename_count(prog, prog->objects_, t);
      occurances += collection_typename_count(prog, prog->services_, t);
      return occurances;
    }
  
    /**
     * Search a type collection for duplicate typenames
     * @param prog            the program to search
     * @param type_collection the type collection to search
     * @param t               the type to test for collisions
     * @return                the number of certain typename collisions
     */
    template <class T>
    int collection_typename_count(t_program * prog, T type_collection, t_type * t) {
      int occurances = 0;
      for (typename T::iterator it = type_collection.begin(); it != type_collection.end(); ++it)
        if (t != *it && 0 == t->get_name().compare((*it)->get_name()) && is_common_namespace(prog, t))
          ++occurances;
      return occurances;
    }
  
    /**
     * Determine whether identical typenames will collide based on namespaces.
     *
     * Because we do not know which languages the user will generate code for,
     * collisions within programs (IDL files) having namespace declarations can be
     * difficult to determine. Only guaranteed collisions return true (cause an error).
     * Possible collisions involving explicit namespace declarations produce a warning.
     * Other possible collisions go unreported.
     * @param prog the program containing the preexisting typename
     * @param t    the type containing the typename match
     * @return     true if a collision within namespaces is found, otherwise false
     */
    bool is_common_namespace(t_program * prog, t_type * t) {
      //Case 1: Typenames are in the same program [collision]
      if (prog == t->get_program()) {
        pwarning(1, "Duplicate typename %s found in %s",
                 t->get_name().c_str(), t->get_program()->get_name().c_str());
        return true;
      }
  
      //Case 2: Both programs have identical namespace scope/name declarations [collision]
      bool match = true;
      for (std::map<std::string, std::string>::iterator it = prog->namespaces_.begin();
           it != prog->namespaces_.end(); ++it) {
        if (0 == it->second.compare(t->get_program()->get_namespace(it->first))) {
          pwarning(1, "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
                   t->get_name().c_str(),
                   t->get_program()->get_name().c_str(), it->first.c_str(), it->second.c_str(),
                   prog->get_name().c_str(), it->first.c_str(), it->second.c_str());
        } else {
          match = false;
        }
      }
      for (std::map<std::string, std::string>::iterator it = t->get_program()->namespaces_.begin();
           it != t->get_program()->namespaces_.end(); ++it) {
        if (0 == it->second.compare(prog->get_namespace(it->first))) {
          pwarning(1, "Duplicate typename %s found in %s,%s,%s and %s,%s,%s [file,scope,ns]",
                   t->get_name().c_str(),
                   t->get_program()->get_name().c_str(), it->first.c_str(), it->second.c_str(),
                   prog->get_name().c_str(), it->first.c_str(), it->second.c_str());
        } else {
          match = false;
        }
      }
      if (0 == prog->namespaces_.size() && 0 == t->get_program()->namespaces_.size()) {
        pwarning(1, "Duplicate typename %s found in %s and %s",
          t->get_name().c_str(), t->get_program()->get_name().c_str(),  prog->get_name().c_str());
      }
      return match;
    }
  
    // Scoping and namespacing
    void set_namespace(std::string name) {
      namespace_ = name;
    }
  
    // Scope accessor
    t_scope* scope() const {
      return scope_;
    }
  
    // Includes
  
    void add_include(std::string path, std::string include_site) {
      t_program* program = new t_program(path);
  
      // include prefix for this program is the site at which it was included
      // (minus the filename)
      std::string include_prefix;
      std::string::size_type last_slash = std::string::npos;
      if ((last_slash = include_site.rfind("/")) != std::string::npos) {
        include_prefix = include_site.substr(0, last_slash);
      }
  
      program->set_include_prefix(include_prefix);
      includes_.push_back(program);
    }
  
    std::vector<t_program*>& get_includes() {
      return includes_;
    }
  
    void set_include_prefix(std::string include_prefix) {
      include_prefix_ = include_prefix;
  
      // this is intended to be a directory; add a trailing slash if necessary
      int len = include_prefix_.size();
      if (len > 0 && include_prefix_[len - 1] != '/') {
        include_prefix_ += '/';
      }
    }
  
    // Language neutral namespace / packaging
    void set_namespace(std::string language, std::string name_space) {
      if (language != "*") {
        size_t sub_index = language.find('.');
        std::string base_language = language.substr(0, sub_index);
        std::string sub_namespace;
  
        if(base_language == "smalltalk") {
          pwarning(1, "Namespace 'smalltalk' is deprecated. Use 'st' instead");
          base_language = "st";
        }
  
        t_generator_registry::gen_map_t my_copy = t_generator_registry::get_generator_map();
  
        t_generator_registry::gen_map_t::iterator it;
        it=my_copy.find(base_language);
  
        if (it == my_copy.end()) {
          std::string warning = "No generator named '" + base_language + "' could be found!";
          pwarning(1, warning.c_str());
        } else {
          if (sub_index != std::string::npos) {
            std::string sub_namespace = language.substr(sub_index+1);
            if ( ! it->second->is_valid_namespace(sub_namespace)) {
              std::string warning = base_language + " generator does not accept '" + sub_namespace + "' as sub-namespace!";
              pwarning(1, warning.c_str());
            }
          }
        }
      }
  
      namespaces_[language] = name_space;
    }
  
    std::string get_namespace(std::string language) const {
      std::map<std::string, std::string>::const_iterator iter;
      if ((iter = namespaces_.find(language)) != namespaces_.end() ||
          (iter = namespaces_.find("*"     )) != namespaces_.end()) {
        return iter->second;
      }
      return std::string();
    }
  
    // Language specific namespace / packaging
  
    void add_cpp_include(std::string path) {
      cpp_includes_.push_back(path);
    }
  
    const std::vector<std::string>& get_cpp_includes() {
      return cpp_includes_;
    }
  
    void add_c_include(std::string path) {
      c_includes_.push_back(path);
    }
  
    const std::vector<std::string>& get_c_includes() {
      return c_includes_;
    }
  
   private:
  
    // File path
    std::string path_;
  
    // Name
    std::string name_;
  
    // Output directory
    std::string out_path_;
  
    // Output directory is absolute location for generated source (no gen-*)
    bool out_path_is_absolute_;
  
    // Namespace
    std::string namespace_;
  
    // Included programs
    std::vector<t_program*> includes_;
  
    // Include prefix for this program, if any
    std::string include_prefix_;
  
    // Identifier lookup scope
    t_scope* scope_;
  
    // Components to generate code for
    std::vector<t_typedef*> typedefs_;
    std::vector<t_enum*>    enums_;
    std::vector<t_const*>   consts_;
    std::vector<t_struct*>  objects_;
    std::vector<t_struct*>  structs_;
    std::vector<t_struct*>  xceptions_;
    std::vector<t_service*> services_;
  
    // Dynamic namespaces
    std::map<std::string, std::string> namespaces_;
  
    // C++ extra includes
    std::vector<std::string> cpp_includes_;
  
    // C extra includes
    std::vector<std::string> c_includes_;
  
  };
  
  #endif
  