/**
  * Generates an Enum class for method params that have an allowed values list.
  *
  * @param resources
  * @param templateGroup
  */
 private void generateEnumForAllowedValues(
     List<Resource> resources, StringTemplateGroup templateGroup) {
   List<String> generatedEnums = new ArrayList<String>();
   StringTemplate template;
   String valuePrefix, valueSuffix = "";
   String enumName;
   for (Resource resource : resources) {
     if (resource.getEndPoints() != null) {
       for (Endpoint endpoint : resource.getEndPoints()) {
         if (endpoint.getOperations() != null) {
           for (EndpointOperation operation : endpoint.getOperations()) {
             // ResourceMethod method = operation.generateMethod(endpoint, resource, config);
             if (operation.getParameters() != null) {
               for (ModelField operationParam : operation.getParameters()) {
                 // skipping the case where there is just one item - TODO process case of
                 // allowableValue like '0 to 1000'
                 if (operationParam.getAllowableValues() != null
                     && operationParam
                         .getAllowableValues()
                         .getClass()
                         .isAssignableFrom(AllowableListValues.class)) {
                   if (!generatedEnums.contains(operationParam.getName())) {
                     // generate enum
                     template = templateGroup.getInstanceOf(ENUM_OBJECT_TEMPLATE);
                     List<String> imports = new ArrayList<String>();
                     imports.addAll(this.config.getDefaultModelImports());
                     enumName = this.getNameGenerator().getEnumName(operationParam.getName());
                     template.setAttribute("className", enumName);
                     template.setAttribute("description", operationParam.getDescription());
                     template.setAttribute(
                         "enumValueType",
                         this.getDataTypeMappingProvider()
                             .getClassType(operationParam.getDataType(), true));
                     for (String allowableValue :
                         ((AllowableListValues) operationParam.getAllowableValues()).getValues()) {
                       if (operationParam.getDataType().equalsIgnoreCase("string")) {
                         valuePrefix = valueSuffix = "\"";
                       } else {
                         valuePrefix = valueSuffix = "";
                       }
                       ;
                       String namePrefix = "";
                       if (isNameStartsWithInteger(allowableValue)
                           && !canEnumNameStartsWithNumber()) {
                         namePrefix = "ENUM_";
                       }
                       template.setAttribute(
                           "values.{name,value}",
                           namePrefix
                               + this.getNameGenerator()
                                   .applyClassNamingPolicy(allowableValue.replaceAll("-", "_")),
                           this.getNameGenerator()
                               .applyMethodNamingPolicy(
                                   valuePrefix.concat(allowableValue).concat(valueSuffix)));
                     }
                     template.setAttribute(PACKAGE_NAME, config.getModelPackageName());
                     File aFile =
                         new File(
                             languageConfig.getModelClassLocation()
                                 + enumName
                                 + languageConfig.getClassFileExtension());
                     writeFile(aFile, template.toString(), "Enum class");
                     generatedEnums.add(operationParam.getName());
                   }
                 }
               }
             }
           }
         }
       }
     }
   }
 }
  public ResourceMethod generateMethod(
      Endpoint endPoint,
      Resource resource,
      DataTypeMappingProvider dataTypeMapper,
      NamingPolicyProvider nameGenerator) {
    if (method == null) {
      method = new ResourceMethod();
      // add method description
      method.setTitle(this.getSummary());
      method.setDescription(this.getNotes());

      // add method name
      // get resource path for making web service call
      /**
       * Logic for method names 1. remove all path parameters 2. Remove format path parameter 3. For
       * POST add save 4. For PUT add update 5. For DELETE add delete 6. For GET add get 7.
       * Concatenate rest of the path with init caps 8.
       */
      String inputobjectName =
          nameGenerator.getInputObjectName(
              resource.generateClassName(nameGenerator), endPoint.getPath());

      String[] pathElements = endPoint.getPath().split("/");
      StringBuilder urlPath = new StringBuilder("");
      if (pathElements != null) {
        for (int i = 0; i < pathElements.length; i++) {
          String pathElement = pathElements[i];
          if (pathElement != null && pathElement.length() > 0) {
            int position = pathElement.indexOf("{");
            if (urlPath.length() > 0) {
              urlPath.append("+");
            }
            if (position < 0) {
              urlPath.append("\"/" + pathElement + "\"");
            } else if (position == 0) {
              urlPath.append("\"/\"+" + pathElement.substring(1, pathElement.length() - 1));
            } else {
              urlPath.append("\"/" + pathElement.replace("{format}", "json") + "\"");
            }
          }
        }
      }
      method.setResourcePath(endPoint.getPath());
      method.setName(nameGenerator.getMethodName(endPoint.getPath(), this.getNickname()));

      // create method argument
      /**
       * 1. API token need not be included as that is always added to the calls as HTTP headers 2.
       * We need to handle auth token specially, hence need to differentiate that 3. Query
       * parameters needs to be added as query string hence need to separate them out 4. Post
       * parameters are usually WordnikObjects, hence we need to handle them separately
       */
      List<String> argNames = new ArrayList<String>();
      if (this.getParameters() != null) {
        List<MethodArgument> arguments = new ArrayList<MethodArgument>();
        List<MethodArgument> queryParams = new ArrayList<MethodArgument>();
        List<MethodArgument> pathParams = new ArrayList<MethodArgument>();
        List<MethodArgument> headerParams = new ArrayList<MethodArgument>();

        method.setArguments(arguments);
        method.setQueryParameters(queryParams);
        method.setPathParameters(pathParams);
        method.setHeaderParameters(headerParams);

        for (ModelField modelField : this.getParameters()) {
          if (!argNames.contains(modelField.getName())) {
            argNames.add(modelField.getName());
            MethodArgument anArgument = new MethodArgument();
            anArgument.setAllowedValues(modelField.getAllowedValuesString());
            // check if arguments has auth token
            if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_HEADER)
                && modelField.getName().equals(AUTH_TOKEN_PARAM_NAME)) {
              method.setAuthToken(true);
              anArgument.setName(AUTH_TOKEN_ARGUMENT_NAME);
              anArgument.setDataType(
                  dataTypeMapper.getClassType(MethodArgument.ARGUMENT_STRING, true));
              anArgument.setDescription(modelField.getDescription());
              anArgument.setRequired(modelField.isRequired());
              anArgument.setDefaultValue(modelField.getDefaultValue());
              arguments.add(anArgument);
              headerParams.add(anArgument);
            } else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_HEADER)
                && modelField.getName().equals(API_KEY_PARAM_NAME)) {
              anArgument.setName(API_KEY_PARAM_NAME);
              anArgument.setDataType(
                  dataTypeMapper.getClassType(MethodArgument.ARGUMENT_STRING, true));
              anArgument.setRequired(true);
              arguments.add(anArgument);
              headerParams.add(anArgument);
            } else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_PATH)
                && !modelField.getName().equalsIgnoreCase(FORMAT_PARAM_NAME)) {
              anArgument.setName(modelField.getName());
              anArgument.setDataType(
                  dataTypeMapper.getClassType(MethodArgument.ARGUMENT_STRING, true));
              anArgument.setDescription(modelField.getDescription());
              anArgument.setRequired(true); // 	always true
              anArgument.setDefaultValue(modelField.getDefaultValue());
              arguments.add(anArgument);
              pathParams.add(anArgument);
            } else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_QUERY)) {
              anArgument.setName(modelField.getName());
              anArgument.setDataType(
                  dataTypeMapper.getClassType(MethodArgument.ARGUMENT_STRING, true));
              anArgument.setDescription(modelField.getDescription());
              anArgument.setRequired(modelField.isRequired());
              anArgument.setDefaultValue(modelField.getDefaultValue());
              queryParams.add(anArgument);
              arguments.add(anArgument);
            } else if (modelField.getParamType().equalsIgnoreCase(PARAM_TYPE_BODY)) {
              if (modelField.getName() == null) {
                modelField.setName(POST_PARAM_NAME);
              }
              anArgument.setName(modelField.getName());
              anArgument.setDataType(dataTypeMapper.getClassType(modelField.getDataType(), false));
              anArgument.setDescription(modelField.getDescription());
              anArgument.setRequired(modelField.isRequired());
              anArgument.setDefaultValue(modelField.getDefaultValue());
              arguments.add(anArgument);
              method.setPostObject(true);
            }

            if (modelField.isAllowMultiple()
                && dataTypeMapper.isPrimitiveType(modelField.getDataType())) {
              anArgument.setDataType(
                  dataTypeMapper.getListReturnTypeSignature(
                      dataTypeMapper.getClassType(modelField.getDataType(), false)));
            }
            anArgument.setInputModelClassArgument(inputobjectName, nameGenerator);
          }
        }
      }

      // check if input model with the given name is already generated for some other path
      boolean inputModelAlreadyGenerated = false;
      if (alreadyGeneratedModels.containsKey(inputobjectName)) {
        if (!alreadyGeneratedModels.get(inputobjectName).equals(endPoint.getPath())) {
          inputModelAlreadyGenerated = true;
        }
      }

      // check for number of arguments, if we have more than 4 then send the arguments as input
      // object
      if (method.getArguments() != null
          && method.getArguments().size() > ARG_COUNT_FOR_INPUT_MODEL
          && !inputModelAlreadyGenerated) {
        List<MethodArgument> arguments = new ArrayList<MethodArgument>();
        Model modelforMethodInput = new Model();
        modelforMethodInput.setName(inputobjectName);
        List<ModelField> fields = new ArrayList<ModelField>();
        for (MethodArgument argument : method.getArguments()) {
          if (!argument.getName().equals(POST_PARAM_NAME)) {
            ModelField aModelField = new ModelField();
            aModelField.setAllowedValues(argument.getAllowedValues());
            aModelField.setDescription(argument.getDescription());
            aModelField.setName(argument.getName());
            aModelField.setParamType(argument.getDataType());
            fields.add(aModelField);
          } else {
            arguments.add(argument);
          }
        }
        modelforMethodInput.setFields(fields);

        MethodArgument anArgument = new MethodArgument();
        anArgument.setDataType(inputobjectName);
        anArgument.setName(nameGenerator.applyMethodNamingPolicy(inputobjectName));
        arguments.add(anArgument);
        method.setArguments(arguments);
        method.setInputModel(modelforMethodInput);
        alreadyGeneratedModels.put(inputobjectName, endPoint.getPath());
      }

      List<String> argumentDefinitions = new ArrayList<String>();
      List<String> argumentNames = new ArrayList<String>();
      if (method.getArguments() != null && method.getArguments().size() > 0) {
        for (MethodArgument arg : method.getArguments()) {
          if (!arg.getName().equalsIgnoreCase(FORMAT_PARAM_NAME)) {
            argumentDefinitions.add(
                dataTypeMapper.getArgumentDefinition(arg.getDataType(), arg.getName()));
            argumentNames.add(arg.getName());
          }
        }
        method.setArgumentDefinitions(argumentDefinitions);
        method.setArgumentNames(argumentNames);
      }

      // get method type
      method.setMethodType(this.getHttpMethod());

      // get return value
      String returnType = dataTypeMapper.getClassType(responseClass, false);
      if ("".equals(returnType)) {
        method.setHasResponseValue(false);
      } else {
        method.setHasResponseValue(true);
      }
      // set the original response name, this is used in identifying if the respone is single valued
      // or multi valued
      method.setReturnValueFromOperationJson(responseClass);
      method.setReturnValue(dataTypeMapper.getClassType(responseClass, false));
      method.setReturnClassName(dataTypeMapper.getGenericType(responseClass));

      // if this is a list return type
      if (method
          .getReturnClassName()
          .equals(dataTypeMapper.getListReturnTypeSignature(responseClass))) {
        String returnValueTypeName = method.getReturnValue();
        Model listWrapperModel = new Model();
        listWrapperModel.setName(nameGenerator.getListWrapperName(returnValueTypeName));
        List<ModelField> fields = new ArrayList<ModelField>();
        ModelField aModelField = new ModelField();
        aModelField.setName(nameGenerator.applyMethodNamingPolicy(returnValueTypeName));
        aModelField.setParamType(responseClass);
        fields.add(aModelField);
        listWrapperModel.setFields(fields);
        method.setListWrapperModel(listWrapperModel);
      }

      // get description string for exception
      method.setExceptionDescription(calculateExceptionMessage());
    }
    return method;
  }