text: /*************************************************************************
   * libjson-rpc-cpp
   *************************************************************************
   * @file    jsclientstubgenerator.cpp
   * @date    10/22/2014
   * @author  Peter Spiess-Knafl <peter.knafl@gmail.com>
   * @license See attached LICENSE.txt
   ************************************************************************/
  
  #include "jsclientstubgenerator.h"
  #include <algorithm>
  
  using namespace jsonrpc;
  using namespace std;
  
  #define TEMPLATE_JS_PROLOG "function <class>(url) {\n\
      this.url = url;\n\
      var id = 1;\n\
      \n\
      function doJsonRpcRequest(method, params, methodCall, callback_success, callback_error) {\n\
          var request = {};\n\
          if (methodCall)\n\
              request.id = id++;\n\
          request.jsonrpc = \"2.0\";\n\
          request.method = method;\n\
          if (params !== null) {\n\
              request.params = params;\n\
          }\n\
          JSON.stringify(request);\n\
          \n\
          $.ajax({\n\
              type: \"POST\",\n\
              url: url,\n\
              data: JSON.stringify(request),\n\
              success: function (response) {\n\
                  if (methodCall) {\n\
                      if (response.hasOwnProperty(\"result\") && response.hasOwnProperty(\"id\")) {\n\
                          callback_success(response.id, response.result);\n\
                      } else if (response.hasOwnProperty(\"error\")) {\n\
                          if (callback_error != null)\n\
                              callback_error(response.error.code,response.error.message);\n\
                      } else {\n\
                          if (callback_error != null)\n\
                              callback_error(-32001, \"Invalid Server response: \" + response);\n\
                      }\n\
                  }\n\
              },\n\
              error: function () {\n\
                  if (methodCall)\n\
                      callback_error(-32002, \"AJAX Error\");\n\
              },\n\
              dataType: \"json\"\n\
          });\n\
          return id-1;\n\
      }\n\
      this.doRPC = function(method, params, methodCall, callback_success, callback_error) {\n\
          return doJsonRpcRequest(method, params, methodCall, callback_success, callback_error);\n\
      }\n\
  }\n"
  
  #define TEMPLATE_JS_METHOD "<class>.prototype.<procedure> = function(<params>callbackSuccess, callbackError) {"
  #define TEMPLATE_JS_PARAM_NAMED "var params = {<params>};"
  #define TEMPLATE_JS_PARAM_POSITIONAL "var params = [<params>];"
  #define TEMPLATE_JS_PARAM_EMPTY "var params = null;"
  #define TEMPLATE_JS_CALL_METHOD "return this.doRPC(\"<procedure>\", params, true, callbackSuccess, callbackError);"
  #define TEMPLATE_JS_CALL_NOTIFICATION "this.doRPC(\"<procedure>\", params, false, callbackSuccess, callbackError);"
  
  
  
  JSClientStubGenerator::JSClientStubGenerator(const string &stubname, std::vector<Procedure> &procedures, ostream &outputstream) :
      StubGenerator(stubname, procedures, outputstream)
  {
  }
  
  JSClientStubGenerator::JSClientStubGenerator(const string &stubname, std::vector<Procedure> &procedures, const string &filename) :
      StubGenerator(stubname, procedures, filename)
  {
  }
  
  string JSClientStubGenerator::class2Filename(const string &classname)
  {
      string result = classname;
      std::transform(result.begin(), result.end(), result.begin(), ::tolower);
      return result + ".js";
  }
  
  void JSClientStubGenerator::generateStub()
  {
      this->writeLine("/**");
      this->writeLine(" * This file is generated by jsonrpcstub, DO NOT CHANGE IT MANUALLY!");
      this->writeLine(" */");
      this->write(replaceAll(TEMPLATE_JS_PROLOG, "<class>", stubname));
      this->writeNewLine();
  
      for (unsigned int i=0; i < procedures.size(); i++)
      {
          this->generateMethod(procedures[i]);
      }
  
  }
  
  void JSClientStubGenerator::generateMethod(Procedure &proc)
  {
      string method = TEMPLATE_JS_METHOD;
      replaceAll2(method, "<class>", stubname);
      replaceAll2(method, "<procedure>", noramlizeJsLiteral(proc.GetProcedureName()));
  
      stringstream param_string;
      stringstream params_assignment;
  
      parameterNameList_t list = proc.GetParameters();
      for (parameterNameList_t::iterator it = list.begin(); it != list.end();)
      {
          param_string << it->first;
  
          if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME)
              params_assignment << it->first << " : " << it->first;
          else
              params_assignment << it->first;
  
          if (++it != list.end())
          {
                  params_assignment << ", ";
          }
          param_string << ", ";
      }
  
      replaceAll2(method, "<params>", param_string.str());
  
      this->writeLine(method);
      this->increaseIndentation();
  
      string params;
  
      if (proc.GetParameters().size() > 0)
      {
          if (proc.GetParameterDeclarationType() == PARAMS_BY_NAME)
              params = TEMPLATE_JS_PARAM_NAMED;
          else
              params = TEMPLATE_JS_PARAM_POSITIONAL;
  
          replaceAll2(params, "<params>", params_assignment.str());
          this->writeLine(params);
      }
      else
      {
          this->writeLine(TEMPLATE_JS_PARAM_EMPTY);
      }
  
  
      if (proc.GetProcedureType() == RPC_METHOD)
          method = TEMPLATE_JS_CALL_METHOD;
      else
          method = TEMPLATE_JS_CALL_NOTIFICATION;
  
      replaceAll2(method, "<procedure>", proc.GetProcedureName());
  
      this->writeLine(method);
  
      this->decreaseIndentation();
      this->writeLine("};");
  
  }
  
  string JSClientStubGenerator::noramlizeJsLiteral(const string &literal)
  {
      string result = literal;
      for(unsigned int i=0; i < literal.length(); i++)
      {
          if (!((literal[i] >= 'a' && literal[i] <= 'z') || (literal[i] >= 'A' && literal[i] <= 'Z') || (literal[i] >= '0' && literal[i] <= '9') || literal[i] == '_'))
          {
              result[i] = '_';
          }
      }
      return result;
  }
  